diff --git a/.github/scripts/build.sh b/.github/scripts/build.sh index 272a8327c35..24dc493fbd8 100755 --- a/.github/scripts/build.sh +++ b/.github/scripts/build.sh @@ -3,21 +3,11 @@ set -e echo "${VERSION}" -echo "Make" -make - -echo "Build" -make docker_build - if [[ -v PUSH_REGISTRY ]] then echo "Logging in to registry" docker login -u "${REGISTRY_USER}" -p "${REGISTRY_PASS}" "${DOCKER_REGISTRY}" - make TAG="${VERSION}" docker_tag docker_push + make buildpush +else + make IMAGE_PULL_POLICY=IfNotPresent buildpushkind fi - -echo "Push to registry" -make -j 4 docker_tag docker_push - -echo "Generate templates" -make templates diff --git a/.github/scripts/olm_test.sh b/.github/scripts/olm_test.sh index bb1a56adfd9..9a564c1fe3e 100755 --- a/.github/scripts/olm_test.sh +++ b/.github/scripts/olm_test.sh @@ -6,5 +6,5 @@ export TEMPLATES=${PWD}/templates/build/enmasse-${TAG} export OCP4_EXTERNAL_IMAGE_REGISTRY=$(docker inspect -f '{{range .NetworkSettings.Networks}}{{.IPAddress}}{{end}}' image-registry):5000 -echo "Running OLM tests" -time make TESTCASE=olm.** PROFILE=olm-pr systemtests +echo "OLM TESTS DISABLED" +#time make TESTCASE=olm.** PROFILE=olm-pr systemtests diff --git a/.github/scripts/publish_docs.sh b/.github/scripts/publish_docs.sh index 56bf7ffaa81..2a41de4298c 100755 --- a/.github/scripts/publish_docs.sh +++ b/.github/scripts/publish_docs.sh @@ -3,7 +3,7 @@ set -e DOCVERSION=master if [ "${RELEASE}" == "true" ]; then - DOCVERSION=${VERSION} + DOCVERSION=${TAG} fi echo "Publishing docs for version ${DOCVERSION}" diff --git a/.github/scripts/setup_kind.sh b/.github/scripts/setup_kind.sh new file mode 100755 index 00000000000..15e8c4ea748 --- /dev/null +++ b/.github/scripts/setup_kind.sh @@ -0,0 +1,26 @@ +#!/usr/bin/env bash +curl -Lo ./kind "https://kind.sigs.k8s.io/dl/v0.8.1/kind-$(uname)-amd64" +chmod +x ./kind + +cat < 2.10.4 ## 0.31.4 * #4704: [OpenShift 3.11] Console returns 500 internal error when configured with custom certificate diff --git a/DEVELOPMENT.md b/DEVELOPMENT.md index c19ab223010..9ad09decaf7 100644 --- a/DEVELOPMENT.md +++ b/DEVELOPMENT.md @@ -5,9 +5,8 @@ This document describes the development guidelines when writing and maintaining ## General considerations As EnMasse is a polyglot project with multiple languages in the same repo, it uses `make` as a -single entry point for invoking a full project build. Make will invoke the language-specific build single entry point for invoking a full project build. Make will invoke the language-specific build -system. +systems. ### Introducing new dependencies diff --git a/HACKING.md b/HACKING.md index d8c1969989a..a1a4baed9b2 100644 --- a/HACKING.md +++ b/HACKING.md @@ -21,23 +21,6 @@ The EnMasse java and node modules are built using maven. All docker images are b To run EnMasse you need a Kubernetes cluster. Most EnMasse developers use [OKD](https://www.okd.io/) for running tests on their machine. -## Checking out for Go - -If you want to work with the Go parts of this repository, you will need to perform the -following steps. - -### Create a Go workspace - -Create a new directory and set the `GOPATH` environment variable: - - export GOPATH=/home/user/my-enmasse-gobase - mkdir -p $GOPATH/src/github.com/enmasseproject - -### Clone the git repository into this workspace - - cd $GOPATH/src/github.com/enmasseproject - git clone https://github.com/enmasseproject/enmasse - ## Building ### Pre-installation @@ -85,20 +68,6 @@ external registries. kubectl config set-context $(kubectl config current-context) --namespace=enmasse-infra kubectl apply -f templates/build/enmasse-latest/install/bundles/enmasse - kubectl apply -f templates/build/enmasse-latest/install/components/example-plans - kubectl apply -f templates/build/enmasse-latest/install/components/example-authservices - -#### Deploying to an OKD instance assuming already logged in with cluster-admin permissions - - oc new-project enmasse-infra || oc project enmasse-infra - oc apply -f templates/build/enmasse-latest/install/bundles/enmasse - oc apply -f templates/build/enmasse-latest/install/components/example-plans - oc apply -f templates/build/enmasse-latest/install/components/example-authservices - - -#### Running smoketests against a deployed instance - - make PROFILE=smoke systemtests ### Running full systemtest suite @@ -176,17 +145,6 @@ Use this command to change environment variables values for the deployment Where $CMD is `oc` or `kubectl` command depends of the environment. -The following deployment names are available depending on their types and EnMasse configuration: - - * `address-space-controller` - * `admin` - * `keycloak-controller` - * `standard-controller` - * `service-broker` - * `topic-forwarder` - * `mqtt-gateway` - * `mqtt-lwt` - For forwarding port from the remote pod to the local host invoke following command (it will lock terminal) and then connect with development tool to the forwarded port on localhost @@ -232,18 +190,6 @@ container. value: -agentlib:jdwp=transport=dt_socket,server=y,suspend=n,address=5005 ``` -#### NodeJS components - -If you have a NodeJS component running in a pod that you wish to debug, temporarily edit the deployment and add the -NodeJS debug option `--inspect` to a `_NODE_OPTIONS` environment variable to the container. By default, NodeJS -will listen on port 9229. - -```yaml -- env: - - name: _NODE_OPTIONS - value: --inspect -``` - #### Port Forwarding On OpenShift, you can then issue a `oc port-forward :` command to conveniently route diff --git a/Makefile b/Makefile index 10478610e9a..20047ea144b 100644 --- a/Makefile +++ b/Makefile @@ -8,15 +8,6 @@ GO_DIRS = \ broker-plugin DOCKER_DIRS = \ - agent \ - topic-forwarder \ - address-space-controller \ - none-authservice \ - standard-controller \ - keycloak-plugin \ - mqtt-gateway \ - mqtt-lwt \ - service-broker \ console/console-init \ olm-manifest \ iot/iot-tenant-service \ @@ -96,13 +87,13 @@ coverage_go: buildpush: $(MAKE) $(MAKE) docker_build - $(MAKE) docker_tag - $(MAKE) docker_push + $(MAKE) -j 4 docker_tag + $(MAKE) -j 4 docker_push buildpushkind: $(MAKE) $(MAKE) docker_build - $(MAKE) docker_tag + $(MAKE) -j 4 docker_tag $(MAKE) kind_load_image clean_java: @@ -175,7 +166,7 @@ manifests: @echo "Skipping generating manifests from source" else manifests: controller-gen - $(CONTROLLER_GEN) crd paths=./pkg/apis/enmasse/v1beta2 output:dir=./templates/shared-infra + $(CONTROLLER_GEN) crd paths=./pkg/apis/enmasse/v1beta2 output:dir=./templates/crds endif #endregion diff --git a/Makefile.common b/Makefile.common index 8a058d682ef..2a560c21183 100644 --- a/Makefile.common +++ b/Makefile.common @@ -17,6 +17,14 @@ ifeq ($(MAVEN_BATCH),true) MAVEN_ARGS+=-B endif +ifneq (, $(shell which podman)) +QUARKUS_CONTAINER_RUNTIME=podman +else ifneq (, $(shell which docker)) +QUARKUS_CONTAINER_RUNTIME=docker +else +$(error "No container runtime found for native Quarkus build. You need either 'podman' or 'docker') +endif + all: init build test package init: diff --git a/Makefile.java.mk b/Makefile.java.mk index 08e2884a16b..18de3926d34 100644 --- a/Makefile.java.mk +++ b/Makefile.java.mk @@ -21,7 +21,7 @@ else endif package_java: - $(IMAGE_ENV) IMAGE_ENV="$(IMAGE_ENV)" mvn package -DskipTests $(MAVEN_ARGS) + $(IMAGE_ENV) IMAGE_ENV="$(IMAGE_ENV)" mvn package -DskipTests $(MAVEN_ARGS) $(MAVEN_PACKAGE_ARGS) package: package_java endif diff --git a/TESTING.md b/TESTING.md index c68830b3efd..47495cd7a1e 100644 --- a/TESTING.md +++ b/TESTING.md @@ -42,17 +42,11 @@ make PROFILE=${PROFILE} TESTCASE="**.SmokeTest" systemtests ``` Where $PROFILE can be: + * systemtests -* soak -* iot -* shared -* isolated -* shared-iot -* isolated-iot -* smoke -* smoke-iot +* acceptance * upgrade -* olm +* soak ## Running upgrade test diff --git a/address-space-controller/Dockerfile b/address-space-controller/Dockerfile deleted file mode 100644 index b4a2202fc5a..00000000000 --- a/address-space-controller/Dockerfile +++ /dev/null @@ -1,12 +0,0 @@ -FROM quay.io/enmasse/java-base:11-5 - -ARG version -ARG revision -ARG maven_version -ENV VERSION=${version} REVISION=${revision} MAVEN_VERSION=${maven_version} - -ADD target/address-space-controller-${maven_version}-dist.tar.gz / - -ENV LOG_LEVEL info - -CMD ["/opt/run-java/launch_java.sh", "-jar", "/opt/address-space-controller.jar"] diff --git a/address-space-controller/Makefile b/address-space-controller/Makefile deleted file mode 100644 index a930db3739b..00000000000 --- a/address-space-controller/Makefile +++ /dev/null @@ -1,4 +0,0 @@ -include ../Makefile.java.mk -TEMPLATE_DIR=$(shell mktemp -d) -run: - RESOURCES_DIR=target/classes VERSION=$(VERSION) TEMPLATE_DIR=$(TEMPLATE_DIR) NAMESPACE=$(NAMESPACE) java -jar target/address-space-controller-$(MAVEN_VERSION).jar diff --git a/address-space-controller/README.md b/address-space-controller/README.md deleted file mode 100644 index d403233179d..00000000000 --- a/address-space-controller/README.md +++ /dev/null @@ -1,7 +0,0 @@ -## Address Space Controller - -The Address Space Controller is a global multitenant components that implements an API for creating and deleting EnMasse infrastructure instances as well as deploying and modifying the address configuration per instance. It combines the address configuration with the flavor configuration to find the appropriate OpenShift template to use for a given address, and instantiates the template with parameters as specified by the flavor. - -# Build instructions - - make diff --git a/address-space-controller/pom.xml b/address-space-controller/pom.xml deleted file mode 100644 index c71f947fd31..00000000000 --- a/address-space-controller/pom.xml +++ /dev/null @@ -1,167 +0,0 @@ - - - - io.enmasse - enmasse - 0.32-SNAPSHOT - - 4.0.0 - address-space-controller - - - io.enmasse - metrics-api - compile - - - io.enmasse - api-model - - - io.enmasse - api-common - - - io.enmasse - keycloak-user-api - compile - - - org.jboss.resteasy - resteasy-client - compile - - - org.jboss.resteasy - resteasy-multipart-provider - compile - - - io.enmasse - k8s-api - compile - - - io.fabric8 - openshift-client - compile - - - org.slf4j - slf4j-api - compile - - - io.fabric8 - openshift-server-mock - test - - - ch.qos.logback - logback-classic - runtime - - - com.fasterxml.jackson.core - jackson-core - compile - - - com.fasterxml.jackson.core - jackson-databind - compile - - - org.jboss.resteasy - resteasy-jackson2-provider - compile - - - org.junit.jupiter - junit-jupiter-api - test - - - org.junit.jupiter - junit-jupiter-engine - test - - - org.mockito - mockito-core - test - - - io.enmasse - k8s-api-testutil - test - - - io.fabric8 - kubernetes-server-mock - test - - - - - - - src/main/resources - true - - - - - org.apache.maven.plugins - maven-shade-plugin - - - package - - shade - - - - - - io.enmasse.controller.AddressSpaceController - - - - - *:* - - META-INF/*.SF - META-INF/*.DSA - META-INF/*.RSA - - - - - - - - - org.apache.maven.plugins - maven-assembly-plugin - - - dist - package - - single - - - false - - src/assembly/unix-dist.xml - - posix - - - - - - - diff --git a/address-space-controller/src/assembly/unix-dist.xml b/address-space-controller/src/assembly/unix-dist.xml deleted file mode 100644 index debe929a582..00000000000 --- a/address-space-controller/src/assembly/unix-dist.xml +++ /dev/null @@ -1,50 +0,0 @@ - - - - - dist - - - tar.gz - zip - - False - - - ${project.basedir}/target/classes - /opt - - brokeredinfraconfigs/* - standardinfraconfigs/* - addressspaceplans/* - addressplans/* - templates/* - configmaps/* - - - - - - ${project.basedir}/target/address-space-controller-${project.version}.jar - /opt - address-space-controller.jar - 0644 - - - diff --git a/address-space-controller/src/main/java/io/enmasse/controller/AbstractFinalizerController.java b/address-space-controller/src/main/java/io/enmasse/controller/AbstractFinalizerController.java deleted file mode 100644 index cbf96b141cf..00000000000 --- a/address-space-controller/src/main/java/io/enmasse/controller/AbstractFinalizerController.java +++ /dev/null @@ -1,153 +0,0 @@ -/* - * Copyright 2019, EnMasse authors. - * License: Apache License 2.0 (see the file LICENSE or http://apache.org/licenses/LICENSE-2.0.html). - */ - -package io.enmasse.controller; - -import io.enmasse.address.model.Phase; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import io.enmasse.address.model.AddressSpace; -import io.enmasse.address.model.AddressSpaceBuilder; - -import java.util.Objects; - -/** - * Abstract base class for finalizer controllers. - */ -public abstract class AbstractFinalizerController implements Controller { - - private static final Logger log = LoggerFactory.getLogger(AbstractFinalizerController.class.getName()); - - protected static interface Result { - public AddressSpace getAddressSpace(); - - public boolean isFinalized(); - - public static Result waiting(final AddressSpace addressSpace) { - return new Result() { - @Override - public boolean isFinalized() { - return false; - } - - @Override - public AddressSpace getAddressSpace() { - return addressSpace; - } - }; - } - - public static Result completed(final AddressSpace addressSpace) { - return new Result() { - @Override - public boolean isFinalized() { - return true; - } - - @Override - public AddressSpace getAddressSpace() { - return addressSpace; - } - }; - } - } - - protected final String id; - - public AbstractFinalizerController(final String id) { - this.id = id; - } - - /** - * Process the finalizer logic. - * - * @param addressSpace The address space to work on. - * @return The result of the process. Must never return {@code null}. - */ - protected abstract Result processFinalizer(AddressSpace addressSpace); - - @Override - public ReconcileResult reconcileAnyState(final AddressSpace addressSpace) throws Exception { - - log.debug("Reconcile finalizer - id: {}, addressSpace: {}/{} -> {} ({})", - this.id, addressSpace.getMetadata().getNamespace(), addressSpace.getMetadata().getName(), addressSpace.getMetadata().getDeletionTimestamp(), - addressSpace.getMetadata().getFinalizers()); - - // if we are not deleted ... - if (!Controller.isDeleted(addressSpace)) { - // ... we ensure we are added to the list of finalizers. - AddressSpace modified = ensureFinalizer(addressSpace); - - // Persist finalizer state so that we are sure to be cleaned up if it is deleted - if (!Objects.equals(modified.getMetadata().getFinalizers(), addressSpace.getMetadata().getFinalizers())) { - return ReconcileResult.createRequeued(modified, true); - } else { - return ReconcileResult.create(modified); - } - } - - // if we are deleted, set phase to Terminating - addressSpace.getStatus().setPhase(Phase.Terminating); - - // if we are deleted, and no longer have the finalizer ... - if (!addressSpace.getMetadata().getFinalizers().contains(this.id)) { - // ... we have nothing to do. - return ReconcileResult.create(addressSpace); - } - - // process the finalizer - final Result result = processFinalizer(addressSpace); - - // if we finished finalizing ... - if (result == null || result.isFinalized()) { - // ... remove ourselves from the list. - return ReconcileResult.createRequeued(removeFinalizer(result == null ? addressSpace : result.getAddressSpace()), true); - } - - // we still need to wait and will try again. - return ReconcileResult.create(result.getAddressSpace()); - - } - - /** - * Remove the finalizer from the list. - * - * @param addressSpace The original address space. - * @return A modified copy of the original, not having the finalizer set. - */ - protected AddressSpace removeFinalizer(final AddressSpace addressSpace) { - - return new AddressSpaceBuilder(addressSpace) - .editOrNewMetadata() - .removeFromFinalizers(this.id) - .endMetadata() - .build(); - - } - - /** - * Ensure the finalizer is in the list. - * - * @param addressSpace The original address space. - * @return A modified copy of the original, having the finalizer set. - */ - protected AddressSpace ensureFinalizer(final AddressSpace addressSpace) { - - // if the finalizer list does contain our id ... - if (addressSpace.getMetadata().getFinalizers().contains(this.id)) { - // ... we are done. - return addressSpace; - } - - // ... otherwise we add ourselves to the list. - return new AddressSpaceBuilder(addressSpace) - .editOrNewMetadata() - .addNewFinalizer(this.id) - .endMetadata() - .build(); - - } -} diff --git a/address-space-controller/src/main/java/io/enmasse/controller/AddressFinalizerController.java b/address-space-controller/src/main/java/io/enmasse/controller/AddressFinalizerController.java deleted file mode 100644 index f738677990b..00000000000 --- a/address-space-controller/src/main/java/io/enmasse/controller/AddressFinalizerController.java +++ /dev/null @@ -1,101 +0,0 @@ -/* - * Copyright 2019, EnMasse authors. - * License: Apache License 2.0 (see the file LICENSE or http://apache.org/licenses/LICENSE-2.0.html). - */ - -package io.enmasse.controller; - -import io.fabric8.kubernetes.client.KubernetesClientException; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import io.enmasse.address.model.Address; -import io.enmasse.address.model.AddressSpace; -import io.enmasse.k8s.api.AddressApi; -import io.enmasse.k8s.api.AddressSpaceApi; -import io.enmasse.k8s.api.ContinuationResult; - -public class AddressFinalizerController extends AbstractFinalizerController { - - private static final Logger log = LoggerFactory.getLogger(AddressFinalizerController.class); - - public static final String FINALIZER_ADDRESSES = "enmasse.io/addresses"; - - private static final Integer BATCH_SIZE = Integer.getInteger("io.enmasse.controller.AddressFinalizerController.batchSize", 100); - - private AddressSpaceApi addressSpaceApi; - - public AddressFinalizerController(final AddressSpaceApi addressSpaceApi) { - super(FINALIZER_ADDRESSES); - this.addressSpaceApi = addressSpaceApi; - } - - @Override - public Result processFinalizer(final AddressSpace addressSpace) { - - log.info("Processing address finalizer for {}/{}", addressSpace.getMetadata().getNamespace(), addressSpace.getMetadata().getName()); - - final String addressSpaceNamespace = addressSpace.getMetadata().getNamespace(); - final AddressApi addressApi = addressSpaceApi.withAddressSpace(addressSpace); - - ContinuationResult
list = null; - do { - try { - list = addressApi.listAddresses(addressSpaceNamespace, BATCH_SIZE, list, null); - } catch (KubernetesClientException e) { - // If not found, the address CRD does not exist so we drop the finalizer - if (e.getCode() == 404) { - log.warn("Got 404 when listing addresses for {}/{}. Marking as finalized.", addressSpace.getMetadata().getNamespace(), addressSpace.getMetadata().getName(), e); - return Result.completed(addressSpace); - } else { - log.warn("Error finalizing {}/{}", addressSpace.getMetadata().getNamespace(), addressSpace.getMetadata().getName(), e); - return Result.waiting(addressSpace); - } - } - - log.debug("Processing addresses - found: {} -> continue: '{}'", list.getItems().size(), list.getContinuation()); - - for (final Address address : list.getItems()) { - processAddress(addressSpace, address); - } - - if (!list.canContinue()) { - log.debug("Completed cleanup"); - return Result.completed(addressSpace); - } - - } while (true); - - } - - /** - * Process a single address. - * - * @param addressSpace The address space to clean up. - * @param address The address to process. - */ - private void processAddress(final AddressSpace addressSpace, final Address address) { - - final AddressApi addressApi = this.addressSpaceApi.withAddressSpace(addressSpace); - - if (matchesAddressSpace(addressSpace.getMetadata().getName(), address)) { - addressApi.deleteAddress(address); - } - - } - - /** - * Check if the address is part of the address space, by matching the annotation. - * - * @param addressSpace The address space to test for. - * @param address The address to test. - * @return {@code true} if the name of the address starts with the addressSpace name plus the ".". - */ - private boolean matchesAddressSpace(final String addressSpace, final Address address) { - return address.getMetadata().getName().startsWith(addressSpace + "."); - } - - public String toString() { - return AddressFinalizerController.class.getSimpleName(); - } -} diff --git a/address-space-controller/src/main/java/io/enmasse/controller/AddressSpaceController.java b/address-space-controller/src/main/java/io/enmasse/controller/AddressSpaceController.java deleted file mode 100644 index 5fc64dc17a5..00000000000 --- a/address-space-controller/src/main/java/io/enmasse/controller/AddressSpaceController.java +++ /dev/null @@ -1,313 +0,0 @@ -/* - * Copyright 2016-2018, EnMasse authors. - * License: Apache License 2.0 (see the file LICENSE or http://apache.org/licenses/LICENSE-2.0.html). - */ - -package io.enmasse.controller; - -import io.enmasse.address.model.Address; -import io.enmasse.address.model.AddressSpace; -import io.enmasse.admin.model.v1.AuthenticationServiceType; -import io.enmasse.api.common.OpenShift; -import io.enmasse.config.LabelKeys; -import io.enmasse.controller.auth.AuthController; -import io.enmasse.controller.auth.CertBundleCertProvider; -import io.enmasse.controller.auth.CertManager; -import io.enmasse.controller.auth.CertProvider; -import io.enmasse.controller.auth.OpenSSLCertManager; -import io.enmasse.controller.auth.OpenshiftCertProvider; -import io.enmasse.controller.auth.SelfsignedCertProvider; -import io.enmasse.controller.auth.WildcardCertProvider; -import io.enmasse.controller.common.ControllerKind; -import io.enmasse.controller.common.ControllerReason; -import io.enmasse.controller.common.Kubernetes; -import io.enmasse.controller.common.KubernetesHelper; -import io.enmasse.controller.keycloak.RealmController; -import io.enmasse.k8s.api.AddressApi; -import io.enmasse.k8s.api.AddressSpaceApi; -import io.enmasse.k8s.api.AuthenticationServiceRegistry; -import io.enmasse.k8s.api.CachingSchemaProvider; -import io.enmasse.k8s.api.ConfigMapAddressSpaceApi; -import io.enmasse.k8s.api.EventLogger; -import io.enmasse.k8s.api.KubeAddressSpaceApi; -import io.enmasse.k8s.api.KubeEventLogger; -import io.enmasse.k8s.api.KubeResourceApplier; -import io.enmasse.k8s.api.KubeSchemaApi; -import io.enmasse.k8s.api.LogEventLogger; -import io.enmasse.k8s.api.SchemaAuthenticationServiceRegistry; -import io.enmasse.metrics.api.Metrics; -import io.enmasse.model.CustomResourceDefinitions; -import io.enmasse.user.keycloak.KeycloakFactory; -import io.enmasse.user.keycloak.KeycloakRealmApi; -import io.enmasse.user.keycloak.KubeKeycloakFactory; -import io.fabric8.kubernetes.api.model.ConfigMap; -import io.fabric8.kubernetes.client.Config; -import io.fabric8.kubernetes.client.ConfigBuilder; -import io.fabric8.kubernetes.client.DefaultKubernetesClient; -import io.fabric8.kubernetes.client.NamespacedKubernetesClient; -import io.fabric8.kubernetes.client.utils.HttpClientUtils; -import okhttp3.OkHttpClient; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import java.io.File; -import java.time.Clock; -import java.time.Duration; -import java.util.List; -import java.util.stream.Collectors; - -public class AddressSpaceController { - private static final Logger log = LoggerFactory.getLogger(AddressSpaceController.class.getName()); - private final NamespacedKubernetesClient controllerClient; - private final AddressSpaceControllerOptions options; - - static { - try { - CustomResourceDefinitions.registerAll(); - } catch (RuntimeException t) { - t.printStackTrace(); - throw new ExceptionInInitializerError(t); - } - } - - private HTTPServer metricsServer; - private ControllerChain controllerChain; - private RouterStatusCache routerStatusCache; - - private AddressSpaceController(AddressSpaceControllerOptions options) { - Config config = new ConfigBuilder().build(); - OkHttpClient httpClient = HttpClientUtils.createHttpClient(config); - httpClient = httpClient.newBuilder() - .connectTimeout(options.getKubernetesApiConnectTimeout()) - .writeTimeout(options.getKubernetesApiWriteTimeout()) - .readTimeout(options.getKubernetesApiReadTimeout()) - .build(); - this.controllerClient = new DefaultKubernetesClient(httpClient, config); - this.options = options; - } - - public void start() throws Exception { - boolean isOpenShift = OpenShift.isOpenShift(controllerClient); - KubeSchemaApi schemaApi = KubeSchemaApi.create(controllerClient, controllerClient.getNamespace(), options.getVersion(), isOpenShift, true); - - log.info("AddressSpaceController starting with options: {}", options); - if (options.isInstallDefaultResources()) { - configureDefaultResources(controllerClient, options.getResourcesDir()); - } - AddressSpaceSchemaUpdater addressSpaceSchemaUpdater = new AddressSpaceSchemaUpdater(controllerClient); - CachingSchemaProvider schemaProvider = new CachingSchemaProvider(); - schemaProvider.registerListener(addressSpaceSchemaUpdater); - - schemaApi.watchSchema(schemaProvider, options.getResyncInterval()); - Kubernetes kubernetes = new KubernetesHelper(controllerClient.getNamespace(), controllerClient, options.getTemplateDir(), isOpenShift); - - AddressSpaceApi addressSpaceApi = KubeAddressSpaceApi.create(controllerClient, null, options.getVersion()); - EventLogger eventLogger = options.isEnableEventLogger() ? new KubeEventLogger(controllerClient, controllerClient.getNamespace(), Clock.systemUTC(), "address-space-controller") - : new LogEventLogger(); - - // Convert all address spaces from configmaps to CRD variant - - CertManager certManager = OpenSSLCertManager.create(controllerClient); - CertProviderFactory certProviderFactory = createCertProviderFactory(options, certManager); - AuthController authController = new AuthController(certManager, eventLogger, certProviderFactory, options.isDisableExternalCertProvisioning()); - AuthenticationServiceRegistry authenticationServiceRegistry = new SchemaAuthenticationServiceRegistry(schemaProvider); - AuthenticationServiceResolver authenticationServiceResolver = new AuthenticationServiceResolver(authenticationServiceRegistry); - - InfraResourceFactory infraResourceFactory = new TemplateInfraResourceFactory(kubernetes, System.getenv(), schemaProvider); - - Clock clock = Clock.systemUTC(); - KeycloakFactory keycloakFactory = new KubeKeycloakFactory(controllerClient); - KeycloakRealmApi keycloakRealmApi = new KeycloakRealmApi(keycloakFactory, clock, Duration.ZERO); - schemaProvider.registerListener(newSchema -> keycloakRealmApi.retainAuthenticationServices(newSchema.findAuthenticationServiceType(AuthenticationServiceType.standard))); - - routerStatusCache = new RouterStatusCache(eventLogger, options.getRouterStatusCheckInterval(), controllerClient, controllerClient.getNamespace(), options.getManagementConnectTimeout(), options.getManagementQueryTimeout()); - routerStatusCache.start(); - - Metrics metrics = new Metrics(); - controllerChain = new ControllerChain(addressSpaceApi, schemaProvider, eventLogger, options.getRecheckInterval(), options.getResyncInterval()); - /* - * Add controllers managing a separate of the reconciliation. The order between some of these are important: - * - DefaultsController should always be first to ensure defaults are set - * - Finalizers should run next to ensure finalizers are added and persisted - * - StatusInitializer should then run to clear the status and messages before the remaining controllers run. - * - The remaining controllers can be added in any order - */ - controllerChain.addController(new DefaultsController(authenticationServiceRegistry, kubernetes)); - controllerChain.addController(new AddressFinalizerController(addressSpaceApi)); - controllerChain.addController(new MessagingUserFinalizerController(controllerClient)); - controllerChain.addController(new ComponentFinalizerController(kubernetes)); - controllerChain.addController(new RealmFinalizerController(keycloakRealmApi, authenticationServiceRegistry)); - controllerChain.addController(new StatusInitializer()); - controllerChain.addController(new CreateController(kubernetes, schemaProvider, infraResourceFactory, eventLogger, authController.getDefaultCertProvider(), options.getVersion(), addressSpaceApi, authenticationServiceResolver)); - controllerChain.addController(new RouterConfigController(controllerClient, controllerClient.getNamespace(), authenticationServiceResolver, routerStatusCache)); - controllerChain.addController(new PodDisruptionBudgetController(controllerClient, controllerClient.getNamespace())); - controllerChain.addController(new RealmController(keycloakRealmApi, authenticationServiceRegistry)); - controllerChain.addController(new NetworkPolicyController(controllerClient)); - controllerChain.addController(new StatusController(kubernetes, schemaProvider, infraResourceFactory, authenticationServiceRegistry, keycloakRealmApi, routerStatusCache)); - controllerChain.addController(routerStatusCache); - controllerChain.addController(new EndpointController(controllerClient, options.isExposeEndpointsByDefault(), isOpenShift)); - controllerChain.addController(new ExportsController(controllerClient)); - controllerChain.addController(authController); - controllerChain.addController(new MetricsReporterController(metrics, options.getVersion(), kubernetes)); - - metricsServer = new HTTPServer(8080, metrics); - metricsServer.start(); - - convertLegacyConfigMaps(controllerClient, options, addressSpaceApi, eventLogger); - - controllerChain.start(); - } - - private static void convertLegacyConfigMaps(NamespacedKubernetesClient controllerClient, AddressSpaceControllerOptions options, AddressSpaceApi addressSpaceApi, EventLogger eventLogger) throws Exception { - ConfigMapAddressSpaceApi legacyAddressSpaceApi = new ConfigMapAddressSpaceApi(controllerClient, options.getVersion()); - - for (AddressSpace legacy : legacyAddressSpaceApi.listAllAddressSpaces()) { - String globalName = legacy.getMetadata().getNamespace() + "." + legacy.getMetadata().getName(); - try { - legacy.getMetadata().setResourceVersion(null); - addressSpaceApi.createAddressSpace(legacy); - - // Should not fail, but if it does it will be handled below - AddressSpace created = addressSpaceApi.getAddressSpaceWithName(legacy.getMetadata().getNamespace(), legacy.getMetadata().getName()).get(); - - AddressApi addressApi = addressSpaceApi.withAddressSpace(created); - AddressApi legacyAddressApi = legacyAddressSpaceApi.withAddressSpace(legacy); - List
legacyAddresses = legacyAddressApi.listAddresses(legacy.getMetadata().getNamespace()).stream().filter(address -> address.getMetadata().getName().startsWith(legacy.getMetadata().getName())).collect(Collectors.toList()); - for (Address address : legacyAddresses) { - address.getMetadata().setResourceVersion(null); - addressApi.createAddress(address); - log.info("Converted address {} in {} from ConfigMap to Address CRD", address.getMetadata().getName(), address.getMetadata().getNamespace()); - } - String message = String.format("Converted address space %s in %s from ConfigMap to AddressSpace CRD. %d addresses converted", legacy.getMetadata().getName(), legacy.getMetadata().getNamespace(), legacyAddresses.size()); - eventLogger.log(ControllerReason.AddressSpaceConverted, message, EventLogger.Type.Normal, ControllerKind.AddressSpace, globalName); - } catch (Exception e) { - eventLogger.log(ControllerReason.AddressSpaceConversionFailed, String.format("Error converting address space %s: %s", legacy.getMetadata().getName(), e.getMessage()), EventLogger.Type.Warning, ControllerKind.AddressSpace, globalName); - throw e; - } - } - - try { - log.info("Deleting all Address ConfigMaps"); - controllerClient.configMaps().inNamespace(controllerClient.getNamespace()).withLabel(LabelKeys.TYPE, "address-config").delete(); - log.info("Deleting all AddressSpace ConfigMaps"); - controllerClient.configMaps().inNamespace(controllerClient.getNamespace()).withLabel(LabelKeys.TYPE, "address-space").delete(); - log.info("Conversion completed"); - } catch (Exception e) { - log.warn("Error deleting legacy ConfigMaps"); - throw e; - } - } - - private void stop() { - try { - log.info("AddressSpaceController stopping"); - - if (metricsServer != null) { - metricsServer.stop(); - } - - } finally { - try { - if (controllerChain != null) { - try { - controllerChain.stop(); - } catch (Exception ignore) { - } - } - } finally { - controllerClient.close(); - try { - if (routerStatusCache != null) { - try { - routerStatusCache.stop(); - } catch (Exception ignore) { - } - } - } finally { - log.info("AddressSpaceController stopped"); - } - } - } - } - - private void configureDefaultResources(NamespacedKubernetesClient client, File resourcesDir) { - String namespace = client.getNamespace(); - - KubeResourceApplier.applyIfDifferent(new File(resourcesDir, "configmaps"), - client.configMaps().inNamespace(namespace), - ConfigMap.class, - (c1, c2) -> c1.getData().equals(c2.getData()) ? 0 : -1); - } - - private CertProviderFactory createCertProviderFactory(AddressSpaceControllerOptions options, CertManager certManager) { - return new CertProviderFactory() { - @Override - public CertProvider createProvider(String provider) { - if ("wildcard".equals(provider)) { - String secretName = options.getWildcardCertSecret(); - return new WildcardCertProvider(controllerClient, secretName); - } else if ("openshift".equals(provider)) { - return new OpenshiftCertProvider(controllerClient); - } else if ("certBundle".equals(provider)) { - return new CertBundleCertProvider(controllerClient); - } else { - return new SelfsignedCertProvider(controllerClient, certManager); - } - } - - @Override - public String getDefaultProviderName() { - if (options.getWildcardCertSecret() != null) { - return "wildcard"; - } else { - return "selfsigned"; - } - } - }; - } - - public static void main(String args[]) { - setUncaughtExceptionHandler(); - - AddressSpaceController controller = null; - try { - final AddressSpaceControllerOptions options = AddressSpaceControllerOptions.fromEnv(System.getenv()); - log.info("AddressSpaceController starting with options: {}", options); - controller = new AddressSpaceController(options); - controller.start(); - } catch (IllegalArgumentException e) { - System.out.println(String.format("Unable to parse arguments: %s", e.getMessage())); - System.exit(1); - } catch (Exception e) { - System.out.println("Error starting address space controller: " + e.getMessage()); - System.exit(1); - } finally { - if (controller != null) { - Runtime.getRuntime().addShutdownHook(new Thread(controller::stop)); - } - } - } - - protected static void setUncaughtExceptionHandler() { - Thread.setDefaultUncaughtExceptionHandler((t, e) -> { - try { - System.err.println("########################################################################"); - System.err.println("#"); - System.err.print("# Uncaught Exception "); - System.err.print(e.toString()); - System.err.print(" in Thread "); - System.err.println(t.getName()); - System.err.println("#"); - System.err.println("#"); - System.err.println("########################################################################"); - e.printStackTrace(System.err); - - log.error("Fatal, uncaught exception", e); - - } finally { - Runtime.getRuntime().halt(1); - } - }); - } - -} diff --git a/address-space-controller/src/main/java/io/enmasse/controller/AddressSpaceControllerOptions.java b/address-space-controller/src/main/java/io/enmasse/controller/AddressSpaceControllerOptions.java deleted file mode 100644 index 0801487f145..00000000000 --- a/address-space-controller/src/main/java/io/enmasse/controller/AddressSpaceControllerOptions.java +++ /dev/null @@ -1,273 +0,0 @@ -/* - * Copyright 2016-2018, EnMasse authors. - * License: Apache License 2.0 (see the file LICENSE or http://apache.org/licenses/LICENSE-2.0.html). - */ - -package io.enmasse.controller; - -import java.io.File; -import java.io.IOException; -import java.time.Duration; -import java.util.Map; -import java.util.Optional; - -public final class AddressSpaceControllerOptions { - - private File templateDir; - private File resourcesDir; - private boolean enableEventLogger; - private boolean exposeEndpointsByDefault; - private boolean installDefaultResources; - - private String environment; - - private String wildcardCertSecret; - private boolean disableExternalCertProvisioning; - - private Duration resyncInterval; - private Duration recheckInterval; - - private Duration routerStatusCheckInterval; - - private String version; - - private Duration managementQueryTimeout; - private Duration managementConnectTimeout; - - private Duration kubernetesApiConnectTimeout; - private Duration kubernetesApiReadTimeout; - private Duration kubernetesApiWriteTimeout; - - public File getTemplateDir() { - return templateDir; - } - - public boolean isEnableEventLogger() { - return enableEventLogger; - } - - public String getEnvironment() { - return environment; - } - - public String getWildcardCertSecret() { - return wildcardCertSecret; - } - - public Duration getResyncInterval() { - return resyncInterval; - } - - public Duration getRecheckInterval() { - return recheckInterval; - } - - public String getVersion() { - return version; - } - - public static AddressSpaceControllerOptions fromEnv(Map env) throws IOException { - - AddressSpaceControllerOptions options = new AddressSpaceControllerOptions(); - - File templateDir = new File(getEnvOrThrow(env, "TEMPLATE_DIR")); - if (!templateDir.exists()) { - throw new IllegalArgumentException("Template directory " + templateDir.getAbsolutePath() + " not found"); - } - options.setTemplateDir(templateDir); - - File resourcesDir = new File(getEnvOrThrow(env, "RESOURCES_DIR")); - if (!resourcesDir.exists()) { - throw new IllegalArgumentException("Resources directory " + resourcesDir.getAbsolutePath() + " not found"); - } - options.setResourcesDir(resourcesDir); - - options.setEnableEventLogger(getEnv(env, "ENABLE_EVENT_LOGGER").map(Boolean::parseBoolean).orElse(false)); - - options.setExposeEndpointsByDefault(getEnv(env, "EXPOSE_ENDPOINTS_BY_DEFAULT").map(Boolean::parseBoolean).orElse(true)); - - options.setEnvironment(getEnv(env, "ENVIRONMENT").orElse("development")); - - options.setInstallDefaultResources(getEnv(env, "INSTALL_DEFAULT_RESOURCES").map(Boolean::parseBoolean).orElse(true)); - - options.setWildcardCertSecret(getEnv(env, "WILDCARD_ENDPOINT_CERT_SECRET").orElse(null)); - - options.setDisableExternalCertProvisioning(getEnv(env, "DISABLE_EXTERNAL_CERT_PROVISIONING").map(Boolean::parseBoolean).orElse(false)); - - options.setResyncInterval(getEnv(env, "RESYNC_INTERVAL") - .map(i -> Duration.ofSeconds(Long.parseLong(i))) - .orElse(Duration.ofMinutes(5))); - - options.setRecheckInterval(getEnv(env, "CHECK_INTERVAL") - .map(i -> Duration.ofSeconds(Long.parseLong(i))) - .orElse(Duration.ofSeconds(30))); - - options.setManagementQueryTimeout(getEnv(env, "MANAGEMENT_QUERY_TIMEOUT") - .map(i -> Duration.ofSeconds(Long.parseLong(i))) - .orElse(Duration.ofSeconds(60))); - - options.setManagementConnectTimeout(getEnv(env, "MANAGEMENT_CONNECT_TIMEOUT") - .map(i -> Duration.ofSeconds(Long.parseLong(i))) - .orElse(Duration.ofSeconds(30))); - - options.setKubernetesApiConnectTimeout(getEnv(env, "KUBERNETES_API_CONNECT_TIMEOUT") - .map(i -> Duration.ofSeconds(Long.parseLong(i))) - .orElse(Duration.ofSeconds(30))); - - options.setKubernetesApiReadTimeout(getEnv(env, "KUBERNETES_API_READ_TIMEOUT") - .map(i -> Duration.ofSeconds(Long.parseLong(i))) - .orElse(Duration.ofSeconds(30))); - - options.setKubernetesApiWriteTimeout(getEnv(env, "KUBERNETES_API_WRITE_TIMEOUT") - .map(i -> Duration.ofSeconds(Long.parseLong(i))) - .orElse(Duration.ofSeconds(30))); - - options.setRouterStatusCheckInterval(getEnv(env, "ROUTER_STATUS_CHECK_INTERVAL") - .map(i -> Duration.ofSeconds(Long.parseLong(i))) - .orElse(Duration.ofSeconds(30))); - - options.setVersion(getEnvOrThrow(env, "VERSION")); - return options; - } - - private static Optional getEnv(Map env, String envVar) { - return Optional.ofNullable(env.get(envVar)); - } - - private static String getEnvOrThrow(Map env, String envVar) { - String var = env.get(envVar); - if (var == null) { - throw new IllegalArgumentException(String.format("Unable to find value for required environment var '%s'", envVar)); - } - return var; - } - - public boolean isExposeEndpointsByDefault() { - return exposeEndpointsByDefault; - } - - public void setTemplateDir(File templateDir) { - this.templateDir = templateDir; - } - - public void setEnableEventLogger(boolean enableEventLogger) { - this.enableEventLogger = enableEventLogger; - } - - public void setExposeEndpointsByDefault(boolean exposeEndpointsByDefault) { - this.exposeEndpointsByDefault = exposeEndpointsByDefault; - } - - public void setEnvironment(String environment) { - this.environment = environment; - } - - public void setWildcardCertSecret(String wildcardCertSecret) { - this.wildcardCertSecret = wildcardCertSecret; - } - - public void setResyncInterval(Duration resyncInterval) { - this.resyncInterval = resyncInterval; - } - - public void setRecheckInterval(Duration recheckInterval) { - this.recheckInterval = recheckInterval; - } - - public void setVersion(String version) { - this.version = version; - } - - public File getResourcesDir() { - return resourcesDir; - } - - public void setResourcesDir(File resourcesDir) { - this.resourcesDir = resourcesDir; - } - - @Override - public String toString() { - return "AddressSpaceControllerOptions{" + - "templateDir=" + templateDir + - ", resourcesDir=" + resourcesDir + - ", enableEventLogger=" + enableEventLogger + - ", exposeEndpointsByDefault=" + exposeEndpointsByDefault + - ", environment='" + environment + '\'' + - ", wildcardCertSecret='" + wildcardCertSecret + '\'' + - ", resyncInterval=" + resyncInterval + - ", recheckInterval=" + recheckInterval + - ", version='" + version + '\'' + - ", managementQueryTimeout='" + managementQueryTimeout + '\'' + - ", managementConnectTimeout='" + managementConnectTimeout + '\'' + - ", kubernetesApiConnectTimeout='" + kubernetesApiConnectTimeout + '\'' + - ", kubernetesApiReadTimeout='" + kubernetesApiReadTimeout + '\'' + - ", kubernetesApiWriteTimeout='" + kubernetesApiWriteTimeout + '\'' + - ", disableCertProvisioning='" + disableExternalCertProvisioning + '\'' + - '}'; - } - - public boolean isInstallDefaultResources() { - return installDefaultResources; - } - - public void setInstallDefaultResources(boolean installDefaultResources) { - this.installDefaultResources = installDefaultResources; - } - - public Duration getManagementQueryTimeout() { - return managementQueryTimeout; - } - - public void setManagementQueryTimeout(Duration managementQueryTimeout) { - this.managementQueryTimeout = managementQueryTimeout; - } - - public Duration getManagementConnectTimeout() { - return managementConnectTimeout; - } - - public void setManagementConnectTimeout(Duration managementConnectTimeout) { - this.managementConnectTimeout = managementConnectTimeout; - } - - public Duration getKubernetesApiConnectTimeout() { - return kubernetesApiConnectTimeout; - } - - public void setKubernetesApiConnectTimeout(Duration kubernetesApiConnectTimeout) { - this.kubernetesApiConnectTimeout = kubernetesApiConnectTimeout; - } - - public Duration getKubernetesApiReadTimeout() { - return kubernetesApiReadTimeout; - } - - public void setKubernetesApiReadTimeout(Duration kubernetesApiReadTimeout) { - this.kubernetesApiReadTimeout = kubernetesApiReadTimeout; - } - - public Duration getKubernetesApiWriteTimeout() { - return kubernetesApiWriteTimeout; - } - - public void setKubernetesApiWriteTimeout(Duration kubernetesApiWriteTimeout) { - this.kubernetesApiWriteTimeout = kubernetesApiWriteTimeout; - } - - public Duration getRouterStatusCheckInterval() { - return routerStatusCheckInterval; - } - - public void setRouterStatusCheckInterval(Duration routerStatusCheckInterval) { - this.routerStatusCheckInterval = routerStatusCheckInterval; - } - - public boolean isDisableExternalCertProvisioning() { - return disableExternalCertProvisioning; - } - - public void setDisableExternalCertProvisioning(boolean disableExternalCertProvisioning) { - this.disableExternalCertProvisioning = disableExternalCertProvisioning; - } -} diff --git a/address-space-controller/src/main/java/io/enmasse/controller/AddressSpaceSchemaUpdater.java b/address-space-controller/src/main/java/io/enmasse/controller/AddressSpaceSchemaUpdater.java deleted file mode 100644 index 5897fd74a97..00000000000 --- a/address-space-controller/src/main/java/io/enmasse/controller/AddressSpaceSchemaUpdater.java +++ /dev/null @@ -1,49 +0,0 @@ -/* - * Copyright 2019, EnMasse authors. - * License: Apache License 2.0 (see the file LICENSE or http://apache.org/licenses/LICENSE-2.0.html). - */ -package io.enmasse.controller; - -import io.enmasse.address.model.AddressSpaceSchema; -import io.enmasse.address.model.AddressSpaceSchemaList; -import io.enmasse.address.model.AddressSpaceType; -import io.enmasse.address.model.CoreCrd; -import io.enmasse.address.model.DoneableAddressSpaceSchema; -import io.enmasse.address.model.Schema; -import io.enmasse.k8s.api.SchemaListener; -import io.fabric8.kubernetes.client.KubernetesClient; -import io.fabric8.kubernetes.client.KubernetesClientException; -import io.fabric8.kubernetes.client.dsl.MixedOperation; -import io.fabric8.kubernetes.client.dsl.Resource; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -public class AddressSpaceSchemaUpdater implements SchemaListener { - private static final Logger log = LoggerFactory.getLogger(AddressSpaceSchemaUpdater.class); - - private final MixedOperation> client; - public AddressSpaceSchemaUpdater(KubernetesClient kubernetesClient) { - this.client = kubernetesClient.customResources(CoreCrd.addresseSpaceSchemas(), AddressSpaceSchema.class, AddressSpaceSchemaList.class, DoneableAddressSpaceSchema.class); - } - - @Override - public void onUpdate(Schema newSchema) { - for (AddressSpaceType type : newSchema.getAddressSpaceTypes()) { - try { - AddressSpaceSchema existing = client.withName(type.getName()).get(); - AddressSpaceSchema updated = AddressSpaceSchema.fromAddressSpaceType(type, newSchema.getAuthenticationServices()); - if (existing == null) { - client.create(updated); - } else if (!updated.getSpec().equals(existing.getSpec())) { - client.createOrReplace(updated); - } - } catch (Exception e) { - if (e instanceof KubernetesClientException) { - log.warn("Error updating schema for address space type '" + type.getName() + "'. Status: '" + ((KubernetesClientException) e).getStatus() + "'. Code: " + ((KubernetesClientException) e).getCode(), e); - } else { - log.warn("Error updating schema for address space type '" + type.getName() + "'", e); - } - } - } - } -} diff --git a/address-space-controller/src/main/java/io/enmasse/controller/AppliedConfig.java b/address-space-controller/src/main/java/io/enmasse/controller/AppliedConfig.java deleted file mode 100644 index 1f310eadd39..00000000000 --- a/address-space-controller/src/main/java/io/enmasse/controller/AppliedConfig.java +++ /dev/null @@ -1,91 +0,0 @@ -/* - * Copyright 2017-2020, EnMasse authors. - * License: Apache License 2.0 (see the file LICENSE or http://apache.org/licenses/LICENSE-2.0.html). - */ - -package io.enmasse.controller; - -import java.io.IOException; -import java.util.HashMap; -import java.util.Objects; - -import com.fasterxml.jackson.core.JsonProcessingException; -import com.fasterxml.jackson.databind.ObjectMapper; - -import io.enmasse.address.model.AddressSpaceSpec; -import io.enmasse.address.model.AuthenticationServiceSettings; -import io.enmasse.config.AnnotationKeys; -import io.fabric8.kubernetes.api.model.HasMetadata; - -public final class AppliedConfig { - private static final ObjectMapper mapper = new ObjectMapper(); - - private AuthenticationServiceSettings authenticationServiceSettings; - private AddressSpaceSpec addressSpaceSpec; - - public static AppliedConfig parseCurrentAppliedConfig(String json) throws IOException { - if (json == null) { - return null; - } - return mapper.readValue(json, AppliedConfig.class); - } - - public static AppliedConfig parseCurrentAppliedConfig(final HasMetadata resource) throws IOException { - return parseCurrentAppliedConfig(resource.getMetadata().getAnnotations().get(AnnotationKeys.APPLIED_CONFIGURATION)); - } - - public static void setCurrentAppliedConfig(final HasMetadata metadata, AppliedConfig config) throws JsonProcessingException { - if (metadata.getMetadata().getAnnotations() == null) { - metadata.getMetadata().setAnnotations(new HashMap<>()); - } - // Remove old annotation no longer used - metadata.getMetadata().getAnnotations().remove(AnnotationKeys.APPLIED_PLAN); - - metadata.getMetadata().getAnnotations().put(AnnotationKeys.APPLIED_CONFIGURATION, mapper.writeValueAsString(config)); - } - - public static T normalize(Class clazz, final T data) throws JsonProcessingException { - if ( data == null ) { - return null; - } else { - return mapper.readValue(mapper.writeValueAsString(data), clazz); - } - } - - public static AppliedConfig create(AddressSpaceSpec spec, AuthenticationServiceSettings authServiceSettings) throws JsonProcessingException { - // First normalize the values to your JSON convention. - // If we don't do this, then a serialized object might not be equal to an in-memory object - final AddressSpaceSpec normalizedSpec = normalize(AddressSpaceSpec.class, spec); - final AuthenticationServiceSettings normalizedAuth= normalize(AuthenticationServiceSettings.class, authServiceSettings); - - // create the new instance - final AppliedConfig config = new AppliedConfig(); - config.addressSpaceSpec = normalizedSpec; - config.authenticationServiceSettings = normalizedAuth; - - // and return it - return config; - } - - public AuthenticationServiceSettings getAuthenticationServiceSettings() { - return authenticationServiceSettings; - } - - public AddressSpaceSpec getAddressSpaceSpec() { - return addressSpaceSpec; - } - - @Override - public boolean equals(Object o) { - if (this == o) return true; - if (o == null || getClass() != o.getClass()) return false; - AppliedConfig that = (AppliedConfig) o; - return Objects.equals(authenticationServiceSettings, that.authenticationServiceSettings) && - Objects.equals(addressSpaceSpec, that.addressSpaceSpec); - } - - @Override - public int hashCode() { - return Objects.hash(authenticationServiceSettings, addressSpaceSpec); - } -} diff --git a/address-space-controller/src/main/java/io/enmasse/controller/AuthenticationServiceResolver.java b/address-space-controller/src/main/java/io/enmasse/controller/AuthenticationServiceResolver.java deleted file mode 100644 index 96b12dd46fb..00000000000 --- a/address-space-controller/src/main/java/io/enmasse/controller/AuthenticationServiceResolver.java +++ /dev/null @@ -1,60 +0,0 @@ -/* - * Copyright 2019, EnMasse authors. - * License: Apache License 2.0 (see the file LICENSE or http://apache.org/licenses/LICENSE-2.0.html). - */ -package io.enmasse.controller; - -import io.enmasse.address.model.AddressSpace; -import io.enmasse.address.model.AuthenticationServiceSettings; -import io.enmasse.address.model.AuthenticationServiceSettingsBuilder; -import io.enmasse.admin.model.v1.AuthenticationService; -import io.enmasse.admin.model.v1.AuthenticationServiceType; -import io.enmasse.config.AnnotationKeys; -import io.enmasse.k8s.api.AuthenticationServiceRegistry; - -public class AuthenticationServiceResolver { - private final AuthenticationServiceRegistry registry; - - AuthenticationServiceResolver(AuthenticationServiceRegistry registry) { - this.registry = registry; - } - - AuthenticationServiceSettings resolve(AddressSpace addressSpace) { - AuthenticationService authService = registry.findAuthenticationService(addressSpace.getSpec().getAuthenticationService()) - .orElseThrow(() -> new IllegalArgumentException("Unable to find authentication service " + addressSpace.getSpec().getAuthenticationService())); - - if (authService.getStatus() == null) { - throw new IllegalArgumentException("Authentication service '" + authService.getMetadata().getName() + "' is not yet deployed"); - } - - AuthenticationServiceSettingsBuilder settingsBuilder = new AuthenticationServiceSettingsBuilder(); - settingsBuilder.withHost(authService.getStatus().getHost()); - settingsBuilder.withPort(authService.getStatus().getPort()); - settingsBuilder.withRealm(authService.getSpec().getRealm() != null ? authService.getSpec().getRealm() : addressSpace.getAnnotation(AnnotationKeys.REALM_NAME)); - settingsBuilder.withCaCertSecret(authService.getStatus().getCaCertSecret()); - settingsBuilder.withClientCertSecret(authService.getStatus().getClientCertSecret()); - - if (authService.getSpec().getType().equals(AuthenticationServiceType.external) && authService.getSpec().getExternal() != null && authService.getSpec().getExternal().isAllowOverride()) { - if (addressSpace.getSpec().getAuthenticationService().getOverrides() != null) { - AuthenticationServiceSettings overrides = addressSpace.getSpec().getAuthenticationService().getOverrides(); - if (overrides.getHost() != null) { - settingsBuilder.withHost(overrides.getHost()); - } - if (overrides.getPort() != null) { - settingsBuilder.withPort(overrides.getPort()); - } - if (overrides.getRealm() != null) { - settingsBuilder.withRealm(overrides.getRealm()); - } - if (overrides.getCaCertSecret() != null) { - settingsBuilder.withCaCertSecret(overrides.getCaCertSecret()); - } - if (overrides.getClientCertSecret() != null) { - settingsBuilder.withClientCertSecret(overrides.getClientCertSecret()); - } - } - } - - return settingsBuilder.build(); - } -} diff --git a/address-space-controller/src/main/java/io/enmasse/controller/CertProviderFactory.java b/address-space-controller/src/main/java/io/enmasse/controller/CertProviderFactory.java deleted file mode 100644 index ff317ea520f..00000000000 --- a/address-space-controller/src/main/java/io/enmasse/controller/CertProviderFactory.java +++ /dev/null @@ -1,12 +0,0 @@ -/* - * Copyright 2018, EnMasse authors. - * License: Apache License 2.0 (see the file LICENSE or http://apache.org/licenses/LICENSE-2.0.html). - */ -package io.enmasse.controller; - -import io.enmasse.controller.auth.CertProvider; - -public interface CertProviderFactory { - CertProvider createProvider(String providerName); - String getDefaultProviderName(); -} diff --git a/address-space-controller/src/main/java/io/enmasse/controller/ComponentFinalizerController.java b/address-space-controller/src/main/java/io/enmasse/controller/ComponentFinalizerController.java deleted file mode 100644 index ada096e5ee3..00000000000 --- a/address-space-controller/src/main/java/io/enmasse/controller/ComponentFinalizerController.java +++ /dev/null @@ -1,43 +0,0 @@ -/* - * Copyright 2019, EnMasse authors. - * License: Apache License 2.0 (see the file LICENSE or http://apache.org/licenses/LICENSE-2.0.html). - */ -package io.enmasse.controller; - -import io.enmasse.address.model.AddressSpace; -import io.enmasse.config.AnnotationKeys; -import io.enmasse.controller.common.Kubernetes; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -public class ComponentFinalizerController extends AbstractFinalizerController { - private static final Logger log = LoggerFactory.getLogger(AddressFinalizerController.class); - private final Kubernetes kubernetes; - public static final String FINALIZER_COMPONENTS = "enmasse.io/components"; - - public ComponentFinalizerController(Kubernetes kubernetes) { - super(FINALIZER_COMPONENTS); - this.kubernetes = kubernetes; - } - - @Override - public String toString() { - return "ComponentFinalizerController"; - } - - @Override - protected Result processFinalizer(AddressSpace addressSpace) { - log.info("Processing component finalizer for {}/{}", addressSpace.getMetadata().getNamespace(), addressSpace.getMetadata().getName()); - - final String infraUuid = addressSpace.getAnnotation(AnnotationKeys.INFRA_UUID); - if (infraUuid != null) { - try { - kubernetes.deleteResources(infraUuid); - } catch (Exception e) { - log.warn("Error finalizing {}/{}", addressSpace.getMetadata().getNamespace(), addressSpace.getMetadata().getName(), e); - return Result.waiting(addressSpace); - } - } - return Result.completed(addressSpace); - } -} diff --git a/address-space-controller/src/main/java/io/enmasse/controller/Controller.java b/address-space-controller/src/main/java/io/enmasse/controller/Controller.java deleted file mode 100644 index 090c43cf1d1..00000000000 --- a/address-space-controller/src/main/java/io/enmasse/controller/Controller.java +++ /dev/null @@ -1,67 +0,0 @@ -/* - * Copyright 2018-2019, EnMasse authors. - * License: Apache License 2.0 (see the file LICENSE or http://apache.org/licenses/LICENSE-2.0.html). - */ -package io.enmasse.controller; - -import io.enmasse.address.model.AddressSpace; - -import java.util.List; - -public interface Controller { - default AddressSpace reconcileActive(AddressSpace addressSpace) throws Exception { - return addressSpace; - } - - default ReconcileResult reconcileAnyState(AddressSpace addressSpace) throws Exception { - if (!Controller.isDeleted(addressSpace)) { - return ReconcileResult.create(reconcileActive(addressSpace)); - } else { - return ReconcileResult.create(addressSpace); - } - } - - default void reconcileAll(List addressSpaces) throws Exception {} - - /** - * Test if an address space is in the process of being deleted. - * - * @param addressSpace The address space to test. - * @return {@code true} if the address considered being deleted, {@code false} otherwise. - */ - static boolean isDeleted(final AddressSpace addressSpace) { - return addressSpace.getMetadata().getDeletionTimestamp() != null - && !addressSpace.getMetadata().getDeletionTimestamp().isBlank(); - } - - class ReconcileResult { - private final AddressSpace addressSpace; - private final boolean persistAndRequeue; - - private ReconcileResult(AddressSpace addressSpace, boolean persistAndRequeue) { - this.addressSpace = addressSpace; - this.persistAndRequeue = persistAndRequeue; - } - - public AddressSpace getAddressSpace() { - return addressSpace; - } - - /** - * Signal if the AddressSpace object return in this result should be persisted. It is assumed that the - * AddressSpace object contains the modifications that should be persisted. - */ - public boolean isPersistAndRequeue() { - return persistAndRequeue; - } - - public static ReconcileResult create(AddressSpace addressSpace) { - return new ReconcileResult(addressSpace, false); - } - - public static ReconcileResult createRequeued(AddressSpace addressSpace, boolean requeue) { - return new ReconcileResult(addressSpace, requeue); - } - - } -} diff --git a/address-space-controller/src/main/java/io/enmasse/controller/ControllerChain.java b/address-space-controller/src/main/java/io/enmasse/controller/ControllerChain.java deleted file mode 100644 index d56617a3c09..00000000000 --- a/address-space-controller/src/main/java/io/enmasse/controller/ControllerChain.java +++ /dev/null @@ -1,172 +0,0 @@ -/* - * Copyright 2017-2018, EnMasse authors. - * License: Apache License 2.0 (see the file LICENSE or http://apache.org/licenses/LICENSE-2.0.html). - */ -package io.enmasse.controller; - -import static io.enmasse.controller.common.ControllerReason.AddressSpaceSyncFailed; -import static io.enmasse.k8s.api.EventLogger.Type.Warning; - -import java.time.Duration; -import java.util.ArrayList; -import java.util.List; -import java.util.stream.Collectors; - -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import io.enmasse.address.model.AddressSpace; -import io.enmasse.address.model.AddressSpaceBuilder; -import io.enmasse.controller.common.ControllerKind; -import io.enmasse.k8s.api.AddressSpaceApi; -import io.enmasse.k8s.api.EventLogger; -import io.enmasse.k8s.api.ResourceChecker; -import io.enmasse.k8s.api.SchemaProvider; -import io.enmasse.k8s.api.Watch; -import io.enmasse.k8s.api.Watcher; -import io.fabric8.kubernetes.client.KubernetesClientException; - -/** - * The main controller loop that monitors k8s address spaces - */ -public class ControllerChain implements Watcher { - private static final Logger log = LoggerFactory.getLogger(ControllerChain.class.getName()); - - private final AddressSpaceApi addressSpaceApi; - - private Watch watch; - - private final List chain = new ArrayList<>(); - private final SchemaProvider schemaProvider; - private final EventLogger eventLogger; - private final Duration recheckInterval; - private final Duration resyncInterval; - private ResourceChecker checker; - - public ControllerChain(AddressSpaceApi addressSpaceApi, - SchemaProvider schemaProvider, - EventLogger eventLogger, - Duration recheckInterval, - Duration resyncInterval) { - this.addressSpaceApi = addressSpaceApi; - this.schemaProvider = schemaProvider; - this.eventLogger = eventLogger; - this.recheckInterval = recheckInterval; - this.resyncInterval = resyncInterval; - } - - public void addController(Controller controller) { - chain.add(controller); - } - - public void start() throws Exception { - checker = new ResourceChecker<>(this, recheckInterval); - checker.start(); - this.watch = addressSpaceApi.watchAddressSpaces(checker, resyncInterval); - } - - public void stop() throws Exception { - if (watch != null) { - watch.close(); - watch = null; - } - if (checker != null) { - checker.stop(); - checker = null; - } - } - - @Override - public void onUpdate(List resources) throws Exception { - log.info("Check address spaces: {}", resources.stream().map(a -> a.getMetadata().getNamespace()+":"+a.getMetadata().getName()).collect(Collectors.toSet())); - - if (schemaProvider.getSchema() == null) { - log.info("No schema available"); - return; - } - - List updatedResources = new ArrayList<>(); - - - boolean requeue; - do { - requeue = false; - for (final AddressSpace original : resources) { - - AddressSpace addressSpace = new AddressSpaceBuilder(original).build(); - final String addressSpaceName = addressSpace.getMetadata().getNamespace() + ":" + addressSpace.getMetadata().getName(); - - try { - - log.debug("Controller chain input: {}", original); - - log.info("Checking address space {}", addressSpaceName); - for (Controller controller : chain) { - log.info("Controller {}", controller); - log.debug("Address space input: {}", addressSpace); - Controller.ReconcileResult result = controller.reconcileAnyState(addressSpace); - addressSpace = result.getAddressSpace(); - - // If instructed to requeue, break loop and let the comparison of original vs current - // determine if we need to persist anything. Signal that the reconcile loop should run - // again by setting requeue. This will refresh the current list of address spaces and rerun the - // reconcile loop with persisted finalizers. - if (result.isPersistAndRequeue()) { - requeue = true; - break; - } - } - - log.debug("Controller chain output: {}", addressSpace); - - if (hasAddressSpaceChanged(original, addressSpace)) { - log.debug("Change detected. Executing update."); - if (!this.addressSpaceApi.replaceAddressSpace(addressSpace)) { - log.info("Unable to persist address space state: {}", addressSpaceName); - } - } else { - log.debug("No change detected. Not triggering update."); - } - } catch (KubernetesClientException e) { - log.warn("Error syncing address space {}", addressSpace.getMetadata().getName(), e); - eventLogger.log(AddressSpaceSyncFailed, "Error syncing address space: " + e.getMessage(), Warning, ControllerKind.AddressSpace, addressSpace.getMetadata().getName()); - } catch (Exception e) { - log.warn("Error processing address space {}", addressSpace.getMetadata().getName(), e); - } finally { - updatedResources.add(addressSpace); - } - } - - for (Controller controller : chain) { - try { - controller.reconcileAll(updatedResources); - } catch (Exception e) { - log.warn("Exception in {} reconcileAll", controller, e); - } - } - if (requeue) { - resources = new ArrayList<>(addressSpaceApi.listAllAddressSpaces()); - } - } while(requeue); - } - - static boolean hasAddressSpaceChanged(AddressSpace original, AddressSpace addressSpace) { - - boolean changed = false; - - if (!original.getMetadata().equals(addressSpace.getMetadata())) { - log.debug("Meta changed from {} to {}", original.getMetadata(), addressSpace.getMetadata()); - changed = true; - } - if (!original.getSpec().equals(addressSpace.getSpec())) { - log.debug("Spec changed from {} to {}", original.getSpec(), addressSpace.getSpec()); - changed = true; - } - if (!original.getStatus().equals(addressSpace.getStatus())) { - log.debug("Status changed from {} to {}", original.getStatus(), addressSpace.getStatus()); - changed = true; - } - - return changed; - } -} diff --git a/address-space-controller/src/main/java/io/enmasse/controller/CreateController.java b/address-space-controller/src/main/java/io/enmasse/controller/CreateController.java deleted file mode 100644 index 12fdffc3124..00000000000 --- a/address-space-controller/src/main/java/io/enmasse/controller/CreateController.java +++ /dev/null @@ -1,276 +0,0 @@ -/* - * Copyright 2017-2018, EnMasse authors. - * License: Apache License 2.0 (see the file LICENSE or http://apache.org/licenses/LICENSE-2.0.html). - */ -package io.enmasse.controller; - -import com.fasterxml.jackson.core.JsonProcessingException; -import io.enmasse.address.model.*; -import io.enmasse.admin.model.AddressPlan; -import io.enmasse.admin.model.AddressSpacePlan; -import io.enmasse.admin.model.v1.InfraConfig; -import io.enmasse.config.AnnotationKeys; -import io.enmasse.controller.common.ControllerKind; -import io.enmasse.controller.common.Kubernetes; -import io.enmasse.k8s.api.AddressApi; -import io.enmasse.k8s.api.AddressSpaceApi; -import io.enmasse.k8s.api.EventLogger; -import io.enmasse.k8s.api.SchemaProvider; -import io.fabric8.kubernetes.api.model.HasMetadata; -import io.fabric8.kubernetes.api.model.KubernetesList; -import io.fabric8.kubernetes.api.model.KubernetesListBuilder; -import io.fabric8.kubernetes.api.model.Service; -import io.fabric8.kubernetes.api.model.apps.StatefulSet; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import java.util.*; -import java.util.stream.Collectors; - -import static io.enmasse.controller.common.ControllerReason.AddressSpaceChanged; -import static io.enmasse.controller.common.ControllerReason.AddressSpaceCreated; -import static io.enmasse.controller.common.ControllerReason.AddressSpaceUpgraded; -import static io.enmasse.k8s.api.EventLogger.Type.Normal; - -public class CreateController implements Controller { - private static final Logger log = LoggerFactory.getLogger(CreateController.class.getName()); - - private final Kubernetes kubernetes; - private final SchemaProvider schemaProvider; - private final InfraResourceFactory infraResourceFactory; - private final EventLogger eventLogger; - private final String defaultCertProvider; - private final String version; - private final AddressSpaceApi addressSpaceApi; - private final AuthenticationServiceResolver authenticationServiceResolver; - - public CreateController(Kubernetes kubernetes, SchemaProvider schemaProvider, InfraResourceFactory infraResourceFactory, EventLogger eventLogger, String defaultCertProvider, String version, AddressSpaceApi addressSpaceApi, AuthenticationServiceResolver authenticationServiceResolver) { - this.kubernetes = kubernetes; - this.schemaProvider = schemaProvider; - this.infraResourceFactory = infraResourceFactory; - this.eventLogger = eventLogger; - this.defaultCertProvider = defaultCertProvider; - this.version = version; - this.addressSpaceApi = addressSpaceApi; - this.authenticationServiceResolver = authenticationServiceResolver; - } - - private String getAnnotation(Map annotations, String key, String defaultValue) { - return Optional.ofNullable(annotations) - .flatMap(m -> Optional.ofNullable(m.get(key))) - .orElse(defaultValue); - } - - private List validateEndpoints(AddressSpaceResolver addressSpaceResolver, AddressSpace addressSpace) { - // Set default endpoints from type - AddressSpaceType addressSpaceType = addressSpaceResolver.getType(addressSpace.getSpec().getType()); - AddressSpacePlan addressSpacePlan = addressSpaceResolver.getPlan(addressSpaceType, addressSpace.getSpec().getPlan()); - InfraConfig infraConfig = addressSpaceType.findInfraConfig(addressSpacePlan.getInfraConfigRef()).orElse(null); - List defaultEndpoints = new ArrayList<>(addressSpaceType.getAvailableEndpoints()); - - Map infraAnnotations = infraConfig != null ? infraConfig.getMetadata().getAnnotations() : Collections.emptyMap(); - if (!Boolean.parseBoolean(getAnnotation(infraAnnotations, AnnotationKeys.WITH_MQTT, "false"))) { - defaultEndpoints.removeIf(spec -> "mqtt".equals(spec.getService())); - } - - if (addressSpace.getSpec().getEndpoints().isEmpty()) { - return defaultEndpoints; - } else { - // Validate endpoints; - List endpoints = new ArrayList<>(addressSpace.getSpec().getEndpoints()); - Set services = defaultEndpoints.stream() - .map(EndpointSpec::getService) - .collect(Collectors.toSet()); - Set actualServices = endpoints.stream() - .map(EndpointSpec::getService) - .collect(Collectors.toSet()); - - services.removeAll(actualServices); - - // Add default endpoints not specified by user - for (String service : services) { - for (EndpointSpec endpointSpec : defaultEndpoints) { - if (service.equals(endpointSpec.getService())) { - endpoints.add(endpointSpec); - } - } - } - return endpoints; - } - } - - @Override - public AddressSpace reconcileActive(AddressSpace addressSpace) throws Exception { - Schema schema = schemaProvider.getSchema(); - AddressSpaceResolver addressSpaceResolver = new AddressSpaceResolver(schema); - AppliedConfig appliedConfig = kubernetes.getAppliedConfig(addressSpace); - if (!addressSpaceResolver.validate(addressSpace, Optional.ofNullable(appliedConfig).map(AppliedConfig::getAddressSpaceSpec).orElse(null))) { - return addressSpace; - } - - List endpoints = validateEndpoints(addressSpaceResolver, addressSpace); - - // Ensure the required certs are set - List newEndpoints = new ArrayList<>(); - for (EndpointSpec endpoint : endpoints) { - EndpointSpecBuilder endpointBuilder = new EndpointSpecBuilder(endpoint); - - CertSpecBuilder certSpec = endpoint.getCert() != null ? new CertSpecBuilder(endpoint.getCert()) : new CertSpecBuilder(); - if (certSpec.getProvider() == null) { - certSpec.withProvider(defaultCertProvider); - } - - if (certSpec.getSecretName() == null) { - certSpec.withSecretName(KubeUtil.getExternalCertSecretName(endpoint.getService(), addressSpace)); - } - - endpointBuilder.withCert(certSpec.build()); - newEndpoints.add(endpointBuilder.build()); - } - addressSpace = new AddressSpaceBuilder(addressSpace) - .editOrNewSpec() - .withEndpoints(newEndpoints) - .endSpec() - .build(); - - AddressSpaceType addressSpaceType = addressSpaceResolver.getType(addressSpace.getSpec().getType()); - AddressSpacePlan addressSpacePlan = addressSpaceResolver.getPlan(addressSpaceType, addressSpace.getSpec().getPlan()); - - InfraConfig desiredInfraConfig = getInfraConfig(addressSpace); - InfraConfig currentInfraConfig = kubernetes.getAppliedInfraConfig(addressSpace); - - - // Apply changes to ensure controller logic works as expected - InfraConfigs.setCurrentInfraConfig(addressSpace, currentInfraConfig); - AppliedConfig.setCurrentAppliedConfig(addressSpace, appliedConfig); - - AppliedConfig desiredConfig = AppliedConfig.create(addressSpace.getSpec(), authenticationServiceResolver.resolve(addressSpace)); - - if (currentInfraConfig == null && !kubernetes.existsAddressSpace(addressSpace)) { - KubernetesList resourceList = new KubernetesListBuilder() - .addAllToItems(infraResourceFactory.createInfraResources(addressSpace, desiredInfraConfig, desiredConfig.getAuthenticationServiceSettings())) - .build(); - addAppliedInfraConfigAnnotation(resourceList, desiredInfraConfig); - addAppliedConfigAnnotation(resourceList, desiredConfig); - - log.info("Creating address space {}", addressSpace); - - kubernetes.create(resourceList); - eventLogger.log(AddressSpaceCreated, "Created address space", Normal, ControllerKind.AddressSpace, addressSpace.getMetadata().getName()); - InfraConfigs.setCurrentInfraConfig(addressSpace, desiredInfraConfig); - AppliedConfig.setCurrentAppliedConfig(addressSpace, desiredConfig); - addressSpace.getStatus().setPhase(Phase.Configuring); - } else if (currentInfraConfig == null || !currentInfraConfig.equals(desiredInfraConfig)) { - - if (version.equals(desiredInfraConfig.getVersion())) { - addressSpace.getStatus().setPhase(Phase.Configuring); - if (checkExceedsQuota(addressSpaceType, addressSpacePlan, addressSpace)) { - return addressSpace; - } - KubernetesList resourceList = new KubernetesListBuilder() - .addAllToItems(infraResourceFactory.createInfraResources(addressSpace, desiredInfraConfig, desiredConfig.getAuthenticationServiceSettings())) - .build(); - addAppliedInfraConfigAnnotation(resourceList, desiredInfraConfig); - addAppliedConfigAnnotation(resourceList, desiredConfig); - - log.info("Upgrading address space {}", addressSpace); - - kubernetes.apply(resourceList,desiredInfraConfig.getUpdatePersistentVolumeClaim()); - eventLogger.log(AddressSpaceUpgraded, "Upgraded address space", Normal, ControllerKind.AddressSpace, addressSpace.getMetadata().getName()); - InfraConfigs.setCurrentInfraConfig(addressSpace, desiredInfraConfig); - AppliedConfig.setCurrentAppliedConfig(addressSpace, desiredConfig); - } else { - log.info("Version of desired config ({}) does not match controller version ({}), skipping upgrade", desiredInfraConfig.getVersion(), version); - } - } else if (!desiredConfig.equals(appliedConfig)) { - addressSpace.getStatus().setPhase(Phase.Configuring); - if (checkExceedsQuota(addressSpaceType, addressSpacePlan, addressSpace)) { - return addressSpace; - } - - KubernetesList resourceList = new KubernetesListBuilder() - .addAllToItems(infraResourceFactory.createInfraResources(addressSpace, desiredInfraConfig, desiredConfig.getAuthenticationServiceSettings())) - .build(); - addAppliedInfraConfigAnnotation(resourceList, desiredInfraConfig); - addAppliedConfigAnnotation(resourceList, desiredConfig); - - log.info("Updating address space config {}", addressSpace); - - kubernetes.apply(resourceList, desiredInfraConfig.getUpdatePersistentVolumeClaim()); - eventLogger.log(AddressSpaceChanged, "Changed applied config", Normal, ControllerKind.AddressSpace, addressSpace.getMetadata().getName()); - InfraConfigs.setCurrentInfraConfig(addressSpace, desiredInfraConfig); - AppliedConfig.setCurrentAppliedConfig(addressSpace, desiredConfig); - } - return addressSpace; - } - - private void addAppliedConfigAnnotation(KubernetesList resourceList, AppliedConfig config) throws JsonProcessingException { - for (HasMetadata resource : resourceList.getItems()) { - if (resource instanceof Service) { - AppliedConfig.setCurrentAppliedConfig(resource, config); - } - } - } - - private boolean checkExceedsQuota(AddressSpaceType addressSpaceType, AddressSpacePlan plan, AddressSpace addressSpace) { - AddressApi addressApi = addressSpaceApi.withAddressSpace(addressSpace); - Set
addresses = addressApi.listAddresses(addressSpace.getMetadata().getNamespace()).stream() - .filter(address -> addressSpace.getMetadata().getName().equals(Address.extractAddressSpace(address))) - .collect(Collectors.toSet()); - - Map quota = new HashMap<>(); - Map usage = new HashMap<>(); - for (Map.Entry allowance : plan.getResourceLimits().entrySet()) { - quota.put(allowance.getKey(), allowance.getValue()); - } - - AddressResolver addressResolver = new AddressResolver(addressSpaceType); - for (Address address : addresses) { - AddressPlan addressPlan = addressResolver.getPlan(address); - for (Map.Entry resourceRequest : addressPlan.getResources().entrySet()) { - usage.compute(resourceRequest.getKey(), (s, old) -> { - if (old == null) { - return resourceRequest.getValue(); - } else { - return old + resourceRequest.getValue(); - } - }); - usage.compute("aggregate", (s, old) -> { - if (old == null) { - return resourceRequest.getValue(); - } else { - return old + resourceRequest.getValue(); - } - }); - } - } - - boolean exceedsQuota = false; - for (Map.Entry usageEntry : usage.entrySet()) { - Double quotaValue = quota.get(usageEntry.getKey()); - if (quotaValue != null && usageEntry.getValue() > quotaValue) { - addressSpace.getStatus().appendMessage(String.format("Unable to apply plan %s to address space %s:%s: quota exceeded for resource %s", plan.getAddressPlans(), addressSpace.getMetadata().getNamespace(), addressSpace.getMetadata().getName(), usageEntry.getKey())); - exceedsQuota = true; - } - } - return exceedsQuota; - } - - private void addAppliedInfraConfigAnnotation(KubernetesList resourceList, InfraConfig infraConfig) throws JsonProcessingException { - for (HasMetadata item : resourceList.getItems()) { - if (item instanceof StatefulSet || item instanceof Service) { - InfraConfigs.setCurrentInfraConfig(item, infraConfig); - } - } - } - - private InfraConfig getInfraConfig(AddressSpace addressSpace) { - AddressSpaceResolver addressSpaceResolver = new AddressSpaceResolver(schemaProvider.getSchema()); - return addressSpaceResolver.getInfraConfig(addressSpace.getSpec().getType(), addressSpace.getSpec().getPlan()); - } - - @Override - public String toString() { - return "CreateController"; - } -} diff --git a/address-space-controller/src/main/java/io/enmasse/controller/DefaultsController.java b/address-space-controller/src/main/java/io/enmasse/controller/DefaultsController.java deleted file mode 100644 index f198e4b96f5..00000000000 --- a/address-space-controller/src/main/java/io/enmasse/controller/DefaultsController.java +++ /dev/null @@ -1,61 +0,0 @@ -/* - * Copyright 2019, EnMasse authors. - * License: Apache License 2.0 (see the file LICENSE or http://apache.org/licenses/LICENSE-2.0.html). - */ -package io.enmasse.controller; - -import io.enmasse.address.model.AddressSpace; -import io.enmasse.address.model.AddressSpaceBuilder; -import io.enmasse.address.model.KubeUtil; -import io.enmasse.config.AnnotationKeys; -import io.enmasse.controller.common.Kubernetes; -import io.enmasse.k8s.api.AuthenticationServiceRegistry; - -public class DefaultsController implements Controller { - private final AuthenticationServiceRegistry authenticationServiceRegistry; - private final Kubernetes kubernetes; - - public DefaultsController(AuthenticationServiceRegistry authenticationServiceRegistry, Kubernetes kubernetes) { - this.authenticationServiceRegistry = authenticationServiceRegistry; - this.kubernetes = kubernetes; - } - - @Override - public AddressSpace reconcileActive(AddressSpace addressSpace) { - AddressSpaceBuilder builder = new AddressSpaceBuilder(addressSpace); - - if (addressSpace.getAnnotation(AnnotationKeys.INFRA_UUID) == null) { - String infraUuid = kubernetes.getInfraUuid(addressSpace); - if (infraUuid != null) { - builder.editOrNewMetadata() - .addToAnnotations(AnnotationKeys.INFRA_UUID, infraUuid) - .endMetadata(); - } else { - builder.editOrNewMetadata() - .addToAnnotations(AnnotationKeys.INFRA_UUID, KubeUtil.infraUuid(addressSpace.getMetadata().getNamespace(), addressSpace.getMetadata().getName())) - .endMetadata(); - } - } - - if (addressSpace.getAnnotation(AnnotationKeys.REALM_NAME) == null) { - builder.editOrNewMetadata() - .addToAnnotations(AnnotationKeys.REALM_NAME, KubeUtil.getAddressSpaceRealmName(addressSpace)) - .endMetadata(); - } - - if (addressSpace.getSpec().getAuthenticationService() == null || addressSpace.getSpec().getAuthenticationService().getName() == null) { - authenticationServiceRegistry.findAuthenticationService(addressSpace.getSpec().getAuthenticationService()).ifPresent(a -> - builder.editOrNewSpec().editOrNewAuthenticationService() - .withName(a.getMetadata().getName()) - .endAuthenticationService() - .endSpec()); - } - - return builder.build(); - } - - @Override - public String toString() { - return "DefaultsController"; - } -} diff --git a/address-space-controller/src/main/java/io/enmasse/controller/EndpointController.java b/address-space-controller/src/main/java/io/enmasse/controller/EndpointController.java deleted file mode 100644 index 32de7525156..00000000000 --- a/address-space-controller/src/main/java/io/enmasse/controller/EndpointController.java +++ /dev/null @@ -1,390 +0,0 @@ -/* - * Copyright 2016-2018, EnMasse authors. - * License: Apache License 2.0 (see the file LICENSE or http://apache.org/licenses/LICENSE-2.0.html). - */ -package io.enmasse.controller; - -import java.nio.charset.StandardCharsets; -import java.util.ArrayList; -import java.util.Base64; -import java.util.Collections; -import java.util.HashMap; -import java.util.Iterator; -import java.util.List; -import java.util.Map; -import java.util.stream.Collectors; - -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import io.enmasse.address.model.AddressSpace; -import io.enmasse.address.model.AddressSpaceBuilder; -import io.enmasse.address.model.CertSpec; -import io.enmasse.address.model.EndpointSpec; -import io.enmasse.address.model.EndpointStatus; -import io.enmasse.address.model.EndpointStatusBuilder; -import io.enmasse.address.model.ExposeSpec; -import io.enmasse.address.model.ExposeType; -import io.enmasse.address.model.KubeUtil; -import io.enmasse.address.model.TlsTermination; -import io.enmasse.config.AnnotationKeys; -import io.enmasse.config.LabelKeys; -import io.enmasse.controller.auth.OpenSSLCertManager; -import io.fabric8.kubernetes.api.model.Secret; -import io.fabric8.kubernetes.api.model.Service; -import io.fabric8.kubernetes.api.model.ServiceBuilder; -import io.fabric8.kubernetes.api.model.ServicePort; -import io.fabric8.kubernetes.client.KubernetesClient; -import io.fabric8.kubernetes.client.KubernetesClientException; -import io.fabric8.openshift.api.model.Route; -import io.fabric8.openshift.api.model.RouteBuilder; -import io.fabric8.openshift.client.OpenShiftClient; - -public class EndpointController implements Controller { - private static final Logger log = LoggerFactory.getLogger(EndpointController.class.getName()); - private final KubernetesClient client; - private final boolean exposeServicesByDefault; - private final String namespace; - private final boolean isOpenShift; - - public EndpointController(KubernetesClient client, boolean exposeServicesByDefault, boolean isOpenShift) { - this.client = client; - this.exposeServicesByDefault = exposeServicesByDefault; - namespace = client.getNamespace(); - this.isOpenShift = isOpenShift; - } - - @Override - public AddressSpace reconcileActive(AddressSpace addressSpace) { - addressSpace = removeAddressSpaceConsoleEndpoint(addressSpace); - updateEndpointsStatuses(addressSpace); - updateCaCert(addressSpace); - return addressSpace; - } - - private AddressSpace removeAddressSpaceConsoleEndpoint(AddressSpace addressSpace) { - String infraUuid = addressSpace.getAnnotation(AnnotationKeys.INFRA_UUID); - if (infraUuid == null) { - return addressSpace; - } - - List endpoints = addressSpace.getSpec().getEndpoints(); - List copy = new ArrayList<>(endpoints); - - // Remove any address-space-specific console endpoint. - for (Iterator iterator = copy.iterator(); iterator.hasNext(); ) { - EndpointSpec endpoint = iterator.next(); - - if ("console".equals(endpoint.getName())) { - iterator.remove(); - - if (endpoint.getService() != null) { - String serviceName = KubeUtil.getAddressSpaceServiceName(endpoint.getService(), addressSpace); - Service service = client.services().inNamespace(namespace).withName(serviceName).get(); - if (service != null && service.getMetadata().getLabels() != null && infraUuid.equals(service.getMetadata().getLabels().get(LabelKeys.INFRA_UUID))) { - client.services().inNamespace(namespace).delete(service); - } - } - - CertSpec cert = endpoint.getCert(); - if (cert != null && "selfsigned".equals(cert.getProvider()) && cert.getSecretName() != null) { - Secret secret = client.secrets().inNamespace(namespace).withName(cert.getSecretName()).get(); - if (secret != null && secret.getMetadata().getLabels() != null && infraUuid.equals(secret.getMetadata().getLabels().get(LabelKeys.INFRA_UUID))) { - client.secrets().inNamespace(namespace).delete(secret); - } - } - - if (endpoint.getExpose() != null) { - ExposeType type = endpoint.getExpose().getType(); - switch (type) { - case route: - if (isOpenShift) { - String routeName = KubeUtil.getAddressSpaceRouteName(endpoint.getName(), addressSpace); - Route route = client.adapt(OpenShiftClient.class).routes().inNamespace(namespace).withName(routeName).get(); - if (route != null && route.getMetadata().getLabels() != null && infraUuid.equals(route.getMetadata().getLabels().get(LabelKeys.INFRA_UUID))) { - client.adapt(OpenShiftClient.class).routes().inNamespace(namespace).delete(route); - } - } - break; - case loadbalancer: - String serviceName = KubeUtil.getAddressSpaceExternalServiceName(endpoint.getName(), addressSpace); - Service service = client.services().inNamespace(namespace).withName(serviceName).get(); - if (service != null && service.getMetadata().getLabels() != null && infraUuid.equals(service.getMetadata().getLabels().get(LabelKeys.INFRA_UUID))) { - client.services().inNamespace(namespace).delete(service); - } - } - } - } - } - - if (copy.size() != endpoints.size()) { - return new AddressSpaceBuilder(addressSpace) - .editOrNewSpec() - .withEndpoints(copy) - .endSpec() - .build(); - } else { - return addressSpace; - } - } - - private void updateEndpointsStatuses(AddressSpace addressSpace) { - - Map annotations = new HashMap<>(); - annotations.put(AnnotationKeys.ADDRESS_SPACE, addressSpace.getMetadata().getName()); - - String infraUuid = addressSpace.getAnnotation(AnnotationKeys.INFRA_UUID); - List services = client.services().inNamespace(namespace).withLabel(LabelKeys.INFRA_UUID, infraUuid).list().getItems(); - List endpoints = collectEndpoints(addressSpace, services); - - /* Watch for routes and lb services */ - - final List statuses; - if (exposeServicesByDefault) { - statuses = exposeEndpoints(addressSpace, endpoints); - } else { - statuses = endpoints.stream().map(e -> e.endpointStatus).collect(Collectors.toList()); - } - - log.debug("Updating endpoints for " + addressSpace.getMetadata().getName() + " to " + statuses); - addressSpace.getStatus().setEndpointStatuses(statuses); - } - - private void updateCaCert(final AddressSpace addressSpace) { - final Secret caCert = OpenSSLCertManager.create(client).getCertSecret(KubeUtil.getAddressSpaceExternalCaSecretName(addressSpace)); - if ( caCert != null ) { - addressSpace.getStatus().setCaCert(caCert.getData().get("tls.crt")); - } - } - - private static class EndpointInfo { - private final EndpointSpec endpointSpec; - private final EndpointStatus endpointStatus; - - private EndpointInfo(EndpointSpec endpointSpec, EndpointStatus endpointStatus) { - this.endpointSpec = endpointSpec; - this.endpointStatus = endpointStatus; - } - } - - public List collectEndpoints(AddressSpace addressSpace, List services) { - List endpoints = new ArrayList<>(); - - for (EndpointSpec endpoint : addressSpace.getSpec().getEndpoints()) { - EndpointStatusBuilder statusBuilder = new EndpointStatusBuilder(); - statusBuilder.withName(endpoint.getName()); - statusBuilder.withServiceHost(KubeUtil.getAddressSpaceServiceHost(endpoint.getService(), namespace, addressSpace)); - Service service = findService(services, KubeUtil.getAddressSpaceServiceName(endpoint.getService(), addressSpace)); - if (service == null) { - continue; - } - - statusBuilder.withServicePorts(ServiceHelper.getServicePorts(service)); - endpoints.add(new EndpointInfo(endpoint, statusBuilder.build())); - } - return endpoints; - } - - private Service findService(List services, String serviceName) { - for (Service service : services) { - if (serviceName.equals(service.getMetadata().getName())) { - return service; - } - } - return null; - } - - private List exposeEndpoints(AddressSpace addressSpace, List endpoints) { - List exposedStatuses = new ArrayList<>(); - - for (EndpointInfo endpoint : endpoints) { - - if (endpoint.endpointSpec.getExpose() != null) { - exposedStatuses.add(exposeEndpoint(addressSpace, endpoint, endpoint.endpointSpec.getExpose())); - } else { - EndpointStatusBuilder statusBuilder = new EndpointStatusBuilder(endpoint.endpointStatus); - Secret certSecret = client.secrets().inNamespace(namespace).withName(KubeUtil.getExternalCertSecretName(endpoint.endpointSpec.getService(), addressSpace)).get(); - if (certSecret != null) { - statusBuilder.withCert(certSecret.getData().get("tls.crt")); - } - exposedStatuses.add(endpoint.endpointStatus); - } - } - - return exposedStatuses; - } - - private EndpointStatus exposeEndpoint(AddressSpace addressSpace, EndpointInfo endpointInfo, ExposeSpec exposeSpec) { - EndpointStatusBuilder statusBuilder = new EndpointStatusBuilder(endpointInfo.endpointStatus); - if (exposeSpec.getType() != null) { - try { - switch (exposeSpec.getType()) { - case route: - Route route = ensureRouteExists(addressSpace, endpointInfo.endpointSpec, exposeSpec); - if (route != null) { - statusBuilder.withExternalPorts(Collections.singletonMap(exposeSpec.getRouteServicePort(), 443)); - statusBuilder.withExternalHost(route.getSpec().getHost()); - if (exposeSpec.getRouteTlsTermination().equals(TlsTermination.passthrough)) { - Secret certSecret = client.secrets().inNamespace(namespace) - .withName(KubeUtil.getExternalCertSecretName(endpointInfo.endpointSpec.getService(), addressSpace)).get(); - if (certSecret != null) { - statusBuilder.withCert(certSecret.getData().get("tls.crt")); - } - } else { - statusBuilder.withCert(route.getSpec().getTls().getCertificate()); - } - } - break; - case loadbalancer: - Service service = ensureExternalServiceExists(addressSpace, endpointInfo.endpointSpec, exposeSpec); - if (service != null && service.getSpec().getPorts().size() > 0) { - statusBuilder.withExternalHost(service.getSpec().getLoadBalancerIP()); - statusBuilder.withExternalPorts(endpointInfo.endpointStatus.getServicePorts()); - Secret certSecret = client.secrets().inNamespace(namespace) - .withName(KubeUtil.getExternalCertSecretName(endpointInfo.endpointSpec.getService(), addressSpace)).get(); - if (certSecret != null) { - statusBuilder.withCert(certSecret.getData().get("tls.crt")); - } - } - break; - } - } catch (KubernetesClientException e) { - String error = String.format("Error exposing endpoint %s: %s", endpointInfo.endpointSpec.getName(), e.getMessage()); - log.warn(error); - addressSpace.getStatus().appendMessage(error); - } - } - return statusBuilder.build(); - } - - private Route ensureRouteExists(AddressSpace addressSpace, EndpointSpec endpointSpec, ExposeSpec exposeSpec) { - String infraUuid = addressSpace.getAnnotation(AnnotationKeys.INFRA_UUID); - String routeName = KubeUtil.getAddressSpaceRouteName(endpointSpec.getName(), addressSpace); - - if (!isOpenShift) { - return null; - } - Route existingRoute = client.adapt(OpenShiftClient.class).routes().inNamespace(namespace).withName(routeName).get(); - if (existingRoute != null) { - return existingRoute; - } - - String serviceName = KubeUtil.getAddressSpaceServiceName(endpointSpec.getService(), addressSpace); - - RouteBuilder route = new RouteBuilder() - .editOrNewMetadata() - .withName(routeName) - .withNamespace(namespace) - .addToAnnotations(exposeSpec.getAnnotations() != null ? exposeSpec.getAnnotations() : Collections.singletonMap("haproxy.router.openshift.io/balance", "leastconn")) - .addToAnnotations(AnnotationKeys.ADDRESS_SPACE, addressSpace.getMetadata().getName()) - .addToAnnotations(AnnotationKeys.SERVICE_NAME, serviceName) - .addToLabels(LabelKeys.INFRA_TYPE, addressSpace.getSpec().getType()) - .addToLabels(LabelKeys.INFRA_UUID, infraUuid) - .endMetadata() - .editOrNewSpec() - .withHost(endpointSpec.getExpose() != null ? endpointSpec.getExpose().getRouteHost() : "") - .withNewTo() - .withName(serviceName) - .withKind("Service") - .endTo() - .withNewPort() - .editOrNewTargetPort() - .withStrVal(exposeSpec.getRouteServicePort()) - .endTargetPort() - .endPort() - .endSpec(); - - - if (endpointSpec.getCert() != null ) { - TlsTermination tlsTermination = exposeSpec.getRouteTlsTermination(); - CertSpec certSpec = endpointSpec.getCert(); - - if (TlsTermination.passthrough.equals(tlsTermination)) { - route.editOrNewSpec() - .withNewTls() - .withTermination("passthrough") - .endTls() - .endSpec(); - } else if (TlsTermination.reencrypt.equals(tlsTermination)) { - if ("selfsigned".equals(certSpec.getProvider())) { - String caSecretName = KubeUtil.getAddressSpaceExternalCaSecretName(addressSpace); - Secret secret = client.secrets().inNamespace(namespace).withName(caSecretName).get(); - if (secret != null) { - String consoleCa = new String(Base64.getDecoder().decode(secret.getData().get("tls.crt")), StandardCharsets.UTF_8); - route.editOrNewSpec() - .withNewTls() - .withTermination("reencrypt") - .withDestinationCACertificate(consoleCa) - .endTls() - .endSpec(); - } else { - log.info("Ca secret {} for endpoint {} does not yet exist, skipping route creation for now", caSecretName, endpointSpec); - return null; // Skip this route until secret is available - } - } else { - route.editOrNewSpec() - .withNewTls() - .withTermination("reencrypt") - .endTls() - .endSpec(); - } - } - } - log.info("Creating route {} for endpoint {}", routeName, endpointSpec.getName()); - return client.adapt(OpenShiftClient.class).routes().inNamespace(namespace).create(route.build()); - } - - private Service ensureExternalServiceExists(AddressSpace addressSpace, EndpointSpec endpointSpec, ExposeSpec exposeSpec) { - String infraUuid = addressSpace.getAnnotation(AnnotationKeys.INFRA_UUID); - String serviceName = KubeUtil.getAddressSpaceExternalServiceName(endpointSpec.getName(), addressSpace); - - Service existingService = client.services().inNamespace(namespace).withName(serviceName).get(); - if (existingService != null) { - return existingService; - } - - Service service = client.services().inNamespace(namespace).withName(KubeUtil.getAddressSpaceServiceName(endpointSpec.getService(), addressSpace)).get(); - if (service == null) { - return null; - } - - - List servicePorts = new ArrayList<>(); - for (ServicePort port : service.getSpec().getPorts()) { - for (String portName : exposeSpec.getLoadBalancerPorts()) { - if (port.getName().equals(portName)) { - servicePorts.add(port); - } - } - } - if (servicePorts.isEmpty()) { - return null; - } - - ServiceBuilder svc = new ServiceBuilder() - .editOrNewMetadata() - .withName(serviceName) - .withNamespace(namespace) - .addToAnnotations(exposeSpec.getAnnotations()) - .addToAnnotations(AnnotationKeys.ADDRESS_SPACE, addressSpace.getMetadata().getName()) - .addToAnnotations(AnnotationKeys.SERVICE_NAME, KubeUtil.getAddressSpaceServiceName(endpointSpec.getService(), addressSpace)) - .addToLabels(LabelKeys.INFRA_TYPE, addressSpace.getSpec().getType()) - .addToLabels(LabelKeys.INFRA_UUID, infraUuid) - .endMetadata() - .editOrNewSpec() - .withPorts(servicePorts) - .withSelector(service.getSpec().getSelector()) - .withLoadBalancerSourceRanges(exposeSpec.getLoadBalancerSourceRanges() != null ? exposeSpec.getLoadBalancerSourceRanges() : Collections.emptyList()) - .withType("LoadBalancer") - .endSpec(); - - log.info("Creating loadbalancer service {} for endpoint {}", serviceName, endpointSpec.getName()); - return client.services().inNamespace(namespace).create(svc.build()); - } - - @Override - public String toString() { - return "EndpointController"; - } - -} diff --git a/address-space-controller/src/main/java/io/enmasse/controller/ExportsController.java b/address-space-controller/src/main/java/io/enmasse/controller/ExportsController.java deleted file mode 100644 index 418fb945d12..00000000000 --- a/address-space-controller/src/main/java/io/enmasse/controller/ExportsController.java +++ /dev/null @@ -1,180 +0,0 @@ -/* - * Copyright 2019, EnMasse authors. - * License: Apache License 2.0 (see the file LICENSE or http://apache.org/licenses/LICENSE-2.0.html). - */ -package io.enmasse.controller; - -import io.enmasse.address.model.*; -import io.fabric8.kubernetes.api.model.*; -import io.fabric8.kubernetes.client.KubernetesClient; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import java.nio.charset.StandardCharsets; -import java.util.*; - -public class ExportsController implements Controller { - private static final Logger log = LoggerFactory.getLogger(ExportsController.class.getName()); - - private final KubernetesClient client; - - public ExportsController(KubernetesClient client) { - this.client = client; - } - - @Override - public AddressSpace reconcileActive(AddressSpace addressSpace) { - try { - Map> exportsMap = new HashMap<>(); - for (EndpointSpec endpointSpec : addressSpace.getSpec().getEndpoints()) { - if (endpointSpec.getExports() != null) { - exportsMap.put(endpointSpec.getName(), endpointSpec.getExports()); - } - } - - for (EndpointStatus endpointStatus : addressSpace.getStatus().getEndpointStatuses()) { - List exports = exportsMap.get(endpointStatus.getName()); - if (exports != null) { - for (ExportSpec export : exports) { - switch (export.getKind()) { - case Secret: - exportAsSecret(export.getName(), endpointStatus, addressSpace); - break; - case ConfigMap: - exportAsConfigMap(export.getName(), endpointStatus, addressSpace); - break; - case Service: - exportAsService(export.getName(), endpointStatus, addressSpace); - break; - default: - log.info("Unknown export kind {} for address space {}, ignoring", export.getKind(), addressSpace.getMetadata().getName()); - break; - } - } - } - } - } catch (Exception e) { - log.warn("Error exporting endpoints for address space {}", addressSpace.getMetadata().getName(), e); - } - return addressSpace; - } - - private void exportAsSecret(String name, EndpointStatus endpointStatus, AddressSpace addressSpace) { - Map exportMap = buildExportMap(addressSpace.getStatus(), endpointStatus); - Secret secret = new SecretBuilder() - .editOrNewMetadata() - .withName(name) - .withNamespace(addressSpace.getMetadata().getNamespace()) - .addToOwnerReferences(new OwnerReferenceBuilder() - .withBlockOwnerDeletion(false) - .withApiVersion(addressSpace.getApiVersion()) - .withController(true) - .withKind(addressSpace.getKind()) - .withName(addressSpace.getMetadata().getName()) - .withUid(addressSpace.getMetadata().getUid()) - .build()) - .endMetadata() - .addToStringData(exportMap) - .build(); - - Secret existing = client.secrets().inNamespace(addressSpace.getMetadata().getNamespace()).withName(name).get(); - if (existing != null) { - Map decodedExportMap = decodeExportMap(existing.getData()); - if (!decodedExportMap.equals(exportMap)) { - client.secrets().inNamespace(addressSpace.getMetadata().getNamespace()).withName(name).replace(secret); - } - } else { - client.secrets().inNamespace(addressSpace.getMetadata().getNamespace()).withName(name).createOrReplace(secret); - } - } - - private void exportAsConfigMap(String name, EndpointStatus endpointStatus, AddressSpace addressSpace) { - Map exportMap = buildExportMap(addressSpace.getStatus(), endpointStatus); - ConfigMap configMap = new ConfigMapBuilder() - .editOrNewMetadata() - .withName(name) - .withNamespace(addressSpace.getMetadata().getNamespace()) - .addToOwnerReferences(new OwnerReferenceBuilder() - .withBlockOwnerDeletion(false) - .withApiVersion(addressSpace.getApiVersion()) - .withController(true) - .withKind(addressSpace.getKind()) - .withName(addressSpace.getMetadata().getName()) - .withUid(addressSpace.getMetadata().getUid()) - .build()) - .endMetadata() - .addToData(exportMap) - .build(); - - ConfigMap existing = client.configMaps().inNamespace(addressSpace.getMetadata().getNamespace()).withName(name).get(); - if (existing != null && !exportMap.equals(existing.getData())) { - client.configMaps().inNamespace(addressSpace.getMetadata().getNamespace()).withName(name).replace(configMap); - } else { - client.configMaps().inNamespace(addressSpace.getMetadata().getNamespace()).withName(name).createOrReplace(configMap); - } - } - - private void exportAsService(String name, EndpointStatus endpointStatus, AddressSpace addressSpace) { - Service service = new ServiceBuilder() - .editOrNewMetadata() - .withName(name) - .withNamespace(addressSpace.getMetadata().getNamespace()) - .addToOwnerReferences(new OwnerReferenceBuilder() - .withBlockOwnerDeletion(false) - .withApiVersion(addressSpace.getApiVersion()) - .withController(true) - .withKind(addressSpace.getKind()) - .withName(addressSpace.getMetadata().getName()) - .withUid(addressSpace.getMetadata().getUid()) - .build()) - .endMetadata() - .editOrNewSpec() - .withType("ExternalName") - .withExternalName(endpointStatus.getServiceHost() + ".cluster.local") - .withPorts(ServiceHelper.toServicePortList(endpointStatus.getServicePorts())) - .endSpec() - .build(); - - Service existing = client.services().inNamespace(addressSpace.getMetadata().getNamespace()).withName(name).get(); - if (existing != null && - (!endpointStatus.getServiceHost().equals(existing.getSpec().getExternalName()) - || !endpointStatus.getServicePorts().equals(ServiceHelper.fromServicePortList(existing.getSpec().getPorts())))) { - client.services().inNamespace(addressSpace.getMetadata().getNamespace()).withName(name).replace(service); - } else { - client.services().inNamespace(addressSpace.getMetadata().getNamespace()).withName(name).createOrReplace(service); - } - } - - private static Map buildExportMap(AddressSpaceStatus addressSpaceStatus, EndpointStatus endpointStatus) { - Map map = new HashMap<>(); - map.put("service.host", endpointStatus.getServiceHost()); - for (Map.Entry portEntry : endpointStatus.getServicePorts().entrySet()) { - map.put("service.port." + portEntry.getKey(), String.valueOf(portEntry.getValue())); - } - if (endpointStatus.getExternalHost() != null) { - map.put("external.host", endpointStatus.getExternalHost()); - } - for (Map.Entry portEntry : endpointStatus.getExternalPorts().entrySet()) { - map.put("external.port." + portEntry.getKey(), String.valueOf(portEntry.getValue())); - } - - if (addressSpaceStatus.getCaCert() != null) { - map.put("ca.crt", addressSpaceStatus.getCaCert()); - } - return map; - } - - - private static Map decodeExportMap(Map data) { - Map exportData = new HashMap<>(); - for (Map.Entry entry : data.entrySet()) { - exportData.put(entry.getKey(), new String(Base64.getDecoder().decode(entry.getValue()), StandardCharsets.UTF_8)); - } - return exportData; - } - - @Override - public String toString() { - return "ExportsController"; - } -} diff --git a/address-space-controller/src/main/java/io/enmasse/controller/HTTPServer.java b/address-space-controller/src/main/java/io/enmasse/controller/HTTPServer.java deleted file mode 100644 index d598d65d6dc..00000000000 --- a/address-space-controller/src/main/java/io/enmasse/controller/HTTPServer.java +++ /dev/null @@ -1,67 +0,0 @@ -/* - * Copyright 2016-2018, EnMasse authors. - * License: Apache License 2.0 (see the file LICENSE or http://apache.org/licenses/LICENSE-2.0.html). - */ -package io.enmasse.controller; - -import com.sun.net.httpserver.HttpExchange; -import com.sun.net.httpserver.HttpHandler; -import com.sun.net.httpserver.HttpServer; -import io.enmasse.metrics.api.Metrics; -import io.enmasse.metrics.api.MetricsFormatter; -import io.enmasse.metrics.api.PrometheusMetricsFormatter; - -import java.io.IOException; -import java.io.OutputStream; -import java.net.InetSocketAddress; -import java.nio.charset.StandardCharsets; - -public class HTTPServer { - private final HttpServer server; - - public HTTPServer(int port, Metrics metrics) throws IOException { - this.server = HttpServer.create(new InetSocketAddress(port), 0); - server.createContext("/healthz", new HealthHandler()); - server.createContext("/metrics", new MetricsHandler(metrics)); - server.setExecutor(null); // creates a default executor - } - - public void start() { - server.start(); - } - - public void stop() { - server.stop(0); - } - - private static class HealthHandler implements HttpHandler { - - @Override - public void handle(HttpExchange t) throws IOException { - byte [] response = "OK".getBytes(StandardCharsets.UTF_8); - t.sendResponseHeaders(200, response.length); - OutputStream os = t.getResponseBody(); - os.write(response); - os.close(); - } - } - - private static class MetricsHandler implements HttpHandler { - private final Metrics metrics; - private static final MetricsFormatter metricsFormatter = new PrometheusMetricsFormatter(); - - private MetricsHandler(Metrics metrics) { - this.metrics = metrics; - } - - @Override - public void handle(HttpExchange t) throws IOException { - byte [] response = metricsFormatter.format(metrics.getMetrics(), System.currentTimeMillis()).getBytes(StandardCharsets.UTF_8); - t.getResponseHeaders().add("Content-Type", "text/html"); - t.sendResponseHeaders(200, response.length); - OutputStream os = t.getResponseBody(); - os.write(response); - os.close(); - } - } -} diff --git a/address-space-controller/src/main/java/io/enmasse/controller/InfraConfigs.java b/address-space-controller/src/main/java/io/enmasse/controller/InfraConfigs.java deleted file mode 100644 index fb27578d72a..00000000000 --- a/address-space-controller/src/main/java/io/enmasse/controller/InfraConfigs.java +++ /dev/null @@ -1,43 +0,0 @@ -/* - * Copyright 2017-2018, EnMasse authors. - * License: Apache License 2.0 (see the file LICENSE or http://apache.org/licenses/LICENSE-2.0.html). - */ - -package io.enmasse.controller; - -import java.io.IOException; -import java.util.HashMap; - -import com.fasterxml.jackson.core.JsonProcessingException; -import com.fasterxml.jackson.databind.ObjectMapper; - -import io.enmasse.address.model.AddressSpace; -import io.enmasse.admin.model.v1.InfraConfig; -import io.enmasse.config.AnnotationKeys; -import io.fabric8.kubernetes.api.model.HasMetadata; - -public final class InfraConfigs { - private static final ObjectMapper mapper = new ObjectMapper(); - private InfraConfigs() { - } - - public static InfraConfig parseCurrentInfraConfig(String json) throws IOException { - if (json == null) { - return null; - } - return mapper.readValue(json, InfraConfig.class); - } - - public static InfraConfig parseCurrentInfraConfig(final AddressSpace addressSpace) throws IOException { - return parseCurrentInfraConfig(addressSpace.getAnnotation(AnnotationKeys.APPLIED_INFRA_CONFIG)); - } - - public static void setCurrentInfraConfig(final HasMetadata metadata, InfraConfig infraConfig) throws JsonProcessingException { - if (metadata.getMetadata().getAnnotations() == null) { - metadata.getMetadata().setAnnotations(new HashMap<>()); - } - metadata.getMetadata().getAnnotations().put(AnnotationKeys.APPLIED_INFRA_CONFIG, mapper.writeValueAsString(infraConfig)); - } - - -} diff --git a/address-space-controller/src/main/java/io/enmasse/controller/InfraResourceFactory.java b/address-space-controller/src/main/java/io/enmasse/controller/InfraResourceFactory.java deleted file mode 100644 index da3edf2f1b2..00000000000 --- a/address-space-controller/src/main/java/io/enmasse/controller/InfraResourceFactory.java +++ /dev/null @@ -1,16 +0,0 @@ -/* - * Copyright 2017-2018, EnMasse authors. - * License: Apache License 2.0 (see the file LICENSE or http://apache.org/licenses/LICENSE-2.0.html). - */ -package io.enmasse.controller; - -import io.enmasse.address.model.AddressSpace; -import io.enmasse.address.model.AuthenticationServiceSettings; -import io.enmasse.admin.model.v1.InfraConfig; -import io.fabric8.kubernetes.api.model.HasMetadata; - -import java.util.List; - -public interface InfraResourceFactory { - List createInfraResources(AddressSpace addressSpace, InfraConfig infraConfig, AuthenticationServiceSettings authenticationServiceSettings); -} diff --git a/address-space-controller/src/main/java/io/enmasse/controller/MessagingUserFinalizerController.java b/address-space-controller/src/main/java/io/enmasse/controller/MessagingUserFinalizerController.java deleted file mode 100644 index ea7035a7da3..00000000000 --- a/address-space-controller/src/main/java/io/enmasse/controller/MessagingUserFinalizerController.java +++ /dev/null @@ -1,65 +0,0 @@ -/* - * Copyright 2019, EnMasse authors. - * License: Apache License 2.0 (see the file LICENSE or http://apache.org/licenses/LICENSE-2.0.html). - */ -package io.enmasse.controller; - -import io.enmasse.address.model.AddressSpace; -import io.enmasse.user.model.v1.DoneableUser; -import io.enmasse.user.model.v1.User; -import io.enmasse.user.model.v1.UserCrd; -import io.enmasse.user.model.v1.UserList; -import io.fabric8.kubernetes.client.KubernetesClientException; -import io.fabric8.kubernetes.client.NamespacedKubernetesClient; -import io.fabric8.kubernetes.client.dsl.MixedOperation; -import io.fabric8.kubernetes.client.dsl.Resource; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import java.util.List; -import java.util.stream.Collectors; - -public class MessagingUserFinalizerController extends AbstractFinalizerController { - private static final Logger log = LoggerFactory.getLogger(AddressFinalizerController.class); - public static final String FINALIZER_MESSAGING_USERS = "enmasse.io/messaging-users"; - - private final MixedOperation> userClient; - - public MessagingUserFinalizerController(NamespacedKubernetesClient client) { - this(client.customResources(UserCrd.messagingUser(), User.class, UserList.class, DoneableUser.class)); - } - - MessagingUserFinalizerController(MixedOperation> userClient) { - super(FINALIZER_MESSAGING_USERS); - this.userClient = userClient; - } - - @Override - public String toString() { - return "MessagingUserFinalizerController"; - } - - @Override - protected Result processFinalizer(AddressSpace addressSpace) { - log.info("Processing messaging user finalizer for {}/{}", addressSpace.getMetadata().getNamespace(), addressSpace.getMetadata().getName()); - - try { - final List users = userClient.inNamespace(addressSpace.getMetadata().getNamespace()).list().getItems().stream() - .filter(user -> user.getMetadata().getName().startsWith(addressSpace.getMetadata().getName() + ".")) - .collect(Collectors.toList()); - for (User user : users) { - userClient.inNamespace(user.getMetadata().getNamespace()).withName(user.getMetadata().getName()).cascading(true).delete(); - } - return Result.completed(addressSpace); - } catch (KubernetesClientException e) { - // If not found, the address CRD does not exist so we drop the finalizer - if (e.getCode() == 404) { - log.warn("Got 404 when listing users for {}/{}. Marking as finalized.", addressSpace.getMetadata().getNamespace(), addressSpace.getMetadata().getName(), e); - return Result.completed(addressSpace); - } else { - log.warn("Error finalizing {}/{}", addressSpace.getMetadata().getNamespace(), addressSpace.getMetadata().getName(), e); - return Result.waiting(addressSpace); - } - } - } -} diff --git a/address-space-controller/src/main/java/io/enmasse/controller/MetricsReporterController.java b/address-space-controller/src/main/java/io/enmasse/controller/MetricsReporterController.java deleted file mode 100644 index ed44dccdeec..00000000000 --- a/address-space-controller/src/main/java/io/enmasse/controller/MetricsReporterController.java +++ /dev/null @@ -1,249 +0,0 @@ -/* - * Copyright 2019, EnMasse authors. - * License: Apache License 2.0 (see the file LICENSE or http://apache.org/licenses/LICENSE-2.0.html). - */ -package io.enmasse.controller; - -import io.enmasse.address.model.*; -import io.enmasse.admin.model.v1.BrokeredInfraConfig; -import io.enmasse.admin.model.v1.InfraConfig; -import io.enmasse.admin.model.v1.StandardInfraConfig; -import io.enmasse.config.AnnotationKeys; -import io.enmasse.controller.common.Kubernetes; -import io.enmasse.metrics.api.MetricLabel; -import io.enmasse.metrics.api.MetricType; -import io.enmasse.metrics.api.MetricValue; -import io.enmasse.metrics.api.Metrics; -import io.enmasse.metrics.api.ScalarMetric; - -import java.util.*; -import java.util.stream.Collectors; - -import static io.enmasse.address.model.Phase.Active; -import static io.enmasse.address.model.Phase.Configuring; -import static io.enmasse.address.model.Phase.Failed; -import static io.enmasse.address.model.Phase.Pending; -import static io.enmasse.address.model.Phase.Terminating; - -public class MetricsReporterController implements Controller { - private final String version; - private volatile List readyValues = new ArrayList<>(); - private volatile List notReadyValues = new ArrayList<>(); - private volatile List readyConnectorValues = new ArrayList<>(); - private volatile List notReadyConnectorValues = new ArrayList<>(); - private volatile List numConnectors = new ArrayList<>(); - private volatile List routerMeshNotConnected = new ArrayList<>(); - private volatile List routerMeshUndelivered = new ArrayList<>(); - private volatile int numAddressSpaces = 0; - private volatile Map countByPhase = new HashMap<>(); - private volatile List brokerGlobalMaxSizes = new ArrayList<>(); - private final Kubernetes kubernetes; - - private final static int KB_FACTOR = 1024; - private final static int MB_FACTOR = 1024 * KB_FACTOR; - private final static int GB_FACTOR = 1024 * MB_FACTOR; - - - public MetricsReporterController(Metrics metrics, String version, Kubernetes kubernetes) { - this.version = version; - this.kubernetes = kubernetes; - registerMetrics(metrics); - } - - public void reconcileAll(List addressSpaces) throws Exception { - List readyValues = new ArrayList<>(); - List notReadyValues = new ArrayList<>(); - List readyConnectorValues = new ArrayList<>(); - List notReadyConnectorValues = new ArrayList<>(); - List numConnectors = new ArrayList<>(); - List routerMeshNotConnected = new ArrayList<>(); - List routerMeshUndelivered = new ArrayList<>(); - List brokerGlobalMaxSizes = new ArrayList<>(); - - for (Phase phase : Phase.values()) { - countByPhase.put(phase, 0L); - } - - for (AddressSpace addressSpace : addressSpaces) { - - MetricLabel[] labels = new MetricLabel[]{ - new MetricLabel("address_space_name", addressSpace.getMetadata().getName()), - new MetricLabel("namespace", addressSpace.getMetadata().getNamespace()), - new MetricLabel("broker_prefix", "broker-" + addressSpace.getMetadata().getAnnotations().get(AnnotationKeys.INFRA_UUID)), - new MetricLabel("address_space_infra_uuid", addressSpace.getMetadata().getAnnotations().get(AnnotationKeys.INFRA_UUID)) - }; - readyValues.add(new MetricValue(addressSpace.getStatus().isReady() ? 1 : 0, labels)); - notReadyValues.add(new MetricValue(addressSpace.getStatus().isReady() ? 0 : 1, labels)); - numConnectors.add(new MetricValue(addressSpace.getStatus().getConnectors().size(), labels)); - countByPhase.put(addressSpace.getStatus().getPhase(), 1 + countByPhase.get(addressSpace.getStatus().getPhase())); - - for (AddressSpaceStatusConnector connectorStatus : addressSpace.getStatus().getConnectors()) { - - MetricLabel[] connectorLabels = new MetricLabel[]{new MetricLabel("address_space_name", addressSpace.getMetadata().getName()), new MetricLabel("namespace", addressSpace.getMetadata().getNamespace())}; - readyConnectorValues.add(new MetricValue(connectorStatus.isReady() ? 1 : 0, connectorLabels)); - notReadyConnectorValues.add(new MetricValue(connectorStatus.isReady() ? 0 : 1, connectorLabels)); - } - - // Only check routers if we have some defined. For brokered address space, these metrics will not exist - if (!addressSpace.getStatus().getRouters().isEmpty()) { - int totalNotConnected = 0; - long totalUndelivered = 0; - - Set knownRouters = addressSpace.getStatus().getRouters().stream() - .map(AddressSpaceStatusRouter::getId) - .collect(Collectors.toSet()); - - for (AddressSpaceStatusRouter routerStatus : addressSpace.getStatus().getRouters()) { - - // Verify that this router can reach all neighbors - Set neighborIds = new HashSet<>(routerStatus.getNeighbors()); - if (!neighborIds.containsAll(knownRouters)) { - routerMeshNotConnected.add(new MetricValue(1, new MetricLabel("address_space_name", addressSpace.getMetadata().getName()), new MetricLabel("namespace", addressSpace.getMetadata().getNamespace()), new MetricLabel("router", routerStatus.getId()))); - totalNotConnected++; - } - - // Calculate the sum of undelivered messages in inter-router links - if (routerStatus.getUndelivered() != null) { - totalUndelivered += routerStatus.getUndelivered(); - routerMeshUndelivered.add(new MetricValue(routerStatus.getUndelivered(), new MetricLabel("address_space_name", addressSpace.getMetadata().getName()), new MetricLabel("namespace", addressSpace.getMetadata().getNamespace()), new MetricLabel("router", routerStatus.getId()))); - } - } - - routerMeshNotConnected.add(new MetricValue(totalNotConnected, new MetricLabel("address_space_name", addressSpace.getMetadata().getName()), new MetricLabel("namespace", addressSpace.getMetadata().getNamespace()))); - routerMeshUndelivered.add(new MetricValue(totalUndelivered, new MetricLabel("address_space_name", addressSpace.getMetadata().getName()), new MetricLabel("namespace", addressSpace.getMetadata().getNamespace()))); - } - - InfraConfig infraConfig = kubernetes.getAppliedInfraConfig(addressSpace); - if (infraConfig instanceof StandardInfraConfig) { - if (((StandardInfraConfig) infraConfig).getSpec().getBroker().getGlobalMaxSize() != null) - brokerGlobalMaxSizes.add(new MetricValue(parseHumanReadableBytes(((StandardInfraConfig) infraConfig).getSpec().getBroker().getGlobalMaxSize()), labels)); - } else if (infraConfig instanceof BrokeredInfraConfig) { - infraConfig = (BrokeredInfraConfig) infraConfig; - if (((BrokeredInfraConfig) infraConfig).getSpec().getBroker().getGlobalMaxSize() != null) - brokerGlobalMaxSizes.add(new MetricValue(parseHumanReadableBytes(((BrokeredInfraConfig) infraConfig).getSpec().getBroker().getGlobalMaxSize()), labels)); - } - - } - - this.readyValues = readyValues; - this.notReadyValues = notReadyValues; - this.readyConnectorValues = readyConnectorValues; - this.notReadyConnectorValues = notReadyConnectorValues; - this.numConnectors = numConnectors; - this.numAddressSpaces = addressSpaces.size(); - this.routerMeshNotConnected = routerMeshNotConnected; - this.routerMeshUndelivered = routerMeshUndelivered; - this.brokerGlobalMaxSizes = brokerGlobalMaxSizes; - } - - private void registerMetrics(Metrics metrics) { - metrics.registerMetric(new ScalarMetric( - "version", - "The version of the address-space-controller", - MetricType.gauge, - () -> Collections.singletonList(new MetricValue(0, new MetricLabel("name", "address-space-controller"), new MetricLabel("version", version))))); - - metrics.registerMetric(new ScalarMetric( - "address_space_status_ready", - "Describes whether the address space is in a ready state", - MetricType.gauge, - () -> readyValues)); - - metrics.registerMetric(new ScalarMetric( - "address_space_status_not_ready", - "Describes whether the address space is in a not_ready state", - MetricType.gauge, - () -> notReadyValues)); - - metrics.registerMetric(new ScalarMetric( - "address_spaces_total", - "Total number of address spaces", - MetricType.gauge, - () -> Collections.singletonList(new MetricValue(numAddressSpaces)))); - - metrics.registerMetric(new ScalarMetric( - "address_space_connector_status_ready", - "Describes whether the connector in an address space is in a ready state", - MetricType.gauge, - () -> readyConnectorValues)); - - metrics.registerMetric(new ScalarMetric( - "address_space_connector_status_not_ready", - "Describes whether the connector in an address space is in a not_ready state", - MetricType.gauge, - () -> notReadyConnectorValues)); - - metrics.registerMetric(new ScalarMetric( - "address_space_connectors_total", - "Total number of connectors of address spaces", - MetricType.gauge, - () -> numConnectors)); - - metrics.registerMetric(new ScalarMetric( - "router_mesh_not_connected_total", - "Total number of router mesh networks not connected", - MetricType.gauge, - () -> routerMeshNotConnected)); - - metrics.registerMetric(new ScalarMetric( - "router_mesh_undelivered_total", - "Total number of undelivered messages in router mesh networks", - MetricType.gauge, - () -> routerMeshUndelivered)); - - metrics.registerMetric(new ScalarMetric( - "address_spaces_pending_total", - "Total number of address spaces in Pending state", - MetricType.gauge, - () -> Collections.singletonList(new MetricValue(countByPhase.get(Pending))))); - - metrics.registerMetric(new ScalarMetric( - "address_spaces_failed_total", - "Total number of address spaces in Failed state", - MetricType.gauge, - () -> Collections.singletonList(new MetricValue(countByPhase.get(Failed))))); - - metrics.registerMetric(new ScalarMetric( - "address_spaces_terminating_total", - "Total number of address spaces in Terminating state", - MetricType.gauge, - () -> Collections.singletonList(new MetricValue(countByPhase.get(Terminating))))); - - metrics.registerMetric(new ScalarMetric( - "address_spaces_configuring_total", - "Total number of address spaces in Configuring state", - MetricType.gauge, - () -> Collections.singletonList(new MetricValue(countByPhase.get(Configuring))))); - - metrics.registerMetric(new ScalarMetric( - "address_spaces_active_total", - "Total number of address spaces in Active state", - MetricType.gauge, - () -> Collections.singletonList(new MetricValue(countByPhase.get(Active))))); - - metrics.registerMetric(new ScalarMetric( - "address_space_broker_global_max_size", - "Provides the global max size for brokers in the address space", - MetricType.gauge, - () -> brokerGlobalMaxSizes)); - } - - private static int parseHumanReadableBytes(String bytes) { - String unit = bytes.substring(bytes.length() - 2).toLowerCase(); - int value = Integer.parseInt(bytes.substring(0, bytes.length() - 2).trim()); - switch (unit) { - case "gb": - return value * GB_FACTOR; - case "mb": - return value * MB_FACTOR; - case "kb": - return value * KB_FACTOR; - } - return -1; -} - - @Override - public String toString() { - return "MetricsReporterController"; - } -} diff --git a/address-space-controller/src/main/java/io/enmasse/controller/NetworkPolicyController.java b/address-space-controller/src/main/java/io/enmasse/controller/NetworkPolicyController.java deleted file mode 100644 index f0458b2fcce..00000000000 --- a/address-space-controller/src/main/java/io/enmasse/controller/NetworkPolicyController.java +++ /dev/null @@ -1,102 +0,0 @@ -/* - * Copyright 2017-2018, EnMasse authors. - * License: Apache License 2.0 (see the file LICENSE or http://apache.org/licenses/LICENSE-2.0.html). - */ -package io.enmasse.controller; - -import io.enmasse.address.model.*; -import io.enmasse.admin.model.v1.InfraConfig; -import io.enmasse.admin.model.v1.NetworkPolicy; -import io.enmasse.config.AnnotationKeys; -import io.enmasse.config.LabelKeys; -import io.fabric8.kubernetes.api.model.Service; -import io.fabric8.kubernetes.api.model.networking.*; -import io.fabric8.kubernetes.client.KubernetesClient; - -import static io.enmasse.controller.InfraConfigs.parseCurrentInfraConfig; - -import java.util.List; -import java.util.Objects; - -public class NetworkPolicyController implements Controller { - private final KubernetesClient kubernetesClient; - - public NetworkPolicyController(KubernetesClient kubernetesClient) { - this.kubernetesClient = kubernetesClient; - } - - @Override - public AddressSpace reconcileActive(AddressSpace addressSpace) throws Exception { - NetworkPolicy networkPolicy = null; - InfraConfig infraConfig = parseCurrentInfraConfig(addressSpace); - if (infraConfig != null) { - networkPolicy = infraConfig.getNetworkPolicy(); - } - - if (addressSpace.getSpec().getNetworkPolicy() != null) { - networkPolicy = addressSpace.getSpec().getNetworkPolicy(); - } - - - io.fabric8.kubernetes.api.model.networking.NetworkPolicy existingPolicy = kubernetesClient.network().networkPolicies().withName(KubeUtil.getNetworkPolicyName(addressSpace)).get(); - - if (networkPolicy != null) { - String infraUuid = addressSpace.getAnnotation(AnnotationKeys.INFRA_UUID); - List services = kubernetesClient.services().withLabel(LabelKeys.INFRA_UUID, infraUuid).list().getItems(); - io.fabric8.kubernetes.api.model.networking.NetworkPolicy newPolicy = createNetworkPolicy(networkPolicy, addressSpace, services); - if (existingPolicy == null) { - kubernetesClient.network().networkPolicies().create(newPolicy); - } else if (hasChanged(existingPolicy, newPolicy)) { - kubernetesClient.network().networkPolicies().withName(existingPolicy.getMetadata().getName()).replace(newPolicy); - } - } else if (existingPolicy != null) { - kubernetesClient.network().networkPolicies().delete(existingPolicy); - } - - return addressSpace; - } - - private boolean hasChanged(io.fabric8.kubernetes.api.model.networking.NetworkPolicy existingPolicy, io.fabric8.kubernetes.api.model.networking.NetworkPolicy newPolicy) { - if (!Objects.equals(existingPolicy.getSpec().getIngress(), newPolicy.getSpec().getIngress())) { - return true; - } - - return !Objects.equals(existingPolicy.getSpec().getEgress(), newPolicy.getSpec().getEgress()); - } - - private io.fabric8.kubernetes.api.model.networking.NetworkPolicy createNetworkPolicy(NetworkPolicy networkPolicy, AddressSpace addressSpace, List items) { - NetworkPolicyBuilder builder = new NetworkPolicyBuilder() - .editOrNewMetadata() - .withName(KubeUtil.getNetworkPolicyName(addressSpace)) - .addToLabels(LabelKeys.INFRA_TYPE, addressSpace.getSpec().getType()) - .addToLabels(LabelKeys.INFRA_UUID, addressSpace.getAnnotation(AnnotationKeys.INFRA_UUID)) - .addToLabels(LabelKeys.APP, "enmasse") - .endMetadata(); - builder.editOrNewSpec() - .editOrNewPodSelector() - .addToMatchLabels(LabelKeys.INFRA_UUID, addressSpace.getAnnotation(AnnotationKeys.INFRA_UUID)) - .endPodSelector() - .endSpec(); - - if (networkPolicy.getIngress() != null) { - builder.editOrNewSpec() - .addToPolicyTypes("Ingress") - .addAllToIngress(networkPolicy.getIngress()) - .endSpec(); - } - - if (networkPolicy.getEgress() != null) { - builder.editOrNewSpec() - .addToPolicyTypes("Egress") - .addAllToEgress(networkPolicy.getEgress()) - .endSpec(); - } - - return builder.build(); - } - - @Override - public String toString() { - return "NetworkPolicyController"; - } -} diff --git a/address-space-controller/src/main/java/io/enmasse/controller/PodDisruptionBudgetController.java b/address-space-controller/src/main/java/io/enmasse/controller/PodDisruptionBudgetController.java deleted file mode 100644 index 159da97d81b..00000000000 --- a/address-space-controller/src/main/java/io/enmasse/controller/PodDisruptionBudgetController.java +++ /dev/null @@ -1,171 +0,0 @@ -/* - * Copyright 2020, EnMasse authors. - * License: Apache License 2.0 (see the file LICENSE or http://apache.org/licenses/LICENSE-2.0.html). - */ -package io.enmasse.controller; - -import io.enmasse.address.model.AddressSpace; -import io.enmasse.admin.model.v1.InfraConfig; -import io.enmasse.admin.model.v1.StandardInfraConfig; -import io.enmasse.config.AnnotationKeys; -import io.enmasse.config.LabelKeys; -import io.fabric8.kubernetes.api.model.IntOrString; -import io.fabric8.kubernetes.api.model.policy.PodDisruptionBudget; -import io.fabric8.kubernetes.api.model.policy.PodDisruptionBudgetBuilder; -import io.fabric8.kubernetes.client.KubernetesClientException; -import io.fabric8.kubernetes.client.NamespacedKubernetesClient; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -public class PodDisruptionBudgetController implements Controller { - private static final Logger log = LoggerFactory.getLogger(PodDisruptionBudgetController.class); - - private final NamespacedKubernetesClient client; - private final String namespace; - - public PodDisruptionBudgetController(NamespacedKubernetesClient client, String namespace) { - this.client = client; - this.namespace = namespace; - } - - @Override - public AddressSpace reconcileActive(AddressSpace addressSpace) throws Exception { - InfraConfig infraConfig = InfraConfigs.parseCurrentInfraConfig(addressSpace); - - if (infraConfig instanceof StandardInfraConfig) { - RouterSet routerSet = RouterSet.create(namespace, addressSpace, client); - /* - * NOTE: PodDistruptionBudget spec cannot be modified on Kubernetes versions < 1.15. - * See https://github.com/kubernetes/kubernetes/pull/69867 for more information. - */ - reconcileRouterPodDisruptionBudget(addressSpace, routerSet, (StandardInfraConfig) infraConfig); - reconcileBrokerPodDisruptionBudget(addressSpace, (StandardInfraConfig) infraConfig); - } - return addressSpace; - } - - private static boolean needsUpdate(IntOrString existing, IntOrString updated) { - return updated != null && (existing == null || !updated.equals(existing)); - } - - - private void reconcileRouterPodDisruptionBudget(AddressSpace addressSpace, RouterSet routerSet, StandardInfraConfig infraConfig) { - String name = String.format("enmasse.%s.%s.qdrouterd", addressSpace.getMetadata().getNamespace(), addressSpace.getMetadata().getName()); - if (routerSet.getStatefulSet() != null && infraConfig.getSpec() != null && infraConfig.getSpec().getRouter() != null && (infraConfig.getSpec().getRouter().getMinAvailable() != null || infraConfig.getSpec().getRouter().getMaxUnavailable() != null)) { - String infraUuid = addressSpace.getAnnotation(AnnotationKeys.INFRA_UUID); - try { - boolean changed = false; - PodDisruptionBudget podDisruptionBudget = client.inNamespace(namespace).policy().podDisruptionBudget().withName(name).get(); - if (podDisruptionBudget == null) { - podDisruptionBudget = new PodDisruptionBudgetBuilder() - .editOrNewMetadata() - .withName(name) - .addToLabels("app", "enmasse") - .addToLabels(LabelKeys.INFRA_UUID, infraUuid) - .addToLabels(LabelKeys.INFRA_TYPE, "standard") - .endMetadata() - .editOrNewSpec() - .endSpec() - .build(); - changed = true; - } - - IntOrString minAvailable = infraConfig.getSpec().getRouter().getMinAvailable(); - if (needsUpdate(podDisruptionBudget.getSpec().getMinAvailable(), minAvailable)) { - podDisruptionBudget.getSpec().setMinAvailable(minAvailable); - changed = true; - } - - IntOrString maxUnavailable = infraConfig.getSpec().getRouter().getMaxUnavailable(); - if (needsUpdate(podDisruptionBudget.getSpec().getMaxUnavailable(), maxUnavailable)) { - podDisruptionBudget.getSpec().setMaxUnavailable(maxUnavailable); - changed = true; - } - - if (!routerSet.getStatefulSet().getSpec().getSelector() - .equals(podDisruptionBudget.getSpec().getSelector())) { - podDisruptionBudget.getSpec().setSelector(routerSet.getStatefulSet().getSpec().getSelector()); - changed = true; - } - - if (changed) { - client.inNamespace(namespace).policy().podDisruptionBudget().createOrReplace(podDisruptionBudget); - } - } catch (KubernetesClientException e) { - log.warn("Error creating pod disruption budget for router", e); - } - } else { - deleteIfExists(name); - } - } - - private void deleteIfExists(String name) { - // Make sure it does not exist - PodDisruptionBudget podDisruptionBudget = client.inNamespace(namespace).policy().podDisruptionBudget() - .withName(name).get(); - if (podDisruptionBudget != null) { - try { - client.inNamespace(namespace).policy().podDisruptionBudget().withName(name).delete(); - } catch (KubernetesClientException e) { - log.warn("Error deleting PodDisruptionBudget {}", name, e); - } - } - } - - private void reconcileBrokerPodDisruptionBudget(AddressSpace addressSpace, StandardInfraConfig infraConfig) { - String name = String.format("enmasse.%s.%s.broker", addressSpace.getMetadata().getNamespace(), addressSpace.getMetadata().getName()); - if (infraConfig.getSpec() != null && infraConfig.getSpec().getBroker() != null && - (infraConfig.getSpec().getBroker().getMinAvailable() != null || infraConfig.getSpec().getBroker().getMaxUnavailable() != null)) { - - String infraUuid = addressSpace.getAnnotation(AnnotationKeys.INFRA_UUID); - try { - boolean changed = false; - PodDisruptionBudget podDisruptionBudget = client.inNamespace(namespace).policy().podDisruptionBudget().withName(name).get(); - if (podDisruptionBudget == null) { - podDisruptionBudget = new PodDisruptionBudgetBuilder() - .editOrNewMetadata() - .withName(name) - .addToLabels("app", "enmasse") - .addToLabels(LabelKeys.INFRA_UUID, infraUuid) - .addToLabels(LabelKeys.INFRA_TYPE, "standard") - .endMetadata() - .editOrNewSpec() - .withNewSelector() - .addToMatchLabels("app", "enmasse") - .addToMatchLabels("role", "broker") - .addToMatchLabels(LabelKeys.INFRA_TYPE, "standard") - .addToMatchLabels(LabelKeys.INFRA_UUID, infraUuid) - .endSelector() - .endSpec() - .build(); - changed = true; - } - - IntOrString minAvailable = infraConfig.getSpec().getBroker().getMinAvailable(); - if (needsUpdate(podDisruptionBudget.getSpec().getMinAvailable(), minAvailable)) { - podDisruptionBudget.getSpec().setMinAvailable(minAvailable); - changed = true; - } - - IntOrString maxUnavailable = infraConfig.getSpec().getBroker().getMaxUnavailable(); - if (needsUpdate(podDisruptionBudget.getSpec().getMaxUnavailable(), maxUnavailable)) { - podDisruptionBudget.getSpec().setMaxUnavailable(maxUnavailable); - changed = true; - } - - if (changed) { - client.inNamespace(namespace).policy().podDisruptionBudget().createOrReplace(podDisruptionBudget); - } - } catch (KubernetesClientException e) { - log.warn("Error creating pod disruption budget for broker", e); - } - } else { - deleteIfExists(name); - } - } - - @Override - public String toString() { - return "PodDisruptionBudgetController"; - } -} diff --git a/address-space-controller/src/main/java/io/enmasse/controller/RealmFinalizerController.java b/address-space-controller/src/main/java/io/enmasse/controller/RealmFinalizerController.java deleted file mode 100644 index 8fdfc5be46d..00000000000 --- a/address-space-controller/src/main/java/io/enmasse/controller/RealmFinalizerController.java +++ /dev/null @@ -1,67 +0,0 @@ -/* - * Copyright 2019, EnMasse authors. - * License: Apache License 2.0 (see the file LICENSE or http://apache.org/licenses/LICENSE-2.0.html). - */ -package io.enmasse.controller; - -import io.enmasse.address.model.AddressSpace; -import io.enmasse.admin.model.v1.AuthenticationService; -import io.enmasse.admin.model.v1.AuthenticationServiceType; -import io.enmasse.config.AnnotationKeys; -import io.enmasse.k8s.api.AuthenticationServiceRegistry; -import io.enmasse.user.api.RealmApi; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import java.util.Set; - -public class RealmFinalizerController extends AbstractFinalizerController { - private static final Logger log = LoggerFactory.getLogger(AddressFinalizerController.class); - public static final String FINALIZER_REALMS = "enmasse.io/realms"; - - private final RealmApi realmApi; - private final AuthenticationServiceRegistry authenticationServiceRegistry; - - public RealmFinalizerController(RealmApi realmApi, AuthenticationServiceRegistry authenticationServiceRegistry) { - super(FINALIZER_REALMS); - this.realmApi = realmApi; - this.authenticationServiceRegistry = authenticationServiceRegistry; - } - - @Override - public String toString() { - return "RealmFinalizerController"; - } - - @Override - protected Result processFinalizer(AddressSpace addressSpace) { - log.info("Processing realm finalizer for {}/{}", addressSpace.getMetadata().getNamespace(), addressSpace.getMetadata().getName()); - - final String realmName = addressSpace.getAnnotation(AnnotationKeys.REALM_NAME); - if (realmName != null) { - final AuthenticationService authenticationService = authenticationServiceRegistry.findAuthenticationService(addressSpace.getSpec().getAuthenticationService()).orElse(null); - // Only remove realms on authentication services of type standard where realm is per-address space - if (authenticationService != null && - authenticationService.getStatus() != null && - authenticationService.getSpec().getType().equals(AuthenticationServiceType.standard) && - (authenticationService.getSpec().getRealm() == null || authenticationService.getSpec().getRealm().isEmpty())) { - - try { - deleteRealm(authenticationService, realmName); - } catch (Exception e) { - log.warn("Error finalizing {}/{}", addressSpace.getMetadata().getNamespace(), addressSpace.getMetadata().getName(), e); - return Result.waiting(addressSpace); - } - } - } - return Result.completed(addressSpace); - } - - private void deleteRealm(AuthenticationService authenticationService, String realmName) throws Exception { - Set actualRealms = realmApi.getRealmNames(authenticationService); - if (actualRealms.contains(realmName)) { - log.info("Deleting realm {}", realmName); - realmApi.deleteRealm(authenticationService, realmName); - } - } -} diff --git a/address-space-controller/src/main/java/io/enmasse/controller/RouterConfigController.java b/address-space-controller/src/main/java/io/enmasse/controller/RouterConfigController.java deleted file mode 100644 index 47fbf588fdb..00000000000 --- a/address-space-controller/src/main/java/io/enmasse/controller/RouterConfigController.java +++ /dev/null @@ -1,888 +0,0 @@ -/* - * Copyright 2019, EnMasse authors. - * License: Apache License 2.0 (see the file LICENSE or http://apache.org/licenses/LICENSE-2.0.html). - */ -package io.enmasse.controller; - -import io.enmasse.address.model.AddressSpace; -import io.enmasse.address.model.AddressSpaceSpecConnector; -import io.enmasse.address.model.AddressSpaceSpecConnectorAddressRule; -import io.enmasse.address.model.AddressSpaceSpecConnectorCredentials; -import io.enmasse.address.model.AddressSpaceSpecConnectorEndpoint; -import io.enmasse.address.model.AddressSpaceSpecConnectorTls; -import io.enmasse.address.model.AddressSpaceStatusConnector; -import io.enmasse.address.model.AddressSpaceStatusConnectorBuilder; -import io.enmasse.address.model.AddressSpaceStatusRouter; -import io.enmasse.address.model.AuthenticationServiceSettings; -import io.enmasse.address.model.KubeUtil; -import io.enmasse.address.model.Phase; -import io.enmasse.address.model.StringOrSecretSelector; -import io.enmasse.admin.model.v1.InfraConfig; -import io.enmasse.admin.model.v1.RouterPolicySpec; -import io.enmasse.admin.model.v1.StandardInfraConfig; -import io.enmasse.admin.model.v1.StandardInfraConfigSpecRouter; -import io.enmasse.config.AnnotationKeys; -import io.enmasse.config.LabelKeys; -import io.enmasse.controller.router.config.Address; -import io.enmasse.controller.router.config.AuthServicePlugin; -import io.enmasse.controller.router.config.AutoLink; -import io.enmasse.controller.router.config.Connector; -import io.enmasse.controller.router.config.Distribution; -import io.enmasse.controller.router.config.LinkDirection; -import io.enmasse.controller.router.config.LinkRoute; -import io.enmasse.controller.router.config.Listener; -import io.enmasse.controller.router.config.Policy; -import io.enmasse.controller.router.config.Role; -import io.enmasse.controller.router.config.Router; -import io.enmasse.controller.router.config.RouterConfig; -import io.enmasse.controller.router.config.SslProfile; -import io.enmasse.controller.router.config.VhostPolicy; -import io.enmasse.controller.router.config.VhostPolicyGroup; -import io.fabric8.kubernetes.api.model.ConfigMap; -import io.fabric8.kubernetes.api.model.ConfigMapBuilder; -import io.fabric8.kubernetes.api.model.Secret; -import io.fabric8.kubernetes.api.model.SecretBuilder; -import io.fabric8.kubernetes.api.model.Volume; -import io.fabric8.kubernetes.api.model.VolumeBuilder; -import io.fabric8.kubernetes.api.model.VolumeMountBuilder; -import io.fabric8.kubernetes.api.model.apps.StatefulSet; -import io.fabric8.kubernetes.client.NamespacedKubernetesClient; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import java.io.IOException; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Collections; -import java.util.HashMap; -import java.util.HashSet; -import java.util.Iterator; -import java.util.List; -import java.util.Map; -import java.util.Optional; -import java.util.Set; -import java.util.stream.Collectors; - -public class RouterConfigController implements Controller { - private static final Logger log = LoggerFactory.getLogger(RouterConfigController.class); - - private final NamespacedKubernetesClient client; - private final String namespace; - private final AuthenticationServiceResolver authenticationServiceResolver; - private final RouterStatusCache routerStatusCache; - - public RouterConfigController(NamespacedKubernetesClient client, String namespace, AuthenticationServiceResolver authenticationServiceResolver, RouterStatusCache routerStatusCache) { - this.client = client; - this.namespace = namespace; - this.authenticationServiceResolver = authenticationServiceResolver; - this.routerStatusCache = routerStatusCache; - } - - public AddressSpace reconcileActive(AddressSpace addressSpace) throws Exception { - InfraConfig infraConfig = InfraConfigs.parseCurrentInfraConfig(addressSpace); - - if (infraConfig instanceof StandardInfraConfig) { - resetConnectorStatuses(addressSpace); - RouterSet routerSet = RouterSet.create(namespace, addressSpace, client); - reconcileRouterSetSecrets(addressSpace, routerSet); - reconcileRouterConfig(addressSpace, routerSet, (StandardInfraConfig) infraConfig); - if (routerSet.isModified()) { - addressSpace.getStatus().setPhase(Phase.Configuring); - } - routerSet.apply(client); - checkRouterConnectorStatus(addressSpace); - } - return addressSpace; - } - - private static String routerConfigName(String infraUuid) { - return "qdrouterd-config." + infraUuid; - } - - private static ConfigMapBuilder createNewConfigMap(String infraUuid) { - // NOTE: Deletion of this configmap is handled by ComponentFinalizerController based on these labels - return new ConfigMapBuilder().editOrNewMetadata() - .withName(routerConfigName(infraUuid)) - .addToLabels("app", "enmasse") - .addToLabels("infraType", "standard") - .addToLabels("infraUuid", infraUuid) - .endMetadata(); - } - - private void resetConnectorStatuses(AddressSpace addressSpace) { - List connectorStatuses = addressSpace.getSpec().getConnectors() - .stream() - .map(connector -> new AddressSpaceStatusConnectorBuilder() - .withName(connector.getName()) - .withReady(true) - .build()) - .collect(Collectors.toList()); - addressSpace.getStatus().setConnectors(connectorStatuses); - } - - private void reconcileRouterConfig(AddressSpace addressSpace, RouterSet routerSet, StandardInfraConfig infraConfig) throws IOException { - String infraUuid = addressSpace.getAnnotation(AnnotationKeys.INFRA_UUID); - ConfigMap config = client.configMaps().inNamespace(namespace).withName(routerConfigName(infraUuid)).get(); - RouterConfig current = null; - if (config != null) { - current = RouterConfig.fromMap(config.getData()); - } - RouterConfig desired = generateConfig(addressSpace, authenticationServiceResolver.resolve(addressSpace), infraConfig); - if (!desired.equals(current)) { - log.debug("Router config updated. Before: '{}', After: '{}'", current, desired); - Map data = desired.toMap(); - if (config == null) { - config = createNewConfigMap(infraUuid) - .withData(data) - .build(); - client.configMaps().inNamespace(namespace).withName(config.getMetadata().getName()).create(config); - } else { - config.setData(data); - client.configMaps().inNamespace(namespace).withName(config.getMetadata().getName()).replace(config); - - routerSet.setModified(); - } - } - } - - private void reconcileRouterSetSecrets(AddressSpace addressSpace, RouterSet routerSet) { - - String infraUuid = addressSpace.getAnnotation(AnnotationKeys.INFRA_UUID); - Map secretToConnector = new HashMap<>(); - for (AddressSpaceSpecConnector connector : addressSpace.getSpec().getConnectors()) { - String secretName = String.format("external-connector-%s-%s", infraUuid, connector.getName()); - Secret connectorSecret = client.secrets().inNamespace(namespace).withName(secretName).get(); - if (connectorSecret == null) { - connectorSecret = new SecretBuilder() - .editOrNewMetadata() - .withName(secretName) - .withNamespace(namespace) - .addToLabels(LabelKeys.INFRA_UUID, infraUuid) - .addToLabels(LabelKeys.INFRA_TYPE, "standard") - .endMetadata() - .withType("tls") - .withData(new HashMap<>()) - .build(); - reconcileConnectorSecret(connectorSecret, connector, addressSpace); - client.secrets().inNamespace(namespace).withName(secretName).createOrReplace(connectorSecret); - } else if (reconcileConnectorSecret(connectorSecret, connector, addressSpace)) { - client.secrets().inNamespace(namespace).withName(secretName).createOrReplace(connectorSecret); - } - secretToConnector.put(secretName, connector.getName()); - } - - StatefulSet router = routerSet.getStatefulSet(); - if (routerSet.getStatefulSet() == null) { - log.warn("Unable to find expected router statefulset {}", KubeUtil.getRouterSetName(addressSpace)); - return; - } - - log.debug("Before volumes: {}", router.getSpec().getTemplate().getSpec().getVolumes().stream().map(Volume::getName).collect(Collectors.toSet())); - - Set missingSecrets = new HashSet<>(secretToConnector.keySet()); - boolean hasChanged = false; - - // Remove secrets for non-existing connectors - String secretPrefix = String.format("external-connector-%s-", infraUuid); - Iterator volumeIt = router.getSpec().getTemplate().getSpec().getVolumes().iterator(); - while (volumeIt.hasNext()) { - Volume volume = volumeIt.next(); - if (volume.getSecret() != null) { - if (missingSecrets.contains(volume.getSecret().getSecretName())) { - missingSecrets.remove(volume.getSecret().getSecretName()); - } else if (volume.getSecret().getSecretName().startsWith(secretPrefix) && - !secretToConnector.keySet().contains(volume.getSecret().getSecretName())) { - volumeIt.remove(); - router.getSpec().getTemplate().getSpec().getContainers().get(0).getVolumeMounts() - .removeIf(m -> m.getName().equals(volume.getName())); - hasChanged = true; - } - } - } - - // Add volume for secrets that are missing - for (String secretName : missingSecrets) { - router.getSpec().getTemplate().getSpec().getVolumes().add( - new VolumeBuilder() - .withName(secretName) - .withNewSecret() - .withSecretName(secretName) - .endSecret() - .build()); - router.getSpec().getTemplate().getSpec().getContainers().get(0).getVolumeMounts().add( - new VolumeMountBuilder() - .withMountPath(String.format("/etc/enmasse-connectors/%s", secretToConnector.get(secretName))) - .withName(secretName) - .withReadOnly(true) - .build()); - hasChanged = true; - } - - log.debug("After volumes: {}", router.getSpec().getTemplate().getSpec().getVolumes().stream().map(Volume::getName).collect(Collectors.toSet())); - - if (hasChanged) { - routerSet.setModified(); - } - } - - private boolean reconcileConnectorSecret(Secret connectorSecret, AddressSpaceSpecConnector connector, AddressSpace addressSpace) { - boolean needsUpdate = false; - log.debug("Reconciling connector secret {} for connector {}", connectorSecret, connector); - if (connector.getTls() != null) { - if (connectorSecret.getData() == null) { - connectorSecret.setData(new HashMap<>()); - } - if (connector.getTls().getCaCert() != null) { - String data = getSelectorValue(addressSpace.getMetadata().getNamespace(), connector.getTls().getCaCert(), "ca.crt").orElse(null); - if (data == null) { - updateConnectorStatus(connector.getName(), false, "Unable to locate value or secret for caCert", addressSpace.getStatus().getConnectors()); - } else if (!data.equals(connectorSecret.getData().get("ca.crt"))) { - connectorSecret.getData().put("ca.crt", data); - needsUpdate = true; - } - } - - if (connector.getTls().getClientCert() != null) { - String data = getSelectorValue(addressSpace.getMetadata().getNamespace(), connector.getTls().getClientCert(), "tls.crt").orElse(null); - if (data == null) { - updateConnectorStatus(connector.getName(), false, "Unable to locate value or secret for clientCert", addressSpace.getStatus().getConnectors()); - } else if (!data.equals(connectorSecret.getData().get("tls.crt"))) { - connectorSecret.getData().put("tls.crt", data); - needsUpdate = true; - } - } - - if (connector.getTls().getClientKey() != null) { - String data = getSelectorValue(addressSpace.getMetadata().getNamespace(), connector.getTls().getClientKey(), "tls.key").orElse(null); - if (data == null) { - updateConnectorStatus(connector.getName(), false, "Unable to locate value or secret for clientKey", addressSpace.getStatus().getConnectors()); - } else if (!data.equals(connectorSecret.getData().get("tls.key"))) { - connectorSecret.getData().put("tls.key", data); - needsUpdate = true; - } - } - } - return needsUpdate; - } - - private Optional getSelectorValue(String namespace, StringOrSecretSelector selector, String defaultKey) { - if (selector.getValue() != null) { - return Optional.of(selector.getValue()); - } else if (selector.getValueFromSecret() != null) { - Secret secret = client.secrets().inNamespace(namespace).withName(selector.getValueFromSecret().getName()).get(); - if (secret == null) { - return Optional.empty(); - } - return Optional.ofNullable(secret.getData().get(Optional.ofNullable(selector.getValueFromSecret().getKey()).orElse(defaultKey))); - } - return Optional.empty(); - } - - private RouterConfig generateConfig(AddressSpace addressSpace, AuthenticationServiceSettings authServiceSettings, StandardInfraConfig infraConfig) { - String infraUuid = addressSpace.getAnnotation(AnnotationKeys.INFRA_UUID); - Router router = new Router(); - StandardInfraConfigSpecRouter routerSpec = infraConfig.getSpec() != null ? infraConfig.getSpec().getRouter() : null; - if (routerSpec != null && routerSpec.getWorkerThreads() != null) { - router.setWorkerThreads(routerSpec.getWorkerThreads()); - } - - // SSL Profiles - List sslProfiles = new ArrayList<>(); - SslProfile authServiceSsl = new SslProfile(); - authServiceSsl.setName("auth_service_ssl"); - authServiceSsl.setCaCertFile("/etc/qpid-dispatch/authservice-ca/tls.crt"); - sslProfiles.add(authServiceSsl); - - SslProfile sslDetails = new SslProfile(); - sslDetails.setName("ssl_details"); - sslDetails.setCertFile("/etc/qpid-dispatch/ssl/tls.crt"); - sslDetails.setPrivateKeyFile("/etc/qpid-dispatch/ssl/tls.key"); - sslDetails.setProtocols("TLSv1.2"); - sslProfiles.add(sslDetails); - - SslProfile interRouterTls = new SslProfile(); - interRouterTls.setName("inter_router_tls"); - interRouterTls.setCaCertFile("/etc/enmasse-certs/ca.crt"); - interRouterTls.setCertFile("/etc/enmasse-certs/tls.crt"); - interRouterTls.setPrivateKeyFile("/etc/enmasse-certs/tls.key"); - sslProfiles.add(interRouterTls); - - // Authenticationservice plugin - AuthServicePlugin authService = new AuthServicePlugin(); - authService.setName("auth_service"); - authService.setHost(authServiceSettings.getHost()); - authService.setPort(authServiceSettings.getPort()); - authService.setRealm(authServiceSettings.getRealm()); - authService.setSslProfile("auth_service_ssl"); - - // Listeners - Listener localBypass = new Listener(); - localBypass.setHost("127.0.0.1"); - localBypass.setPort(7777); - localBypass.setAuthenticatePeer(false); - - Listener livenessProbe = new Listener(); - livenessProbe.setHost("127.0.0.1"); - livenessProbe.setPort(7770); - livenessProbe.setAuthenticatePeer(false); - livenessProbe.setHttp(true); - livenessProbe.setMetrics(false); - livenessProbe.setHealthz(true); - livenessProbe.setWebsockets(false); - livenessProbe.setHttpRootDir("invalid"); - - Listener interRouter = new Listener(); - interRouter.setHost("0.0.0.0"); - interRouter.setPort(55672); - interRouter.setRole(Role.inter_router); - interRouter.setAuthenticatePeer(true); - interRouter.setSslProfile("inter_router_tls"); - interRouter.setSaslMechanisms("EXTERNAL"); - if (routerSpec != null) { - if (routerSpec.getIdleTimeout() != null) { - interRouter.setIdleTimeoutSeconds(routerSpec.getIdleTimeout()); - } - - if (routerSpec.getLinkCapacity() != null) { - interRouter.setLinkCapacity(routerSpec.getLinkCapacity()); - } - } - - Listener httpsPublic = new Listener(); - httpsPublic.setHost("0.0.0.0"); - httpsPublic.setPort(8443); - httpsPublic.setSaslPlugin("auth_service"); - httpsPublic.setSslProfile("ssl_details"); - httpsPublic.setHttp(true); - httpsPublic.setAuthenticatePeer(true); - if (routerSpec != null && routerSpec.getPolicy() != null) { - httpsPublic.setPolicyVhost("public"); - } - - if (routerSpec != null) { - if (routerSpec.getIdleTimeout() != null) { - httpsPublic.setIdleTimeoutSeconds(routerSpec.getIdleTimeout()); - } - - if (routerSpec.getLinkCapacity() != null) { - httpsPublic.setLinkCapacity(routerSpec.getLinkCapacity()); - } - } - - Listener amqpPublic = new Listener(); - amqpPublic.setHost("0.0.0.0"); - amqpPublic.setPort(5672); - amqpPublic.setSaslPlugin("auth_service"); - amqpPublic.setAuthenticatePeer(true); - - if (routerSpec != null && routerSpec.getPolicy() != null) { - amqpPublic.setPolicyVhost("public"); - } - - if (routerSpec != null) { - if (routerSpec.getIdleTimeout() != null) { - amqpPublic.setIdleTimeoutSeconds(routerSpec.getIdleTimeout()); - } - - if (routerSpec.getLinkCapacity() != null) { - amqpPublic.setLinkCapacity(routerSpec.getLinkCapacity()); - } - - if (routerSpec.getHandshakeTimeout() != null) { - amqpPublic.setInitialHandshakeTimeoutSeconds(routerSpec.getHandshakeTimeout()); - } - } - - Listener amqpsPublic = new Listener(); - amqpsPublic.setHost("0.0.0.0"); - amqpsPublic.setPort(5671); - amqpsPublic.setSaslPlugin("auth_service"); - amqpsPublic.setSslProfile("ssl_details"); - amqpsPublic.setRequireSsl(true); - amqpsPublic.setAuthenticatePeer(true); - - if (routerSpec != null && routerSpec.getPolicy() != null) { - amqpsPublic.setPolicyVhost("public"); - } - - if (routerSpec != null) { - if (routerSpec.getIdleTimeout() != null) { - amqpsPublic.setIdleTimeoutSeconds(routerSpec.getIdleTimeout()); - } - - if (routerSpec.getLinkCapacity() != null) { - amqpsPublic.setLinkCapacity(routerSpec.getLinkCapacity()); - } - - if (routerSpec.getHandshakeTimeout() != null) { - amqpsPublic.setInitialHandshakeTimeoutSeconds(routerSpec.getHandshakeTimeout()); - } - } - - - Listener amqpsInternal = new Listener(); - amqpsInternal.setHost("0.0.0.0"); - amqpsInternal.setPort(55671); - amqpsInternal.setSslProfile("inter_router_tls"); - amqpsInternal.setRequireSsl(true); - amqpsInternal.setSaslMechanisms("EXTERNAL"); - amqpsInternal.setAuthenticatePeer(true); - - if (routerSpec != null) { - if (routerSpec.getIdleTimeout() != null) { - amqpsInternal.setIdleTimeoutSeconds(routerSpec.getIdleTimeout()); - } - - if (routerSpec.getLinkCapacity() != null) { - amqpsInternal.setLinkCapacity(routerSpec.getLinkCapacity()); - } - } - - Listener amqpsRouteContainer = new Listener(); - amqpsRouteContainer.setHost("0.0.0.0"); - amqpsRouteContainer.setPort(56671); - amqpsRouteContainer.setSslProfile("inter_router_tls"); - amqpsRouteContainer.setRole(Role.route_container); - amqpsRouteContainer.setRequireSsl(true); - amqpsRouteContainer.setSaslMechanisms("EXTERNAL"); - amqpsRouteContainer.setAuthenticatePeer(true); - - if (routerSpec != null) { - if (routerSpec.getIdleTimeout() != null) { - amqpsRouteContainer.setIdleTimeoutSeconds(routerSpec.getIdleTimeout()); - } - - if (routerSpec.getLinkCapacity() != null) { - amqpsRouteContainer.setLinkCapacity(routerSpec.getLinkCapacity()); - } - } - - // Policies - List policies = Collections.emptyList(); - if (routerSpec != null && routerSpec.getPolicy() != null) { - Policy policy = new Policy(); - policy.setEnableVhostPolicy(true); - policies = Collections.singletonList(policy); - } - - // VhostPolicy - List vhostPolicies = Collections.emptyList(); - if (routerSpec != null && routerSpec.getPolicy() != null) { - vhostPolicies = createVhostPolices(routerSpec.getPolicy()); - } - - // Connectors - List connectors = new ArrayList<>(); - Connector ragentConnector = new Connector(); - ragentConnector.setHost("ragent-" + infraUuid); - ragentConnector.setPort(5671); - ragentConnector.setSslProfile("inter_router_tls"); - ragentConnector.setVerifyHostname(false); - connectors.add(ragentConnector); - - // Addresses - List
addresses = new ArrayList<>(); - Address mqttAddress = new Address(); - mqttAddress.setName("override.mqtt"); - mqttAddress.setPrefix("$mqtt"); - mqttAddress.setDistribution(Distribution.balanced); - addresses.add(mqttAddress); - - Address subctrlAddress = new Address(); - subctrlAddress.setName("override.subctrl"); - subctrlAddress.setPrefix("$subctrl"); - subctrlAddress.setDistribution(Distribution.balanced); - addresses.add(subctrlAddress); - - Address tempAddress = new Address(); - tempAddress.setName("override.temp"); - tempAddress.setPrefix("$temp"); - tempAddress.setDistribution(Distribution.balanced); - addresses.add(tempAddress); - - Address dlqAddress = new Address(); - dlqAddress.setName("override.global.dlq"); - dlqAddress.setPattern("!!GLOBAL_DLQ"); - dlqAddress.setDistribution(Distribution.balanced); - dlqAddress.setWaypoint(true); - addresses.add(dlqAddress); - - Address routerHealthCheckAddress = new Address(); - routerHealthCheckAddress.setName("!!HEALTH_CHECK_ROUTER"); - routerHealthCheckAddress.setPrefix("!!HEALTH_CHECK_ROUTER"); - mqttAddress.setDistribution(Distribution.balanced); - addresses.add(routerHealthCheckAddress); - - // Autolinks - List autoLinks = new ArrayList<>(); - AutoLink dlqAutoLink = new AutoLink(); - dlqAutoLink.setName("override.global.dlq.in"); - dlqAutoLink.setAddress("!!GLOBAL_DLQ"); - dlqAutoLink.setDirection(LinkDirection.in); - dlqAutoLink.setContainerId("broker-global-dlq-out"); - autoLinks.add(dlqAutoLink); - - // LinkRoutes - List linkRoutes = new ArrayList<>(); - LinkRoute mqttLwtInLinkRoute = new LinkRoute(); - mqttLwtInLinkRoute.setName("override.lwt_in"); - mqttLwtInLinkRoute.setPrefix("$lwt"); - mqttLwtInLinkRoute.setDirection(LinkDirection.in); - mqttLwtInLinkRoute.setContainerId("lwt-service"); - linkRoutes.add(mqttLwtInLinkRoute); - - LinkRoute mqttLwtOutLinkRoute = new LinkRoute(); - mqttLwtOutLinkRoute.setName("override.lwt_out"); - mqttLwtOutLinkRoute.setPrefix("$lwt"); - mqttLwtOutLinkRoute.setDirection(LinkDirection.out); - mqttLwtOutLinkRoute.setContainerId("lwt-service"); - linkRoutes.add(mqttLwtOutLinkRoute); - - // Connectors and addresses based on Connectors configured by user - for (AddressSpaceSpecConnector connector : addressSpace.getSpec().getConnectors()) { - Connector remoteConnector = new Connector(); - remoteConnector.setName(connector.getName()); - AddressSpaceStatusConnector connectorStatus = addressSpace.getStatus().getConnectors().stream() - .filter(status -> status.getName().equals(connector.getName())) - .findFirst().orElse(null); - - // Don't bother adding connector status if it already failed - if (connectorStatus == null || !connectorStatus.isReady()) { - continue; - } - - Iterator endpointIt = connector.getEndpointHosts().iterator(); - // If connector is missing initial host: - if (!endpointIt.hasNext()) { - updateConnectorStatus(connector.getName(), false, "Missing at least one endpoint on connector", addressSpace.getStatus().getConnectors()); - continue; - } - - AddressSpaceSpecConnectorEndpoint first = endpointIt.next(); - remoteConnector.setHost(first.getHost()); - remoteConnector.setPort(connector.getPort(first.getPort())); - - List failoverUrls = new ArrayList<>(); - while (endpointIt.hasNext()) { - AddressSpaceSpecConnectorEndpoint failover = endpointIt.next(); - String protocol = "amqp" + (connector.getTls() != null ? "s" : ""); - failoverUrls.add(String.format("%s://%s:%d", protocol, failover.getHost(), connector.getPort(failover.getPort()))); - } - if (!failoverUrls.isEmpty()) { - remoteConnector.setFailoverUrls(String.join(",", failoverUrls)); - } - - List saslMechanisms = new ArrayList<>(); - - // If tls field is set, we will enable TLS - AddressSpaceSpecConnectorTls tls = connector.getTls(); - if (tls != null) { - String sslProfileName = String.format("connector_%s_settings", connector.getName()); - remoteConnector.setSslProfile(sslProfileName); - - SslProfile sslProfile = new SslProfile(); - sslProfile.setName(sslProfileName); - - if (tls.getCaCert() != null) { - sslProfile.setCaCertFile(String.format("/etc/enmasse-connectors/%s/ca.crt", connector.getName())); - } - - if (tls.getClientCert() != null || tls.getClientKey() != null) { - if (tls.getClientCert() == null) { - updateConnectorStatus(connector.getName(), false, "Both clientCert and clientKey must be specified (only clientKey is specified)", addressSpace.getStatus().getConnectors()); - continue; - } - - if (tls.getClientKey() == null) { - updateConnectorStatus(connector.getName(), false, "Both clientCert and clientKey must be specified (only clientCert is specified)", addressSpace.getStatus().getConnectors()); - continue; - } - - sslProfile.setCertFile(String.format("/etc/enmasse-connectors/%s/tls.crt", connector.getName())); - sslProfile.setPrivateKeyFile(String.format("/etc/enmasse-connectors/%s/tls.key", connector.getName())); - } - - sslProfiles.add(sslProfile); - saslMechanisms.add("EXTERNAL"); - } - - AddressSpaceSpecConnectorCredentials credentials = connector.getCredentials(); - if (credentials != null) { - if (connector.getCredentials().getUsername() != null) { - String data = getSelectorValue(addressSpace.getMetadata().getNamespace(), connector.getCredentials().getUsername(), "username").orElse(null); - if (data == null) { - updateConnectorStatus(connector.getName(), false, "Unable to locate value or secret for username", addressSpace.getStatus().getConnectors()); - continue; - } - remoteConnector.setSaslUsername(data); - } - - if (connector.getCredentials().getPassword() != null) { - String data = getSelectorValue(addressSpace.getMetadata().getNamespace(), connector.getCredentials().getPassword(), "password").orElse(null); - if (data == null) { - updateConnectorStatus(connector.getName(), false, "Unable to locate value or secret for password", addressSpace.getStatus().getConnectors()); - continue; - } - remoteConnector.setSaslPassword(data); - } - - saslMechanisms.add("PLAIN"); - } - - if (saslMechanisms.isEmpty()) { - saslMechanisms.add("ANONYMOUS"); - } - - remoteConnector.setSaslMechanisms(String.join(" ", saslMechanisms)); - - String prefix = String.format("%s/", connector.getName()); - for (AddressSpaceSpecConnectorAddressRule rule : connector.getAddresses()) { - LinkRoute linkRouteIn = new LinkRoute(); - linkRouteIn.setName("override.connector." + connector.getName() + "." + rule.getName() + ".in"); - linkRouteIn.setPattern(prefix + rule.getPattern()); - linkRouteIn.setDelExternalPrefix(prefix); - linkRouteIn.setDirection(LinkDirection.in); - linkRouteIn.setConnection(connector.getName()); - linkRoutes.add(linkRouteIn); - - LinkRoute linkRouteOut = new LinkRoute(); - linkRouteOut.setName("override.connector." + connector.getName() + "." + rule.getName() + ".out"); - linkRouteOut.setPattern(connector.getName() + "/" + rule.getPattern()); - linkRouteOut.setDelExternalPrefix(prefix); - linkRouteOut.setDirection(LinkDirection.out); - linkRouteOut.setConnection(connector.getName()); - linkRoutes.add(linkRouteOut); - } - - remoteConnector.setRole(Role.forValue(connector.getRole())); - - if (connector.getIdleTimeout() != null) { - remoteConnector.setIdleTimeoutSeconds(connector.getIdleTimeout()); - } - - if (connector.getMaxFrameSize() != null) { - remoteConnector.setMaxFrameSize(Math.max(512 /* AMQP MIN-MAX-FRAME-SIZE */ , connector.getMaxFrameSize())); - } - - connectors.add(remoteConnector); - } - - return new RouterConfig(router, - sslProfiles, - Collections.singletonList(authService), - Arrays.asList(localBypass, livenessProbe, httpsPublic, amqpPublic, amqpsPublic, amqpsInternal, amqpsRouteContainer, interRouter), - policies, - connectors, - autoLinks, - linkRoutes, - addresses, - vhostPolicies); - } - - static List createVhostPolices(RouterPolicySpec policy) { - // Public settings derived from infra config settings - VhostPolicyGroup group = new VhostPolicyGroup(); - group.setRemoteHosts("*"); - group.setSources("*"); - group.setTargets("*"); - group.setAllowAnonymousSender(true); - group.setAllowDynamicSource(true); - - if (policy.getMaxSessionsPerConnection() != null) { - group.setMaxSessions(policy.getMaxSessionsPerConnection()); - } - - if (policy.getMaxSendersPerConnection() != null) { - group.setMaxSenders(policy.getMaxSendersPerConnection()); - } - - if (policy.getMaxReceiversPerConnection() != null) { - group.setMaxReceivers(policy.getMaxReceiversPerConnection()); - } - - VhostPolicy vhostPolicy = new VhostPolicy(); - vhostPolicy.setHostname("public"); - vhostPolicy.setAllowUnknownUser(true); - - if (policy.getMaxConnections() != null) { - vhostPolicy.setMaxConnections(policy.getMaxConnections()); - } - - if (policy.getMaxConnectionsPerHost() != null) { - vhostPolicy.setMaxConnectionsPerHost(policy.getMaxConnectionsPerHost()); - } - - if (policy.getMaxConnectionsPerUser() != null) { - vhostPolicy.setMaxConnectionsPerUser(policy.getMaxConnectionsPerUser()); - } - - vhostPolicy.setGroups(Collections.singletonMap("$default", group)); - - VhostPolicyGroup internalGroup = new VhostPolicyGroup(); - internalGroup.setRemoteHosts("*"); - internalGroup.setSources("*"); - internalGroup.setTargets("*"); - internalGroup.setAllowDynamicSource(true); - internalGroup.setAllowAnonymousSender(true); - - VhostPolicy internalVhostPolicy = new VhostPolicy(); - internalVhostPolicy.setHostname("$default"); - internalVhostPolicy.setAllowUnknownUser(true); - internalVhostPolicy.setGroups(Collections.singletonMap("$default", internalGroup)); - - return Arrays.asList(internalVhostPolicy, vhostPolicy); - } - - private static void updateConnectorStatus(String name, boolean isReady, String message, List connectorStatuses) { - connectorStatuses.stream() - .filter(c -> c.getName().equals(name)) - .findFirst().ifPresent(s -> { - s.setReady(isReady); - s.appendMessage(message); - }); - } - - void checkRouterConnectorStatus(AddressSpace addressSpace) { - Map connectorMap = new HashMap<>(); - for (AddressSpaceSpecConnector connector : addressSpace.getSpec().getConnectors()) { - connectorMap.put(connector.getName(), connector); - } - - List routerStatusList = routerStatusCache.getLatestResult(addressSpace); - - for (AddressSpaceStatusConnector connector : addressSpace.getStatus().getConnectors()) { - checkConnectorStatus(connector, connectorMap.get(connector.getName()), routerStatusList); - } - } - - /* - * Until the connector entity allows querying for the status, we have to go through all connections and - * see if we can find our connector host in there. - */ - private void checkConnectorStatus(AddressSpaceStatusConnector connectorStatus, AddressSpaceSpecConnector connector, List response) { - Map connectionStatuses = new HashMap<>(); - for (AddressSpaceSpecConnectorEndpoint endpoint : connector.getEndpointHosts()) { - String host = String.format("%s:%d", endpoint.getHost(), connector.getPort(endpoint.getPort())); - connectionStatuses.put(host, new ConnectionStatus()); - } - - if (response == null || response.isEmpty()) { - connectorStatus.setReady(false); - connectorStatus.appendMessage("No router status found."); - return; - } - - for (RouterStatus routerStatus : response) { - List hosts = routerStatus.getConnections().getHosts(); - List opened = routerStatus.getConnections().getOpened(); - List operStatus = routerStatus.getConnections().getOperStatus(); - - for (int i = 0; i < hosts.size(); i++) { - ConnectionStatus status = connectionStatuses.get(hosts.get(i)); - if (status != null) { - status.setFound(true); - if (operStatus.get(i).equals("up")) { - status.setConnected(true); - } - if (opened.get(i)) { - status.setOpened(true); - } - } - } - } - - // Assumption/decision: If the primary or failover for any connector is up, we are ok - List found = connectionStatuses.values().stream() - .filter(ConnectionStatus::isFound) - .collect(Collectors.toList()); - - List isConnected = found.stream() - .filter(ConnectionStatus::isConnected) - .collect(Collectors.toList()); - - List isOpened = isConnected.stream() - .filter(ConnectionStatus::isOpened) - .collect(Collectors.toList()); - - if (found.isEmpty()) { - connectorStatus.setReady(false); - connectorStatus.appendMessage("Unable to find active connection for connector '" + connector.getName() + "'"); - return; - } - - if (isConnected.isEmpty()) { - connectorStatus.setReady(false); - connectorStatus.appendMessage("Unable to find connection in the connected state for connector '" + connector.getName() + "'"); - } - - if (isOpened.isEmpty()) { - connectorStatus.setReady(false); - connectorStatus.appendMessage("Unable to find connection in the opened state for connector '" + connector.getName() + "'"); - } - } - - private void checkRouterMesh(AddressSpace addressSpace, List routerStatusList) { - final List routers = new ArrayList<>(); - Set routerIds = routerStatusList.stream().map(RouterStatus::getRouterId).collect(Collectors.toSet()); - - for (RouterStatus routerStatus : routerStatusList) { - String routerId = routerStatus.getRouterId(); - List neighbors = new ArrayList<>(routerStatus.getNeighbors()); - // Add ourselves to make the comparison simpler - neighbors.add(routerId); - - if (!neighbors.containsAll(routerIds)) { - Set missing = new HashSet<>(routerIds); - missing.removeAll(neighbors); - String msg = String.format("Router %s is missing connection to %s.", routerId, missing); - log.warn(msg); - addressSpace.getStatus().setReady(false); - addressSpace.getStatus().appendMessage(msg); - } - - AddressSpaceStatusRouter addressSpaceStatusRouter = new AddressSpaceStatusRouter(); - addressSpaceStatusRouter.setId(routerId); - addressSpaceStatusRouter.setNeighbors(neighbors); - addressSpaceStatusRouter.setUndelivered(routerStatus.getUndelivered()); - - log.debug("Router {} has neighbors: {} and undelivered: {}", routerId, neighbors, routerStatus.getUndelivered()); - routers.add(addressSpaceStatusRouter); - } - addressSpace.getStatus().setRouters(routers); - } - - private static class ConnectionStatus { - private boolean isFound = false; - private boolean isConnected = false; - private boolean isOpened = false; - - boolean isConnected() { - return isConnected; - } - - void setConnected(boolean connected) { - isConnected = connected; - } - - boolean isOpened() { - return isOpened; - } - - void setOpened(boolean opened) { - isOpened = opened; - } - - boolean isFound() { - return isFound; - } - - void setFound(boolean found) { - isFound = found; - } - } - - @Override - public String toString() { - return "RouterConfigController"; - } -} diff --git a/address-space-controller/src/main/java/io/enmasse/controller/RouterConnections.java b/address-space-controller/src/main/java/io/enmasse/controller/RouterConnections.java deleted file mode 100644 index 13d0433df79..00000000000 --- a/address-space-controller/src/main/java/io/enmasse/controller/RouterConnections.java +++ /dev/null @@ -1,31 +0,0 @@ -/* - * Copyright 2020, EnMasse authors. - * License: Apache License 2.0 (see the file LICENSE or http://apache.org/licenses/LICENSE-2.0.html). - */ -package io.enmasse.controller; - -import java.util.List; - -public class RouterConnections { - private final List hosts; - private final List opened; - private final List operStatus; - - public RouterConnections(List hosts, List opened, List operStatus) { - this.hosts = hosts; - this.opened = opened; - this.operStatus = operStatus; - } - - public List getHosts() { - return hosts; - } - - public List getOpened() { - return opened; - } - - public List getOperStatus() { - return operStatus; - } -} diff --git a/address-space-controller/src/main/java/io/enmasse/controller/RouterSet.java b/address-space-controller/src/main/java/io/enmasse/controller/RouterSet.java deleted file mode 100644 index 427fe46df77..00000000000 --- a/address-space-controller/src/main/java/io/enmasse/controller/RouterSet.java +++ /dev/null @@ -1,61 +0,0 @@ -/* - * Copyright 2019, EnMasse authors. - * License: Apache License 2.0 (see the file LICENSE or http://apache.org/licenses/LICENSE-2.0.html). - */ -package io.enmasse.controller; - -import io.enmasse.address.model.AddressSpace; -import io.enmasse.address.model.KubeUtil; -import io.enmasse.config.AnnotationKeys; -import io.fabric8.kubernetes.api.model.apps.StatefulSet; -import io.fabric8.kubernetes.client.NamespacedKubernetesClient; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import java.util.HashMap; -import java.util.Map; - -public class RouterSet { - private static final Logger log = LoggerFactory.getLogger(RouterSet.class); - private final StatefulSet statefulSet; - private final AddressSpace addressSpace; - private final String namespace; - private boolean modified; - - public RouterSet(StatefulSet statefulSet, String namespace, AddressSpace addressSpace) { - this.statefulSet = statefulSet; - this.namespace = namespace; - this.addressSpace = addressSpace; - this.modified = false; - } - - public static RouterSet create(String namespace, AddressSpace addressSpace, NamespacedKubernetesClient client) { - StatefulSet router = client.apps().statefulSets().withName(KubeUtil.getRouterSetName(addressSpace)).get(); - return new RouterSet(router, namespace, addressSpace); - } - - public StatefulSet getStatefulSet() { - return statefulSet; - } - - public void apply(NamespacedKubernetesClient client) { - if (modified && statefulSet != null) { - log.debug("Applying changes to router: {}", statefulSet); - Map annotations = statefulSet.getSpec().getTemplate().getMetadata().getAnnotations(); - if (annotations == null) { - annotations = new HashMap<>(); - } - annotations.put(AnnotationKeys.APPLIED_CONFIGURATION, addressSpace.getAnnotation(AnnotationKeys.APPLIED_CONFIGURATION)); - statefulSet.getSpec().getTemplate().getMetadata().setAnnotations(annotations); - client.apps().statefulSets().inNamespace(namespace).withName(statefulSet.getMetadata().getName()).cascading(false).patch(statefulSet); - } - } - - public void setModified() { - this.modified = true; - } - - public boolean isModified() { - return this.modified; - } -} diff --git a/address-space-controller/src/main/java/io/enmasse/controller/RouterStatus.java b/address-space-controller/src/main/java/io/enmasse/controller/RouterStatus.java deleted file mode 100644 index d110952e686..00000000000 --- a/address-space-controller/src/main/java/io/enmasse/controller/RouterStatus.java +++ /dev/null @@ -1,37 +0,0 @@ -/* - * Copyright 2016-2020, EnMasse authors. - * License: Apache License 2.0 (see the file LICENSE or http://apache.org/licenses/LICENSE-2.0.html). - */ -package io.enmasse.controller; - -import java.util.List; - -class RouterStatus { - private final String routerId; - private final RouterConnections connections; - private final List neighbors; - private final long undelivered; - - RouterStatus(String routerId, RouterConnections connections, List neighbors, long undelivered) { - this.routerId = routerId; - this.connections = connections; - this.neighbors = neighbors; - this.undelivered = undelivered; - } - - public String getRouterId() { - return routerId; - } - - public RouterConnections getConnections() { - return connections; - } - - public List getNeighbors() { - return neighbors; - } - - public long getUndelivered() { - return undelivered; - } -} diff --git a/address-space-controller/src/main/java/io/enmasse/controller/RouterStatusCache.java b/address-space-controller/src/main/java/io/enmasse/controller/RouterStatusCache.java deleted file mode 100644 index 4029dfac664..00000000000 --- a/address-space-controller/src/main/java/io/enmasse/controller/RouterStatusCache.java +++ /dev/null @@ -1,309 +0,0 @@ -/* - * Copyright 2019, EnMasse authors. - * License: Apache License 2.0 (see the file LICENSE or http://apache.org/licenses/LICENSE-2.0.html). - */ -package io.enmasse.controller; - -import io.enmasse.address.model.AddressSpace; -import io.enmasse.address.model.AddressSpaceBuilder; -import io.enmasse.address.model.KubeUtil; -import io.enmasse.amqp.RouterEntity; -import io.enmasse.amqp.RouterManagement; -import io.enmasse.config.AnnotationKeys; -import io.enmasse.config.LabelKeys; -import io.enmasse.controller.common.ControllerKind; -import io.enmasse.k8s.api.EventLogger; -import io.fabric8.kubernetes.api.model.Container; -import io.fabric8.kubernetes.api.model.ContainerPort; -import io.fabric8.kubernetes.api.model.Pod; -import io.fabric8.kubernetes.api.model.Secret; -import io.fabric8.kubernetes.client.NamespacedKubernetesClient; -import io.fabric8.kubernetes.client.internal.readiness.Readiness; -import io.vertx.core.Vertx; -import org.apache.qpid.proton.amqp.UnsignedLong; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import java.time.Duration; -import java.util.ArrayList; -import java.util.Base64; -import java.util.HashMap; -import java.util.List; -import java.util.Map; -import java.util.concurrent.ExecutorCompletionService; -import java.util.concurrent.ForkJoinPool; -import java.util.stream.Collectors; - -import static io.enmasse.controller.common.ControllerReason.RouterCheckFailed; -import static io.enmasse.k8s.api.EventLogger.Type.Warning; - -public class RouterStatusCache implements Runnable, Controller { - private static final Logger log = LoggerFactory.getLogger(RouterStatusCache.class); - - private final EventLogger eventLogger; - - private volatile boolean running = false; - private Thread thread; - private final Duration checkInterval; - private final Object monitor = new Object(); - private boolean needCheck = false; - - private final NamespacedKubernetesClient client; - private final String namespace; - private final Vertx vertx = Vertx.vertx(); - private final Duration connectTimeout; - private final Duration queryTimeout; - - private final Object lock = new Object(); - private final List currentAddressSpaces = new ArrayList<>(); - private final Map> routerStatusMap = new HashMap<>(); - - RouterStatusCache(EventLogger eventLogger, Duration checkInterval, NamespacedKubernetesClient client, String namespace, Duration connectTimeout, Duration queryTimeout) - { - this.eventLogger = eventLogger; - this.checkInterval = checkInterval; - this.client = client; - this.namespace = namespace; - this.connectTimeout = connectTimeout; - this.queryTimeout = queryTimeout; - } - - List getLatestResult(AddressSpace addressSpace) { - synchronized (lock) { - return routerStatusMap.get(addressSpace); - } - } - - @Override - public void reconcileAll(List addressSpaces) { - synchronized (lock) { - // Clear stale entries to prevent old status data to be present - routerStatusMap.entrySet().removeIf(e -> !addressSpaces.contains(e.getKey())); - currentAddressSpaces.clear(); - // Take a deep copy of address spaces to ensure stable data - currentAddressSpaces.addAll(addressSpaces.stream() - .filter(addressSpace -> "standard".equals(addressSpace.getSpec().getType())) - .map(addressSpace -> new AddressSpaceBuilder(addressSpace).build()) - .collect(Collectors.toList())); - } - wakeup(); - } - - public void start() { - running = true; - thread = new Thread(this); - thread.setName("router-status-cache"); - thread.setDaemon(true); - thread.start(); - } - - public void stop() { - try { - running = false; - thread.interrupt(); - thread.join(); - } catch (InterruptedException ignored) { - log.warn("Interrupted while stopping", ignored); - Thread.currentThread().interrupt(); - } - } - - @Override - public void run() { - KubeRouterStatusChecker routerStatusChecker = new KubeRouterStatusChecker(); - while (running) { - try { - checkRouterStatus(routerStatusChecker); - synchronized (monitor) { - if (!needCheck) { - monitor.wait(checkInterval.toMillis()); - } - needCheck = false; - } - } catch (InterruptedException e) { - Thread.currentThread().interrupt(); - } catch (Exception e) { - log.warn("Exception in collector task", e); - } - } - } - - public void wakeup() { - synchronized (monitor) { - needCheck = true; - monitor.notifyAll(); - } - } - - void checkRouterStatus(RouterStatusChecker routerStatusChecker) { - // First grab a copy of address spaces to check - List toCheck = null; - synchronized (lock) { - toCheck = new ArrayList<>(currentAddressSpaces); - } - - Map> checked = new HashMap<>(); - for (AddressSpace addressSpace : toCheck) { - List routerStatusList = routerStatusChecker.collectRouterStatuses(addressSpace); - if (routerStatusList != null) { - checked.put(addressSpace, routerStatusList); - } - } - - // Update the status map based on current set of address spaces, in case it changed - // while we were querying for the router status and to prevent stale entries. - synchronized (lock) { - routerStatusMap.clear(); - for (AddressSpace addressSpace : currentAddressSpaces) { - routerStatusMap.put(addressSpace, checked.get(addressSpace)); - } - } - } - - - private static final RouterEntity connection = new RouterEntity( "org.apache.qpid.dispatch.connection", "operStatus", "opened", "host"); - private static final RouterEntity node = new RouterEntity("org.apache.qpid.dispatch.router.node", "id", "nextHop"); - private static final RouterEntity link = new RouterEntity("org.apache.qpid.dispatch.router.link", "linkType", "undeliveredCount"); - private static final RouterEntity[] entities = new RouterEntity[]{connection, node, link}; - - @Override - public String toString() { - return "RouterStatusCache"; - } - - interface RouterStatusChecker { - List collectRouterStatuses(AddressSpace addressSpace); - } - - private class KubeRouterStatusChecker implements RouterStatusChecker { - - @Override - public List collectRouterStatuses(AddressSpace addressSpace) { - String addressSpaceCaSecretName = KubeUtil.getAddressSpaceCaSecretName(addressSpace); - Secret addressSpaceCa = client.secrets().inNamespace(namespace).withName(addressSpaceCaSecretName).get(); - if (addressSpaceCa == null) { - log.warn("Unable to check router status, missing address space CA secret for {}!", addressSpace); - return null; - } - - Base64.Decoder decoder = Base64.getDecoder(); - byte[] key = decoder.decode(addressSpaceCa.getData().get("tls.key")); - byte[] cert = decoder.decode(addressSpaceCa.getData().get("tls.crt")); - if (key == null) { - log.warn("Unable to check router status, missing address space CA key for {}!", addressSpace); - return null; - } - - if (cert == null) { - log.warn("Unable to check router status, missing address space CA cert for {}!", addressSpace); - return null; - } - - RouterManagement routerManagement = RouterManagement.withCerts(vertx, "address-space-controller", connectTimeout, queryTimeout, cert, cert, key); - - String infraUuid = addressSpace.getAnnotation(AnnotationKeys.INFRA_UUID); - - List routerPods = client.pods().withLabel(LabelKeys.CAPABILITY, "router").withLabel(LabelKeys.INFRA_UUID, infraUuid).list().getItems().stream() - .filter(Readiness::isPodReady) - .collect(Collectors.toList()); - - ExecutorCompletionService service = new ExecutorCompletionService<>(ForkJoinPool.commonPool()); - for (Pod router : routerPods) { - service.submit(() -> collectStatus(routerManagement, router)); - } - - List routerStatusList = new ArrayList<>(); - for (int i = 0; i < routerPods.size(); i++) { - try { - RouterStatus status = service.take().get(); - if (status != null) { - routerStatusList.add(status); - } - } catch (Exception e) { - log.info("Error requesting router status. Ignoring", e); - eventLogger.log(RouterCheckFailed, e.getMessage(), Warning, ControllerKind.AddressSpace, addressSpace.getMetadata().getName()); - } - } - return routerStatusList; - } - - private RouterStatus collectStatus(RouterManagement routerManagement, Pod router) { - try { - - int port = 0; - for (Container container : router.getSpec().getContainers()) { - if (container.getName().equals("router")) { - for (ContainerPort containerPort : container.getPorts()) { - if (containerPort.getName().equals("amqps-normal")) { - port = containerPort.getContainerPort(); - } - } - } - } - - if (port != 0) { - // Until the connector entity allows querying for the status, we have to list - // all connections and match with the connector host. - Map>> response = routerManagement.query(router.getStatus().getPodIP(), port, entities); - - RouterConnections connections = null; - if (response.containsKey(connection)) { - connections = collectConnectionInfo(response.get(connection)); - } - - List neighbors = null; - if (response.containsKey(node)) { - // Remove this router from the neighbour list - neighbors = filterOnAttribute(String.class, 0, response.get(node)).stream() - .filter(n -> !n.equals(router.getMetadata().getName())) - .collect(Collectors.toList()); - } - - long undeliveredTotal = 0; - if (response.containsKey(link)) { - - List linkTypes = filterOnAttribute(String.class, 0, response.get(link)); - List undelivered = filterOnAttribute(UnsignedLong.class, 1, response.get(link)); - for (int i = 0; i < linkTypes.size(); i++) { - if ("inter-router".equals(linkTypes.get(i))) { - undeliveredTotal += undelivered.get(i) != null ? undelivered.get(i).longValue() : 0; - } - } - } - return new RouterStatus(router.getMetadata().getName(), connections, neighbors, undeliveredTotal); - } - } catch (Exception e) { - log.info("Error requesting registered topics from {}. Ignoring", router.getMetadata().getName(), e); - } - return null; - } - - /* - * Until the connector entity allows querying for the status, we have to go through all connections and - * see if we can find our connector host in there. - */ - private RouterConnections collectConnectionInfo(List> response) { - int hostIdx = connection.getAttributeIndex("host"); - int openedIdx = connection.getAttributeIndex("opened"); - int operStatusIdx = connection.getAttributeIndex("operStatus"); - - List hosts = filterOnAttribute(String.class, hostIdx, response); - List opened = filterOnAttribute(Boolean.class, openedIdx, response); - List operStatus = filterOnAttribute(String.class, operStatusIdx, response); - - return new RouterConnections(hosts, opened, operStatus); - } - } - - private static List filterOnAttribute(Class type, int attrNum, List> list) { - List filtered = new ArrayList<>(); - for (List entry : list) { - T filteredValue = type.cast(entry.get(attrNum)); - if (filteredValue != null) { - filtered.add(filteredValue); - } - } - return filtered; - } - -} diff --git a/address-space-controller/src/main/java/io/enmasse/controller/ServiceHelper.java b/address-space-controller/src/main/java/io/enmasse/controller/ServiceHelper.java deleted file mode 100644 index ab8d17bd168..00000000000 --- a/address-space-controller/src/main/java/io/enmasse/controller/ServiceHelper.java +++ /dev/null @@ -1,51 +0,0 @@ -/* - * Copyright 2016-2018, EnMasse authors. - * License: Apache License 2.0 (see the file LICENSE or http://apache.org/licenses/LICENSE-2.0.html). - */ -package io.enmasse.controller; - -import io.enmasse.config.AnnotationKeys; -import io.fabric8.kubernetes.api.model.Service; -import io.fabric8.kubernetes.api.model.ServicePort; -import io.fabric8.kubernetes.api.model.ServicePortBuilder; - -import java.util.ArrayList; -import java.util.HashMap; -import java.util.List; -import java.util.Map; - -public class ServiceHelper { - public static Map getServicePorts(Service service) { - Map servicePorts = new HashMap<>(); - Map serviceAnnotations = service.getMetadata().getAnnotations(); - for (Map.Entry annotationEntry : serviceAnnotations.entrySet()) { - String annotationKey = annotationEntry.getKey(); - String annotationValue = annotationEntry.getValue(); - if (annotationKey.startsWith(AnnotationKeys.SERVICE_PORT_PREFIX)) { - String portName = annotationKey.substring(AnnotationKeys.SERVICE_PORT_PREFIX.length()); - int portValue = Integer.parseInt(annotationValue); - servicePorts.put(portName, portValue); - } - } - return servicePorts; - } - - public static Map fromServicePortList(List servicePorts) { - Map servicePortMap = new HashMap<>(); - for (ServicePort servicePort : servicePorts) { - servicePortMap.put(servicePort.getName(), servicePort.getPort()); - } - return servicePortMap; - } - - public static List toServicePortList(Map servicePorts) { - List servicePortList = new ArrayList<>(); - for (Map.Entry entry : servicePorts.entrySet()) { - servicePortList.add(new ServicePortBuilder() - .withName(entry.getKey()) - .withPort(entry.getValue()) - .build()); - } - return servicePortList; - } -} diff --git a/address-space-controller/src/main/java/io/enmasse/controller/StatusController.java b/address-space-controller/src/main/java/io/enmasse/controller/StatusController.java deleted file mode 100644 index ea84d945168..00000000000 --- a/address-space-controller/src/main/java/io/enmasse/controller/StatusController.java +++ /dev/null @@ -1,246 +0,0 @@ -/* - * Copyright 2017-2018, EnMasse authors. - * License: Apache License 2.0 (see the file LICENSE or http://apache.org/licenses/LICENSE-2.0.html). - */ -package io.enmasse.controller; - -import io.enmasse.address.model.AddressSpace; -import io.enmasse.address.model.AddressSpaceResolver; -import io.enmasse.address.model.AddressSpaceSpec; -import io.enmasse.address.model.AddressSpaceStatusRouter; -import io.enmasse.address.model.EndpointSpec; -import io.enmasse.address.model.EndpointStatus; -import io.enmasse.address.model.ExposeType; -import io.enmasse.address.model.Phase; -import io.enmasse.admin.model.v1.AuthenticationService; -import io.enmasse.admin.model.v1.AuthenticationServiceType; -import io.enmasse.admin.model.v1.InfraConfig; -import io.enmasse.admin.model.v1.StandardInfraConfig; -import io.enmasse.config.AnnotationKeys; -import io.enmasse.controller.common.Kubernetes; -import io.enmasse.controller.common.KubernetesHelper; -import io.enmasse.k8s.api.AuthenticationServiceRegistry; -import io.enmasse.k8s.api.SchemaProvider; -import io.enmasse.user.api.RealmApi; -import io.fabric8.kubernetes.api.model.HasMetadata; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import java.io.IOException; -import java.util.ArrayList; -import java.util.HashMap; -import java.util.HashSet; -import java.util.List; -import java.util.Map; -import java.util.Optional; -import java.util.Set; -import java.util.stream.Collectors; - -import static io.enmasse.controller.InfraConfigs.parseCurrentInfraConfig; - -public class StatusController implements Controller { - private static final Logger log = LoggerFactory.getLogger(StatusController.class.getName()); - private final Kubernetes kubernetes; - private final SchemaProvider schemaProvider; - private final InfraResourceFactory infraResourceFactory; - private final AuthenticationServiceRegistry authenticationServiceRegistry; - private final AuthenticationServiceResolver authenticationServiceResolver; - private final RealmApi realmApi; - private final RouterStatusCache routerStatusCache; - - public StatusController(Kubernetes kubernetes, SchemaProvider schemaProvider, InfraResourceFactory infraResourceFactory, AuthenticationServiceRegistry authenticationServiceRegistry, RealmApi realmApi, RouterStatusCache routerStatusCache) { - this.kubernetes = kubernetes; - this.schemaProvider = schemaProvider; - this.infraResourceFactory = infraResourceFactory; - this.authenticationServiceRegistry = authenticationServiceRegistry; - this.authenticationServiceResolver = new AuthenticationServiceResolver(authenticationServiceRegistry); - this.realmApi = realmApi; - this.routerStatusCache = routerStatusCache; - } - - @Override - public AddressSpace reconcileActive(AddressSpace addressSpace) throws IOException { - if (addressSpace.getStatus().isReady()) { - checkComponentsReady(addressSpace); - checkRouterStatus(addressSpace); - checkAuthServiceReady(addressSpace); - checkExposedEndpoints(addressSpace); - } - - if (addressSpace.getStatus().isReady()) { - final AppliedConfig appliedConfig = AppliedConfig.parseCurrentAppliedConfig(addressSpace); - final AddressSpaceSpec spec = AppliedConfig.normalize(AddressSpaceSpec.class, addressSpace.getSpec()); - - if (spec.equals(appliedConfig.getAddressSpaceSpec())) { - addressSpace.getStatus().setPhase(Phase.Active); - } else if (log.isDebugEnabled()) { - log.debug("Applied config does not match requested\nApplied : {}\nRequested: {}", appliedConfig.getAddressSpaceSpec(), spec); - } - } else { - if (Phase.Active.equals(addressSpace.getStatus().getPhase())) { - addressSpace.getStatus().setPhase(Phase.Failed); - } - } - return addressSpace; - } - - private void checkExposedEndpoints(AddressSpace addressSpace) { - Map exposedEndpoints = new HashMap<>(); - - if (addressSpace.getSpec() != null && addressSpace.getSpec().getEndpoints() != null) { - for (EndpointSpec endpointSpec : addressSpace.getSpec().getEndpoints()) { - if (endpointSpec != null - && endpointSpec.getExpose() != null - && endpointSpec.getExpose().getType() != null - && endpointSpec.getExpose().getType().equals(ExposeType.route)) { - exposedEndpoints.put(endpointSpec.getName(), endpointSpec); - } - } - } - - if (addressSpace.getStatus().getEndpointStatuses() == null) { - addressSpace.getStatus().setEndpointStatuses(new ArrayList<>()); - } - - for (EndpointStatus endpointStatus : addressSpace.getStatus().getEndpointStatuses()) { - if (exposedEndpoints.containsKey(endpointStatus.getName())) { - if (endpointStatus.getExternalHost() == null) { - String msg = String.format("Endpoint '%s' is not yet exposed", endpointStatus.getName()); - addressSpace.getStatus().setReady(false); - addressSpace.getStatus().appendMessage(msg); - } - } - } - } - - private InfraConfig getInfraConfig(AddressSpace addressSpace) { - AddressSpaceResolver addressSpaceResolver = new AddressSpaceResolver(schemaProvider.getSchema()); - return addressSpaceResolver.getInfraConfig(addressSpace.getSpec().getType(), addressSpace.getSpec().getPlan()); - } - - private void checkComponentsReady(AddressSpace addressSpace) { - try { - InfraConfig infraConfig = Optional.ofNullable(parseCurrentInfraConfig(addressSpace)).orElseGet(() -> getInfraConfig(addressSpace)); - List requiredResources = infraResourceFactory.createInfraResources(addressSpace, infraConfig, authenticationServiceResolver.resolve(addressSpace)); - - checkDeploymentsReady(addressSpace, requiredResources); - checkStatefulSetsReady(addressSpace, requiredResources); - } catch (Exception e) { - String msg = String.format("Error checking for ready components: %s", e.getMessage()); - log.warn(msg, e); - addressSpace.getStatus().setReady(false); - addressSpace.getStatus().appendMessage(msg); - } - } - - private void checkStatefulSetsReady(AddressSpace addressSpace, List requiredResources) { - Set readyStatefulSets = kubernetes.getReadyStatefulSets(addressSpace).stream() - .map(statefulSet -> statefulSet.getMetadata().getName()) - .collect(Collectors.toSet()); - - - Set requiredStatefulSets = requiredResources.stream() - .filter(KubernetesHelper::isStatefulSet) - .map(item -> item.getMetadata().getName()) - .collect(Collectors.toSet()); - - boolean isReady = readyStatefulSets.containsAll(requiredStatefulSets); - if (!isReady) { - Set missing = new HashSet<>(requiredStatefulSets); - missing.removeAll(readyStatefulSets); - addressSpace.getStatus().setReady(false); - addressSpace.getStatus().appendMessage("The following stateful sets are not ready: " + missing); - } - } - - private void checkDeploymentsReady(AddressSpace addressSpace, List requiredResources) { - Set readyDeployments = kubernetes.getReadyDeployments(addressSpace).stream() - .map(deployment -> deployment.getMetadata().getName()) - .collect(Collectors.toSet()); - - - Set requiredDeployments = requiredResources.stream() - .filter(KubernetesHelper::isDeployment) - .map(item -> item.getMetadata().getName()) - .collect(Collectors.toSet()); - - boolean isReady = readyDeployments.containsAll(requiredDeployments); - if (!isReady) { - Set missing = new HashSet<>(requiredDeployments); - missing.removeAll(readyDeployments); - addressSpace.getStatus().setReady(false); - addressSpace.getStatus().appendMessage("The following deployments are not ready: " + missing); - } - } - - private void checkAuthServiceReady(AddressSpace addressSpace) { - AuthenticationService authenticationService = authenticationServiceRegistry.findAuthenticationService(addressSpace.getSpec().getAuthenticationService()).orElse(null); - if (authenticationService != null && AuthenticationServiceType.standard.equals(authenticationService.getSpec().getType())) { - String realm = authenticationService.getSpec().getRealm(); - if (realm == null) { - realm = addressSpace.getAnnotation(AnnotationKeys.REALM_NAME); - } - try { - boolean isReady = realmApi.getRealmNames(authenticationService).contains(realm); - if (!isReady) { - addressSpace.getStatus().setReady(false); - addressSpace.getStatus().appendMessage("Authentication service is not configured with realm " + addressSpace.getAnnotation(AnnotationKeys.REALM_NAME)); - } - } catch (Exception e) { - String msg = String.format("Error checking authentication service status: %s", e.getMessage()); - log.warn(msg); - addressSpace.getStatus().setReady(false); - addressSpace.getStatus().appendMessage(msg); - } - } - } - - private void checkRouterStatus(AddressSpace addressSpace) throws IOException { - InfraConfig infraConfig = InfraConfigs.parseCurrentInfraConfig(addressSpace); - if (infraConfig instanceof StandardInfraConfig) { - List routerStatusList = routerStatusCache.getLatestResult(addressSpace); - if (routerStatusList == null || routerStatusList.isEmpty()) { - addressSpace.getStatus().setReady(false); - addressSpace.getStatus().appendMessage("No router status found."); - } else { - checkRouterMesh(addressSpace, routerStatusList); - } - } - } - - private void checkRouterMesh(AddressSpace addressSpace, List routerStatusList) { - final List routers = new ArrayList<>(); - Set routerIds = routerStatusList.stream().map(RouterStatus::getRouterId).collect(Collectors.toSet()); - - for (RouterStatus routerStatus : routerStatusList) { - String routerId = routerStatus.getRouterId(); - List neighbors = new ArrayList<>(routerStatus.getNeighbors()); - // Add ourselves to make the comparison simpler - neighbors.add(routerId); - - if (!neighbors.containsAll(routerIds)) { - Set missing = new HashSet<>(routerIds); - missing.removeAll(neighbors); - String msg = String.format("Router %s is missing connection to %s.", routerId, missing); - log.warn(msg); - addressSpace.getStatus().setReady(false); - addressSpace.getStatus().appendMessage(msg); - } - - AddressSpaceStatusRouter addressSpaceStatusRouter = new AddressSpaceStatusRouter(); - addressSpaceStatusRouter.setId(routerId); - addressSpaceStatusRouter.setNeighbors(neighbors); - addressSpaceStatusRouter.setUndelivered(routerStatus.getUndelivered()); - - log.debug("Router {} has neighbors: {} and undelivered: {}", routerId, neighbors, routerStatus.getUndelivered()); - routers.add(addressSpaceStatusRouter); - } - addressSpace.getStatus().setRouters(routers); - } - - @Override - public String toString() { - return "StatusController"; - } - -} diff --git a/address-space-controller/src/main/java/io/enmasse/controller/StatusInitializer.java b/address-space-controller/src/main/java/io/enmasse/controller/StatusInitializer.java deleted file mode 100644 index 5a88cb28a4e..00000000000 --- a/address-space-controller/src/main/java/io/enmasse/controller/StatusInitializer.java +++ /dev/null @@ -1,21 +0,0 @@ -/* - * Copyright 2020, EnMasse authors. - * License: Apache License 2.0 (see the file LICENSE or http://apache.org/licenses/LICENSE-2.0.html). - */ -package io.enmasse.controller; - -import io.enmasse.address.model.AddressSpace; - -public class StatusInitializer implements Controller { - - public AddressSpace reconcileActive(AddressSpace addressSpace) { - addressSpace.getStatus().setReady(true); - addressSpace.getStatus().clearMessages(); - return addressSpace; - } - - @Override - public String toString() { - return "StatusInitializer"; - } -} diff --git a/address-space-controller/src/main/java/io/enmasse/controller/TemplateInfraResourceFactory.java b/address-space-controller/src/main/java/io/enmasse/controller/TemplateInfraResourceFactory.java deleted file mode 100644 index 843308c9b78..00000000000 --- a/address-space-controller/src/main/java/io/enmasse/controller/TemplateInfraResourceFactory.java +++ /dev/null @@ -1,329 +0,0 @@ -/* - * Copyright 2017-2018, EnMasse authors. - * License: Apache License 2.0 (see the file LICENSE or http://apache.org/licenses/LICENSE-2.0.html). - */ -package io.enmasse.controller; - -import static io.enmasse.address.model.KubeUtil.applyCpuMemory; -import static io.enmasse.address.model.KubeUtil.applyPodTemplate; -import static io.enmasse.address.model.KubeUtil.lookupResource; -import static io.enmasse.address.model.KubeUtil.overrideFsGroup; -import static io.enmasse.config.Apps.setConnectsTo; -import static io.enmasse.config.Apps.setPartOf; - -import java.io.File; -import java.io.IOException; -import java.io.UncheckedIOException; -import java.nio.file.Files; -import java.util.*; - -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import io.enmasse.address.model.AddressSpace; -import io.enmasse.address.model.AuthenticationServiceSettings; -import io.enmasse.address.model.CertSpec; -import io.enmasse.address.model.EndpointSpec; -import io.enmasse.address.model.KubeUtil; -import io.enmasse.admin.model.v1.BrokeredInfraConfig; -import io.enmasse.admin.model.v1.ConsoleService; -import io.enmasse.admin.model.v1.ConsoleServiceSpec; -import io.enmasse.admin.model.v1.ConsoleServiceStatus; -import io.enmasse.admin.model.v1.InfraConfig; -import io.enmasse.admin.model.v1.StandardInfraConfig; -import io.enmasse.config.AnnotationKeys; -import io.enmasse.config.LabelKeys; -import io.enmasse.controller.common.Kubernetes; -import io.enmasse.controller.common.TemplateParameter; -import io.enmasse.k8s.api.SchemaProvider; -import io.fabric8.kubernetes.api.model.HasMetadata; -import io.fabric8.kubernetes.api.model.PersistentVolumeClaim; -import io.fabric8.kubernetes.api.model.PodTemplateSpec; -import io.fabric8.kubernetes.api.model.SecretReference; -import io.fabric8.kubernetes.api.model.Secret; -import io.fabric8.kubernetes.api.model.apps.Deployment; -import io.fabric8.kubernetes.api.model.apps.StatefulSet; - -public class TemplateInfraResourceFactory implements InfraResourceFactory { - private static final Logger log = LoggerFactory.getLogger(TemplateInfraResourceFactory.class); - private final String WELL_KNOWN_CONSOLE_SERVICE_NAME = "console"; - - private final Kubernetes kubernetes; - private final Map env; - private final SchemaProvider schemaProvider; - - public TemplateInfraResourceFactory(Kubernetes kubernetes, Map env, SchemaProvider schemaProvider) { - this.kubernetes = kubernetes; - this.env = env; - this.schemaProvider = schemaProvider; - } - - private void prepareParameters(AddressSpace addressSpace, - AuthenticationServiceSettings authServiceSettings, - Map parameters) { - - String infraUuid = addressSpace.getAnnotation(AnnotationKeys.INFRA_UUID); - parameters.put(TemplateParameter.INFRA_NAMESPACE, kubernetes.getNamespace()); - parameters.put(TemplateParameter.ADDRESS_SPACE, addressSpace.getMetadata().getName()); - parameters.put(TemplateParameter.INFRA_UUID, infraUuid); - parameters.put(TemplateParameter.ADDRESS_SPACE_NAMESPACE, addressSpace.getMetadata().getNamespace()); - parameters.put(TemplateParameter.AUTHENTICATION_SERVICE_HOST, authServiceSettings.getHost()); - parameters.put(TemplateParameter.AUTHENTICATION_SERVICE_PORT, String.valueOf(authServiceSettings.getPort())); - parameters.put(TemplateParameter.ADDRESS_SPACE_PLAN, addressSpace.getSpec().getPlan()); - - String encodedCaCert = Optional.ofNullable(authServiceSettings.getCaCertSecret()) - .map(secretName -> - kubernetes.getSecret(secretName.getName()).map(secret -> - secret.getData().get("tls.crt")) - .orElseThrow(() -> new IllegalArgumentException("Unable to decode secret " + secretName))) - .orElseGet(() -> { - try { - return Base64.getEncoder().encodeToString(Files.readAllBytes(new File("/etc/ssl/certs/ca-bundle.crt").toPath())); - } catch (IOException e) { - throw new UncheckedIOException(e); - } - }); - parameters.put(TemplateParameter.AUTHENTICATION_SERVICE_CA_CERT, encodedCaCert); - if (authServiceSettings.getClientCertSecret() != null) { - parameters.put(TemplateParameter.AUTHENTICATION_SERVICE_CLIENT_SECRET, authServiceSettings.getClientCertSecret().getName()); - } - - parameters.put(TemplateParameter.AUTHENTICATION_SERVICE_SASL_INIT_HOST, authServiceSettings.getRealm()); - - Map serviceCertMapping = new HashMap<>(); - for (EndpointSpec endpoint : addressSpace.getSpec().getEndpoints()) { - if (endpoint.getCert() != null) { - serviceCertMapping.put(endpoint.getService(), endpoint.getCert()); - } - } - parameters.put(TemplateParameter.MESSAGING_SECRET, serviceCertMapping.get("messaging").getSecretName()); - - Optional secret = kubernetes.getSecret("broker-support-" + infraUuid); - if (secret.isPresent()) { - Map secretData = secret.get().getData(); - if (secretData.containsKey("username") && secretData.containsKey("password")) { - parameters.put(TemplateParameter.BROKER_SUPPORT_USER, secret.get().getData().get("username")); - parameters.put(TemplateParameter.BROKER_SUPPORT_PWD, secret.get().getData().get("password")); - } else { - createSupportCredentials(parameters); - } - } else { - createSupportCredentials(parameters); - } - } - - private void createSupportCredentials(Map parameters) { - String brokerSupportUser = String.format("broker-support-%s", UUID.randomUUID()); - String brokerSupportPwd = UUID.randomUUID().toString(); - parameters.put(TemplateParameter.BROKER_SUPPORT_USER, Base64.getEncoder().encodeToString(brokerSupportUser.getBytes())); - parameters.put(TemplateParameter.BROKER_SUPPORT_PWD, Base64.getEncoder().encodeToString(brokerSupportPwd.getBytes())); - } - - private void prepareMqttParameters(AddressSpace addressSpace, Map parameters) { - String infraUuid = addressSpace.getAnnotation(AnnotationKeys.INFRA_UUID); - parameters.put(TemplateParameter.ADDRESS_SPACE, addressSpace.getMetadata().getName()); - parameters.put(TemplateParameter.INFRA_UUID, infraUuid); - Map serviceCertMapping = new HashMap<>(); - for (EndpointSpec endpoint : addressSpace.getSpec().getEndpoints()) { - if (endpoint.getCert() != null) { - serviceCertMapping.put(endpoint.getService(), endpoint.getCert()); - } - } - parameters.put(TemplateParameter.MQTT_SECRET, serviceCertMapping.get("mqtt").getSecretName()); - setIfEnvPresent(parameters, TemplateParameter.AGENT_IMAGE); - setIfEnvPresent(parameters, TemplateParameter.MQTT_GATEWAY_IMAGE); - setIfEnvPresent(parameters, TemplateParameter.MQTT_LWT_IMAGE); - setIfEnvPresent(parameters, TemplateParameter.IMAGE_PULL_POLICY); - } - - private List createStandardInfraMqtt(AddressSpace addressSpace, String templateName) { - Map parameters = new HashMap<>(); - prepareMqttParameters(addressSpace, parameters); - return new ArrayList<>(kubernetes.processTemplate(templateName, parameters).getItems()); - } - - - private List createStandardInfra(AddressSpace addressSpace, StandardInfraConfig standardInfraConfig, AuthenticationServiceSettings authenticationServiceSettings) { - - Map parameters = new HashMap<>(); - - prepareParameters(addressSpace, authenticationServiceSettings, parameters); - - if (standardInfraConfig.getSpec().getBroker() != null) { - if (standardInfraConfig.getSpec().getBroker().getResources() != null) { - if (standardInfraConfig.getSpec().getBroker().getResources().getStorage() != null) { - parameters.put(TemplateParameter.BROKER_STORAGE_CAPACITY, standardInfraConfig.getSpec().getBroker().getResources().getStorage()); - } - } - - if (standardInfraConfig.getSpec().getBroker().getAddressFullPolicy() != null) { - parameters.put(TemplateParameter.BROKER_ADDRESS_FULL_POLICY, standardInfraConfig.getSpec().getBroker().getAddressFullPolicy()); - } - - if (standardInfraConfig.getSpec().getBroker().getGlobalMaxSize() != null) { - parameters.put(TemplateParameter.BROKER_GLOBAL_MAX_SIZE, standardInfraConfig.getSpec().getBroker().getGlobalMaxSize()); - } - } - - parameters.put(TemplateParameter.STANDARD_INFRA_CONFIG_NAME, standardInfraConfig.getMetadata().getName()); - setIfEnvPresent(parameters, TemplateParameter.AGENT_IMAGE); - setIfEnvPresent(parameters, TemplateParameter.STANDARD_CONTROLLER_IMAGE); - setIfEnvPresent(parameters, TemplateParameter.ROUTER_IMAGE); - setIfEnvPresent(parameters, TemplateParameter.BROKER_IMAGE); - setIfEnvPresent(parameters, TemplateParameter.BROKER_PLUGIN_IMAGE); - setIfEnvPresent(parameters, TemplateParameter.TOPIC_FORWARDER_IMAGE); - setIfEnvPresent(parameters, TemplateParameter.IMAGE_PULL_POLICY); - setIfEnvPresent(parameters, TemplateParameter.FS_GROUP_FALLBACK_MAP); - - Map infraAnnotations = standardInfraConfig.getMetadata().getAnnotations(); - String templateName = getAnnotation(infraAnnotations, AnnotationKeys.TEMPLATE_NAME, "standard-space-infra"); - List items = new ArrayList<>(kubernetes.processTemplate(templateName, parameters).getItems()); - - if (standardInfraConfig.getSpec().getRouter() != null) { - // Workaround since parameterized integer fields cannot be loaded locally by fabric8 kubernetes-client - for (HasMetadata item : items) { - if (item instanceof StatefulSet && "qdrouterd".equals(item.getMetadata().getLabels().get(LabelKeys.NAME))) { - StatefulSet router = (StatefulSet) item; - if (standardInfraConfig.getSpec().getRouter().getMinReplicas() != null) { - router.getSpec().setReplicas(standardInfraConfig.getSpec().getRouter().getMinReplicas()); - } - if (standardInfraConfig.getSpec().getRouter().getResources() != null ) { - applyCpuMemory(router.getSpec().getTemplate(), standardInfraConfig.getSpec().getRouter().getResources().getCpu(), standardInfraConfig.getSpec().getRouter().getResources().getMemory()); - } - } - } - } - - Deployment adminDeployment = lookupResource(Deployment.class, "Deployment", KubeUtil.getAdminDeploymentName(addressSpace), items); - if (standardInfraConfig.getSpec().getAdmin() != null && standardInfraConfig.getSpec().getAdmin().getResources() != null) { - applyCpuMemory(adminDeployment.getSpec().getTemplate(), standardInfraConfig.getSpec().getAdmin().getResources().getCpu(), standardInfraConfig.getSpec().getAdmin().getResources().getMemory()); - } - if (standardInfraConfig.getSpec().getAdmin() != null && standardInfraConfig.getSpec().getAdmin().getPodTemplate() != null) { - PodTemplateSpec podTemplate = standardInfraConfig.getSpec().getAdmin().getPodTemplate(); - PodTemplateSpec actualPodTemplate = adminDeployment.getSpec().getTemplate(); - applyPodTemplate(actualPodTemplate, podTemplate); - } - setPartOf(adminDeployment, addressSpace); - - StatefulSet routerSet = lookupResource(StatefulSet.class, "StatefulSet", KubeUtil.getRouterSetName(addressSpace), items); - if (standardInfraConfig.getSpec().getRouter() != null && standardInfraConfig.getSpec().getRouter().getPodTemplate() != null) { - PodTemplateSpec podTemplate = standardInfraConfig.getSpec().getRouter().getPodTemplate(); - PodTemplateSpec actualPodTemplate = routerSet.getSpec().getTemplate(); - applyPodTemplate(actualPodTemplate, podTemplate); - } - setPartOf(routerSet, addressSpace); - setConnectsTo(adminDeployment, routerSet.getMetadata().getName()); - - if (Boolean.parseBoolean(getAnnotation(infraAnnotations, AnnotationKeys.WITH_MQTT, "false"))) { - String mqttTemplateName = getAnnotation(infraAnnotations, AnnotationKeys.MQTT_TEMPLATE_NAME, "standard-space-infra-mqtt"); - items.addAll(createStandardInfraMqtt(addressSpace, mqttTemplateName)); - } - - if (standardInfraConfig.getSpec().getBroker() != null) { - return applyStorageClassName(standardInfraConfig.getSpec().getBroker().getStorageClassName(), items); - } else { - return items; - } - } - - - private String getAnnotation(Map annotations, String key, String defaultValue) { - return Optional.ofNullable(annotations) - .flatMap(m -> Optional.ofNullable(m.get(key))) - .orElse(defaultValue); - } - - private List createBrokeredInfra(AddressSpace addressSpace, BrokeredInfraConfig brokeredInfraConfig, AuthenticationServiceSettings authenticationServiceSettings) { - Map parameters = new HashMap<>(); - - prepareParameters(addressSpace, authenticationServiceSettings, parameters); - - if (brokeredInfraConfig.getSpec().getBroker() != null) { - if (brokeredInfraConfig.getSpec().getBroker().getResources() != null) { - if (brokeredInfraConfig.getSpec().getBroker().getResources().getStorage() != null) { - parameters.put(TemplateParameter.BROKER_STORAGE_CAPACITY, brokeredInfraConfig.getSpec().getBroker().getResources().getStorage()); - } - } - - if (brokeredInfraConfig.getSpec().getBroker().getAddressFullPolicy() != null) { - parameters.put(TemplateParameter.BROKER_ADDRESS_FULL_POLICY, brokeredInfraConfig.getSpec().getBroker().getAddressFullPolicy()); - } - - if (brokeredInfraConfig.getSpec().getBroker().getGlobalMaxSize() != null) { - parameters.put(TemplateParameter.BROKER_GLOBAL_MAX_SIZE, brokeredInfraConfig.getSpec().getBroker().getGlobalMaxSize()); - } - - if (brokeredInfraConfig.getSpec().getBroker().getJavaOpts() != null) { - parameters.put(TemplateParameter.BROKER_JAVA_OPTS, brokeredInfraConfig.getSpec().getBroker().getJavaOpts()); - } - } - - setIfEnvPresent(parameters, TemplateParameter.AGENT_IMAGE); - setIfEnvPresent(parameters, TemplateParameter.BROKER_IMAGE); - setIfEnvPresent(parameters, TemplateParameter.BROKER_PLUGIN_IMAGE); - setIfEnvPresent(parameters, TemplateParameter.IMAGE_PULL_POLICY); - - List items; - String templateName = getAnnotation(brokeredInfraConfig.getMetadata().getAnnotations(), AnnotationKeys.TEMPLATE_NAME, "brokered-space-infra"); - if (brokeredInfraConfig.getSpec().getBroker() != null) { - items = applyStorageClassName(brokeredInfraConfig.getSpec().getBroker().getStorageClassName(), kubernetes.processTemplate(templateName, parameters).getItems()); - } else { - items = kubernetes.processTemplate(templateName, parameters).getItems(); - } - - Deployment adminDeployment = lookupResource(Deployment.class, "Deployment", KubeUtil.getAgentDeploymentName(addressSpace), items); - if (brokeredInfraConfig.getSpec().getAdmin() != null && brokeredInfraConfig.getSpec().getAdmin().getResources() != null) { - applyCpuMemory(adminDeployment.getSpec().getTemplate(), brokeredInfraConfig.getSpec().getAdmin().getResources().getCpu(), brokeredInfraConfig.getSpec().getAdmin().getResources().getMemory()); - } - if (brokeredInfraConfig.getSpec().getAdmin() != null && brokeredInfraConfig.getSpec().getAdmin().getPodTemplate() != null) { - PodTemplateSpec podTemplate = brokeredInfraConfig.getSpec().getAdmin().getPodTemplate(); - PodTemplateSpec actualPodTemplate = adminDeployment.getSpec().getTemplate(); - applyPodTemplate(actualPodTemplate, podTemplate); - } - setPartOf(adminDeployment, addressSpace); - - Deployment brokerDeployment = lookupResource(Deployment.class, "Deployment", KubeUtil.getBrokeredBrokerSetName(addressSpace), items); - if (brokeredInfraConfig.getSpec().getBroker() != null && brokeredInfraConfig.getSpec().getBroker().getResources() != null) { - applyCpuMemory(brokerDeployment.getSpec().getTemplate(), brokeredInfraConfig.getSpec().getBroker().getResources().getCpu(), brokeredInfraConfig.getSpec().getBroker().getResources().getMemory()); - } - if (brokeredInfraConfig.getSpec().getBroker() != null && brokeredInfraConfig.getSpec().getBroker().getPodTemplate() != null) { - PodTemplateSpec podTemplate = brokeredInfraConfig.getSpec().getBroker().getPodTemplate(); - PodTemplateSpec actualPodTemplate = brokerDeployment.getSpec().getTemplate(); - applyPodTemplate(actualPodTemplate, podTemplate); - } - overrideFsGroup(brokerDeployment.getSpec().getTemplate(), "broker", kubernetes.getNamespace()); - setPartOf(brokerDeployment, addressSpace); - - return items; - } - - private List applyStorageClassName(String storageClassName, List items) { - if (storageClassName != null) { - for (HasMetadata item : items) { - if (item instanceof PersistentVolumeClaim) { - ((PersistentVolumeClaim) item).getSpec().setStorageClassName(storageClassName); - } - } - } - return items; - } - - private void setIfEnvPresent(Map parameters, String key) { - if (env.get(key) != null) { - parameters.put(key, env.get(key)); - } - } - - - @Override - public List createInfraResources(AddressSpace addressSpace, InfraConfig infraConfig, AuthenticationServiceSettings authenticationServiceSettings) { - if ("standard".equals(addressSpace.getSpec().getType())) { - return createStandardInfra(addressSpace, (StandardInfraConfig) infraConfig, authenticationServiceSettings); - } else if ("brokered".equals(addressSpace.getSpec().getType())) { - return createBrokeredInfra(addressSpace, (BrokeredInfraConfig) infraConfig, authenticationServiceSettings); - } else { - throw new IllegalArgumentException("Unknown address space type " + addressSpace.getSpec().getType()); - } - } - -} diff --git a/address-space-controller/src/main/java/io/enmasse/controller/auth/AuthController.java b/address-space-controller/src/main/java/io/enmasse/controller/auth/AuthController.java deleted file mode 100644 index 19b5df1e33d..00000000000 --- a/address-space-controller/src/main/java/io/enmasse/controller/auth/AuthController.java +++ /dev/null @@ -1,152 +0,0 @@ -/* - * Copyright 2017-2018, EnMasse authors. - * License: Apache License 2.0 (see the file LICENSE or http://apache.org/licenses/LICENSE-2.0.html). - */ -package io.enmasse.controller.auth; - -import java.util.*; -import java.util.stream.Collectors; - -import io.enmasse.address.model.*; -import io.enmasse.config.AnnotationKeys; -import io.enmasse.config.LabelKeys; -import io.enmasse.controller.CertProviderFactory; -import io.enmasse.controller.Controller; -import io.enmasse.controller.common.ControllerKind; -import io.enmasse.k8s.api.*; -import io.fabric8.kubernetes.api.model.Secret; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import static io.enmasse.controller.common.ControllerReason.CertCreateFailed; -import static io.enmasse.controller.common.ControllerReason.CertCreated; -import static io.enmasse.k8s.api.EventLogger.Type.Normal; -import static io.enmasse.k8s.api.EventLogger.Type.Warning; - -/** - * Manages certificates issuing, revoking etc. for EnMasse services - */ -public class AuthController implements Controller { - private static final Logger log = LoggerFactory.getLogger(AuthController.class.getName()); - - private final CertManager certManager; - private final EventLogger eventLogger; - private final CertProviderFactory certProviderFactory; - private final boolean disableExternalCertProvisioning; - - public AuthController(CertManager certManager, - EventLogger eventLogger, - CertProviderFactory certProviderFactory, boolean disableExternalCertProvisioning) { - this.certManager = certManager; - this.eventLogger = eventLogger; - this.certProviderFactory = certProviderFactory; - this.disableExternalCertProvisioning = disableExternalCertProvisioning; - } - - public void issueExternalCertificates(AddressSpace addressSpace) { - List endpoints = addressSpace.getSpec().getEndpoints(); - if (endpoints != null) { - Map endpointSpecMap = new HashMap<>(); - Map endpointInfoMap = new HashMap<>(); - - for (EndpointSpec endpoint : endpoints) { - endpointSpecMap.put(endpoint.getName(), endpoint); - if (endpoint.getCert() != null) { - EndpointInfo info = endpointInfoMap.get(endpoint.getService()); - if (info == null) { - info = new EndpointInfo(endpoint.getService(), endpoint.getCert()); - endpointInfoMap.put(endpoint.getService(), info); - } - } - } - - for (EndpointStatus status : addressSpace.getStatus().getEndpointStatuses()) { - EndpointSpec spec = endpointSpecMap.get(status.getName()); - EndpointInfo info = endpointInfoMap.get(spec.getService()); - if (info != null) { - info.addHost(status.getServiceHost()); - if (status.getExternalHost() != null && !status.getExternalHost().isEmpty()) { - info.addHost(status.getExternalHost()); - } - } - } - - for (EndpointInfo info : endpointInfoMap.values()) { - try { - CertProvider certProvider = certProviderFactory.createProvider(info.getCertSpec().getProvider()); - certProvider.provideCert(addressSpace, info); - } catch (Exception e) { - log.warn("Error providing certificate for service {} hosts {}: {}", info.getServiceName(), info.getHosts(), e.getMessage(), e); - } - } - } - } - - - public Secret issueAddressSpaceCert(final AddressSpace addressSpace) { - try { - final String addressSpaceCaSecretName = KubeUtil.getAddressSpaceCaSecretName(addressSpace); - Secret secret = certManager.getCertSecret(addressSpaceCaSecretName); - if (secret == null) { - String infraUuid = addressSpace.getAnnotation(AnnotationKeys.INFRA_UUID); - Map labels = new HashMap<>(); - labels.put(LabelKeys.INFRA_UUID, infraUuid); - labels.put(LabelKeys.INFRA_TYPE, addressSpace.getSpec().getType()); - secret = certManager.createSelfSignedCertSecret(addressSpaceCaSecretName, labels); - //put crt into address space - eventLogger.log(CertCreated, "Created address space CA", Normal, ControllerKind.AddressSpace, addressSpace.getMetadata().getName()); - } - return secret; - } catch (Exception e) { - log.warn("Error issuing addressspace ca certificate", e); - eventLogger.log(CertCreateFailed, "Error creating certificate: " + e.getMessage(), Warning, ControllerKind.AddressSpace, addressSpace.getMetadata().getName()); - return null; - } - } - - public void issueComponentCertificates(AddressSpace addressSpace, Secret addressSpaceCaSecret) { - try { - Map labels = new HashMap<>(); - String infraUuid = addressSpace.getAnnotation(AnnotationKeys.INFRA_UUID); - labels.put(LabelKeys.INFRA_UUID, infraUuid); - labels.put(LabelKeys.INFRA_TYPE, addressSpace.getSpec().getType()); - List certs = certManager.listComponents(infraUuid).stream() - .filter(component -> !certManager.certExists(component)) - .map(certManager::createCsr) - .map(request -> certManager.signCsr(request, addressSpaceCaSecret, Collections.emptySet())) - .map(cert -> { - certManager.createSecret(cert, addressSpaceCaSecret, labels); - return cert; }) - .collect(Collectors.toList()); - - if (!certs.isEmpty()) { - eventLogger.log(CertCreated, "Created component certificates", Normal, ControllerKind.AddressSpace, addressSpace.getMetadata().getName()); - } - } catch (Exception e) { - log.warn("Error issuing component certificates", e); - eventLogger.log(CertCreateFailed, "Error creating component certificates: " + e.getMessage(), Warning, ControllerKind.AddressSpace, addressSpace.getMetadata().getName()); - } - } - - public String getDefaultCertProvider() { - return certProviderFactory.getDefaultProviderName(); - } - - @Override - public AddressSpace reconcileActive(AddressSpace addressSpace) throws Exception { - Secret addressSpaceCa = issueAddressSpaceCert(addressSpace); - if (addressSpaceCa != null) { - issueComponentCertificates(addressSpace, addressSpaceCa); - } - - if (!disableExternalCertProvisioning) { - issueExternalCertificates(addressSpace); - } - return addressSpace; - } - - @Override - public String toString() { - return "AuthController"; - } -} diff --git a/address-space-controller/src/main/java/io/enmasse/controller/auth/Cert.java b/address-space-controller/src/main/java/io/enmasse/controller/auth/Cert.java deleted file mode 100644 index f1684f00a41..00000000000 --- a/address-space-controller/src/main/java/io/enmasse/controller/auth/Cert.java +++ /dev/null @@ -1,36 +0,0 @@ -/* - * Copyright 2017-2018, EnMasse authors. - * License: Apache License 2.0 (see the file LICENSE or http://apache.org/licenses/LICENSE-2.0.html). - */ -package io.enmasse.controller.auth; - -import java.io.File; - -public class Cert { - private final CertComponent component; - private final File keyFile; - private final File certFile; - - Cert(CertComponent component, File keyFile, File certFile) { - this.component = component; - this.keyFile = keyFile; - this.certFile = certFile; - } - - public CertComponent getComponent() { - return component; - } - - public File getKeyFile() { - return keyFile; - } - - public File getCertFile() { - return certFile; - } - - @Override - public String toString() { - return component.toString(); - } -} diff --git a/address-space-controller/src/main/java/io/enmasse/controller/auth/CertBundleCertProvider.java b/address-space-controller/src/main/java/io/enmasse/controller/auth/CertBundleCertProvider.java deleted file mode 100644 index 96dce636245..00000000000 --- a/address-space-controller/src/main/java/io/enmasse/controller/auth/CertBundleCertProvider.java +++ /dev/null @@ -1,68 +0,0 @@ -/* - * Copyright 2018, EnMasse authors. - * License: Apache License 2.0 (see the file LICENSE or http://apache.org/licenses/LICENSE-2.0.html). - */ -package io.enmasse.controller.auth; - -import io.enmasse.address.model.AddressSpace; -import io.enmasse.config.AnnotationKeys; -import io.enmasse.config.LabelKeys; -import io.fabric8.kubernetes.api.model.Secret; -import io.fabric8.kubernetes.api.model.SecretBuilder; -import io.fabric8.kubernetes.client.KubernetesClient; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import java.util.HashMap; -import java.util.Map; - -public class CertBundleCertProvider implements CertProvider { - private static final Logger log = LoggerFactory.getLogger(CertBundleCertProvider.class); - private final KubernetesClient client; - private final String namespace; - - public CertBundleCertProvider(KubernetesClient client) { - this.client = client; - this.namespace = client.getNamespace(); - } - - @Override - public void provideCert(AddressSpace addressSpace, EndpointInfo endpointInfo) { - Map data = new HashMap<>(); - String tlsKey = endpointInfo.getCertSpec().getTlsKey(); - String tlsCert = endpointInfo.getCertSpec().getTlsCert(); - if (tlsKey == null) { - log.warn("tlsKey not present, not providing cert for {}", endpointInfo.getServiceName()); - return; - } - - if (tlsCert == null) { - log.warn("tlsCert not present, not providing cert for {}", endpointInfo.getServiceName()); - return; - } - - data.put("tls.key", tlsKey.replace("\r\n", "").replace("\n", "")); - data.put("tls.crt", tlsCert.replace("\r\n", "").replace("\n", "")); - - Secret secret = new SecretBuilder() - .editOrNewMetadata() - .withName(endpointInfo.getCertSpec().getSecretName()) - .withNamespace(namespace) - .addToLabels(LabelKeys.INFRA_UUID, addressSpace.getAnnotation(AnnotationKeys.INFRA_UUID)) - .addToLabels(LabelKeys.INFRA_TYPE, addressSpace.getSpec().getType()) - .addToLabels("app", "enmasse") - .endMetadata() - .withType("kubernetes.io/tls") - .withData(data) - .build(); - - Secret existing = client.secrets().inNamespace(namespace).withName(endpointInfo.getCertSpec().getSecretName()).get(); - if (existing == null) { - log.info("Creating cert secret {} with certBundle input", secret.getMetadata().getName()); - client.secrets().inNamespace(namespace).createOrReplace(secret); - } else if (!data.equals(existing.getData())) { - log.info("Replacing cert secret {} with certBundle input", secret.getMetadata().getName()); - client.secrets().inNamespace(namespace).withName(endpointInfo.getCertSpec().getSecretName()).patch(secret); - } - } -} diff --git a/address-space-controller/src/main/java/io/enmasse/controller/auth/CertComponent.java b/address-space-controller/src/main/java/io/enmasse/controller/auth/CertComponent.java deleted file mode 100644 index faa7a56d93d..00000000000 --- a/address-space-controller/src/main/java/io/enmasse/controller/auth/CertComponent.java +++ /dev/null @@ -1,36 +0,0 @@ -/* - * Copyright 2017-2018, EnMasse authors. - * License: Apache License 2.0 (see the file LICENSE or http://apache.org/licenses/LICENSE-2.0.html). - */ -package io.enmasse.controller.auth; - -public class CertComponent { - private final String name; - private final String uuid; - private final String secretName; - - CertComponent(String name, String uuid, String secretName) { - this.name = name; - this.uuid = uuid; - this.secretName = secretName; - } - - public String getName() { - return name; - } - - public String getUuid() { - return uuid; - } - - public String getSecretName() { - return secretName; - } - - @Override - public String toString() { - return "{name=" + name + "," + - "uuid=" + uuid + "," + - "secretName=" + secretName + "}"; - } -} diff --git a/address-space-controller/src/main/java/io/enmasse/controller/auth/CertManager.java b/address-space-controller/src/main/java/io/enmasse/controller/auth/CertManager.java deleted file mode 100644 index 9c50138979b..00000000000 --- a/address-space-controller/src/main/java/io/enmasse/controller/auth/CertManager.java +++ /dev/null @@ -1,24 +0,0 @@ -/* - * Copyright 2017-2018, EnMasse authors. - * License: Apache License 2.0 (see the file LICENSE or http://apache.org/licenses/LICENSE-2.0.html). - */ -package io.enmasse.controller.auth; - -import io.fabric8.kubernetes.api.model.Secret; - -import java.util.Collection; -import java.util.Map; - -/** - * Interface for certificate managers - */ -public interface CertManager { - Collection listComponents(String uuid); - boolean certExists(CertComponent component); - Secret getCertSecret(String name); - CertSigningRequest createCsr(CertComponent component); - Cert signCsr(CertSigningRequest request, Secret secret, Collection hosts); - Secret createSecret(Cert cert, final Secret caSecret, Map labels); - - Secret createSelfSignedCertSecret(String secretName, Map labels); -} diff --git a/address-space-controller/src/main/java/io/enmasse/controller/auth/CertProvider.java b/address-space-controller/src/main/java/io/enmasse/controller/auth/CertProvider.java deleted file mode 100644 index 406fadc57c2..00000000000 --- a/address-space-controller/src/main/java/io/enmasse/controller/auth/CertProvider.java +++ /dev/null @@ -1,11 +0,0 @@ -/* - * Copyright 2018, EnMasse authors. - * License: Apache License 2.0 (see the file LICENSE or http://apache.org/licenses/LICENSE-2.0.html). - */ -package io.enmasse.controller.auth; - -import io.enmasse.address.model.AddressSpace; - -public interface CertProvider { - void provideCert(AddressSpace addressSpace, EndpointInfo endpointInfo); -} diff --git a/address-space-controller/src/main/java/io/enmasse/controller/auth/CertSigningRequest.java b/address-space-controller/src/main/java/io/enmasse/controller/auth/CertSigningRequest.java deleted file mode 100644 index 3f1a2895be7..00000000000 --- a/address-space-controller/src/main/java/io/enmasse/controller/auth/CertSigningRequest.java +++ /dev/null @@ -1,31 +0,0 @@ -/* - * Copyright 2017-2018, EnMasse authors. - * License: Apache License 2.0 (see the file LICENSE or http://apache.org/licenses/LICENSE-2.0.html). - */ -package io.enmasse.controller.auth; - -import java.io.File; - -public class CertSigningRequest { - private final CertComponent certComponent; - private final File csrFile; - private final File keyFile; - - CertSigningRequest(CertComponent certComponent, File csrFile, File keyFile) { - this.certComponent = certComponent; - this.csrFile = csrFile; - this.keyFile = keyFile; - } - - public CertComponent getCertComponent() { - return certComponent; - } - - public File getCsrFile() { - return csrFile; - } - - public File getKeyFile() { - return keyFile; - } -} diff --git a/address-space-controller/src/main/java/io/enmasse/controller/auth/EndpointInfo.java b/address-space-controller/src/main/java/io/enmasse/controller/auth/EndpointInfo.java deleted file mode 100644 index 98c6e5a0df9..00000000000 --- a/address-space-controller/src/main/java/io/enmasse/controller/auth/EndpointInfo.java +++ /dev/null @@ -1,37 +0,0 @@ -/* - * Copyright 2017-2018, EnMasse authors. - * License: Apache License 2.0 (see the file LICENSE or http://apache.org/licenses/LICENSE-2.0.html). - */ -package io.enmasse.controller.auth; - -import io.enmasse.address.model.CertSpec; - -import java.util.*; - -class EndpointInfo { - private final String serviceName; - private final CertSpec certs; - private final List hosts = new ArrayList<>(); - - EndpointInfo(String serviceName, CertSpec certs) { - this.serviceName = serviceName; - this.certs = certs; - } - - public String getServiceName() { - return serviceName; - } - - public CertSpec getCertSpec() { - return certs; - } - - public List getHosts() { - return Collections.unmodifiableList(hosts); - } - - public EndpointInfo addHost(String host) { - hosts.add(host); - return this; - } -} diff --git a/address-space-controller/src/main/java/io/enmasse/controller/auth/OpenSSLCertManager.java b/address-space-controller/src/main/java/io/enmasse/controller/auth/OpenSSLCertManager.java deleted file mode 100644 index 4c0dad692f9..00000000000 --- a/address-space-controller/src/main/java/io/enmasse/controller/auth/OpenSSLCertManager.java +++ /dev/null @@ -1,260 +0,0 @@ -/* - * Copyright 2017-2018, EnMasse authors. - * License: Apache License 2.0 (see the file LICENSE or http://apache.org/licenses/LICENSE-2.0.html). - */ -package io.enmasse.controller.auth; - -import java.io.*; -import java.util.*; -import java.util.concurrent.LinkedBlockingDeque; -import java.util.concurrent.TimeUnit; -import java.util.stream.Collectors; - -import io.enmasse.config.AnnotationKeys; -import io.enmasse.config.LabelKeys; -import io.fabric8.kubernetes.api.model.*; -import io.fabric8.kubernetes.client.KubernetesClient; -import org.apache.commons.io.FileUtils; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -/** - * Controller that creates self-signed certificates for instances. - */ -public class OpenSSLCertManager implements CertManager { - private static final Logger log = LoggerFactory.getLogger(OpenSSLCertManager.class); - private static final int PROCESS_LINE_BUFFER_SIZE = 10; - private final KubernetesClient client; - private final String namespace; - private final File certDir; - - public OpenSSLCertManager(KubernetesClient controllerClient, - File certDir) { - this.client = controllerClient; - this.certDir = certDir; - this.namespace = controllerClient.getNamespace(); - } - - private static void createSelfSignedCert(final File keyFile, final File certFile) { - runCommand("openssl", "req", "-new", "-days", "11000", "-x509", "-batch", "-nodes", - "-out", certFile.getAbsolutePath(), "-keyout", keyFile.getAbsolutePath()); - } - - private Secret createSecretFromCertAndKeyFiles(final String secretName, - final Map secretLabels, - final File keyFile, - final File certFile, - final KubernetesClient client) - throws IOException { - return createSecretFromCertAndKeyFiles(secretName, secretLabels, "tls.key", "tls.crt", keyFile, certFile, client); - } - - private Secret createSecretFromCertAndKeyFiles(final String secretName, - final Map secretLabels, - final String keyKey, - final String certKey, - final File keyFile, - final File certFile, - final KubernetesClient client) - throws IOException { - Map data = new LinkedHashMap<>(); - Base64.Encoder encoder = Base64.getEncoder(); - data.put(keyKey, encoder.encodeToString(FileUtils.readFileToByteArray(keyFile))); - data.put(certKey, encoder.encodeToString(FileUtils.readFileToByteArray(certFile))); - return client.secrets().inNamespace(namespace).withName(secretName).createOrReplaceWithNew() - .editOrNewMetadata() - .withName(secretName) - .withLabels(secretLabels) - .endMetadata() - .addToData(data) - .done(); - } - - private static void runCommand(String... cmd) { - ProcessBuilder keyGenBuilder = new ProcessBuilder(cmd).redirectErrorStream(true); - - log.info("Running command '{}'", keyGenBuilder.command()); - Deque outBuf = new LinkedBlockingDeque<>(PROCESS_LINE_BUFFER_SIZE); - boolean success = false; - try { - Process process = keyGenBuilder.start(); - try (BufferedReader reader = new BufferedReader(new InputStreamReader(process.getInputStream()))) { - String line; - while ((line = reader.readLine()) != null) { - boolean added = outBuf.offerLast(line); - if (log.isDebugEnabled()) { - log.debug("Command output: {}", line); - } - if (!added) { - outBuf.removeFirst(); - outBuf.addLast(line); - } - } - } - if (!process.waitFor(1, TimeUnit.MINUTES)) { - throw new RuntimeException(String.format("Command '%s' timed out", keyGenBuilder.command())); - } - - final int exitValue = process.waitFor(); - success = exitValue == 0; - String msg = String.format("Command '%s' completed with exit value %d", keyGenBuilder.command(), exitValue); - if (success) { - log.info(msg); - } else { - log.error(msg); - throw new RuntimeException(String.format("Command '%s' failed with exit value %d", keyGenBuilder.command(), exitValue)); - } - } catch (InterruptedException ignored) { - Thread.currentThread().interrupt(); - } catch (IOException e) { - throw new UncheckedIOException(e); - } finally { - if (!success && !outBuf.isEmpty()) { - log.error("Last {} line(s) written by command to stdout/stderr follow", outBuf.size()); - outBuf.forEach(line -> log.error("Command output: {}", line)); - } - } - } - - @Override - public Collection listComponents(String uuid) { - List components = new ArrayList<>(); - - components.addAll(client.apps().deployments().inNamespace(namespace).withLabel(LabelKeys.INFRA_UUID, uuid).list().getItems()); - components.addAll(client.apps().statefulSets().inNamespace(namespace).withLabel(LabelKeys.INFRA_UUID, uuid).list().getItems()); - - return components.stream() - .filter(object -> object.getMetadata().getAnnotations() != null && object.getMetadata().getAnnotations().containsKey(AnnotationKeys.CERT_SECRET_NAME)) - .map(object -> { - Map annotations = object.getMetadata().getAnnotations(); - String cn = annotations.getOrDefault(AnnotationKeys.CERT_CN, object.getMetadata().getName()); - return new CertComponent(cn, uuid, annotations.get(AnnotationKeys.CERT_SECRET_NAME)); - }) - .collect(Collectors.toList()); - } - - @Override - public boolean certExists(CertComponent component) { - return client.secrets().inNamespace(namespace).withName(component.getSecretName()).get() != null; - } - - @Override - public Secret getCertSecret(String name) { - return client.secrets().inNamespace(namespace).withName(name).get(); - } - - - @Override - public CertSigningRequest createCsr(CertComponent component) { - File keyFile = new File(certDir, component.getName() + "." + component.getUuid() + ".key"); - File csrFile = new File(certDir, component.getName() + "." + component.getUuid() + ".csr"); - String subjString = "/O=io.enmasse"; - if (component.getName().length() <= 64) { - subjString += "/CN=" + component.getName(); - } - runCommand("openssl", "req", "-new", "-batch", "-nodes", "-keyout", keyFile.getAbsolutePath(), "-subj", subjString, "-out", csrFile.getAbsolutePath()); - return new CertSigningRequest(component, csrFile, keyFile); - } - - @Override - public Cert signCsr(CertSigningRequest request, Secret secret, Collection sans) { - File crtFile = new File(certDir, request.getCertComponent().getName() + "." + request.getCertComponent().getUuid() + ".crt"); - - File caKey = createTempFileFromSecret(secret, "tls.key"); - File caCert = createTempFileFromSecret(secret, "tls.crt"); - - try { - if (sans.size() > 0) { - String sansString = "subjectAltName=DNS:" + sans.stream().collect(Collectors.joining(",DNS:")); - runCommand("bash", - "-c", - "openssl x509 -req -extfile <(printf \"" + sansString + "\") -days 11000 -in " + request.getCsrFile().getAbsolutePath() + - " -CA " + caCert.getAbsolutePath() + - " -CAkey " + caKey.getAbsolutePath() + - " -CAcreateserial -out " + crtFile.getAbsolutePath()); - } else { - runCommand("openssl", - "x509", - "-req", - "-days", - "11000", - "-in", - request.getCsrFile().getAbsolutePath(), - "-CA", - caCert.getAbsolutePath(), - "-CAkey", - caKey.getAbsolutePath(), - "-CAcreateserial", - "-out", - crtFile.getAbsolutePath()); - } - return new Cert(request.getCertComponent(), request.getKeyFile(), crtFile); - } finally { - caKey.delete(); - caCert.delete(); - } - - } - - private File createTempFileFromSecret(Secret secret, String key) { - try { - if (secret.getData() == null || secret.getData().get(key) == null) { - throw new IllegalStateException(String.format("No secret data found for key '%s'", key)); - } - String data = secret.getData().get(key); - File file = File.createTempFile("secret", "pem"); - final Base64.Decoder decoder = Base64.getDecoder(); - try (FileOutputStream outputStream = new FileOutputStream(file)) { - outputStream.write(decoder.decode(data)); - } - return file; - } catch (IOException e) { - throw new UncheckedIOException(e); - } - } - - @Override - public Secret createSecret(Cert cert, Secret caSecret, Map labels) { - try { - Map data = new LinkedHashMap<>(); - Base64.Encoder encoder = Base64.getEncoder(); - data.put("tls.key", encoder.encodeToString(FileUtils.readFileToByteArray(cert.getKeyFile()))); - data.put("tls.crt", encoder.encodeToString(FileUtils.readFileToByteArray(cert.getCertFile()))); - data.put("ca.crt", caSecret.getData().get("tls.crt")); - - return client.secrets().inNamespace(namespace).createNew() - .editOrNewMetadata() - .withName(cert.getComponent().getSecretName()) - .withLabels(labels) - .endMetadata() - .withType("kubernetes.io/tls") - .addToData(data) - .done(); - } catch (IOException e) { - throw new UncheckedIOException(e); - } - } - - @Override - public Secret createSelfSignedCertSecret(final String secretName, Map labels) { - try { - File key = File.createTempFile("tls", "key"); - File cert = File.createTempFile("tls", "crt"); - try { - createSelfSignedCert(key, cert); - - return createSecretFromCertAndKeyFiles(secretName, labels, key, cert, this.client); - } finally { - key.delete(); - cert.delete(); - } - } catch (IOException e) { - throw new UncheckedIOException(e); - } - } - - public static OpenSSLCertManager create(KubernetesClient controllerClient) { - return new OpenSSLCertManager(controllerClient, new File("/tmp")); - } - -} diff --git a/address-space-controller/src/main/java/io/enmasse/controller/auth/OpenshiftCertProvider.java b/address-space-controller/src/main/java/io/enmasse/controller/auth/OpenshiftCertProvider.java deleted file mode 100644 index 8f53fa416b7..00000000000 --- a/address-space-controller/src/main/java/io/enmasse/controller/auth/OpenshiftCertProvider.java +++ /dev/null @@ -1,42 +0,0 @@ -/* - * Copyright 2018, EnMasse authors. - * License: Apache License 2.0 (see the file LICENSE or http://apache.org/licenses/LICENSE-2.0.html). - */ -package io.enmasse.controller.auth; - -import io.enmasse.address.model.AddressSpace; -import io.enmasse.address.model.KubeUtil; -import io.enmasse.config.AnnotationKeys; -import io.fabric8.kubernetes.api.model.Secret; -import io.fabric8.kubernetes.api.model.Service; -import io.fabric8.kubernetes.client.KubernetesClient; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -public class OpenshiftCertProvider implements CertProvider { - private static final Logger log = LoggerFactory.getLogger(OpenshiftCertProvider.class); - private final KubernetesClient client; - private final String namespace; - - public OpenshiftCertProvider(KubernetesClient client) { - this.client = client; - this.namespace = client.getNamespace(); - } - - @Override - public void provideCert(AddressSpace addressSpace, EndpointInfo endpointInfo) { - Secret secret = client.secrets().inNamespace(namespace).withName(endpointInfo.getCertSpec().getSecretName()).get(); - if (secret == null) { - String serviceName = KubeUtil.getAddressSpaceServiceName(endpointInfo.getServiceName(), addressSpace); - Service service = client.services().withName(serviceName).get(); - if (service != null && (service.getMetadata().getAnnotations() == null || service.getMetadata().getAnnotations().get(AnnotationKeys.OPENSHIFT_SERVING_CERT_SECRET_NAME) == null)) { - log.info("Adding service annotation to generate OpenShift cert"); - client.services().withName(serviceName).edit() - .editMetadata() - .addToAnnotations(AnnotationKeys.OPENSHIFT_SERVING_CERT_SECRET_NAME, endpointInfo.getCertSpec().getSecretName()) - .endMetadata() - .done(); - } - } - } -} diff --git a/address-space-controller/src/main/java/io/enmasse/controller/auth/SelfsignedCertProvider.java b/address-space-controller/src/main/java/io/enmasse/controller/auth/SelfsignedCertProvider.java deleted file mode 100644 index fe805fca022..00000000000 --- a/address-space-controller/src/main/java/io/enmasse/controller/auth/SelfsignedCertProvider.java +++ /dev/null @@ -1,75 +0,0 @@ -/* - * Copyright 2018, EnMasse authors. - * License: Apache License 2.0 (see the file LICENSE or http://apache.org/licenses/LICENSE-2.0.html). - */ -package io.enmasse.controller.auth; - -import io.enmasse.address.model.*; -import io.enmasse.config.AnnotationKeys; -import io.enmasse.config.LabelKeys; -import io.fabric8.kubernetes.api.model.Secret; -import io.fabric8.kubernetes.client.KubernetesClient; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import java.util.HashMap; -import java.util.List; -import java.util.Map; - -public class SelfsignedCertProvider implements CertProvider { - private static final Logger log = LoggerFactory.getLogger(SelfsignedCertProvider.class); - private final KubernetesClient client; - private final CertManager certManager; - private final String namespace; - - public SelfsignedCertProvider(KubernetesClient client, CertManager certManager) { - this.client = client; - this.certManager = certManager; - this.namespace = client.getNamespace(); - } - - private Secret issueAddressSpaceCert(final AddressSpace addressSpace, Map labels) { - try { - final String addressSpaceCaSecretName = KubeUtil.getAddressSpaceExternalCaSecretName(addressSpace); - Secret secret = certManager.getCertSecret(addressSpaceCaSecretName); - if (secret == null) { - secret = certManager.createSelfSignedCertSecret(addressSpaceCaSecretName, labels); - log.info("Created CA secret for {}", addressSpace.getMetadata().getName()); - } - return secret; - } catch (Exception e) { - log.warn("Error issuing self-signed external route ca certificate", e); - return null; - } - } - - @Override - public void provideCert(AddressSpace addressSpace, EndpointInfo info) { - Secret secret = client.secrets().inNamespace(namespace).withName(info.getCertSpec().getSecretName()).get(); - if (secret == null) { - List hosts = info.getHosts(); - String cn = null; - if (!hosts.isEmpty()) { - cn = hosts.iterator().next(); - } - - log.info("Creating self-signed certificates for {}", cn); - String infraUuid = addressSpace.getAnnotation(AnnotationKeys.INFRA_UUID); - Map labels = new HashMap<>(); - labels.put(LabelKeys.INFRA_TYPE, addressSpace.getSpec().getType()); - labels.put(LabelKeys.INFRA_UUID, infraUuid); - if (cn != null) { - Secret ca = issueAddressSpaceCert(addressSpace, labels); - if (ca != null) { - CertComponent component = new CertComponent(cn, namespace, info.getCertSpec().getSecretName()); - CertSigningRequest csr = certManager.createCsr(component); - Cert cert = certManager.signCsr(csr, ca, hosts); - - certManager.createSecret(cert, ca, labels); - } - } else { - certManager.createSelfSignedCertSecret(info.getCertSpec().getSecretName(), labels); - } - } - } -} diff --git a/address-space-controller/src/main/java/io/enmasse/controller/auth/SingleUserAuthenticator.java b/address-space-controller/src/main/java/io/enmasse/controller/auth/SingleUserAuthenticator.java deleted file mode 100644 index d2ebb40d945..00000000000 --- a/address-space-controller/src/main/java/io/enmasse/controller/auth/SingleUserAuthenticator.java +++ /dev/null @@ -1,22 +0,0 @@ -/* - * Copyright 2018, EnMasse authors. - * License: Apache License 2.0 (see the file LICENSE or http://apache.org/licenses/LICENSE-2.0.html). - */ -package io.enmasse.controller.auth; - -import java.net.PasswordAuthentication; -import java.util.Arrays; - -public class SingleUserAuthenticator implements UserAuthenticator { - private PasswordAuthentication passwordAuthentication; - - public SingleUserAuthenticator(PasswordAuthentication passwordAuthentication) { - this.passwordAuthentication = passwordAuthentication; - } - - @Override - public boolean authenticate(String username, String password) { - return passwordAuthentication.getUserName().equals(username) - && Arrays.equals(passwordAuthentication.getPassword(), password.toCharArray()); - } -} diff --git a/address-space-controller/src/main/java/io/enmasse/controller/auth/UserAuthenticator.java b/address-space-controller/src/main/java/io/enmasse/controller/auth/UserAuthenticator.java deleted file mode 100644 index c31b9915ef7..00000000000 --- a/address-space-controller/src/main/java/io/enmasse/controller/auth/UserAuthenticator.java +++ /dev/null @@ -1,9 +0,0 @@ -/* - * Copyright 2017-2018, EnMasse authors. - * License: Apache License 2.0 (see the file LICENSE or http://apache.org/licenses/LICENSE-2.0.html). - */ -package io.enmasse.controller.auth; - -public interface UserAuthenticator { - boolean authenticate(String username, String password); -} diff --git a/address-space-controller/src/main/java/io/enmasse/controller/auth/WildcardCertProvider.java b/address-space-controller/src/main/java/io/enmasse/controller/auth/WildcardCertProvider.java deleted file mode 100644 index 9f3e3702c5a..00000000000 --- a/address-space-controller/src/main/java/io/enmasse/controller/auth/WildcardCertProvider.java +++ /dev/null @@ -1,55 +0,0 @@ -/* - * Copyright 2018, EnMasse authors. - * License: Apache License 2.0 (see the file LICENSE or http://apache.org/licenses/LICENSE-2.0.html). - */ -package io.enmasse.controller.auth; - -import io.enmasse.address.model.AddressSpace; -import io.fabric8.kubernetes.api.model.Secret; -import io.fabric8.kubernetes.client.KubernetesClient; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import java.util.LinkedHashMap; -import java.util.Map; - -public class WildcardCertProvider implements CertProvider { - private static final Logger log = LoggerFactory.getLogger(WildcardCertProvider.class); - private final KubernetesClient client; - private final String wildcardSecretName; - private final String namespace; - - public WildcardCertProvider(KubernetesClient client, String wildcardSecretName) { - this.client = client; - this.wildcardSecretName = wildcardSecretName; - this.namespace = client.getNamespace(); - } - - @Override - public void provideCert(AddressSpace addressSpace, EndpointInfo endpointInfo) { - Secret secret = client.secrets().inNamespace(namespace).withName(endpointInfo.getCertSpec().getSecretName()).get(); - if (secret == null) { - Secret wildcardSecret = null; - if (wildcardSecretName != null) { - wildcardSecret = client .secrets().withName(wildcardSecretName).get(); - } - if (wildcardSecret == null) { - String message = String.format("Requested 'wildcard' certificate provider but no secret '%s' found", wildcardSecretName); - throw new IllegalStateException(message); - } - log.info("Copying wildcard certificate for {}", endpointInfo.getServiceName()); - - Map data = new LinkedHashMap<>(); - data.put("tls.key", wildcardSecret.getData().get("tls.key")); - data.put("tls.crt", wildcardSecret.getData().get("tls.crt")); - - client.secrets().inNamespace(namespace).createNew() - .editOrNewMetadata() - .withName(endpointInfo.getCertSpec().getSecretName()) - .endMetadata() - .withType("kubernetes.io/tls") - .addToData(data) - .done(); - } - } -} diff --git a/address-space-controller/src/main/java/io/enmasse/controller/common/ConfigAdapter.java b/address-space-controller/src/main/java/io/enmasse/controller/common/ConfigAdapter.java deleted file mode 100644 index c536acc5625..00000000000 --- a/address-space-controller/src/main/java/io/enmasse/controller/common/ConfigAdapter.java +++ /dev/null @@ -1,67 +0,0 @@ -/* - * Copyright 2016-2018, EnMasse authors. - * License: Apache License 2.0 (see the file LICENSE or http://apache.org/licenses/LICENSE-2.0.html). - */ - -package io.enmasse.controller.common; - -import io.fabric8.kubernetes.api.model.ConfigMap; -import io.fabric8.kubernetes.client.KubernetesClientException; -import io.fabric8.kubernetes.client.Watch; -import io.fabric8.kubernetes.client.Watcher; -import io.fabric8.openshift.client.OpenShiftClient; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -/** - * An adapter for subscribing to updates to a config map that manages the watch. - */ -public class ConfigAdapter implements Watcher { - private static final Logger log = LoggerFactory.getLogger(ConfigAdapter.class.getName()); - - private Watch watch; - private final OpenShiftClient openshiftClient; - private final String configName; - private final ConfigSubscriber configSubscriber; - - public ConfigAdapter(OpenShiftClient openshiftClient, String configName, ConfigSubscriber configSubscriber) { - this.openshiftClient = openshiftClient; - this.configName = configName; - this.configSubscriber = configSubscriber; - } - - public void start() { - ConfigMap initial = openshiftClient.configMaps().withName(configName).get(); - if (initial != null) { - eventReceived(Action.ADDED, initial); - } - watch = openshiftClient.configMaps().withName(configName).watch(this); - } - - public void stop() { - if (watch != null) { - watch.close(); - } - } - - @Override - public void eventReceived(Action action, ConfigMap resource) { - try { - configSubscriber.configUpdated(action, resource); - } catch (Exception e) { - log.warn("Error handling config update", e); - } - } - - @Override - public void onClose(KubernetesClientException cause) { - if (cause != null) { - log.info("Received onClose for watcher", cause); - stop(); - log.info("Watch for " + configName + " closed, recreating"); - start(); - } else { - log.info("Watch for " + configName + " force closed, stopping"); - } - } -} diff --git a/address-space-controller/src/main/java/io/enmasse/controller/common/ConfigSubscriber.java b/address-space-controller/src/main/java/io/enmasse/controller/common/ConfigSubscriber.java deleted file mode 100644 index ac3307d9ba1..00000000000 --- a/address-space-controller/src/main/java/io/enmasse/controller/common/ConfigSubscriber.java +++ /dev/null @@ -1,18 +0,0 @@ -/* - * Copyright 2016-2018, EnMasse authors. - * License: Apache License 2.0 (see the file LICENSE or http://apache.org/licenses/LICENSE-2.0.html). - */ - -package io.enmasse.controller.common; - -import io.fabric8.kubernetes.api.model.ConfigMap; -import io.fabric8.kubernetes.client.Watcher; - -import java.io.IOException; - -/** - * Represents a component subscribes to config map updates. - */ -public interface ConfigSubscriber { - void configUpdated(Watcher.Action action, ConfigMap configMap) throws IOException; -} diff --git a/address-space-controller/src/main/java/io/enmasse/controller/common/ControllerKind.java b/address-space-controller/src/main/java/io/enmasse/controller/common/ControllerKind.java deleted file mode 100644 index 68cd1c292c3..00000000000 --- a/address-space-controller/src/main/java/io/enmasse/controller/common/ControllerKind.java +++ /dev/null @@ -1,14 +0,0 @@ -/* - * Copyright 2017-2018, EnMasse authors. - * License: Apache License 2.0 (see the file LICENSE or http://apache.org/licenses/LICENSE-2.0.html). - */ -package io.enmasse.controller.common; - -import io.enmasse.k8s.api.EventLogger; - -public enum ControllerKind implements EventLogger.ObjectKind { - Address, - AddressSpace, - Broker, - Controller -} diff --git a/address-space-controller/src/main/java/io/enmasse/controller/common/ControllerReason.java b/address-space-controller/src/main/java/io/enmasse/controller/common/ControllerReason.java deleted file mode 100644 index d4d169ce560..00000000000 --- a/address-space-controller/src/main/java/io/enmasse/controller/common/ControllerReason.java +++ /dev/null @@ -1,25 +0,0 @@ -/* - * Copyright 2017-2018, EnMasse authors. - * License: Apache License 2.0 (see the file LICENSE or http://apache.org/licenses/LICENSE-2.0.html). - */ -package io.enmasse.controller.common; - -import io.enmasse.k8s.api.EventLogger; - -public enum ControllerReason implements EventLogger.Reason { - AddressSpaceCreated, - AddressSpaceChanged, - AddressSpaceUpgraded, - AddressSpaceConverted, - AddressSpaceConversionFailed, - BrokerCreated, - AddressSpaceSyncFailed, - CertCreated, - CertCreateFailed, - RouterCheckFailed, - BrokerDeleted, - BrokerDeleteFailed, - AddressSpaceDeleted, - AddressSpaceDeleteFailed, - AddressSyncFailed; -} diff --git a/address-space-controller/src/main/java/io/enmasse/controller/common/Kubernetes.java b/address-space-controller/src/main/java/io/enmasse/controller/common/Kubernetes.java deleted file mode 100644 index 1b9e4bf0e35..00000000000 --- a/address-space-controller/src/main/java/io/enmasse/controller/common/Kubernetes.java +++ /dev/null @@ -1,44 +0,0 @@ -/* - * Copyright 2017-2018, EnMasse authors. - * License: Apache License 2.0 (see the file LICENSE or http://apache.org/licenses/LICENSE-2.0.html). - */ -package io.enmasse.controller.common; - -import java.io.IOException; -import java.util.Map; -import java.util.Optional; -import java.util.Set; - -import io.enmasse.address.model.AddressSpace; -import io.enmasse.admin.model.v1.InfraConfig; -import io.enmasse.controller.AppliedConfig; -import io.fabric8.kubernetes.api.model.KubernetesList; -import io.fabric8.kubernetes.api.model.Secret; -import io.fabric8.kubernetes.api.model.apps.Deployment; -import io.fabric8.kubernetes.api.model.apps.StatefulSet; - -/** - * Interface for Kubernetes operations done by the address space controller - */ -public interface Kubernetes { - - String getNamespace(); - - void create(KubernetesList resources); - void apply(KubernetesList resourceList, boolean patchPersistentVolumeClaims); - KubernetesList processTemplate(String templateName, Map parameters); - - void deleteResources(String infraUuid) throws InterruptedException; - - Set getReadyDeployments(AddressSpace addressSpace); - Set getReadyStatefulSets(AddressSpace addressSpace); - - Optional getSecret(String secretName); - - boolean existsAddressSpace(AddressSpace addressSpace); - - InfraConfig getAppliedInfraConfig(AddressSpace addressSpace) throws IOException; - AppliedConfig getAppliedConfig(AddressSpace addressSpace) throws IOException; - - String getInfraUuid(AddressSpace addressSpace); -} diff --git a/address-space-controller/src/main/java/io/enmasse/controller/common/KubernetesHelper.java b/address-space-controller/src/main/java/io/enmasse/controller/common/KubernetesHelper.java deleted file mode 100644 index a5f81e9f259..00000000000 --- a/address-space-controller/src/main/java/io/enmasse/controller/common/KubernetesHelper.java +++ /dev/null @@ -1,255 +0,0 @@ -/* - * Copyright 2016-2018, EnMasse authors. - * License: Apache License 2.0 (see the file LICENSE or http://apache.org/licenses/LICENSE-2.0.html). - */ - -package io.enmasse.controller.common; - -import io.enmasse.address.model.AddressSpace; -import io.enmasse.address.model.KubeUtil; -import io.enmasse.admin.model.v1.InfraConfig; -import io.enmasse.config.AnnotationKeys; -import io.enmasse.config.LabelKeys; -import io.enmasse.controller.AppliedConfig; -import io.enmasse.controller.InfraConfigs; -import io.enmasse.k8s.util.Templates; -import io.fabric8.kubernetes.api.model.ConfigMap; -import io.fabric8.kubernetes.api.model.HasMetadata; -import io.fabric8.kubernetes.api.model.KubernetesList; -import io.fabric8.kubernetes.api.model.PersistentVolumeClaim; -import io.fabric8.kubernetes.api.model.Secret; -import io.fabric8.kubernetes.api.model.Service; -import io.fabric8.kubernetes.api.model.ServiceList; -import io.fabric8.kubernetes.api.model.apps.Deployment; -import io.fabric8.kubernetes.api.model.apps.StatefulSet; -import io.fabric8.kubernetes.api.model.networking.NetworkPolicy; -import io.fabric8.kubernetes.client.KubernetesClientException; -import io.fabric8.kubernetes.client.NamespacedKubernetesClient; -import io.fabric8.openshift.client.OpenShiftClient; - -import java.io.File; -import java.io.IOException; -import java.util.HashSet; -import java.util.Map; -import java.util.Optional; -import java.util.Set; -import java.util.stream.Collectors; - -/** - * Wraps the Kubernetes client and adds some helper methods. - */ -public class KubernetesHelper implements Kubernetes { - private static final String TEMPLATE_SUFFIX = ".yaml"; - - private final NamespacedKubernetesClient client; - private final String namespace; - private final File templateDir; - private final boolean isOpenShift; - - public KubernetesHelper(String namespace, NamespacedKubernetesClient client, File templateDir, boolean isOpenShift) { - this.client = client; - this.namespace = namespace; - this.templateDir = templateDir; - this.isOpenShift = isOpenShift; - } - - @Override - public void create(KubernetesList resources) { - client.lists().inNamespace(namespace).create(resources); - } - - @Override - public void apply(KubernetesList resources, boolean patchPersistentVolumeClaims) { - for (HasMetadata resource : resources.getItems()) { - try { - if (resource instanceof ConfigMap) { - client.configMaps().withName(resource.getMetadata().getName()).createOrReplace((ConfigMap) resource); - } else if (resource instanceof Secret) { - client.secrets().withName(resource.getMetadata().getName()).createOrReplace((Secret) resource); - } else if (resource instanceof Deployment) { - client.apps().deployments().withName(resource.getMetadata().getName()).patch((Deployment) resource); - } else if (resource instanceof StatefulSet) { - client.apps().statefulSets().withName(resource.getMetadata().getName()).cascading(false).patch((StatefulSet) resource); - } else if (resource instanceof Service) { - client.services().withName(resource.getMetadata().getName()).createOrReplace((Service) resource); - } else if (resource instanceof NetworkPolicy) { - client.network().networkPolicies().withName(resource.getMetadata().getName()).createOrReplace((NetworkPolicy) resource); - } else if (resource instanceof PersistentVolumeClaim && patchPersistentVolumeClaims) { - client.persistentVolumeClaims().withName(resource.getMetadata().getName()).replace((PersistentVolumeClaim) resource); - } - } catch (KubernetesClientException e) { - if (e.getCode() == 404) { - // Create it if it does not exist - client.resource(resource).createOrReplace(); - } else { - throw e; - } - } - } - } - - @Override - public String getNamespace() { - return namespace; - } - - @Override - public KubernetesList processTemplate(String templateName, Map parameters) { - File templateFile = new File(templateDir, templateName + TEMPLATE_SUFFIX); - return Templates.process(templateFile, parameters); - } - - @Override - public Set getReadyDeployments(AddressSpace addressSpace) { - String infraUuid = addressSpace.getAnnotation(AnnotationKeys.INFRA_UUID); - return client.apps().deployments().inNamespace(namespace).withLabel(LabelKeys.INFRA_UUID, infraUuid).list().getItems().stream() - .filter(KubernetesHelper::isReady) - .collect(Collectors.toSet()); - } - - public static boolean isDeployment(HasMetadata res) { - return res.getKind().equals("Deployment"); // TODO: is there an existing constant for this somewhere? - } - - @Override - public Set getReadyStatefulSets(AddressSpace addressSpace) { - String infraUuid = addressSpace.getAnnotation(AnnotationKeys.INFRA_UUID); - return client.apps().statefulSets().inNamespace(namespace).withLabel(LabelKeys.INFRA_UUID, infraUuid).list().getItems().stream() - .filter(KubernetesHelper::isReady) - .collect(Collectors.toSet()); - } - - public static boolean isStatefulSet(HasMetadata res) { - return res.getKind().equals("StatefulSet"); // TODO: is there an existing constant for this somewhere? - } - - private void waitForZeroPods(String infraUuid, Map labels, long timeoutInMillis) throws InterruptedException { - long end = System.currentTimeMillis() + timeoutInMillis; - int podsLeft = client.pods().withLabels(labels).list().getItems().size(); - while (podsLeft > 0 && System.currentTimeMillis() < end) { - Thread.sleep(5000); - podsLeft = client.pods().withLabels(labels).list().getItems().size(); - } - podsLeft = client.pods().withLabels(labels).list().getItems().size(); - if (podsLeft > 0) { - String message = String.format("Timed out finalizing infra with uuid %s: %d pods still exists", infraUuid, podsLeft); - throw new RuntimeException(message); - } - } - - @Override - public void deleteResources(String infraUuid) throws InterruptedException { - // Delete and await deployments to be deleted so that the per-address space controllers are not re-creating any resources. - // Parallelize deletiong by triggering delete of all deployments and then wait for pods - Set> labelSet = new HashSet<>(); - for (Deployment deployment : client.apps().deployments().withLabel(LabelKeys.INFRA_UUID, infraUuid).list().getItems()) { - if (!client.apps().deployments().inNamespace(deployment.getMetadata().getNamespace()).withName(deployment.getMetadata().getName()).withPropagationPolicy("Background").delete()) { - throw new RuntimeException("Failed to delete infra with uuid " + infraUuid + ": failed to delete deployment " + deployment.getMetadata().getName()); - } - labelSet.add(deployment.getSpec().getTemplate().getMetadata().getLabels()); - } - - // If 5 minutes pass, the address space may not be finalized due to issues garbage collecting the pods. At this point it is - // outside the scope of EnMasse to clean up. - for (Map labels : labelSet) { - waitForZeroPods(infraUuid, labels, 300_000); - } - - client.apps().statefulSets().withLabel(LabelKeys.INFRA_UUID, infraUuid).withPropagationPolicy("Background").delete(); - client.secrets().withLabel(LabelKeys.INFRA_UUID, infraUuid).withPropagationPolicy("Background").delete(); - client.configMaps().withLabel(LabelKeys.INFRA_UUID, infraUuid).withPropagationPolicy("Background").delete(); - client.network().networkPolicies().withLabel(LabelKeys.INFRA_UUID, infraUuid).withPropagationPolicy("Background").delete(); - client.services().withLabel(LabelKeys.INFRA_UUID, infraUuid).withPropagationPolicy("Background").delete(); - client.persistentVolumeClaims().withLabel(LabelKeys.INFRA_UUID, infraUuid).withPropagationPolicy("Background").delete(); - client.policy().podDisruptionBudget().withLabel(LabelKeys.INFRA_UUID, infraUuid) - .withPropagationPolicy("Background").delete(); - if (isOpenShift) { - client.adapt(OpenShiftClient.class).routes().withLabel(LabelKeys.INFRA_UUID, infraUuid).withPropagationPolicy("Background").delete(); - } - } - - @Override - public Optional getSecret(String secretName) { - return Optional.ofNullable(client.secrets().inNamespace(namespace).withName(secretName).get()); - } - - private static boolean isReady(Deployment deployment) { - // TODO: Assuming at least one replica is ok - Integer readyReplicas = deployment.getStatus().getReadyReplicas(); - return readyReplicas != null && readyReplicas >= 1; - } - - private static boolean isReady(StatefulSet statefulSet) { - // TODO: Assuming at least one replica is ok - Integer readyReplicas = statefulSet.getStatus().getReadyReplicas(); - return readyReplicas != null && readyReplicas >= 1; - } - - @Override - public boolean existsAddressSpace(AddressSpace addressSpace) { - return client.services().inNamespace(namespace).withName(KubeUtil.getAddressSpaceServiceName("messaging", addressSpace)).get() != null; - } - - @Override - public AppliedConfig getAppliedConfig(AddressSpace addressSpace) throws IOException { - if (addressSpace.getAnnotation(AnnotationKeys.APPLIED_CONFIGURATION) != null) { - return AppliedConfig.parseCurrentAppliedConfig(addressSpace.getAnnotation(AnnotationKeys.APPLIED_CONFIGURATION)); - } - Service messaging = client.services().inNamespace(namespace).withName(KubeUtil.getAddressSpaceServiceName("messaging", addressSpace)).get(); - if (messaging == null) { - return null; - } - if (messaging.getMetadata().getAnnotations() == null) { - return null; - } - return AppliedConfig.parseCurrentAppliedConfig(messaging.getMetadata().getAnnotations().get(AnnotationKeys.APPLIED_CONFIGURATION)); - } - - /** - * Attempt to find the infra uuid of an address space by doing a reverse-lookup on the expected services. - */ - @Override - public String getInfraUuid(AddressSpace addressSpace) { - if (addressSpace.getAnnotation(AnnotationKeys.INFRA_UUID) != null) { - return addressSpace.getAnnotation(AnnotationKeys.INFRA_UUID); - } - ServiceList services = client.services().inNamespace(namespace).list(); - if (services == null) { - return null; - } - Service found = null; - for (Service service : services.getItems()) { - if (service.getMetadata().getAnnotations() != null) { - String addressSpaceName = service.getMetadata().getAnnotations().get(AnnotationKeys.ADDRESS_SPACE); - String addressSpaceNamespace = service.getMetadata().getAnnotations().get(AnnotationKeys.ADDRESS_SPACE_NAMESPACE); - if (addressSpace.getMetadata().getName().equals(addressSpaceName) && addressSpace.getMetadata().getNamespace().equals(addressSpaceNamespace)) { - found = service; - break; - } - } - } - if (found == null || found.getMetadata().getLabels() == null) { - return null; - } - return found.getMetadata().getLabels().get(LabelKeys.INFRA_UUID); - } - - @Override - public InfraConfig getAppliedInfraConfig(AddressSpace addressSpace) throws IOException { - InfraConfig config = InfraConfigs.parseCurrentInfraConfig(addressSpace); - if (config != null) { - return config; - } - - Service messaging = client.services().inNamespace(namespace).withName(KubeUtil.getAddressSpaceServiceName("messaging", addressSpace)).get(); - if (messaging == null) { - return null; - } - - if (messaging.getMetadata().getAnnotations() == null) { - return null; - } - - return InfraConfigs.parseCurrentInfraConfig(messaging.getMetadata().getAnnotations().get(AnnotationKeys.APPLIED_INFRA_CONFIG)); - } -} diff --git a/address-space-controller/src/main/java/io/enmasse/controller/common/NamespaceInfo.java b/address-space-controller/src/main/java/io/enmasse/controller/common/NamespaceInfo.java deleted file mode 100644 index 1e32910a595..00000000000 --- a/address-space-controller/src/main/java/io/enmasse/controller/common/NamespaceInfo.java +++ /dev/null @@ -1,58 +0,0 @@ -/* - * Copyright 2017-2018, EnMasse authors. - * License: Apache License 2.0 (see the file LICENSE or http://apache.org/licenses/LICENSE-2.0.html). - */ -package io.enmasse.controller.common; - -final public class NamespaceInfo { - private final String configName; - private final String addressSpace; - private final String namespace; - private final String createdBy; - - public NamespaceInfo(String configName, String addressSpace, String namespace, String createdBy) { - this.configName = configName; - this.addressSpace = addressSpace; - this.namespace = namespace; - this.createdBy = createdBy; - } - - public String getCreatedBy() { - return createdBy; - } - - public String getAddressSpace() { - return addressSpace; - } - - public String getConfigName() { - return configName; - } - - public String getNamespace() { - return namespace; - } - - @Override - public String toString() { - StringBuilder sb = new StringBuilder(); - sb.append("{addressSpace=").append(addressSpace).append(",") - .append("namespace=").append(namespace).append("}"); - return sb.toString(); - } - - @Override - public boolean equals(Object o) { - if (this == o) return true; - if (o == null || getClass() != o.getClass()) return false; - - NamespaceInfo that = (NamespaceInfo) o; - - return addressSpace.equals(that.addressSpace); - } - - @Override - public int hashCode() { - return addressSpace.hashCode(); - } -} diff --git a/address-space-controller/src/main/java/io/enmasse/controller/common/TemplateParameter.java b/address-space-controller/src/main/java/io/enmasse/controller/common/TemplateParameter.java deleted file mode 100644 index 2aa64a9decf..00000000000 --- a/address-space-controller/src/main/java/io/enmasse/controller/common/TemplateParameter.java +++ /dev/null @@ -1,47 +0,0 @@ -/* - * Copyright 2017-2018, EnMasse authors. - * License: Apache License 2.0 (see the file LICENSE or http://apache.org/licenses/LICENSE-2.0.html). - */ - -package io.enmasse.controller.common; - -/** - * Template parameters that are dynamically set by the address space controller. - */ -public interface TemplateParameter { - String ADDRESS_SPACE = "ADDRESS_SPACE"; - String ADDRESS_SPACE_NAMESPACE = "ADDRESS_SPACE_NAMESPACE"; - - String MESSAGING_SECRET = "MESSAGING_SECRET"; - String MQTT_SECRET = "MQTT_SECRET"; - - String AUTHENTICATION_SERVICE_HOST = "AUTHENTICATION_SERVICE_HOST"; - String AUTHENTICATION_SERVICE_PORT = "AUTHENTICATION_SERVICE_PORT"; - String AUTHENTICATION_SERVICE_CA_CERT = "AUTHENTICATION_SERVICE_CA_CERT"; - String AUTHENTICATION_SERVICE_CLIENT_SECRET = "AUTHENTICATION_SERVICE_CLIENT_SECRET"; - String AUTHENTICATION_SERVICE_SASL_INIT_HOST = "AUTHENTICATION_SERVICE_SASL_INIT_HOST"; - String INFRA_UUID = "INFRA_UUID"; - String ADDRESS_SPACE_PLAN = "ADDRESS_SPACE_PLAN"; - - String INFRA_NAMESPACE = "INFRA_NAMESPACE"; - String BROKER_STORAGE_CAPACITY = "BROKER_STORAGE_CAPACITY"; - String BROKER_ADDRESS_FULL_POLICY = "BROKER_ADDRESS_FULL_POLICY"; - String BROKER_GLOBAL_MAX_SIZE = "BROKER_GLOBAL_MAX_SIZE"; - String BROKER_JAVA_OPTS = "BROKER_JAVA_OPTS"; - String STANDARD_INFRA_CONFIG_NAME = "STANDARD_INFRA_CONFIG_NAME"; - - String IMAGE_PULL_POLICY = "IMAGE_PULL_POLICY"; - String ROUTER_IMAGE = "ROUTER_IMAGE"; - String STANDARD_CONTROLLER_IMAGE = "STANDARD_CONTROLLER_IMAGE"; - String AGENT_IMAGE = "AGENT_IMAGE"; - String BROKER_IMAGE = "BROKER_IMAGE"; - String BROKER_PLUGIN_IMAGE = "BROKER_PLUGIN_IMAGE"; - String TOPIC_FORWARDER_IMAGE = "TOPIC_FORWARDER_IMAGE"; - String MQTT_GATEWAY_IMAGE = "MQTT_GATEWAY_IMAGE"; - String MQTT_LWT_IMAGE = "MQTT_LWT_IMAGE"; - - String FS_GROUP_FALLBACK_MAP = "FS_GROUP_FALLBACK_MAP"; - String BROKER_SUPPORT_USER = "BROKER_SUPPORT_USER"; - String BROKER_SUPPORT_PWD = "BROKER_SUPPORT_PWD"; - -} diff --git a/address-space-controller/src/main/java/io/enmasse/controller/keycloak/RealmController.java b/address-space-controller/src/main/java/io/enmasse/controller/keycloak/RealmController.java deleted file mode 100644 index ad574e9e4a5..00000000000 --- a/address-space-controller/src/main/java/io/enmasse/controller/keycloak/RealmController.java +++ /dev/null @@ -1,120 +0,0 @@ -/* - * Copyright 2017-2018, EnMasse authors. - * License: Apache License 2.0 (see the file LICENSE or http://apache.org/licenses/LICENSE-2.0.html). - */ - -package io.enmasse.controller.keycloak; - -import io.enmasse.address.model.AddressSpace; -import io.enmasse.admin.model.v1.AuthenticationService; -import io.enmasse.admin.model.v1.AuthenticationServiceType; -import io.enmasse.config.AnnotationKeys; -import io.enmasse.controller.Controller; -import io.enmasse.k8s.api.AuthenticationServiceRegistry; -import io.enmasse.user.api.RealmApi; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import java.util.*; -import java.util.stream.Collectors; - -public class RealmController implements Controller { - private static final Logger log = LoggerFactory.getLogger(RealmController.class); - private static final String MASTER_REALM = "master"; - private final RealmApi keycloak; - private final AuthenticationServiceRegistry authenticationServiceRegistry; - - public RealmController(RealmApi keycloak, AuthenticationServiceRegistry authenticationServiceRegistry) { - this.keycloak = keycloak; - this.authenticationServiceRegistry = authenticationServiceRegistry; - } - - private static class AuthServiceEntry { - private final List addressSpaces = new ArrayList<>(); - private final AuthenticationService authenticationService; - - private AuthServiceEntry(AuthenticationService authenticationService) { - this.authenticationService = authenticationService; - } - - public void addAddressSpace(AddressSpace addressSpace) { - this.addressSpaces.add(addressSpace); - } - - public List getAddressSpaces() { - return Collections.unmodifiableList(addressSpaces); - } - - public AuthenticationService getAuthenticationService() { - return authenticationService; - } - } - - @Override - public void reconcileAll(List spaces) throws Exception { - Map authserviceMap = new HashMap<>(); - - for (AddressSpace addressSpace : spaces) { - AuthenticationService authenticationService = authenticationServiceRegistry.findAuthenticationService(addressSpace.getSpec().getAuthenticationService()).orElse(null); - if (authenticationService == null) { - continue; - } - - if (authenticationService.getStatus() == null) { - log.debug("Standard authentication service not configured, not performing any operations"); - continue; - } - - if (authenticationService.getSpec().getRealm() != null) { - log.debug("Standard authentication service overriding realm, not performing any operations"); - continue; - } - - if (!authenticationService.getSpec().getType().equals(AuthenticationServiceType.standard)) { - log.debug("Will only process standard authentication services"); - continue; - } - - AuthServiceEntry entry = authserviceMap.computeIfAbsent(authenticationService.getMetadata().getName(), k -> new AuthServiceEntry(authenticationService)); - entry.addAddressSpace(addressSpace); - } - - for (AuthenticationService authenticationService : authenticationServiceRegistry.listAuthenticationServices()) { - if (authenticationService.getSpec().getType().equals(AuthenticationServiceType.standard) && authenticationService.getSpec().getRealm() == null) { - Set actualRealms = keycloak.getRealmNames(authenticationService); - Set desiredRealms = authserviceMap.getOrDefault(authenticationService.getMetadata().getName(), new AuthServiceEntry(authenticationService)).getAddressSpaces().stream() - .map(a -> a.getAnnotation(AnnotationKeys.REALM_NAME)) - .collect(Collectors.toSet()); - - log.info("Actual: {}, Desired: {}", actualRealms, desiredRealms); - for (String realmName : actualRealms) { - if (!desiredRealms.contains(realmName) && !MASTER_REALM.equals(realmName)) { - log.info("Deleting realm {}", realmName); - keycloak.deleteRealm(authenticationService, realmName); - } - } - } - } - - for (AuthServiceEntry entry : authserviceMap.values()) { - AuthenticationService authenticationService = entry.getAuthenticationService(); - List addressSpaces = entry.getAddressSpaces(); - - Set actualRealms = keycloak.getRealmNames(authenticationService); - - for (AddressSpace addressSpace : addressSpaces) { - String realmName = addressSpace.getAnnotation(AnnotationKeys.REALM_NAME); - if (actualRealms.contains(realmName)) { - continue; - } - log.info("Creating realm {} in authentication service {}", realmName, authenticationService.getMetadata().getName()); - keycloak.createRealm(authenticationService, addressSpace.getMetadata().getNamespace(), realmName); - } - } - } - - @Override - public String toString() { - return "RealmController"; - } -} diff --git a/address-space-controller/src/main/java/io/enmasse/controller/router/config/Address.java b/address-space-controller/src/main/java/io/enmasse/controller/router/config/Address.java deleted file mode 100644 index bf3b431103a..00000000000 --- a/address-space-controller/src/main/java/io/enmasse/controller/router/config/Address.java +++ /dev/null @@ -1,86 +0,0 @@ -/* - * Copyright 2019, EnMasse authors. - * License: Apache License 2.0 (see the file LICENSE or http://apache.org/licenses/LICENSE-2.0.html). - */ -package io.enmasse.controller.router.config; - -import com.fasterxml.jackson.annotation.JsonInclude; - -import java.util.Objects; - -@JsonInclude(JsonInclude.Include.NON_NULL) -public class Address { - private String name; - private String prefix; - private String pattern; - private Boolean waypoint; - private Distribution distribution; - - public String getName() { - return name; - } - - public void setName(String name) { - this.name = name; - } - - public String getPrefix() { - return prefix; - } - - public void setPrefix(String prefix) { - this.prefix = prefix; - } - - public Distribution getDistribution() { - return distribution; - } - - public void setDistribution(Distribution distribution) { - this.distribution = distribution; - } - - public String getPattern() { - return pattern; - } - - public void setPattern(String pattern) { - this.pattern = pattern; - } - - public Boolean getWaypoint() { - return waypoint; - } - - public void setWaypoint(Boolean waypoint) { - this.waypoint = waypoint; - } - - @Override - public String toString() { - return "Address{" + - "name='" + name + '\'' + - ", prefix='" + prefix + '\'' + - ", pattern='" + pattern + '\'' + - ", waypoint=" + waypoint + - ", distribution=" + distribution + - '}'; - } - - @Override - public boolean equals(Object o) { - if (this == o) return true; - if (o == null || getClass() != o.getClass()) return false; - Address address = (Address) o; - return Objects.equals(name, address.name) && - Objects.equals(prefix, address.prefix) && - Objects.equals(pattern, address.pattern) && - Objects.equals(waypoint, address.waypoint) && - distribution == address.distribution; - } - - @Override - public int hashCode() { - return Objects.hash(name, prefix, pattern, waypoint, distribution); - } -} diff --git a/address-space-controller/src/main/java/io/enmasse/controller/router/config/AuthServicePlugin.java b/address-space-controller/src/main/java/io/enmasse/controller/router/config/AuthServicePlugin.java deleted file mode 100644 index 16d541cb28e..00000000000 --- a/address-space-controller/src/main/java/io/enmasse/controller/router/config/AuthServicePlugin.java +++ /dev/null @@ -1,86 +0,0 @@ -/* - * Copyright 2019, EnMasse authors. - * License: Apache License 2.0 (see the file LICENSE or http://apache.org/licenses/LICENSE-2.0.html). - */ -package io.enmasse.controller.router.config; - -import com.fasterxml.jackson.annotation.JsonInclude; - -import java.util.Objects; - -@JsonInclude(JsonInclude.Include.NON_NULL) -public class AuthServicePlugin { - private String name = "auth_service"; - private String host; - private int port; - private String realm; - private String sslProfile; - - public String getName() { - return name; - } - - public void setName(String name) { - this.name = name; - } - - public String getHost() { - return host; - } - - public void setHost(String host) { - this.host = host; - } - - public int getPort() { - return port; - } - - public void setPort(int port) { - this.port = port; - } - - public String getRealm() { - return realm; - } - - public void setRealm(String realm) { - this.realm = realm; - } - - public String getSslProfile() { - return sslProfile; - } - - public void setSslProfile(String sslProfile) { - this.sslProfile = sslProfile; - } - - @Override - public String toString() { - return "AuthServicePlugin{" + - "name='" + name + '\'' + - ", host='" + host + '\'' + - ", port=" + port + - ", realm='" + realm + '\'' + - ", sslProfile='" + sslProfile + '\'' + - '}'; - } - - @Override - public boolean equals(Object o) { - if (this == o) return true; - if (o == null || getClass() != o.getClass()) return false; - AuthServicePlugin that = (AuthServicePlugin) o; - return port == that.port && - Objects.equals(name, that.name) && - Objects.equals(host, that.host) && - Objects.equals(realm, that.realm) && - Objects.equals(sslProfile, that.sslProfile); - } - - @Override - public int hashCode() { - return Objects.hash(name, host, port, realm, sslProfile); - } -} diff --git a/address-space-controller/src/main/java/io/enmasse/controller/router/config/AutoLink.java b/address-space-controller/src/main/java/io/enmasse/controller/router/config/AutoLink.java deleted file mode 100644 index 461f72d493a..00000000000 --- a/address-space-controller/src/main/java/io/enmasse/controller/router/config/AutoLink.java +++ /dev/null @@ -1,86 +0,0 @@ -/* - * Copyright 2019, EnMasse authors. - * License: Apache License 2.0 (see the file LICENSE or http://apache.org/licenses/LICENSE-2.0.html). - */ -package io.enmasse.controller.router.config; - -import com.fasterxml.jackson.annotation.JsonInclude; - -import java.util.Objects; - -@JsonInclude(JsonInclude.Include.NON_NULL) -public class AutoLink { - private String name; - private String address; - private LinkDirection direction; - private String containerId; - private String connection; - - public String getName() { - return name; - } - - public void setName(String name) { - this.name = name; - } - - public LinkDirection getDirection() { - return direction; - } - - public void setDirection(LinkDirection direction) { - this.direction = direction; - } - - public String getContainerId() { - return containerId; - } - - public void setContainerId(String containerId) { - this.containerId = containerId; - } - - public String getConnection() { - return connection; - } - - public void setConnection(String connection) { - this.connection = connection; - } - - public String getAddress() { - return address; - } - - public void setAddress(String address) { - this.address = address; - } - - @Override - public String toString() { - return "AutoLink{" + - "name='" + name + '\'' + - ", address='" + address + '\'' + - ", direction=" + direction + - ", containerId='" + containerId + '\'' + - ", connection='" + connection + '\'' + - '}'; - } - - @Override - public boolean equals(Object o) { - if (this == o) return true; - if (o == null || getClass() != o.getClass()) return false; - AutoLink autoLink = (AutoLink) o; - return Objects.equals(name, autoLink.name) && - Objects.equals(address, autoLink.address) && - direction == autoLink.direction && - Objects.equals(containerId, autoLink.containerId) && - Objects.equals(connection, autoLink.connection); - } - - @Override - public int hashCode() { - return Objects.hash(name, address, direction, containerId, connection); - } -} diff --git a/address-space-controller/src/main/java/io/enmasse/controller/router/config/Connector.java b/address-space-controller/src/main/java/io/enmasse/controller/router/config/Connector.java deleted file mode 100644 index 3eb25a2344e..00000000000 --- a/address-space-controller/src/main/java/io/enmasse/controller/router/config/Connector.java +++ /dev/null @@ -1,161 +0,0 @@ -/* - * Copyright 2019, EnMasse authors. - * License: Apache License 2.0 (see the file LICENSE or http://apache.org/licenses/LICENSE-2.0.html). - */ -package io.enmasse.controller.router.config; - -import java.util.Objects; - -import com.fasterxml.jackson.annotation.JsonInclude; - -@JsonInclude(JsonInclude.Include.NON_NULL) -public class Connector { - private String name; - private String host; - private int port; - private String sslProfile; - private Boolean verifyHostname; - private String saslUsername; - private String saslPassword; - private String saslMechanisms; - private String failoverUrls; - private Role role; - private Integer maxFrameSize; - private Integer idleTimeoutSeconds; - - public String getHost() { - return host; - } - - public void setHost(String host) { - this.host = host; - } - - public int getPort() { - return port; - } - - public void setPort(int port) { - this.port = port; - } - - public String getSslProfile() { - return sslProfile; - } - - public void setSslProfile(String sslProfile) { - this.sslProfile = sslProfile; - } - - public Boolean getVerifyHostname() { - return verifyHostname; - } - - public void setVerifyHostname(Boolean verifyHostname) { - this.verifyHostname = verifyHostname; - } - - public String getSaslUsername() { - return saslUsername; - } - - public void setSaslUsername(String saslUsername) { - this.saslUsername = saslUsername; - } - - public String getSaslPassword() { - return saslPassword; - } - - public void setSaslPassword(String saslPassword) { - this.saslPassword = saslPassword; - } - - public String getSaslMechanisms() { - return saslMechanisms; - } - - public void setSaslMechanisms(String saslMechanisms) { - this.saslMechanisms = saslMechanisms; - } - - public String getFailoverUrls() { - return failoverUrls; - } - - public void setFailoverUrls(String failoverUrls) { - this.failoverUrls = failoverUrls; - } - - public Role getRole() { - return role; - } - - public void setRole(Role role) { - this.role = role; - } - - public String getName() { - return name; - } - - public void setName(String name) { - this.name = name; - } - - public Integer getMaxFrameSize() { - return maxFrameSize; - } - - public void setMaxFrameSize(Integer maxFrameSize) { - this.maxFrameSize = maxFrameSize; - } - - public Integer getIdleTimeoutSeconds() { - return idleTimeoutSeconds; - } - - public void setIdleTimeoutSeconds(Integer idleTimeoutSeconds) { - this.idleTimeoutSeconds = idleTimeoutSeconds; - } - - @Override - public String toString() { - return "Connector{" + - "name='" + name + '\'' + - ", host='" + host + '\'' + - ", port=" + port + - ", sslProfile='" + sslProfile + '\'' + - ", verifyHostname=" + verifyHostname + - ", saslMechanisms='" + saslMechanisms + '\'' + - ", failoverUrls='" + failoverUrls + '\'' + - ", idleTimeoutSeconds=" + idleTimeoutSeconds + - ", maxFrameSize=" + maxFrameSize + - ", role=" + role + - '}'; - } - - @Override - public boolean equals(Object o) { - if (this == o) return true; - if (o == null || getClass() != o.getClass()) return false; - Connector connector = (Connector) o; - return port == connector.port && - Objects.equals(name, connector.name) && - Objects.equals(host, connector.host) && - Objects.equals(sslProfile, connector.sslProfile) && - Objects.equals(verifyHostname, connector.verifyHostname) && - Objects.equals(saslUsername, connector.saslUsername) && - Objects.equals(saslPassword, connector.saslPassword) && - Objects.equals(saslMechanisms, connector.saslMechanisms) && - Objects.equals(failoverUrls, connector.failoverUrls) && - role == connector.role && - Objects.equals(idleTimeoutSeconds, connector.idleTimeoutSeconds) && - Objects.equals(maxFrameSize, connector.maxFrameSize); - } - - @Override - public int hashCode() { - return Objects.hash(name, host, port, sslProfile, verifyHostname, saslUsername, saslPassword, saslMechanisms, failoverUrls, role, idleTimeoutSeconds, maxFrameSize); - } -} diff --git a/address-space-controller/src/main/java/io/enmasse/controller/router/config/Distribution.java b/address-space-controller/src/main/java/io/enmasse/controller/router/config/Distribution.java deleted file mode 100644 index 0ce3e97d51f..00000000000 --- a/address-space-controller/src/main/java/io/enmasse/controller/router/config/Distribution.java +++ /dev/null @@ -1,13 +0,0 @@ -/* - * Copyright 2019, EnMasse authors. - * License: Apache License 2.0 (see the file LICENSE or http://apache.org/licenses/LICENSE-2.0.html). - */ -package io.enmasse.controller.router.config; - -import com.fasterxml.jackson.annotation.JsonInclude; - -@JsonInclude(JsonInclude.Include.NON_NULL) -public enum Distribution { - unavailable, - balanced -} diff --git a/address-space-controller/src/main/java/io/enmasse/controller/router/config/LinkDirection.java b/address-space-controller/src/main/java/io/enmasse/controller/router/config/LinkDirection.java deleted file mode 100644 index a76f530c6ec..00000000000 --- a/address-space-controller/src/main/java/io/enmasse/controller/router/config/LinkDirection.java +++ /dev/null @@ -1,10 +0,0 @@ -/* - * Copyright 2019, EnMasse authors. - * License: Apache License 2.0 (see the file LICENSE or http://apache.org/licenses/LICENSE-2.0.html). - */ -package io.enmasse.controller.router.config; - -public enum LinkDirection { - in, - out -} diff --git a/address-space-controller/src/main/java/io/enmasse/controller/router/config/LinkRoute.java b/address-space-controller/src/main/java/io/enmasse/controller/router/config/LinkRoute.java deleted file mode 100644 index d727a2dff46..00000000000 --- a/address-space-controller/src/main/java/io/enmasse/controller/router/config/LinkRoute.java +++ /dev/null @@ -1,109 +0,0 @@ -/* - * Copyright 2019, EnMasse authors. - * License: Apache License 2.0 (see the file LICENSE or http://apache.org/licenses/LICENSE-2.0.html). - */ -package io.enmasse.controller.router.config; - -import com.fasterxml.jackson.annotation.JsonInclude; - -import java.util.Objects; - -@JsonInclude(JsonInclude.Include.NON_NULL) -public class LinkRoute { - private String name; - private String prefix; - private String pattern; - private String delExternalPrefix; - private LinkDirection direction; - private String containerId; - private String connection; - - public String getName() { - return name; - } - - public void setName(String name) { - this.name = name; - } - - public String getPrefix() { - return prefix; - } - - public void setPrefix(String prefix) { - this.prefix = prefix; - } - - public LinkDirection getDirection() { - return direction; - } - - public void setDirection(LinkDirection direction) { - this.direction = direction; - } - - public String getContainerId() { - return containerId; - } - - public void setContainerId(String containerId) { - this.containerId = containerId; - } - - public String getConnection() { - return connection; - } - - public void setConnection(String connection) { - this.connection = connection; - } - - public String getPattern() { - return pattern; - } - - public void setPattern(String pattern) { - this.pattern = pattern; - } - - public String getDelExternalPrefix() { - return delExternalPrefix; - } - - public void setDelExternalPrefix(String delExternalPrefix) { - this.delExternalPrefix = delExternalPrefix; - } - - @Override - public String toString() { - return "LinkRoute{" + - "name='" + name + '\'' + - ", prefix='" + prefix + '\'' + - ", pattern='" + pattern + '\'' + - ", delExternalPrefix='" + delExternalPrefix + '\'' + - ", direction=" + direction + - ", containerId='" + containerId + '\'' + - ", connection='" + connection + '\'' + - '}'; - } - - @Override - public boolean equals(Object o) { - if (this == o) return true; - if (o == null || getClass() != o.getClass()) return false; - LinkRoute linkRoute = (LinkRoute) o; - return Objects.equals(name, linkRoute.name) && - Objects.equals(prefix, linkRoute.prefix) && - Objects.equals(pattern, linkRoute.pattern) && - Objects.equals(delExternalPrefix, linkRoute.delExternalPrefix) && - direction == linkRoute.direction && - Objects.equals(containerId, linkRoute.containerId) && - Objects.equals(connection, linkRoute.connection); - } - - @Override - public int hashCode() { - return Objects.hash(name, prefix, pattern, delExternalPrefix, direction, containerId, connection); - } - -} diff --git a/address-space-controller/src/main/java/io/enmasse/controller/router/config/Listener.java b/address-space-controller/src/main/java/io/enmasse/controller/router/config/Listener.java deleted file mode 100644 index 79c45940cb0..00000000000 --- a/address-space-controller/src/main/java/io/enmasse/controller/router/config/Listener.java +++ /dev/null @@ -1,218 +0,0 @@ -/* - * Copyright 2019, EnMasse authors. - * License: Apache License 2.0 (see the file LICENSE or http://apache.org/licenses/LICENSE-2.0.html). - */ -package io.enmasse.controller.router.config; - -import com.fasterxml.jackson.annotation.JsonInclude; - -import java.util.Objects; - -@JsonInclude(JsonInclude.Include.NON_NULL) -public class Listener { - private String host; - private int port; - private Role role; - private Boolean authenticatePeer; - private Boolean http; - private Boolean requireSsl; - private Boolean metrics; - private Boolean healthz; - private Boolean websockets; - private String httpRootDir; - private String saslPlugin; - private String sslProfile; - private String saslMechanisms; - private Integer idleTimeoutSeconds; - private Integer linkCapacity; - private Integer initialHandshakeTimeoutSeconds; - private String policyVhost; - - public String getHost() { - return host; - } - - public void setHost(String host) { - this.host = host; - } - - public int getPort() { - return port; - } - - public void setPort(int port) { - this.port = port; - } - - public Boolean getAuthenticatePeer() { - return authenticatePeer; - } - - public void setAuthenticatePeer(Boolean authenticatePeer) { - this.authenticatePeer = authenticatePeer; - } - - public Boolean getHttp() { - return http; - } - - public void setHttp(Boolean http) { - this.http = http; - } - - public Boolean getMetrics() { - return metrics; - } - - public void setMetrics(Boolean metrics) { - this.metrics = metrics; - } - - public Boolean getWebsockets() { - return websockets; - } - - public void setWebsockets(Boolean websockets) { - this.websockets = websockets; - } - - public String getHttpRootDir() { - return httpRootDir; - } - - public void setHttpRootDir(String httpRootDir) { - this.httpRootDir = httpRootDir; - } - - public String getSaslPlugin() { - return saslPlugin; - } - - public void setSaslPlugin(String saslPlugin) { - this.saslPlugin = saslPlugin; - } - - public Integer getIdleTimeoutSeconds() { - return idleTimeoutSeconds; - } - - public void setIdleTimeoutSeconds(Integer idleTimeoutSeconds) { - this.idleTimeoutSeconds = idleTimeoutSeconds; - } - - public Integer getLinkCapacity() { - return linkCapacity; - } - - public void setLinkCapacity(Integer linkCapacity) { - this.linkCapacity = linkCapacity; - } - - public Integer getInitialHandshakeTimeoutSeconds() { - return initialHandshakeTimeoutSeconds; - } - - public void setInitialHandshakeTimeoutSeconds(Integer initialHandshakeTimeoutSeconds) { - this.initialHandshakeTimeoutSeconds = initialHandshakeTimeoutSeconds; - } - - public String getPolicyVhost() { - return policyVhost; - } - - public void setPolicyVhost(String policyVhost) { - this.policyVhost = policyVhost; - } - - public Boolean getHealthz() { - return healthz; - } - - public void setHealthz(Boolean healthz) { - this.healthz = healthz; - } - - public Role getRole() { - return role; - } - - public void setRole(Role role) { - this.role = role; - } - - public String getSslProfile() { - return sslProfile; - } - - public void setSslProfile(String sslProfile) { - this.sslProfile = sslProfile; - } - - public String getSaslMechanisms() { - return saslMechanisms; - } - - public void setSaslMechanisms(String saslMechanisms) { - this.saslMechanisms = saslMechanisms; - } - - public Boolean getRequireSsl() { - return requireSsl; - } - - public void setRequireSsl(Boolean requireSsl) { - this.requireSsl = requireSsl; - } - - @Override - public String toString() { - return "Listener{" + - "host='" + host + '\'' + - ", port=" + port + - ", role=" + role + - ", authenticatePeer=" + authenticatePeer + - ", http=" + http + - ", requireSsl=" + requireSsl + - ", metrics=" + metrics + - ", sslProfile=" + sslProfile + - ", saslMechanisms=" + saslMechanisms + - ", healthz=" + metrics + - ", websockets=" + websockets + - ", httpRootDir='" + httpRootDir + '\'' + - ", saslPlugin='" + saslPlugin + '\'' + - ", idleTimeoutSeconds=" + idleTimeoutSeconds + - ", linkCapacity=" + linkCapacity + - ", initialHandshakeTimeoutSeconds=" + initialHandshakeTimeoutSeconds + - ", policyVhost='" + policyVhost + '\'' + - '}'; - } - - @Override - public boolean equals(Object o) { - if (this == o) return true; - if (o == null || getClass() != o.getClass()) return false; - Listener listener = (Listener) o; - return port == listener.port && - Objects.equals(host, listener.host) && - Objects.equals(role, listener.role) && - Objects.equals(authenticatePeer, listener.authenticatePeer) && - Objects.equals(http, listener.http) && - Objects.equals(requireSsl, listener.requireSsl) && - Objects.equals(metrics, listener.metrics) && - Objects.equals(sslProfile, listener.sslProfile) && - Objects.equals(saslMechanisms, listener.saslMechanisms) && - Objects.equals(healthz, listener.healthz) && - Objects.equals(websockets, listener.websockets) && - Objects.equals(httpRootDir, listener.httpRootDir) && - Objects.equals(saslPlugin, listener.saslPlugin) && - Objects.equals(idleTimeoutSeconds, listener.idleTimeoutSeconds) && - Objects.equals(linkCapacity, listener.linkCapacity) && - Objects.equals(initialHandshakeTimeoutSeconds, listener.initialHandshakeTimeoutSeconds) && - Objects.equals(policyVhost, listener.policyVhost); - } - - @Override - public int hashCode() { - return Objects.hash(host, port, role, authenticatePeer, http, requireSsl, metrics, sslProfile, saslMechanisms, healthz, websockets, httpRootDir, saslPlugin, idleTimeoutSeconds, linkCapacity, initialHandshakeTimeoutSeconds, policyVhost); - } -} diff --git a/address-space-controller/src/main/java/io/enmasse/controller/router/config/Policy.java b/address-space-controller/src/main/java/io/enmasse/controller/router/config/Policy.java deleted file mode 100644 index d3ec970c896..00000000000 --- a/address-space-controller/src/main/java/io/enmasse/controller/router/config/Policy.java +++ /dev/null @@ -1,42 +0,0 @@ -/* - * Copyright 2019, EnMasse authors. - * License: Apache License 2.0 (see the file LICENSE or http://apache.org/licenses/LICENSE-2.0.html). - */ -package io.enmasse.controller.router.config; - -import com.fasterxml.jackson.annotation.JsonInclude; - -import java.util.Objects; - -@JsonInclude(JsonInclude.Include.NON_NULL) -public class Policy { - private Boolean enableVhostPolicy; - - public Boolean getEnableVhostPolicy() { - return enableVhostPolicy; - } - - public void setEnableVhostPolicy(Boolean enableVhostPolicy) { - this.enableVhostPolicy = enableVhostPolicy; - } - - @Override - public String toString() { - return "Policy{" + - "enableVhostPolicy=" + enableVhostPolicy + - '}'; - } - - @Override - public boolean equals(Object o) { - if (this == o) return true; - if (o == null || getClass() != o.getClass()) return false; - Policy policy = (Policy) o; - return Objects.equals(enableVhostPolicy, policy.enableVhostPolicy); - } - - @Override - public int hashCode() { - return Objects.hash(enableVhostPolicy); - } -} diff --git a/address-space-controller/src/main/java/io/enmasse/controller/router/config/Role.java b/address-space-controller/src/main/java/io/enmasse/controller/router/config/Role.java deleted file mode 100644 index 4b4caedb8dc..00000000000 --- a/address-space-controller/src/main/java/io/enmasse/controller/router/config/Role.java +++ /dev/null @@ -1,48 +0,0 @@ -/* - * Copyright 2019, EnMasse authors. - * License: Apache License 2.0 (see the file LICENSE or http://apache.org/licenses/LICENSE-2.0.html). - */ -package io.enmasse.controller.router.config; - -import com.fasterxml.jackson.annotation.JsonCreator; -import com.fasterxml.jackson.annotation.JsonValue; - -public enum Role { - normal("normal"), - inter_router("inter-router"), - route_container("route-container"), - edge("edge"); - - private final String desc; - Role(String desc) { - this.desc = desc; - } - - @JsonValue - public String toValue() { - return desc; - } - - @JsonCreator - public static Role forValue(String value) { - switch (value) { - case "normal": - return Role.normal; - case "route-container": - return Role.route_container; - case "inter-router": - return Role.inter_router; - case "edge": - return Role.edge; - default: - throw new IllegalArgumentException("Unknown role '" + value + "'"); - } - } - - @Override - public String toString() { - return "Role{" + - "desc='" + desc + '\'' + - '}'; - } -} diff --git a/address-space-controller/src/main/java/io/enmasse/controller/router/config/Router.java b/address-space-controller/src/main/java/io/enmasse/controller/router/config/Router.java deleted file mode 100644 index 9a3add584b3..00000000000 --- a/address-space-controller/src/main/java/io/enmasse/controller/router/config/Router.java +++ /dev/null @@ -1,112 +0,0 @@ -/* - * Copyright 2019, EnMasse authors. - * License: Apache License 2.0 (see the file LICENSE or http://apache.org/licenses/LICENSE-2.0.html). - */ -package io.enmasse.controller.router.config; - -import com.fasterxml.jackson.annotation.JsonInclude; - -import java.util.Objects; - -@JsonInclude(JsonInclude.Include.NON_NULL) -public class Router { - private Mode mode = Mode.interior; - private String id = "${HOSTNAME}"; - private int workerThreads = 4; - private Distribution defaultDistribution = Distribution.unavailable; - private boolean allowResumableLinkRoute = false; - private boolean timestampsInUTC = true; - - public String getId() { - return id; - } - - public void setId(String id) { - this.id = id; - } - - public int getWorkerThreads() { - return workerThreads; - } - - public void setWorkerThreads(int workerThreads) { - this.workerThreads = workerThreads; - } - - public Mode getMode() { - return mode; - } - - public void setMode(Mode mode) { - this.mode = mode; - } - - public Distribution getDefaultDistribution() { - return defaultDistribution; - } - - public void setDefaultDistribution(Distribution defaultDistribution) { - this.defaultDistribution = defaultDistribution; - } - - public boolean isAllowResumableLinkRoute() { - return allowResumableLinkRoute; - } - - public void setAllowResumableLinkRoute(boolean allowResumableLinkRoute) { - this.allowResumableLinkRoute = allowResumableLinkRoute; - } - - public boolean isTimestampsInUTC() { - return timestampsInUTC; - } - - public void setTimestampsInUTC(boolean timestampsInUTC) { - this.timestampsInUTC = timestampsInUTC; - } - - public enum Mode { - interior - } - - @Override - public String toString() { - return "Router{" + - "mode=" + mode + - ", id='" + id + '\'' + - ", workerThreads=" + workerThreads + - ", defaultDistribution=" + defaultDistribution + - ", allowResumableLinkRoute=" + allowResumableLinkRoute + - ", timestampsInUTC=" + timestampsInUTC + - '}'; - } - - @Override - public boolean equals(Object o) { - if (this == o) return true; - if (o == null || getClass() != o.getClass()) return false; - Router router = (Router) o; - return workerThreads == router.workerThreads && - allowResumableLinkRoute == router.allowResumableLinkRoute && - timestampsInUTC == router.timestampsInUTC && - mode == router.mode && - Objects.equals(id, router.id) && - defaultDistribution == router.defaultDistribution; - } - - @Override - public int hashCode() { - return Objects.hash(mode, id, workerThreads, defaultDistribution, allowResumableLinkRoute, timestampsInUTC); - } - - /* - router { - mode: interior - id: ${HOSTNAME} - workerThreads: ${ROUTER_WORKER_THREADS} - defaultDistribution: unavailable - allowResumableLinkRoute: false - timestampsInUTC: true - } - */ -} diff --git a/address-space-controller/src/main/java/io/enmasse/controller/router/config/RouterConfig.java b/address-space-controller/src/main/java/io/enmasse/controller/router/config/RouterConfig.java deleted file mode 100644 index 113bd9e7654..00000000000 --- a/address-space-controller/src/main/java/io/enmasse/controller/router/config/RouterConfig.java +++ /dev/null @@ -1,204 +0,0 @@ -/* - * Copyright 2019, EnMasse authors. - * License: Apache License 2.0 (see the file LICENSE or http://apache.org/licenses/LICENSE-2.0.html). - */ -package io.enmasse.controller.router.config; - -import com.fasterxml.jackson.core.JsonProcessingException; -import com.fasterxml.jackson.databind.JsonNode; -import com.fasterxml.jackson.databind.ObjectMapper; -import com.fasterxml.jackson.databind.node.ArrayNode; - -import java.io.IOException; -import java.nio.charset.StandardCharsets; -import java.util.*; -import java.util.stream.Collectors; - -public class RouterConfig { - private static final ObjectMapper mapper = new ObjectMapper(); - - private final Router router; - private final List sslProfiles; - private final List authServicePlugins; - private final List listeners; - private final List policies; - private final List connectors; - private final List autoLinks; - private final List linkRoutes; - private final List
addresses; - private final List vhosts; - - public RouterConfig(Router router, List sslProfiles, List authServicePlugins, List listeners, List policies, List connectors, List autoLinks, List linkRoutes, List
addresses, List vhosts) { - this.router = router; - this.sslProfiles = sslProfiles; - this.authServicePlugins = authServicePlugins; - this.listeners = listeners; - this.policies = policies; - this.connectors = connectors; - this.autoLinks = autoLinks; - this.linkRoutes = linkRoutes; - this.addresses = addresses; - this.vhosts = vhosts; - } - - @Override - public String toString() { - return "RouterConfig{" + - "router=" + router + - ", sslProfiles=" + sslProfiles + - ", authServicePlugins=" + authServicePlugins + - ", listeners=" + listeners + - ", policies=" + policies + - ", connectors=" + connectors + - ", autoLinks=" + autoLinks + - ", linkRoutes=" + linkRoutes + - ", addresses=" + addresses + - ", vhosts=" + vhosts + - '}'; - } - - @Override - public boolean equals(Object o) { - if (this == o) return true; - if (o == null || getClass() != o.getClass()) return false; - RouterConfig that = (RouterConfig) o; - return Objects.equals(router, that.router) && - Objects.equals(sslProfiles, that.sslProfiles) && - Objects.equals(authServicePlugins, that.authServicePlugins) && - Objects.equals(listeners, that.listeners) && - Objects.equals(policies, that.policies) && - Objects.equals(connectors, that.connectors) && - Objects.equals(autoLinks, that.autoLinks) && - Objects.equals(linkRoutes, that.linkRoutes) && - Objects.equals(addresses, that.addresses) && - Objects.equals(vhosts, that.vhosts); - } - - @Override - public int hashCode() { - return Objects.hash(router, sslProfiles, authServicePlugins, listeners, policies, connectors, autoLinks, linkRoutes, addresses, vhosts); - } - - public Map toMap() throws JsonProcessingException { - byte [] json = this.asJson(); - return Collections.singletonMap("qdrouterd.json", new String(json, StandardCharsets.UTF_8)); - } - - public byte[] asJson() throws JsonProcessingException { - List data = new ArrayList<>(); - data.add(Arrays.asList("router", router)); - data.addAll(entriesToList("sslProfile", sslProfiles)); - data.addAll(entriesToList("authServicePlugin", authServicePlugins)); - data.addAll(entriesToList("listener", listeners)); - data.addAll(entriesToList("policy", policies)); - data.addAll(entriesToList("autoLink", autoLinks)); - data.addAll(entriesToList("linkRoute", linkRoutes)); - data.addAll(entriesToList("address", addresses)); - data.addAll(entriesToList("connector", connectors)); - data.addAll(entriesToList("vhost", vhosts)); - return mapper.writeValueAsBytes(data); - } - - private List> entriesToList(String entryName, List entries) { - return entries.stream() - .map(e -> Arrays.asList(entryName, e)) - .collect(Collectors.toList()); - } - - public static RouterConfig fromMap(Map data) throws IOException { - byte [] json = Optional.ofNullable(data.get("qdrouterd.json")).orElse("[]").getBytes(StandardCharsets.UTF_8); - return RouterConfig.fromJson(json); - } - - public static RouterConfig fromJson(byte [] json) throws IOException { - Router router = null; - List sslProfiles = new ArrayList<>(); - List authServicePlugins = new ArrayList<>(); - List listeners = new ArrayList<>(); - List policies = new ArrayList<>(); - List autoLinks = new ArrayList<>(); - List linkRoutes = new ArrayList<>(); - List
addresses = new ArrayList<>(); - List connectors = new ArrayList<>(); - List vhostPolicies = new ArrayList<>(); - - ArrayNode entries = mapper.readValue(json, ArrayNode.class); - for (int i = 0; i < entries.size(); i++) { - ArrayNode entry = (ArrayNode) entries.get(i); - String type = entry.get(0).asText(); - JsonNode value = entry.get(1); - switch (type) { - case "router": - router = mapper.treeToValue(value, Router.class); - break; - case "sslProfile": - sslProfiles.add(mapper.treeToValue(value, SslProfile.class)); - break; - case "authServicePlugin": - authServicePlugins.add(mapper.treeToValue(value, AuthServicePlugin.class)); - break; - case "listener": - listeners.add(mapper.treeToValue(value, Listener.class)); - break; - case "policy": - policies.add(mapper.treeToValue(value, Policy.class)); - break; - case "autoLink": - autoLinks.add(mapper.treeToValue(value, AutoLink.class)); - break; - case "linkRoute": - linkRoutes.add(mapper.treeToValue(value, LinkRoute.class)); - break; - case "address": - addresses.add(mapper.treeToValue(value, Address.class)); - break; - case "connector": - connectors.add(mapper.treeToValue(value, Connector.class)); - break; - case "vhost": - vhostPolicies.add(mapper.treeToValue(value, VhostPolicy.class)); - break; - } - } - return new RouterConfig(router, sslProfiles, authServicePlugins, listeners, policies, connectors, autoLinks, linkRoutes, addresses, vhostPolicies); - } - public Router getRouter() { - return router; - } - - public List getSslProfiles() { - return sslProfiles; - } - - public List getAuthServicePlugins() { - return authServicePlugins; - } - - public List getListeners() { - return listeners; - } - - public List getPolicies() { - return policies; - } - - public List getConnectors() { - return connectors; - } - - public List getAutoLinks() { - return autoLinks; - } - - public List getLinkRoutes() { - return linkRoutes; - } - - public List
getAddresses() { - return addresses; - } - - public List getVhosts() { - return vhosts; - } -} diff --git a/address-space-controller/src/main/java/io/enmasse/controller/router/config/SslProfile.java b/address-space-controller/src/main/java/io/enmasse/controller/router/config/SslProfile.java deleted file mode 100644 index ae4dd5e31d0..00000000000 --- a/address-space-controller/src/main/java/io/enmasse/controller/router/config/SslProfile.java +++ /dev/null @@ -1,86 +0,0 @@ -/* - * Copyright 2019, EnMasse authors. - * License: Apache License 2.0 (see the file LICENSE or http://apache.org/licenses/LICENSE-2.0.html). - */ -package io.enmasse.controller.router.config; - -import com.fasterxml.jackson.annotation.JsonInclude; - -import java.util.Objects; - -@JsonInclude(JsonInclude.Include.NON_NULL) -public class SslProfile { - private String name; - private String protocols; - private String caCertFile; - private String certFile; - private String privateKeyFile; - - public String getName() { - return name; - } - - public void setName(String name) { - this.name = name; - } - - public String getCaCertFile() { - return caCertFile; - } - - public void setCaCertFile(String caCertFile) { - this.caCertFile = caCertFile; - } - - public String getCertFile() { - return certFile; - } - - public void setCertFile(String certFile) { - this.certFile = certFile; - } - - public String getPrivateKeyFile() { - return privateKeyFile; - } - - public void setPrivateKeyFile(String privateKeyFile) { - this.privateKeyFile = privateKeyFile; - } - - public String getProtocols() { - return protocols; - } - - public void setProtocols(String protocols) { - this.protocols = protocols; - } - - @Override - public String toString() { - return "SslProfile{" + - "name='" + name + '\'' + - ", protocols='" + protocols + '\'' + - ", caCertFile='" + caCertFile + '\'' + - ", certFile='" + certFile + '\'' + - ", privateKeyFile='" + privateKeyFile + '\'' + - '}'; - } - - @Override - public boolean equals(Object o) { - if (this == o) return true; - if (o == null || getClass() != o.getClass()) return false; - SslProfile that = (SslProfile) o; - return Objects.equals(name, that.name) && - Objects.equals(protocols, that.protocols) && - Objects.equals(caCertFile, that.caCertFile) && - Objects.equals(certFile, that.certFile) && - Objects.equals(privateKeyFile, that.privateKeyFile); - } - - @Override - public int hashCode() { - return Objects.hash(name, protocols, caCertFile, certFile, privateKeyFile); - } -} diff --git a/address-space-controller/src/main/java/io/enmasse/controller/router/config/VhostPolicy.java b/address-space-controller/src/main/java/io/enmasse/controller/router/config/VhostPolicy.java deleted file mode 100644 index 2fd6e36e461..00000000000 --- a/address-space-controller/src/main/java/io/enmasse/controller/router/config/VhostPolicy.java +++ /dev/null @@ -1,98 +0,0 @@ -/* - * Copyright 2019, EnMasse authors. - * License: Apache License 2.0 (see the file LICENSE or http://apache.org/licenses/LICENSE-2.0.html). - */ -package io.enmasse.controller.router.config; - -import com.fasterxml.jackson.annotation.JsonInclude; - -import java.util.Map; -import java.util.Objects; - -@JsonInclude(JsonInclude.Include.NON_NULL) -public class VhostPolicy { - private String hostname; - private Boolean allowUnknownUser; - private Integer maxConnections; - private Integer maxConnectionsPerUser; - private Integer maxConnectionsPerHost; - private Map groups; - - public String getHostname() { - return hostname; - } - - public void setHostname(String hostname) { - this.hostname = hostname; - } - - public Boolean getAllowUnknownUser() { - return allowUnknownUser; - } - - public void setAllowUnknownUser(Boolean allowUnknownUser) { - this.allowUnknownUser = allowUnknownUser; - } - - public Integer getMaxConnections() { - return maxConnections; - } - - public void setMaxConnections(Integer maxConnections) { - this.maxConnections = maxConnections; - } - - public Integer getMaxConnectionsPerUser() { - return maxConnectionsPerUser; - } - - public void setMaxConnectionsPerUser(Integer maxConnectionsPerUser) { - this.maxConnectionsPerUser = maxConnectionsPerUser; - } - - public Integer getMaxConnectionsPerHost() { - return maxConnectionsPerHost; - } - - public void setMaxConnectionsPerHost(Integer maxConnectionsPerHost) { - this.maxConnectionsPerHost = maxConnectionsPerHost; - } - - public Map getGroups() { - return groups; - } - - public void setGroups(Map groups) { - this.groups = groups; - } - - @Override - public String toString() { - return "VhostPolicy{" + - "hostname='" + hostname + '\'' + - ", allowUnknownUser=" + allowUnknownUser + - ", maxConnections=" + maxConnections + - ", maxConnectionsPerUser=" + maxConnectionsPerUser + - ", maxConnectionsPerHost=" + maxConnectionsPerHost + - ", groups=" + groups + - '}'; - } - - @Override - public boolean equals(Object o) { - if (this == o) return true; - if (o == null || getClass() != o.getClass()) return false; - VhostPolicy that = (VhostPolicy) o; - return Objects.equals(hostname, that.hostname) && - Objects.equals(allowUnknownUser, that.allowUnknownUser) && - Objects.equals(maxConnections, that.maxConnections) && - Objects.equals(maxConnectionsPerUser, that.maxConnectionsPerUser) && - Objects.equals(maxConnectionsPerHost, that.maxConnectionsPerHost) && - Objects.equals(groups, that.groups); - } - - @Override - public int hashCode() { - return Objects.hash(hostname, allowUnknownUser, maxConnections, maxConnectionsPerUser, maxConnectionsPerHost, groups); - } -} diff --git a/address-space-controller/src/main/java/io/enmasse/controller/router/config/VhostPolicyGroup.java b/address-space-controller/src/main/java/io/enmasse/controller/router/config/VhostPolicyGroup.java deleted file mode 100644 index b0ac6801076..00000000000 --- a/address-space-controller/src/main/java/io/enmasse/controller/router/config/VhostPolicyGroup.java +++ /dev/null @@ -1,141 +0,0 @@ -/* - * Copyright 2019, EnMasse authors. - * License: Apache License 2.0 (see the file LICENSE or http://apache.org/licenses/LICENSE-2.0.html). - */ -package io.enmasse.controller.router.config; - -import com.fasterxml.jackson.annotation.JsonInclude; - -import java.util.Objects; - -@JsonInclude(JsonInclude.Include.NON_NULL) -public class VhostPolicyGroup { - private String remoteHosts; - private String sources; - private String targets; - private Integer maxFrameSize; - private Integer maxSessionWindow; - private Integer maxSessions; - private Integer maxSenders; - private Integer maxReceivers; - private Boolean allowDynamicSource; - private Boolean allowAnonymousSender; - - public String getRemoteHosts() { - return remoteHosts; - } - - public void setRemoteHosts(String remoteHosts) { - this.remoteHosts = remoteHosts; - } - - public String getSources() { - return sources; - } - - public void setSources(String sources) { - this.sources = sources; - } - - public String getTargets() { - return targets; - } - - public void setTargets(String targets) { - this.targets = targets; - } - - public Integer getMaxFrameSize() { - return maxFrameSize; - } - - public void setMaxFrameSize(Integer maxFrameSize) { - this.maxFrameSize = maxFrameSize; - } - - public Integer getMaxSessionWindow() { - return maxSessionWindow; - } - - public void setMaxSessionWindow(Integer maxSessionWindow) { - this.maxSessionWindow = maxSessionWindow; - } - - public Integer getMaxSessions() { - return maxSessions; - } - - public void setMaxSessions(Integer maxSessions) { - this.maxSessions = maxSessions; - } - - public Integer getMaxSenders() { - return maxSenders; - } - - public void setMaxSenders(Integer maxSenders) { - this.maxSenders = maxSenders; - } - - public Integer getMaxReceivers() { - return maxReceivers; - } - - public void setMaxReceivers(Integer maxReceivers) { - this.maxReceivers = maxReceivers; - } - - public Boolean getAllowDynamicSource() { - return allowDynamicSource; - } - - public void setAllowDynamicSource(Boolean allowDynamicSource) { - this.allowDynamicSource = allowDynamicSource; - } - - public Boolean getAllowAnonymousSender() { - return allowAnonymousSender; - } - - public void setAllowAnonymousSender(Boolean allowAnonymousSender) { - this.allowAnonymousSender = allowAnonymousSender; - } - - @Override - public String toString() { - return "VhostPolicyGroup{" + - "remoteHosts='" + remoteHosts + '\'' + - ", sources='" + sources + '\'' + - ", targets='" + targets + '\'' + - ", maxFrameSize=" + maxFrameSize + - ", maxSessionWindow=" + maxSessionWindow + - ", maxSessions=" + maxSessions + - ", maxSenders=" + maxSenders + - ", maxReceivers=" + maxReceivers + - ", allowDynamicSource=" + allowDynamicSource + - ", allowAnonymousSender=" + allowAnonymousSender + - '}'; - } - - @Override - public boolean equals(Object o) { - if (this == o) return true; - if (o == null || getClass() != o.getClass()) return false; - VhostPolicyGroup that = (VhostPolicyGroup) o; - return Objects.equals(remoteHosts, that.remoteHosts) && - Objects.equals(sources, that.sources) && - Objects.equals(targets, that.targets) && - Objects.equals(maxFrameSize, that.maxFrameSize) && - Objects.equals(maxSessionWindow, that.maxSessionWindow) && - Objects.equals(maxSessions, that.maxSessions) && - Objects.equals(maxSenders, that.maxSenders) && - Objects.equals(maxReceivers, that.maxReceivers) && - Objects.equals(allowDynamicSource, that.allowDynamicSource) && - Objects.equals(allowAnonymousSender, that.allowAnonymousSender); - } - - @Override - public int hashCode() { - return Objects.hash(remoteHosts, sources, targets, maxFrameSize, maxSessionWindow, maxSessions, maxSenders, maxReceivers, allowDynamicSource, allowAnonymousSender); - } -} diff --git a/address-space-controller/src/main/resources/logback.xml b/address-space-controller/src/main/resources/logback.xml deleted file mode 100644 index 9ed3476bbee..00000000000 --- a/address-space-controller/src/main/resources/logback.xml +++ /dev/null @@ -1,15 +0,0 @@ - - - - - - - %d{yyyy-MM-dd'T'HH:mm:ss.SSS'Z',GMT} %-5p [%c{0}] %m%n - - - - - - - - diff --git a/address-space-controller/src/main/resources/templates/brokered-space-infra.yaml b/address-space-controller/src/main/resources/templates/brokered-space-infra.yaml deleted file mode 100644 index 02198c7fa22..00000000000 --- a/address-space-controller/src/main/resources/templates/brokered-space-infra.yaml +++ /dev/null @@ -1,476 +0,0 @@ -apiVersion: v1 -kind: Template -metadata: - labels: - app: enmasse - name: brokered-space-infra -objects: -- apiVersion: v1 - data: - tls.crt: ${AUTHENTICATION_SERVICE_CA_CERT} - kind: Secret - metadata: - name: authservice-ca.${INFRA_UUID} - labels: - app: enmasse - infraUuid: ${INFRA_UUID} - infraType: brokered -- apiVersion: v1 - kind: Secret - metadata: - name: broker-support-${INFRA_UUID} - annotations: - addressSpace: ${ADDRESS_SPACE} - labels: - app: enmasse - infraType: standard - infraUuid: ${INFRA_UUID} - role: support-credentials - type: Opaque - data: - username: ${BROKER_SUPPORT_USER} - password: ${BROKER_SUPPORT_PWD} -- apiVersion: v1 - kind: PersistentVolumeClaim - metadata: - annotations: - addressSpace: ${ADDRESS_SPACE} - labels: - app: enmasse - infraType: brokered - infraUuid: ${INFRA_UUID} - name: broker-data.${INFRA_UUID} - spec: - accessModes: - - ReadWriteOnce - resources: - requests: - storage: ${BROKER_STORAGE_CAPACITY} -- apiVersion: apps/v1 - kind: Deployment - metadata: - annotations: - addressSpace: ${ADDRESS_SPACE} - enmasse.io/cert-secret: broker-internal-cert.${INFRA_UUID} - labels: - app: enmasse - role: broker - infraType: brokered - infraUuid: ${INFRA_UUID} - name: broker.${INFRA_UUID} - spec: - replicas: 1 - strategy: - type: Recreate - selector: - matchLabels: - app: enmasse - name: broker - role: broker - infraUuid: ${INFRA_UUID} - template: - metadata: - annotations: - addressSpace: ${ADDRESS_SPACE} - labels: - app: enmasse - name: broker - role: broker - infraUuid: ${INFRA_UUID} - spec: - affinity: - nodeAffinity: - preferredDuringSchedulingIgnoredDuringExecution: - - weight: 1 - preference: - matchExpressions: - - key: node-role.enmasse.io/messaging-infra - operator: In - values: - - "true" - containers: - - env: - - name: INFRA_UUID - value: ${INFRA_UUID} - - name: ADDRESS_SPACE_TYPE - value: brokered - - name: ADDRESS_FULL_POLICY - value: ${BROKER_ADDRESS_FULL_POLICY} - - name: JAVA_OPTS - value: ${BROKER_JAVA_OPTS} - - name: CERT_DIR - value: /etc/enmasse-certs - - name: AUTHENTICATION_SERVICE_HOST - value: ${AUTHENTICATION_SERVICE_HOST} - - name: AUTHENTICATION_SERVICE_PORT - value: ${AUTHENTICATION_SERVICE_PORT} - - name: AUTHENTICATION_SERVICE_CLIENT_SECRET - value: ${AUTHENTICATION_SERVICE_CLIENT_SECRET} - - name: AUTHENTICATION_SERVICE_SASL_INIT_HOST - value: ${AUTHENTICATION_SERVICE_SASL_INIT_HOST} - - name: AMQ_NAME - value: serverData - - name: HOME - value: /var/run/artemis/split-1/ - image: ${BROKER_IMAGE} - imagePullPolicy: ${IMAGE_PULL_POLICY} - command: - - /opt/apache-artemis/custom/bin/launch-broker.sh - livenessProbe: - exec: - command: - - sh - - -c - - $ARTEMIS_HOME/custom/bin/probe.sh - initialDelaySeconds: 300 - name: broker - ports: - - containerPort: 5672 - name: amqp - - containerPort: 5671 - name: amqps - - containerPort: 55671 - name: amqps-normal - - containerPort: 8161 - name: jolokia - readinessProbe: - exec: - command: - - sh - - -c - - $ARTEMIS_HOME/custom/bin/probe.sh - initialDelaySeconds: 10 - volumeMounts: - - mountPath: /var/run/artemis - name: data - - mountPath: /opt/apache-artemis/custom - name: broker-custom - readOnly: false - - mountPath: /opt/apache-artemis/support - name: broker-support - readOnly: true - initContainers: - - env: - - name: INFRA_UUID - value: ${INFRA_UUID} - - name: ADDRESS_SPACE_TYPE - value: brokered - - name: ADDRESS_FULL_POLICY - value: ${BROKER_ADDRESS_FULL_POLICY} - - name: JAVA_OPTS - value: ${BROKER_JAVA_OPTS} - - name: GLOBAL_MAX_SIZE - value: ${BROKER_GLOBAL_MAX_SIZE} - - name: AUTHENTICATION_SERVICE_HOST - value: ${AUTHENTICATION_SERVICE_HOST} - - name: AUTHENTICATION_SERVICE_PORT - value: ${AUTHENTICATION_SERVICE_PORT} - - name: AUTHENTICATION_SERVICE_CLIENT_SECRET - value: ${AUTHENTICATION_SERVICE_CLIENT_SECRET} - - name: AUTHENTICATION_SERVICE_SASL_INIT_HOST - value: ${AUTHENTICATION_SERVICE_SASL_INIT_HOST} - - name: AMQ_NAME - value: serverData - - name: HOME - value: /var/run/artemis/split-1/ - image: ${BROKER_PLUGIN_IMAGE} - imagePullPolicy: ${IMAGE_PULL_POLICY} - name: broker-plugin - volumeMounts: - - mountPath: /var/run/artemis - name: data - - mountPath: /etc/enmasse-certs - name: broker-internal-cert - readOnly: true - - mountPath: /etc/external-certs - name: external-cert - readOnly: true - - mountPath: /etc/authservice-ca - name: authservice-ca - readOnly: true - - mountPath: /opt/apache-artemis/custom - name: broker-custom - readOnly: false - volumes: - - name: data - persistentVolumeClaim: - claimName: broker-data.${INFRA_UUID} - - emptyDir: {} - name: broker-custom - - name: broker-internal-cert - secret: - secretName: broker-internal-cert.${INFRA_UUID} - - name: authservice-ca - secret: - secretName: authservice-ca.${INFRA_UUID} - - name: external-cert - secret: - secretName: ${MESSAGING_SECRET} - - name: broker-support - secret: - secretName: broker-support-${INFRA_UUID} -- apiVersion: v1 - kind: Service - metadata: - labels: - monitoring-key: enmasse-tenants - app: enmasse - component: broker - infraType: brokered - infraUuid: ${INFRA_UUID} - name: broker-${INFRA_UUID} - spec: - ports: - - name: amqps-normal - port: 55671 - targetPort: amqps-normal - - name: health-tls - port: 8161 - protocol: TCP - targetPort: jolokia - selector: - role: broker - infraUuid: ${INFRA_UUID} -- apiVersion: v1 - kind: Service - metadata: - annotations: - addressSpace: ${ADDRESS_SPACE} - enmasse.io/address-space-namespace: ${ADDRESS_SPACE_NAMESPACE} - enmasse.io/service-port.amqp: 5672 - enmasse.io/service-port.amqps: 5671 - labels: - app: enmasse - infraType: brokered - infraUuid: ${INFRA_UUID} - name: messaging-${INFRA_UUID} - spec: - ports: - - name: amqp - port: 5672 - targetPort: amqp - - name: amqps - port: 5671 - targetPort: amqps - selector: - role: broker - infraUuid: ${INFRA_UUID} -- apiVersion: apps/v1 - kind: Deployment - metadata: - annotations: - addressSpace: ${ADDRESS_SPACE} - enmasse.io/cert-secret: agent-internal-cert.${INFRA_UUID} - labels: - app: enmasse - role: agent - infraType: brokered - infraUuid: ${INFRA_UUID} - name: agent.${INFRA_UUID} - spec: - replicas: 1 - strategy: - type: Recreate - selector: - matchLabels: - app: enmasse - name: agent - role: agent - infraUuid: ${INFRA_UUID} - template: - metadata: - annotations: - addressSpace: ${ADDRESS_SPACE} - namespace: ${ADDRESS_SPACE_NAMESPACE} - labels: - app: enmasse - name: agent - role: agent - infraUuid: ${INFRA_UUID} - spec: - affinity: - nodeAffinity: - preferredDuringSchedulingIgnoredDuringExecution: - - weight: 1 - preference: - matchExpressions: - - key: node-role.enmasse.io/operator-infra - operator: In - values: - - "true" - containers: - - env: - - name: BROKER_SERVICE_HOST - value: broker-${INFRA_UUID} - - name: BROKER_SERVICE_PORT - value: 55671 - - name: ADDRESS_SPACE_PLAN - value: ${ADDRESS_SPACE_PLAN} - - name: ADDRESS_SPACE - value: ${ADDRESS_SPACE} - - name: ADDRESS_SPACE_NAMESPACE - value: ${ADDRESS_SPACE_NAMESPACE} - - name: INFRA_UUID - value: ${INFRA_UUID} - - name: ENABLE_EVENT_LOGGER - value: ${ENABLE_EVENT_LOGGER} - - name: ADDRESS_SPACE_TYPE - value: brokered - - name: CERT_DIR - value: /etc/enmasse-certs - - name: CONSOLE_CERT_DIR - value: /etc/console-certs - - name: MESSAGING_CERT - value: /opt/agent/messaging-cert/tls.crt - - name: HEALTH_PORT - value: '8088' - - name: RESYNC_INTERVAL - value: ${CONTROLLER_RESYNC_INTERVAL} - - name: AUTHENTICATION_SERVICE_HOST - value: ${AUTHENTICATION_SERVICE_HOST} - - name: AUTHENTICATION_SERVICE_PORT - value: ${AUTHENTICATION_SERVICE_PORT} - - name: AUTHENTICATION_SERVICE_CLIENT_SECRET - value: ${AUTHENTICATION_SERVICE_CLIENT_SECRET} - - name: AUTHENTICATION_SERVICE_SASL_INIT_HOST - value: ${AUTHENTICATION_SERVICE_SASL_INIT_HOST} - - name: BROKER_GLOBAL_MAX_SIZE - value: ${BROKER_GLOBAL_MAX_SIZE} - image: ${AGENT_IMAGE} - imagePullPolicy: ${IMAGE_PULL_POLICY} - livenessProbe: - httpGet: - path: /healthz - port: http - scheme: HTTP - name: agent - ports: - - containerPort: 8080 - name: https - - containerPort: 8088 - name: http - readinessProbe: - httpGet: - path: /healthz - port: http - scheme: HTTP - volumeMounts: - - mountPath: /opt/agent/authservice-ca - name: authservice-ca - readOnly: true - - mountPath: /etc/enmasse-certs - name: agent-internal-cert - readOnly: true - - mountPath: /opt/agent/messaging-cert - name: messaging-cert - readOnly: true - serviceAccountName: ${ADDRESS_SPACE_ADMIN_SA} - volumes: - - name: authservice-ca - secret: - secretName: authservice-ca.${INFRA_UUID} - - name: agent-internal-cert - secret: - secretName: agent-internal-cert.${INFRA_UUID} - - name: messaging-cert - secret: - secretName: ${MESSAGING_SECRET} -- apiVersion: v1 - kind: Service - metadata: - annotations: - addressSpace: ${ADDRESS_SPACE} - addressSpaceNamespace: ${ADDRESS_SPACE_NAMESPACE} - namespace: ${ADDRESS_SPACE_NAMESPACE} - enmasse.io/service-port.https: 8081 - labels: - app: enmasse - infraType: brokered - infraUuid: ${INFRA_UUID} - component: agent - name: agent-${INFRA_UUID} - spec: - ports: - - name: amqps - port: 5671 - targetPort: 56710 - - name: health - port: 8088 - targetPort: 8088 - selector: - role: agent - infraUuid: ${INFRA_UUID} - -parameters: -- name: INFRA_UUID - description: UUID to use for infrastructure - required: true -- name: INFRA_NAMESPACE - description: Namespace where infrastructure is created - required: true -- description: Storage capacity required for volume claims - name: BROKER_STORAGE_CAPACITY - value: 2Gi -- description: The name of our address space - name: ADDRESS_SPACE - required: true -- description: The namespace of our address space - name: ADDRESS_SPACE_NAMESPACE - required: true -- name: ADDRESS_SPACE_PLAN - description: Name of address space plan followed - required: true -- description: Certificate to be used for public messaging service - name: MESSAGING_SECRET - required: true -- description: The hostname of the authentication service used by this address space - name: AUTHENTICATION_SERVICE_HOST - required: true -- description: The port of the authentication service used by this address space - name: AUTHENTICATION_SERVICE_PORT - required: true -- description: The CA cert to use for validating authentication service cert - name: AUTHENTICATION_SERVICE_CA_CERT - required: true -- description: The client cert to use as identity against authentication service - name: AUTHENTICATION_SERVICE_CLIENT_SECRET -- description: The hostname to use in sasl init - name: AUTHENTICATION_SERVICE_SASL_INIT_HOST -- description: Enable logging of kubernetes events - name: ENABLE_EVENT_LOGGER - value: 'true' -- description: The service account with address space admin privileges - name: ADDRESS_SPACE_ADMIN_SA - value: address-space-admin -- description: Global max size for all addresses in broker. Cannot be larger than - a 1/4 of JVM size - name: BROKER_GLOBAL_MAX_SIZE - value: "-1" -- description: Broker address full policy - name: BROKER_ADDRESS_FULL_POLICY - value: FAIL -- description: Broker Java Options - name: BROKER_JAVA_OPTS - value: "" -- name: IMAGE_PULL_POLICY - description: Image Pull Policy - value: ${env.IMAGE_PULL_POLICY} -- name: AGENT_IMAGE - description: Agent Image - value: ${env.AGENT_IMAGE} -- name: BROKER_IMAGE - description: Broker Image - value: ${env.BROKER_IMAGE} -- name: BROKER_PLUGIN_IMAGE - description: Broker Plugin Image - value: ${env.BROKER_PLUGIN_IMAGE} -- name: TOPIC_FORWARDER_IMAGE - description: Topic Forwarder Image - value: ${env.TOPIC_FORWARDER_IMAGE} -- description: Interval (in seconds) to use between controller resync - name: CONTROLLER_RESYNC_INTERVAL - value: '600' -- description: Username for broker support access - name: BROKER_SUPPORT_USER -- description: Password for broker support access - name: BROKER_SUPPORT_PWD \ No newline at end of file diff --git a/address-space-controller/src/main/resources/templates/standard-space-infra-mqtt.yaml b/address-space-controller/src/main/resources/templates/standard-space-infra-mqtt.yaml deleted file mode 100644 index 779c737d137..00000000000 --- a/address-space-controller/src/main/resources/templates/standard-space-infra-mqtt.yaml +++ /dev/null @@ -1,304 +0,0 @@ -apiVersion: v1 -kind: Template -metadata: - labels: - app: enmasse - name: standard-space-infra-mqtt -objects: -- apiVersion: v1 - kind: Service - metadata: - annotations: - addressSpace: ${ADDRESS_SPACE} - labels: - app: enmasse - infraType: standard - infraUuid: ${INFRA_UUID} - name: subscription-${INFRA_UUID} - spec: - ports: - - name: amqp - port: 5672 - protocol: TCP - targetPort: 5672 - selector: - name: subserv - infraUuid: ${INFRA_UUID} -- apiVersion: apps/v1 - kind: Deployment - metadata: - annotations: - addressSpace: ${ADDRESS_SPACE} - enmasse.io/cert-secret: subserv-internal-cert.${INFRA_UUID} - labels: - app: enmasse - name: subserv - infraType: standard - infraUuid: ${INFRA_UUID} - name: subserv.${INFRA_UUID} - spec: - affinity: - nodeAffinity: - preferredDuringSchedulingIgnoredDuringExecution: - - weight: 1 - preference: - matchExpressions: - - key: node-role.enmasse.io/messaging-infra - operator: In - values: - - "true" - replicas: 1 - selector: - matchLabels: - app: enmasse - name: subserv - infraType: standard - infraUuid: ${INFRA_UUID} - template: - metadata: - annotations: - addressSpace: ${ADDRESS_SPACE} - labels: - app: enmasse - name: subserv - infraType: standard - infraUuid: ${INFRA_UUID} - spec: - serviceAccountName: address-space-admin - containers: - - env: - - name: MESSAGING_SERVICE_HOST - value: messaging-${INFRA_UUID} - - name: MESSAGING_SERVICE_PORT_AMQPS_NORMAL - value: 55671 - - name: MESSAGING_SERVICE_PORT_AMQPS_BROKER - value: 56671 - command: - - /opt/app-root/src/bin/launch_node.sh - - /opt/app-root/src/bin/subserv.js - image: ${AGENT_IMAGE} - imagePullPolicy: ${IMAGE_PULL_POLICY} - livenessProbe: - initialDelaySeconds: 60 - tcpSocket: - port: amqp - name: subserv - ports: - - containerPort: 5672 - name: amqp - protocol: TCP - resources: - limits: - memory: 128Mi - requests: - memory: 128Mi - volumeMounts: - - mountPath: /etc/enmasse-certs - name: subserv-internal-cert - readOnly: true - volumes: - - name: subserv-internal-cert - secret: - secretName: subserv-internal-cert.${INFRA_UUID} -- apiVersion: v1 - kind: Service - metadata: - annotations: - addressSpace: ${ADDRESS_SPACE} - enmasse.io/service-port.mqtt: 1883 - enmasse.io/service-port.mqtts: 8883 - labels: - app: enmasse - infraType: standard - infraUuid: ${INFRA_UUID} - name: mqtt-${INFRA_UUID} - spec: - ports: - - name: mqtt - port: 1883 - protocol: TCP - targetPort: 1883 - - name: secure-mqtt - port: 8883 - protocol: TCP - targetPort: 8883 - selector: - name: mqtt-gateway - infraUuid: ${INFRA_UUID} - type: ClusterIP -- apiVersion: apps/v1 - kind: Deployment - metadata: - annotations: - addressSpace: ${ADDRESS_SPACE} - labels: - app: enmasse - name: mqtt-gateway - infraType: standard - infraUuid: ${INFRA_UUID} - name: mqtt-gateway.${INFRA_UUID} - spec: - affinity: - nodeAffinity: - preferredDuringSchedulingIgnoredDuringExecution: - - weight: 1 - preference: - matchExpressions: - - key: node-role.enmasse.io/messaging-infra - operator: In - values: - - "true" - replicas: 1 - selector: - matchLabels: - app: enmasse - name: mqtt-gateway - infraType: standard - infraUuid: ${INFRA_UUID} - template: - metadata: - annotations: - addressSpace: ${ADDRESS_SPACE} - labels: - app: enmasse - name: mqtt-gateway - infraType: standard - infraUuid: ${INFRA_UUID} - spec: - containers: - - env: - - name: MESSAGING_SERVICE_HOST - value: messaging-${INFRA_UUID} - - name: MESSAGING_SERVICE_PORT - value: 5672 - - name: ENMASSE_MQTT_SSL - value: 'true' - - name: ENMASSE_MQTT_KEYFILE - value: /etc/mqtt-gateway/ssl/tls.key - - name: ENMASSE_MQTT_CERTFILE - value: /etc/mqtt-gateway/ssl/tls.crt - - name: ENMASSE_MQTT_LISTENPORT - value: '8883' - - name: ENMASSE_MQTT_MAXMESSAGESIZE - value: ${MQTT_MAXMESSAGESIZE} - image: ${MQTT_GATEWAY_IMAGE} - imagePullPolicy: ${IMAGE_PULL_POLICY} - livenessProbe: - initialDelaySeconds: 60 - tcpSocket: - port: secure-mqtt - name: mqtt-gateway-tls - ports: - - containerPort: 8883 - name: secure-mqtt - protocol: TCP - volumeMounts: - - mountPath: /etc/mqtt-gateway/ssl - name: ssl-certs - readOnly: true - - image: ${MQTT_GATEWAY_IMAGE} - imagePullPolicy: ${IMAGE_PULL_POLICY} - env: - - name: MESSAGING_SERVICE_HOST - value: messaging-${INFRA_UUID} - - name: MESSAGING_SERVICE_PORT - value: 5672 - livenessProbe: - initialDelaySeconds: 60 - tcpSocket: - port: mqtt - name: mqtt-gateway - ports: - - containerPort: 1883 - name: mqtt - protocol: TCP - volumes: - - name: ssl-certs - secret: - secretName: ${MQTT_SECRET} -- apiVersion: apps/v1 - kind: Deployment - metadata: - annotations: - addressSpace: ${ADDRESS_SPACE} - enmasse.io/cert-secret: mqtt-lwt-internal-cert.${INFRA_UUID} - labels: - app: enmasse - name: mqtt-lwt - infraType: standard - infraUuid: ${INFRA_UUID} - name: mqtt-lwt.${INFRA_UUID} - spec: - affinity: - nodeAffinity: - preferredDuringSchedulingIgnoredDuringExecution: - - weight: 1 - preference: - matchExpressions: - - key: node-role.enmasse.io/messaging-infra - operator: In - values: - - "true" - replicas: 1 - selector: - matchLabels: - app: enmasse - name: mqtt-lwt - infraType: standard - infraUuid: ${INFRA_UUID} - template: - metadata: - annotations: - addressSpace: ${ADDRESS_SPACE} - labels: - app: enmasse - name: mqtt-lwt - infraType: standard - infraUuid: ${INFRA_UUID} - spec: - containers: - - env: - - name: CERT_DIR - value: /etc/enmasse-certs - - name: MESSAGING_SERVICE_HOST - value: messaging-${INFRA_UUID} - - name: MESSAGING_SERVICE_NORMAL_PORT - value: 55671 - - name: MESSAGING_SERVICE_ROUTE_CONTAINER_PORT - value: 56671 - image: ${MQTT_LWT_IMAGE} - imagePullPolicy: ${IMAGE_PULL_POLICY} - name: mqtt-lwt - volumeMounts: - - mountPath: /etc/enmasse-certs - name: mqtt-lwt-internal-cert - readOnly: true - volumes: - - name: mqtt-lwt-internal-cert - secret: - secretName: mqtt-lwt-internal-cert.${INFRA_UUID} -parameters: -- name: INFRA_UUID - description: UUID to use for infrastructure - required: true -- description: The name of our address space - name: ADDRESS_SPACE - required: true -- description: Maximum message size allowed by the MQTT Gateway - name: MQTT_MAXMESSAGESIZE - value: '131072' -- description: The secret with cert for the mqtt service - name: MQTT_SECRET - required: true -- name: AGENT_IMAGE - description: Agent Image - value: ${env.AGENT_IMAGE} -- name: MQTT_GATEWAY_IMAGE - description: MQTT Gateway Image - value: ${env.MQTT_GATEWAY_IMAGE} -- name: MQTT_LWT_IMAGE - description: MQTT LwT Image - value: ${env.MQTT_LWT_IMAGE} -- name: IMAGE_PULL_POLICY - description: Image Pull Policy - value: ${env.IMAGE_PULL_POLICY} diff --git a/address-space-controller/src/main/resources/templates/standard-space-infra.yaml b/address-space-controller/src/main/resources/templates/standard-space-infra.yaml deleted file mode 100644 index 816d3f77fee..00000000000 --- a/address-space-controller/src/main/resources/templates/standard-space-infra.yaml +++ /dev/null @@ -1,587 +0,0 @@ -apiVersion: v1 -kind: Template -metadata: - labels: - app: enmasse - name: standard-space-infra -objects: -- apiVersion: v1 - kind: Service - metadata: - annotations: - addressSpace: ${ADDRESS_SPACE} - enmasse.io/address-space-namespace: ${ADDRESS_SPACE_NAMESPACE} - enmasse.io/service-port.amqp: 5672 - enmasse.io/service-port.amqps: 5671 - enmasse.io/service-port.amqp-wss: 443 - labels: - app: enmasse - infraType: standard - infraUuid: ${INFRA_UUID} - name: messaging-${INFRA_UUID} - spec: - ports: - - name: amqp - port: 5672 - protocol: TCP - targetPort: 5672 - - name: amqps - port: 5671 - protocol: TCP - targetPort: 5671 - - name: amqps-normal - port: 55671 - protocol: TCP - targetPort: amqps-normal - - name: amqps-broker - port: 56671 - protocol: TCP - targetPort: amqps-broker - - name: inter-router - port: 55672 - protocol: TCP - targetPort: 55672 - - name: https - port: 443 - protocol: TCP - targetPort: 8443 - selector: - capability: router - infraUuid: ${INFRA_UUID} -- apiVersion: apps/v1 - kind: StatefulSet - metadata: - annotations: - addressSpace: ${ADDRESS_SPACE} - enmasse.io/cert-cn: router.${INFRA_UUID} - enmasse.io/cert-secret: router-internal-cert.${INFRA_UUID} - prometheus.io/path: /metrics - prometheus.io/port: '8080' - prometheus.io/scrape: 'true' - labels: - app: enmasse - name: qdrouterd - infraType: standard - infraUuid: ${INFRA_UUID} - name: qdrouterd-${INFRA_UUID} - spec: - serviceName: qdrouterd-headless-${INFRA_UUID} - replicas: 1 - selector: - matchLabels: - app: enmasse - capability: router - name: qdrouterd - infraType: standard - infraUuid: ${INFRA_UUID} - template: - metadata: - annotations: - addressSpace: ${ADDRESS_SPACE} - labels: - app: enmasse - capability: router - name: qdrouterd - infraType: standard - infraUuid: ${INFRA_UUID} - spec: - affinity: - podAntiAffinity: - preferredDuringSchedulingIgnoredDuringExecution: - - weight: 1 - podAffinityTerm: - labelSelector: - matchLabels: - app: enmasse - capability: router - name: qdrouterd - infraType: standard - infraUuid: ${INFRA_UUID} - topologyKey: kubernetes.io/hostname - nodeAffinity: - preferredDuringSchedulingIgnoredDuringExecution: - - weight: 1 - preference: - matchExpressions: - - key: node-role.enmasse.io/operator-infra - operator: In - values: - - "true" - containers: - - env: - - name: QDROUTERD_CONF_TYPE - value: json - - name: QDROUTERD_CONF - value: "/etc/qpid-dispatch/config/qdrouterd.json" - - name: QDROUTERD_AUTO_MESH_DISCOVERY - value: "INFER" - - name: QDROUTERD_AUTO_MESH_SERVICE_NAME - value: "qdrouterd-headless-${INFRA_UUID}" - image: ${ROUTER_IMAGE} - imagePullPolicy: ${IMAGE_PULL_POLICY} - livenessProbe: - initialDelaySeconds: 30 - httpGet: - path: /healthz - port: local-http - scheme: HTTP - readinessProbe: - initialDelaySeconds: 60 - httpGet: - path: /healthz - port: http - scheme: HTTP - name: router - ports: - - containerPort: 5672 - name: amqp - protocol: TCP - - containerPort: 5671 - name: amqps - protocol: TCP - - containerPort: 7777 - name: local - protocol: TCP - - containerPort: 7770 - name: local-http - protocol: TCP - - containerPort: 8080 - name: http - protocol: TCP - - containerPort: 8443 - name: https - protocol: TCP - - containerPort: 55671 - name: amqps-normal - protocol: TCP - - containerPort: 56671 - name: amqps-broker - protocol: TCP - volumeMounts: - - mountPath: /etc/qpid-dispatch/ssl - name: ssl-certs - readOnly: true - - mountPath: /etc/qpid-dispatch/authservice-ca - name: authservice-ca - readOnly: true - - mountPath: /etc/enmasse-certs - name: router-internal-cert - readOnly: true - - mountPath: /etc/qpid-dispatch/config - name: qdrouterd-config - volumes: - - name: ssl-certs - secret: - secretName: ${MESSAGING_SECRET} - - name: authservice-ca - secret: - secretName: authservice-ca.${INFRA_UUID} - - name: router-internal-cert - secret: - secretName: router-internal-cert.${INFRA_UUID} - - name: qdrouterd-config - configMap: - name: qdrouterd-config.${INFRA_UUID} -- apiVersion: v1 - data: - tls.crt: ${AUTHENTICATION_SERVICE_CA_CERT} - kind: Secret - metadata: - name: authservice-ca.${INFRA_UUID} - labels: - app: enmasse - infraType: standard - infraUuid: ${INFRA_UUID} -- apiVersion: v1 - kind: Secret - metadata: - name: broker-support-${INFRA_UUID} - annotations: - addressSpace: ${ADDRESS_SPACE} - labels: - app: enmasse - infraType: standard - infraUuid: ${INFRA_UUID} - role: support-credentials - type: Opaque - data: - username: ${BROKER_SUPPORT_USER} - password: ${BROKER_SUPPORT_PWD} -- apiVersion: apps/v1 - kind: Deployment - metadata: - annotations: - addressSpace: ${ADDRESS_SPACE} - enmasse.io/cert-secret: admin-internal-cert.${INFRA_UUID} - labels: - app: enmasse - name: admin - infraType: standard - infraUuid: ${INFRA_UUID} - name: admin.${INFRA_UUID} - spec: - replicas: 1 - strategy: - type: Recreate - selector: - matchLabels: - app: enmasse - name: admin - infraType: standard - infraUuid: ${INFRA_UUID} - template: - metadata: - annotations: - addressSpace: ${ADDRESS_SPACE} - labels: - app: enmasse - name: admin - infraType: standard - infraUuid: ${INFRA_UUID} - spec: - affinity: - nodeAffinity: - preferredDuringSchedulingIgnoredDuringExecution: - - weight: 1 - preference: - matchExpressions: - - key: node-role.enmasse.io/operator-infra - operator: In - values: - - "true" - containers: - - env: - - name: MESSAGING_SERVICE_HOST - value: messaging-${INFRA_UUID} - - name: MESSAGING_SERVICE_PORT_AMQPS_NORMAL - value: 55671 - - name: MESSAGING_SERVICE_PORT_AMQPS_BROKER - value: 56671 - - name: CERT_DIR - value: /etc/enmasse-certs - - name: TEMPLATE_DIR - value: /opt/templates - - name: STANDARD_INFRA_CONFIG_NAME - value: ${STANDARD_INFRA_CONFIG_NAME} - - name: ADDRESS_SPACE - value: ${ADDRESS_SPACE} - - name: ADDRESS_SPACE_NAMESPACE - value: ${ADDRESS_SPACE_NAMESPACE} - - name: INFRA_UUID - value: ${INFRA_UUID} - - name: ADDRESS_SPACE_PLAN - value: ${ADDRESS_SPACE_PLAN} - - name: RESYNC_INTERVAL - value: ${CONTROLLER_RESYNC_INTERVAL} - - name: CHECK_INTERVAL - value: ${CONTROLLER_CHECK_INTERVAL} - - name: EVENT_QUEUE_SIZE - value: ${CONTROLLER_EVENT_QUEUE_SIZE} - - name: ENABLE_EVENT_LOGGER - value: ${ENABLE_EVENT_LOGGER} - - name: AUTHENTICATION_SERVICE_HOST - value: ${AUTHENTICATION_SERVICE_HOST} - - name: AUTHENTICATION_SERVICE_PORT - value: ${AUTHENTICATION_SERVICE_PORT} - - name: AUTHENTICATION_SERVICE_CA_SECRET - value: authservice-ca - - name: AUTHENTICATION_SERVICE_CLIENT_SECRET - value: ${AUTHENTICATION_SERVICE_CLIENT_SECRET} - - name: JAVA_OPTS - value: -verbose:gc - - name: AUTHENTICATION_SERVICE_SASL_INIT_HOST - value: ${AUTHENTICATION_SERVICE_SASL_INIT_HOST} - - name: MESSAGING_SECRET - value: ${MESSAGING_SECRET} - - name: BROKER_IMAGE - value: ${BROKER_IMAGE} - - name: BROKER_PLUGIN_IMAGE - value: ${BROKER_PLUGIN_IMAGE} - - name: TOPIC_FORWARDER_IMAGE - value: ${TOPIC_FORWARDER_IMAGE} - - name: IMAGE_PULL_POLICY - value: ${IMAGE_PULL_POLICY} - - name: FS_GROUP_FALLBACK_MAP - value: ${FS_GROUP_FALLBACK_MAP} - image: ${STANDARD_CONTROLLER_IMAGE} - imagePullPolicy: ${IMAGE_PULL_POLICY} - livenessProbe: - httpGet: - path: /healthz - port: http - scheme: HTTP - initialDelaySeconds: 30 - name: standard-controller - ports: - - containerPort: 8889 - name: http - readinessProbe: - httpGet: - path: /healthz - port: http - scheme: HTTP - initialDelaySeconds: 30 - volumeMounts: - - mountPath: /etc/enmasse-certs - name: admin-internal-cert - readOnly: true - - env: - - name: MESSAGING_SERVICE_HOST - value: messaging-${INFRA_UUID} - - name: MESSAGING_SERVICE_PORT_AMQPS_NORMAL - value: 55671 - - name: MESSAGING_SERVICE_PORT_AMQPS_BROKER - value: 56671 - - name: INFRA_UUID - value: ${INFRA_UUID} - - name: ADDRESS_SPACE_TYPE - value: standard - - name: ADDRESS_SPACE_PLAN - value: ${ADDRESS_SPACE_PLAN} - - name: BROKER_GLOBAL_MAX_SIZE - value: ${BROKER_GLOBAL_MAX_SIZE} - - name: CERT_DIR - value: /etc/enmasse-certs - - name: RESYNC_INTERVAL - value: ${CONTROLLER_RESYNC_INTERVAL} - - name: AUTHENTICATION_SERVICE_HOST - value: ${AUTHENTICATION_SERVICE_HOST} - - name: AUTHENTICATION_SERVICE_PORT - value: ${AUTHENTICATION_SERVICE_PORT} - - name: AUTHENTICATION_SERVICE_CA_SECRET - value: authservice-ca - - name: AUTHENTICATION_SERVICE_CLIENT_SECRET - value: ${AUTHENTICATION_SERVICE_CLIENT_SECRET} - - name: AUTHENTICATION_SERVICE_SASL_INIT_HOST - value: ${AUTHENTICATION_SERVICE_SASL_INIT_HOST} - - name: ADDRESS_SPACE - value: ${ADDRESS_SPACE} - - name: ADDRESS_SPACE_NAMESPACE - value: ${ADDRESS_SPACE_NAMESPACE} - - name: MESSAGING_CERT - value: /opt/agent/messaging-cert/tls.crt - image: ${AGENT_IMAGE} - imagePullPolicy: ${IMAGE_PULL_POLICY} - livenessProbe: - httpGet: - path: /healthz - port: http - scheme: HTTP - periodSeconds: 30 - timeoutSeconds: 5 - name: agent - ports: - - containerPort: 8888 - name: http - - containerPort: 56710 - name: amqps - - containerPort: 56720 - name: amqp-ws - readinessProbe: - httpGet: - path: /healthz - port: http - scheme: HTTP - periodSeconds: 30 - timeoutSeconds: 5 - volumeMounts: - - mountPath: /opt/agent/authservice-ca - name: authservice-ca - readOnly: true - - mountPath: /etc/enmasse-certs - name: admin-internal-cert - readOnly: true - - mountPath: /opt/agent/messaging-cert - name: messaging-cert - readOnly: true - serviceAccountName: ${ADDRESS_SPACE_ADMIN_SA} - volumes: - - name: authservice-ca - secret: - secretName: authservice-ca.${INFRA_UUID} - - name: admin-internal-cert - secret: - secretName: admin-internal-cert.${INFRA_UUID} - - name: messaging-cert - secret: - secretName: ${MESSAGING_SECRET} -- apiVersion: v1 - kind: Service - metadata: - annotations: - addressSpace: ${ADDRESS_SPACE} - labels: - monitoring-key: enmasse-tenants - app: enmasse - infraType: standard - infraUuid: ${INFRA_UUID} - component: router - name: qdrouterd-headless-${INFRA_UUID} - spec: - clusterIP: None - ports: - - name: inter-router - port: 55672 - targetPort: 55672 - - name: health - port: 8080 - protocol: TCP - targetPort: http - selector: - capability: router - infraUuid: ${INFRA_UUID} -- apiVersion: v1 - kind: Service - metadata: - annotations: - addressSpace: ${ADDRESS_SPACE} - labels: - app: enmasse - infraType: standard - infraUuid: ${INFRA_UUID} - name: ragent-${INFRA_UUID} - spec: - ports: - - name: amqp - port: 5671 - targetPort: 55671 - selector: - name: admin - infraUuid: ${INFRA_UUID} -- apiVersion: v1 - kind: Service - metadata: - annotations: - addressSpace: ${ADDRESS_SPACE} - labels: - app: enmasse - component: admin - infraType: standard - infraUuid: ${INFRA_UUID} - name: standard-controller-${INFRA_UUID} - spec: - ports: - - name: health - port: 8080 - protocol: TCP - targetPort: 8889 - selector: - name: admin - infraUuid: ${INFRA_UUID} -- apiVersion: v1 - kind: Service - metadata: - annotations: - addressSpace: ${ADDRESS_SPACE} - labels: - app: enmasse - infraType: standard - infraUuid: ${INFRA_UUID} - name: queue-scheduler-${INFRA_UUID} - spec: - ports: - - name: amqp - port: 5672 - targetPort: 55671 - selector: - name: admin - infraUuid: ${INFRA_UUID} -- apiVersion: v1 - kind: Service - metadata: - annotations: - addressSpace: ${ADDRESS_SPACE} - addressSpaceNamespace: ${ADDRESS_SPACE_NAMESPACE} - labels: - app: enmasse - infraType: standard - infraUuid: ${INFRA_UUID} - component: agent - name: agent-${INFRA_UUID} - spec: - ports: - - name: amqps - port: 5671 - targetPort: 56710 - selector: - name: admin - infraUuid: ${INFRA_UUID} -parameters: -- name: INFRA_UUID - description: UUID to use for infrastructure - required: true -- name: INFRA_NAMESPACE - description: Namespace where infrastructure is created - required: true -- name: ADDRESS_SPACE_PLAN - description: Name of address space plan followed - required: true -- description: The secret with cert for the messaging service - name: MESSAGING_SECRET - required: true -- description: The name of our address space - name: ADDRESS_SPACE - required: true -- description: The namespace of our address space - name: ADDRESS_SPACE_NAMESPACE - required: true -- description: The hostname of the authentication service used by this address space - name: AUTHENTICATION_SERVICE_HOST - required: true -- description: The port of the authentication service used by this address space - name: AUTHENTICATION_SERVICE_PORT - required: true -- description: The CA cert to use for validating authentication service cert - name: AUTHENTICATION_SERVICE_CA_CERT - required: true -- description: The client cert to use as identity against authentication service - name: AUTHENTICATION_SERVICE_CLIENT_SECRET -- description: The hostname to use in sasl init - name: AUTHENTICATION_SERVICE_SASL_INIT_HOST -- description: Name standard infra config - name: STANDARD_INFRA_CONFIG_NAME - required: true -- description: Enable logging of kubernetes events - name: ENABLE_EVENT_LOGGER - value: 'true' -- description: Interval (in seconds) to use between controller resync - name: CONTROLLER_RESYNC_INTERVAL - value: '600' -- description: Interval (in seconds) to use between status checks - name: CONTROLLER_CHECK_INTERVAL - value: '30' -- description: Max number of events queued up for controller - name: CONTROLLER_EVENT_QUEUE_SIZE - value: '10000' -- description: The service account with address space admin privileges - name: ADDRESS_SPACE_ADMIN_SA - value: address-space-admin -- name: IMAGE_PULL_POLICY - description: Image Pull Policy - value: ${env.IMAGE_PULL_POLICY} -- name: ROUTER_IMAGE - description: Router image - value: ${env.ROUTER_IMAGE} -- name: STANDARD_CONTROLLER_IMAGE - description: Standard Controller Image - value: ${env.STANDARD_CONTROLLER_IMAGE} -- name: AGENT_IMAGE - description: Agent Image - value: ${env.AGENT_IMAGE} -- name: BROKER_IMAGE - description: Broker Image - value: ${env.BROKER_IMAGE} -- name: BROKER_PLUGIN_IMAGE - description: Broker Plugin Image - value: ${env.BROKER_PLUGIN_IMAGE} -- name: TOPIC_FORWARDER_IMAGE - description: Topic Forwarder Image - value: ${env.TOPIC_FORWARDER_IMAGE} -- description: - name: FS_GROUP_FALLBACK_MAP -- description: Username for broker support access - name: BROKER_SUPPORT_USER -- description: Password for broker support access - name: BROKER_SUPPORT_PWD -- description: Global max size for all addresses in broker. Cannot be larger than a 1/4 of JVM size - name: BROKER_GLOBAL_MAX_SIZE \ No newline at end of file diff --git a/address-space-controller/src/test/java/io/enmasse/controller/AddressFinalizerControllerTest.java b/address-space-controller/src/test/java/io/enmasse/controller/AddressFinalizerControllerTest.java deleted file mode 100644 index 464d1611a3d..00000000000 --- a/address-space-controller/src/test/java/io/enmasse/controller/AddressFinalizerControllerTest.java +++ /dev/null @@ -1,156 +0,0 @@ -/* - * Copyright 2019, EnMasse authors. - * License: Apache License 2.0 (see the file LICENSE or http://apache.org/licenses/LICENSE-2.0.html). - */ -package io.enmasse.controller; - -import static org.hamcrest.core.IsCollectionContaining.hasItems; -import static org.hamcrest.core.IsNull.notNullValue; -import static org.hamcrest.core.IsNull.nullValue; -import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertThat; -import static org.junit.Assert.assertTrue; - -import java.time.Instant; -import java.time.ZoneOffset; -import java.time.format.DateTimeFormatter; - -import org.junit.jupiter.api.BeforeAll; -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Test; - -import io.enmasse.address.model.Address; -import io.enmasse.address.model.AddressBuilder; -import io.enmasse.address.model.AddressSpace; -import io.enmasse.address.model.AddressSpaceBuilder; -import io.enmasse.k8s.api.AddressApi; -import io.enmasse.k8s.api.AddressSpaceApi; -import io.enmasse.k8s.api.TestAddressSpaceApi; -import io.enmasse.model.CustomResourceDefinitions; - -public class AddressFinalizerControllerTest { - - private AddressFinalizerController controller; - private AddressSpaceApi addressSpaceApi; - - @BeforeAll - public static void init () { - CustomResourceDefinitions.registerAll(); - } - - @BeforeEach - public void setup() { - this.addressSpaceApi = new TestAddressSpaceApi(); - this.controller = new AddressFinalizerController(addressSpaceApi); - } - - /** - * Test calling the finalizer controller with a non-deleted object. - */ - @Test - public void testAddingFinalizer () throws Exception { - - // given a non-delete object - - AddressSpace addressSpace = new AddressSpaceBuilder() - .withNewMetadata() - .withName("foo") - .withNamespace("bar") - .withFinalizers("foo/bar") - .endMetadata() - .build(); - - // when calling the reconcile method of the finalizer controller - - Controller.ReconcileResult result = controller.reconcileAnyState(addressSpace); - assertTrue(result.isPersistAndRequeue()); - addressSpace = result.getAddressSpace(); - - // it should add the finalizer, but not remove existing finalizers - - assertThat(addressSpace, notNullValue()); - assertThat(addressSpace.getMetadata(), notNullValue()); - assertThat(addressSpace.getMetadata().getFinalizers(), hasItems("foo/bar", AddressFinalizerController.FINALIZER_ADDRESSES)); - - } - - @Test - public void testProcessingFinalizer () throws Exception { - - // given a deleted address space, with the finalizer still present - - AddressSpace addressSpace = new AddressSpaceBuilder() - .withNewMetadata() - .withName("foo") - .withNamespace("bar") - .withDeletionTimestamp(Instant.now().atZone(ZoneOffset.UTC).format(DateTimeFormatter.ISO_OFFSET_DATE_TIME)) - .withFinalizers("foo/bar", AddressFinalizerController.FINALIZER_ADDRESSES) - .endMetadata() - .build(); - - addressSpaceApi.createAddressSpace(addressSpace); - AddressApi addressApi = addressSpaceApi.withAddressSpace(addressSpace); - - // and given an existing address for this address space - - Address address = new AddressBuilder() - .withNewMetadata() - .withName("foo.foo") - .withNamespace("bar") - .endMetadata() - .build(); - - addressApi.createAddress(address); - - // and given another address space and address - - AddressSpace otherAddressSpace = new AddressSpaceBuilder() - .withNewMetadata() - .withName("foo2") - .withNamespace("bar") - .withFinalizers("foo/bar", AddressFinalizerController.FINALIZER_ADDRESSES) - .endMetadata() - .build(); - - addressSpaceApi.createAddressSpace(otherAddressSpace); - AddressApi otherAddressApi = addressSpaceApi.withAddressSpace(otherAddressSpace); - - // and given an existing address for this address space - - Address otherAddress = new AddressBuilder() - .withNewMetadata() - .withName("foo2.foo") - .withNamespace("bar") - .endMetadata() - .build(); - - otherAddressApi.createAddress(otherAddress); - - // ensure that the address spaces and addresses should be found - - assertThat(addressSpaceApi.getAddressSpaceWithName("bar", "foo").orElse(null), notNullValue()); - assertThat(addressApi.getAddressWithName("bar", "foo.foo").orElse(null), notNullValue()); - assertThat(addressSpaceApi.getAddressSpaceWithName("bar", "foo2").orElse(null), notNullValue()); - assertThat(otherAddressApi.getAddressWithName("bar", "foo2.foo").orElse(null), notNullValue()); - - // when running the reconcile method - - Controller.ReconcileResult result = controller.reconcileAnyState(addressSpace); - assertTrue(result.isPersistAndRequeue()); - addressSpace = result.getAddressSpace(); - - // then the finalizer of this controller should be removed, and the address should be deleted - - assertThat(addressSpace, notNullValue()); - assertThat(addressSpace.getMetadata(), notNullValue()); - assertThat(addressSpace.getMetadata().getFinalizers(), hasItems("foo/bar")); - - assertThat(addressSpaceApi.getAddressSpaceWithName("bar", "foo").orElse(null), notNullValue()); - assertThat(addressApi.getAddressWithName("bar", "foo.foo").orElse(null), nullValue()); - - // but not the "other" address - - assertThat(addressSpaceApi.getAddressSpaceWithName("bar", "foo2").orElse(null), notNullValue()); - assertThat(otherAddressApi.getAddressWithName("bar", "foo2.foo").orElse(null), notNullValue()); - } -} diff --git a/address-space-controller/src/test/java/io/enmasse/controller/ComponentFinalizerControllerTest.java b/address-space-controller/src/test/java/io/enmasse/controller/ComponentFinalizerControllerTest.java deleted file mode 100644 index 522da9e9a44..00000000000 --- a/address-space-controller/src/test/java/io/enmasse/controller/ComponentFinalizerControllerTest.java +++ /dev/null @@ -1,70 +0,0 @@ -/* - * Copyright 2019, EnMasse authors. - * License: Apache License 2.0 (see the file LICENSE or http://apache.org/licenses/LICENSE-2.0.html). - */ -package io.enmasse.controller; - -import io.enmasse.address.model.AddressSpace; -import io.enmasse.address.model.AddressSpaceBuilder; -import io.enmasse.config.AnnotationKeys; -import io.enmasse.controller.common.Kubernetes; -import io.enmasse.model.CustomResourceDefinitions; -import org.junit.jupiter.api.BeforeAll; -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Test; - -import static org.junit.jupiter.api.Assertions.assertFalse; -import static org.junit.jupiter.api.Assertions.assertNotNull; -import static org.junit.jupiter.api.Assertions.assertTrue; -import static org.mockito.ArgumentMatchers.any; -import static org.mockito.ArgumentMatchers.eq; -import static org.mockito.Mockito.doThrow; -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.verify; - -public class ComponentFinalizerControllerTest { - - private ComponentFinalizerController controller; - private Kubernetes client; - - @BeforeAll - public static void init() { - CustomResourceDefinitions.registerAll(); - } - - @BeforeEach - public void setup() { - this.client = mock(Kubernetes.class); - this.controller = new ComponentFinalizerController(client); - } - - @Test - public void testFinalizerSuccess() throws Exception { - AbstractFinalizerController.Result result = controller.processFinalizer(createTestSpace()); - assertNotNull(result); - assertTrue(result.isFinalized()); - verify(client).deleteResources(eq("1234")); - } - - @Test - public void testFinalizerFailure() throws Exception { - doThrow(new RuntimeException("ERROR")).when(client).deleteResources(any()); - AbstractFinalizerController.Result result = controller.processFinalizer(createTestSpace()); - assertNotNull(result); - assertFalse(result.isFinalized()); - } - - private static AddressSpace createTestSpace() { - return new AddressSpaceBuilder() - .editOrNewMetadata() - .withName("myspace") - .withNamespace("test") - .addToAnnotations(AnnotationKeys.INFRA_UUID, "1234") - .endMetadata() - .editOrNewSpec() - .withType("standard") - .withPlan("standard") - .endSpec() - .build(); - } -} diff --git a/address-space-controller/src/test/java/io/enmasse/controller/ControllerChainTest.java b/address-space-controller/src/test/java/io/enmasse/controller/ControllerChainTest.java deleted file mode 100644 index 73b8f28bc19..00000000000 --- a/address-space-controller/src/test/java/io/enmasse/controller/ControllerChainTest.java +++ /dev/null @@ -1,111 +0,0 @@ -/* - * Copyright 2017-2018, EnMasse authors. - * License: Apache License 2.0 (see the file LICENSE or http://apache.org/licenses/LICENSE-2.0.html). - */ -package io.enmasse.controller; - -import io.enmasse.address.model.AddressSpace; -import io.enmasse.address.model.AddressSpaceBuilder; -import io.enmasse.controller.common.Kubernetes; -import io.enmasse.k8s.api.EventLogger; -import io.enmasse.k8s.api.TestAddressSpaceApi; -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Test; - -import java.time.Duration; -import java.util.Arrays; -import java.util.Collections; - -import static org.mockito.ArgumentMatchers.any; -import static org.mockito.ArgumentMatchers.eq; -import static org.mockito.Mockito.*; - -public class ControllerChainTest { - private TestAddressSpaceApi testApi; - private Kubernetes kubernetes; - - @BeforeEach - public void setup() { - kubernetes = mock(Kubernetes.class); - testApi = new TestAddressSpaceApi(); - - when(kubernetes.getNamespace()).thenReturn("myspace"); - } - - @Test - public void testController() throws Exception { - EventLogger testLogger = mock(EventLogger.class); - ControllerChain controllerChain = new ControllerChain(testApi, new TestSchemaProvider(), testLogger, Duration.ofSeconds(5), Duration.ofSeconds(5)); - Controller mockController = mock(Controller.class); - controllerChain.addController(mockController); - - AddressSpace a1 = new AddressSpaceBuilder() - .withNewMetadata() - .withName("myspace") - .endMetadata() - - .withNewSpec() - .withType("type1") - .withPlan("myplan") - .endSpec() - - .withNewStatus(false) - - .build(); - - AddressSpace a2 = new AddressSpaceBuilder() - .withNewMetadata() - .withName("myspace2") - .endMetadata() - - .withNewSpec() - .withType("type1") - .withPlan("myplan") - .endSpec() - .withNewStatus(false) - .build(); - - when(mockController.reconcileAnyState(eq(a1))).thenReturn(Controller.ReconcileResult.create(a1)); - when(mockController.reconcileAnyState(eq(a2))).thenReturn(Controller.ReconcileResult.create(a2)); - - controllerChain.onUpdate(Arrays.asList(a1, a2)); - - verify(mockController, times(2)).reconcileAnyState(any()); - verify(mockController).reconcileAnyState(eq(a1)); - verify(mockController).reconcileAnyState(eq(a2)); - - - } - - @Test - public void testControllerRequeue() throws Exception { - EventLogger testLogger = mock(EventLogger.class); - ControllerChain controllerChain = new ControllerChain(testApi, new TestSchemaProvider(), testLogger, Duration.ofSeconds(5), Duration.ofSeconds(5)); - Controller mockController = mock(Controller.class); - controllerChain.addController(mockController); - - AddressSpace a1 = new AddressSpaceBuilder() - .withNewMetadata() - .withName("myspace") - .endMetadata() - - .withNewSpec() - .withType("type1") - .withPlan("myplan") - .endSpec() - - .withNewStatus(false) - - .build(); - - when(mockController.reconcileAnyState(eq(a1))).thenReturn(Controller.ReconcileResult.createRequeued(a1, true)); - - controllerChain.onUpdate(Collections.singletonList(a1)); - - verify(mockController, times(1)).reconcileAnyState(any()); - verify(mockController).reconcileAnyState(eq(a1)); - - - } -} - diff --git a/address-space-controller/src/test/java/io/enmasse/controller/ControllerTest.java b/address-space-controller/src/test/java/io/enmasse/controller/ControllerTest.java deleted file mode 100644 index 9c31592aed0..00000000000 --- a/address-space-controller/src/test/java/io/enmasse/controller/ControllerTest.java +++ /dev/null @@ -1,43 +0,0 @@ -/* - * Copyright 2019, EnMasse authors. - * License: Apache License 2.0 (see the file LICENSE or http://apache.org/licenses/LICENSE-2.0.html). - */ -package io.enmasse.controller; - -import static java.time.ZoneOffset.UTC; -import static java.time.format.DateTimeFormatter.ISO_OFFSET_DATE_TIME; -import static org.junit.jupiter.api.Assertions.assertFalse; -import static org.junit.jupiter.api.Assertions.assertTrue; - -import java.time.Instant; - -import org.junit.jupiter.api.Test; - -import io.enmasse.address.model.AddressSpace; -import io.enmasse.address.model.AddressSpaceBuilder; - -public class ControllerTest { - - @Test - public void testIsDeleted() { - - AddressSpace addressSpace = new AddressSpaceBuilder() - .withNewMetadata() - .withName("foo") - .withNamespace("bar") - .endMetadata() - .build(); - - assertFalse(Controller.isDeleted(addressSpace)); - - addressSpace = new AddressSpaceBuilder(addressSpace) - .editOrNewMetadata() - .withDeletionTimestamp(ISO_OFFSET_DATE_TIME.format(Instant.now().atZone(UTC))) - .endMetadata() - .build(); - - assertTrue(Controller.isDeleted(addressSpace)); - - } - -} diff --git a/address-space-controller/src/test/java/io/enmasse/controller/CreateControllerTest.java b/address-space-controller/src/test/java/io/enmasse/controller/CreateControllerTest.java deleted file mode 100644 index 0116b19a539..00000000000 --- a/address-space-controller/src/test/java/io/enmasse/controller/CreateControllerTest.java +++ /dev/null @@ -1,164 +0,0 @@ -/* - * Copyright 2017-2018, EnMasse authors. - * License: Apache License 2.0 (see the file LICENSE or http://apache.org/licenses/LICENSE-2.0.html). - */ -package io.enmasse.controller; - -import static org.hamcrest.CoreMatchers.is; -import static org.hamcrest.MatcherAssert.assertThat; -import static org.junit.jupiter.api.Assertions.assertTrue; -import static org.mockito.ArgumentMatchers.any; -import static org.mockito.ArgumentMatchers.eq; -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.verify; -import static org.mockito.Mockito.when; - -import java.util.Arrays; -import java.util.UUID; - -import org.junit.jupiter.api.BeforeAll; -import org.junit.jupiter.api.Test; -import org.mockito.ArgumentCaptor; - -import com.fasterxml.jackson.databind.ObjectMapper; - -import io.enmasse.address.model.Address; -import io.enmasse.address.model.AddressBuilder; -import io.enmasse.address.model.AddressSpace; -import io.enmasse.address.model.AddressSpaceBuilder; -import io.enmasse.address.model.AddressSpaceSpecBuilder; -import io.enmasse.address.model.AuthenticationServiceBuilder; -import io.enmasse.config.AnnotationKeys; -import io.enmasse.controller.common.Kubernetes; -import io.enmasse.k8s.api.AddressApi; -import io.enmasse.k8s.api.EventLogger; -import io.enmasse.k8s.api.SchemaProvider; -import io.enmasse.k8s.api.TestAddressSpaceApi; -import io.enmasse.model.CustomResourceDefinitions; -import io.fabric8.kubernetes.api.model.ConfigMapBuilder; -import io.fabric8.kubernetes.api.model.KubernetesList; - -public class CreateControllerTest { - private static final ObjectMapper mapper = new ObjectMapper(); - - @BeforeAll - public static void init () { - CustomResourceDefinitions.registerAll(); - } - - @Test - public void testAddressSpaceCreate() throws Exception { - Kubernetes kubernetes = mock(Kubernetes.class); - when(kubernetes.getNamespace()).thenReturn("otherspace"); - - AddressSpace addressSpace = new AddressSpaceBuilder() - .withNewMetadata() - .withName("myspace") - .withUid(UUID.randomUUID().toString()) - .withNamespace("mynamespace") - .endMetadata() - - .withNewSpec() - .withType("type1") - .withPlan("myplan") - .withAuthenticationService(new AuthenticationServiceBuilder().withName("standard").build()) - .endSpec() - - .build(); - - - EventLogger eventLogger = mock(EventLogger.class); - InfraResourceFactory mockResourceFactory = mock(InfraResourceFactory.class); - when(mockResourceFactory.createInfraResources(eq(addressSpace), any(), any())).thenReturn(Arrays.asList(new ConfigMapBuilder() - .editOrNewMetadata() - .withName("mymap") - .endMetadata() - .build())); - - SchemaProvider testSchema = new TestSchemaProvider(); - CreateController createController = new CreateController(kubernetes, testSchema, mockResourceFactory, eventLogger, null, "1.0", new TestAddressSpaceApi(), mock(AuthenticationServiceResolver.class)); - - createController.reconcileAnyState(addressSpace); - - ArgumentCaptor resourceCaptor = ArgumentCaptor.forClass(KubernetesList.class); - verify(kubernetes).create(resourceCaptor.capture()); - KubernetesList value = resourceCaptor.getValue(); - assertThat(value.getItems().size(), is(1)); - } - - @Test - public void testPlanUpdateNotAccepted() throws Exception { - Kubernetes kubernetes = mock(Kubernetes.class); - when(kubernetes.getNamespace()).thenReturn("otherspace"); - SchemaProvider testSchema = new TestSchemaProvider(); - - AddressSpace addressSpace = new AddressSpaceBuilder() - .withNewMetadata() - .withName("myspace") - .withNamespace("mynamespace") - .withUid(UUID.randomUUID().toString()) - .addToAnnotations(AnnotationKeys.APPLIED_INFRA_CONFIG, - mapper.writeValueAsString(testSchema.getSchema().findAddressSpaceType("type1").get().getInfraConfigs().get(0))) - .endMetadata() - - .withNewSpec() - .withType("type1") - .withPlan("myplan") - .withAuthenticationService(new AuthenticationServiceBuilder().withName("standard").build()) - .endSpec() - - .build(); - - - AppliedConfig.setCurrentAppliedConfig(addressSpace, AppliedConfig.create(new AddressSpaceSpecBuilder(addressSpace.getSpec()) - .withPlan("plan1") - .build(), null)); - TestAddressSpaceApi addressSpaceApi = new TestAddressSpaceApi(); - addressSpaceApi.createAddressSpace(addressSpace); - - AddressApi addressApi = addressSpaceApi.withAddressSpace(addressSpace); - - Address address = new AddressBuilder() - .withNewMetadata() - .withName("myspace.q1") - .withNamespace("mynamespace") - .endMetadata() - - .withNewSpec() - .withAddressSpace(addressSpace.getMetadata().getName()) - .withAddress("q1") - .withType("queue") - .withPlan("plan1") - .endSpec() - - .build(); - - Address address2 = new AddressBuilder() - .withNewMetadata() - .withName("myspace.q2") - .withNamespace("mynamespace") - .endMetadata() - - .withNewSpec() - .withAddressSpace(addressSpace.getMetadata().getName()) - .withAddress("q2") - .withType("queue") - .withPlan("plan1") - .endSpec() - - .build(); - - addressApi.createAddress(address); - addressApi.createAddress(address2); - - EventLogger eventLogger = mock(EventLogger.class); - when(kubernetes.existsAddressSpace(eq(addressSpace))).thenReturn(true); - - CreateController createController = new CreateController(kubernetes, testSchema, null, eventLogger, null, "1.0", addressSpaceApi, mock(AuthenticationServiceResolver.class)); - - addressSpace = createController.reconcileAnyState(addressSpace).getAddressSpace(); - - assertThat(addressSpace.getStatus().getMessages().size(), is(1)); - assertTrue(addressSpace.getStatus().getMessages().iterator().next().contains("quota exceeded for resource broker")); - } -} diff --git a/address-space-controller/src/test/java/io/enmasse/controller/EndpointControllerTest.java b/address-space-controller/src/test/java/io/enmasse/controller/EndpointControllerTest.java deleted file mode 100644 index 000fbc2eef2..00000000000 --- a/address-space-controller/src/test/java/io/enmasse/controller/EndpointControllerTest.java +++ /dev/null @@ -1,403 +0,0 @@ -/* - * Copyright 2016-2018, EnMasse authors. - * License: Apache License 2.0 (see the file LICENSE or http://apache.org/licenses/LICENSE-2.0.html). - */ -package io.enmasse.controller; - -import static org.hamcrest.CoreMatchers.*; -import static org.hamcrest.MatcherAssert.assertThat; -import static org.junit.jupiter.api.Assertions.assertNull; -import static org.junit.jupiter.api.Assertions.assertTrue; - -import java.util.Arrays; - -import io.enmasse.address.model.*; -import io.fabric8.kubernetes.api.model.Secret; -import io.fabric8.kubernetes.api.model.SecretBuilder; -import org.junit.jupiter.api.AfterEach; -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Test; - -import io.enmasse.config.AnnotationKeys; -import io.enmasse.config.LabelKeys; -import io.enmasse.k8s.util.JULInitializingTest; -import io.fabric8.kubernetes.api.model.Service; -import io.fabric8.kubernetes.api.model.ServiceBuilder; -import io.fabric8.openshift.client.OpenShiftClient; -import io.fabric8.openshift.client.server.mock.OpenShiftServer; - -public class EndpointControllerTest extends JULInitializingTest { - - private OpenShiftClient client; - private OpenShiftServer openShiftServer = new OpenShiftServer(false, true); - - @BeforeEach - void setup() { - openShiftServer.before(); - client = openShiftServer.getOpenshiftClient(); - } - - @AfterEach - void tearDown() { - openShiftServer.after(); - } - - @Test - public void testRoutesNotCreated() throws Exception { - AddressSpace addressSpace = new AddressSpaceBuilder() - - .withNewMetadata() - .withName("myspace") - .withNamespace("mynamespace") - .addToAnnotations(AnnotationKeys.INFRA_UUID, "1234") - .endMetadata() - - .withNewSpec() - .addToEndpoints(new EndpointSpecBuilder() - .withName("myendpoint") - .withService("messaging") - .build()) - .withType("type1") - .withPlan("myplan") - .endSpec() - - .build(); - - Service service = new ServiceBuilder() - .editOrNewMetadata() - .withName("messaging-1234") - .addToAnnotations(AnnotationKeys.SERVICE_PORT_PREFIX + "amqps", "5671") - .addToLabels(LabelKeys.INFRA_UUID, "1234") - .endMetadata() - .editOrNewSpec() - .addNewPort() - .withName("amqps") - .withPort(1234) - .withNewTargetPort("amqps") - .endPort() - .addToSelector("component", "router") - .endSpec() - .build(); - - client.services().create(service); - - EndpointController controller = new EndpointController(client, false, false); - - AddressSpace newspace = controller.reconcileAnyState(addressSpace).getAddressSpace(); - - assertThat(newspace.getStatus().getEndpointStatuses().size(), is(1)); - assertThat(newspace.getStatus().getEndpointStatuses().get(0).getName(), is("myendpoint")); - assertThat(newspace.getStatus().getEndpointStatuses().get(0).getServiceHost(), is("messaging-1234.test.svc")); - assertThat(newspace.getStatus().getEndpointStatuses().get(0).getServicePorts().size(), is(1)); - assertNull(newspace.getStatus().getEndpointStatuses().get(0).getExternalHost()); - assertTrue(newspace.getStatus().getEndpointStatuses().get(0).getExternalPorts().isEmpty()); - } - - @Test - public void testExternalLoadBalancerCreated() throws Exception { - AddressSpace addressSpace = new AddressSpaceBuilder() - - .withNewMetadata() - .withName("myspace") - .withNamespace("mynamespace") - .addToAnnotations(AnnotationKeys.INFRA_UUID, "1234") - .endMetadata() - - .withNewSpec() - .addNewEndpoint() - .withName("myendpoint") - .withService("messaging") - .withExpose(new ExposeSpecBuilder() - .withType(ExposeType.loadbalancer) - .withLoadBalancerPorts(Arrays.asList("amqps")) - .build()) - .endEndpoint() - .withType("type1") - .withPlan("myplan") - .endSpec() - - .build(); - - - Service service = new ServiceBuilder() - .editOrNewMetadata() - .withName("messaging-1234") - .addToAnnotations(AnnotationKeys.SERVICE_PORT_PREFIX + "amqps", "5671") - .addToLabels(LabelKeys.INFRA_UUID, "1234") - .endMetadata() - .editOrNewSpec() - .addNewPort() - .withName("amqps") - .withPort(1234) - .withNewTargetPort("amqps") - .endPort() - .addToSelector("component", "router") - .endSpec() - .build(); - - client.services().create(service); - - EndpointController controller = new EndpointController(client, true, false); - - AddressSpace newspace = controller.reconcileAnyState(addressSpace).getAddressSpace(); - - assertThat(newspace.getStatus().getEndpointStatuses().size(), is(1)); - assertThat(newspace.getStatus().getEndpointStatuses().get(0).getName(), is("myendpoint")); - assertThat(newspace.getStatus().getEndpointStatuses().get(0).getServiceHost(), is("messaging-1234.test.svc")); - assertThat(newspace.getStatus().getEndpointStatuses().get(0).getServicePorts().size(), is(1)); - assertThat(newspace.getStatus().getEndpointStatuses().get(0).getExternalPorts().size(), is(1)); - } - - public void testExternalRouteCreated() throws Exception { - AddressSpace addressSpace = new AddressSpaceBuilder() - - .withNewMetadata() - .withName("myspace") - .withNamespace("mynamespace") - .addToAnnotations(AnnotationKeys.INFRA_UUID, "1234") - .endMetadata() - - .withNewSpec() - .addNewEndpoint() - .withName("myendpoint") - .withService("messaging") - .withExpose(new ExposeSpecBuilder() - .withType(ExposeType.route) - .withRouteHost("host1.example.com") - .withRouteServicePort("amqps") - .withRouteTlsTermination(TlsTermination.passthrough) - .build()) - .endEndpoint() - .withType("type1") - .withPlan("myplan") - .endSpec() - - .build(); - - - Service service = new ServiceBuilder() - .editOrNewMetadata() - .withName("messaging-1234") - .addToAnnotations(AnnotationKeys.SERVICE_PORT_PREFIX + "amqps", "5671") - .addToLabels(LabelKeys.INFRA_UUID, "1234") - .endMetadata() - .editOrNewSpec() - .addNewPort() - .withName("amqps") - .withPort(1234) - .withNewTargetPort("amqps") - .endPort() - .addToSelector("component", "router") - .endSpec() - .build(); - - client.services().create(service); - - EndpointController controller = new EndpointController(client, true, true); - - AddressSpace newspace = controller.reconcileAnyState(addressSpace).getAddressSpace(); - - assertThat(newspace.getStatus().getEndpointStatuses().size(), is(1)); - assertThat(newspace.getStatus().getEndpointStatuses().get(0).getName(), is("myendpoint")); - assertThat(newspace.getStatus().getEndpointStatuses().get(0).getServiceHost(), is("messaging-1234.test.svc")); - assertThat(newspace.getStatus().getEndpointStatuses().get(0).getServicePorts().size(), is(1)); - assertThat(newspace.getStatus().getEndpointStatuses().get(0).getServicePorts().size(), is(1)); - assertThat(newspace.getStatus().getEndpointStatuses().get(0).getExternalPorts().size(), is(1)); - assertThat(newspace.getStatus().getEndpointStatuses().get(0).getExternalPorts().get("amqps"), is(443)); - assertThat(newspace.getStatus().getEndpointStatuses().get(0).getExternalHost(), is("host1.example.com")); - } - - @Test - public void testConsoleEndpointResourcesRemoved_Route() throws Exception { - - String serviceName = "console-1234"; - String secretName = "consolesecret"; - - AddressSpace addressSpace = new AddressSpaceBuilder() - .withNewMetadata() - .withName("myspace") - .withNamespace("mynamespace") - .addToAnnotations(AnnotationKeys.INFRA_UUID, "1234") - .endMetadata() - - .withNewSpec() - .addToEndpoints(new EndpointSpecBuilder() - .withName("console") - .withService("console") - .withNewCert("selfsigned", secretName, null, null) - .withExpose(new ExposeSpecBuilder() - .withType(ExposeType.route) - .build()) - .build()) - - .withType("type1") - .withPlan("myplan") - .endSpec() - - .build(); - - Service service = new ServiceBuilder() - .editOrNewMetadata() - .withName(serviceName) - .addToLabels(LabelKeys.INFRA_UUID, "1234") - .endMetadata() - .editOrNewSpec() - .addNewPort() - .withName("https") - .withPort(1234) - .withNewTargetPort("https") - .endPort() - .addToSelector("component", "router") - .endSpec() - .build(); - client.services().create(service); - - Secret secret = new SecretBuilder() - .editOrNewMetadata() - .withName(secretName) - .addToLabels(LabelKeys.INFRA_UUID, "1234") - .endMetadata() - .build(); - client.secrets().create(secret); - - - // Problem with adapting the client prevents testing this path -// Route route = new RouteBuilder() -// .editOrNewMetadata() -// .withName(serviceName) -// .addToLabels(LabelKeys.INFRA_UUID, "1234") -// .endMetadata() -// .build(); -// client.adapt(OpenShiftClient.class).routes().create(route); - - EndpointController controller = new EndpointController(client, false, false); - - AddressSpace newspace = controller.reconcileAnyState(addressSpace).getAddressSpace(); - - assertThat(newspace.getSpec().getEndpoints().size(), is(0)); - - assertThat(client.services().withName(serviceName).get(), nullValue()); - assertThat(client.secrets().withName(secretName).get(), nullValue()); - } - - @Test - public void testConsoleEndpointResourcesRemoved_Loadbalance() throws Exception { - - String serviceName = "console-1234"; - String externalServiceName = serviceName + "-external"; - String secretName = "consolesecret"; - - AddressSpace addressSpace = new AddressSpaceBuilder() - .withNewMetadata() - .withName("myspace") - .withNamespace("mynamespace") - .addToAnnotations(AnnotationKeys.INFRA_UUID, "1234") - .endMetadata() - - .withNewSpec() - .addToEndpoints(new EndpointSpecBuilder() - .withName("console") - .withService("console") - .withNewCert("selfsigned", secretName, null, null) - .withExpose(new ExposeSpecBuilder() - .withType(ExposeType.loadbalancer) - .build()) - .build()) - - .withType("type1") - .withPlan("myplan") - .endSpec() - - .build(); - - Service service = new ServiceBuilder() - .editOrNewMetadata() - .withName(serviceName) - .addToLabels(LabelKeys.INFRA_UUID, "1234") - .endMetadata() - .editOrNewSpec() - .addNewPort() - .withName("https") - .withPort(1234) - .withNewTargetPort("https") - .endPort() - .addToSelector("component", "router") - .endSpec() - .build(); - Service extService = new ServiceBuilder() - .editOrNewMetadata() - .withName(externalServiceName) - .addToLabels(LabelKeys.INFRA_UUID, "1234") - .endMetadata() - .editOrNewSpec() - .addNewPort() - .withName("https") - .withPort(1234) - .withNewTargetPort("https") - .endPort() - .addToSelector("component", "router") - .endSpec() - .build(); - client.services().create(service); - client.services().create(extService); - - Secret secret = new SecretBuilder() - .editOrNewMetadata() - .withName(secretName) - .addToLabels(LabelKeys.INFRA_UUID, "1234") - .endMetadata() - .build(); - client.secrets().create(secret); - - EndpointController controller = new EndpointController(client, false, false); - - AddressSpace newspace = controller.reconcileAnyState(addressSpace).getAddressSpace(); - - assertThat(newspace.getSpec().getEndpoints().size(), is(0)); - - assertThat(client.secrets().withName(secretName).get(), nullValue()); - assertThat(client.services().withName(serviceName).get(), nullValue()); - assertThat(client.services().withName(externalServiceName).get(), nullValue()); - } - - @Test - public void testConsoleEndpointRemoved_UserSecretSurvives() throws Exception { - String myCustomisedSecret = "consolesecret"; - - AddressSpace addressSpace = new AddressSpaceBuilder() - .withNewMetadata() - .withName("myspace") - .withNamespace("mynamespace") - .addToAnnotations(AnnotationKeys.INFRA_UUID, "1234") - .endMetadata() - - .withNewSpec() - .addToEndpoints(new EndpointSpecBuilder() - .withName("console") - .withService("console") - .withNewCert("selfsigned", myCustomisedSecret, null, null) - .build()) - - .withType("type1") - .withPlan("myplan") - .endSpec() - - .build(); - - // Secret does not belong to infra (missing annotation) - Secret secret = new SecretBuilder() - .editOrNewMetadata() - .withName(myCustomisedSecret) - .endMetadata() - .build(); - client.secrets().create(secret); - - EndpointController controller = new EndpointController(client, false, false); - - AddressSpace newspace = controller.reconcileAnyState(addressSpace).getAddressSpace(); - - assertThat(newspace.getSpec().getEndpoints().size(), is(0)); - - assertThat(client.secrets().withName(myCustomisedSecret).get(), notNullValue()); - } -} diff --git a/address-space-controller/src/test/java/io/enmasse/controller/EqualityTest.java b/address-space-controller/src/test/java/io/enmasse/controller/EqualityTest.java deleted file mode 100644 index a7a50f071ea..00000000000 --- a/address-space-controller/src/test/java/io/enmasse/controller/EqualityTest.java +++ /dev/null @@ -1,60 +0,0 @@ -/* - * Copyright 2020, EnMasse authors. - * License: Apache License 2.0 (see the file LICENSE or http://apache.org/licenses/LICENSE-2.0.html). - */ - -package io.enmasse.controller; - -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertFalse; -import static org.junit.jupiter.api.Assertions.assertTrue; - -import java.util.Arrays; - -import org.junit.jupiter.api.Test; - -import io.enmasse.address.model.AddressSpace; -import io.enmasse.address.model.AddressSpaceBuilder; - -public class EqualityTest { - - /** - * Ensure that after cloning an instance, the new instance does not influence the old one. - */ - @Test - public void testClone() { - - final AddressSpace s1 = new AddressSpaceBuilder() - .withNewStatus() - .withMessages("foo") - .endStatus() - .build(); - - final AddressSpace s2 = new AddressSpaceBuilder(s1) - .build(); - - // right now they s1 and s2 must be equal - - assertFalse(ControllerChain.hasAddressSpaceChanged(s1, s2)); - assertFalse(ControllerChain.hasAddressSpaceChanged(s2, s1)); - - // test to see if we have the messages - - assertEquals(Arrays.asList("foo"), s1.getStatus().getMessages()); - assertEquals(Arrays.asList("foo"), s2.getStatus().getMessages()); - - // we make a change to s2 - - s2.getStatus().clearMessages(); - - // now s1 and s2 must no longer be equal - - assertTrue(ControllerChain.hasAddressSpaceChanged(s1, s2)); - assertTrue(ControllerChain.hasAddressSpaceChanged(s2, s1)); - - // the lists must be different - - assertEquals(Arrays.asList("foo"), s1.getStatus().getMessages()); - assertEquals(Arrays.asList(), s2.getStatus().getMessages()); - } -} diff --git a/address-space-controller/src/test/java/io/enmasse/controller/ExportsControllerTest.java b/address-space-controller/src/test/java/io/enmasse/controller/ExportsControllerTest.java deleted file mode 100644 index 356b87482f4..00000000000 --- a/address-space-controller/src/test/java/io/enmasse/controller/ExportsControllerTest.java +++ /dev/null @@ -1,162 +0,0 @@ -/* - * Copyright 2016-2018, EnMasse authors. - * License: Apache License 2.0 (see the file LICENSE or http://apache.org/licenses/LICENSE-2.0.html). - */ -package io.enmasse.controller; - -import io.enmasse.address.model.*; -import io.enmasse.k8s.util.JULInitializingTest; -import io.enmasse.model.CustomResourceDefinitions; -import io.fabric8.kubernetes.api.model.ConfigMap; -import io.fabric8.kubernetes.api.model.Secret; -import io.fabric8.kubernetes.api.model.Service; -import io.fabric8.openshift.client.OpenShiftClient; -import io.fabric8.openshift.client.server.mock.OpenShiftServer; -import org.junit.jupiter.api.AfterEach; -import org.junit.jupiter.api.BeforeAll; -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Test; - -import java.util.Map; - -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertNotNull; - -public class ExportsControllerTest extends JULInitializingTest { - private OpenShiftClient client; - - public OpenShiftServer openShiftServer = new OpenShiftServer(false, true); - - @BeforeAll - public static void init() { - CustomResourceDefinitions.registerAll(); - } - - @BeforeEach - public void setup() { - openShiftServer.before(); - client = openShiftServer.getOpenshiftClient(); - } - - @AfterEach - void tearDown() { - openShiftServer.after(); - } - - @Test - public void testExportConfigMap() throws Exception { - AddressSpace addressSpace = createTestSpace(new ExportSpecBuilder() - .withKind(ExportKind.ConfigMap) - .withName("mymap").build()); - - ExportsController controller = new ExportsController(client); - controller.reconcileAnyState(addressSpace); - - ConfigMap configMap = client.configMaps().inNamespace(addressSpace.getMetadata().getNamespace()).withName("mymap").get(); - assertNotNull(configMap); - Map data = configMap.getData(); - assertEquals("5671", data.get("service.port.amqps")); - assertEquals("5672", data.get("service.port.amqp")); - assertEquals("443", data.get("external.port.amqps")); - assertEquals("messaging.svc", data.get("service.host")); - assertEquals("messaging.example.com", data.get("external.host")); - assertEquals("mycert", data.get("ca.crt")); - - addressSpace.getStatus().getEndpointStatuses().get(0).setServiceHost("messaging2.svc"); - controller.reconcileAnyState(addressSpace); - - configMap = client.configMaps().inNamespace(addressSpace.getMetadata().getNamespace()).withName("mymap").get(); - assertNotNull(configMap); - data = configMap.getData(); - assertEquals("messaging2.svc", data.get("service.host")); - } - - @Test - public void testExportSecret() throws Exception { - AddressSpace addressSpace = createTestSpace( - new ExportSpecBuilder() - .withKind(ExportKind.Secret) - .withName("mysecret") - .build()); - ExportsController controller = new ExportsController(client); - controller.reconcileAnyState(addressSpace); - - Secret secret = client.secrets().inNamespace(addressSpace.getMetadata().getNamespace()).withName("mysecret").get(); - assertNotNull(secret); - Map data = secret.getStringData(); - assertEquals("5671", data.get("service.port.amqps")); - assertEquals("5672", data.get("service.port.amqp")); - assertEquals("443", data.get("external.port.amqps")); - assertEquals("messaging.svc", data.get("service.host")); - assertEquals("messaging.example.com", data.get("external.host")); - assertEquals("mycert", data.get("ca.crt")); - - addressSpace.getStatus().getEndpointStatuses().get(0).setServiceHost("messaging2.svc"); - controller.reconcileAnyState(addressSpace); - - secret = client.secrets().inNamespace(addressSpace.getMetadata().getNamespace()).withName("mysecret").get(); - assertNotNull(secret); - data = secret.getStringData(); - assertEquals("messaging2.svc", data.get("service.host")); - } - - @Test - public void testExportService() throws Exception { - AddressSpace addressSpace = createTestSpace( - new ExportSpecBuilder() - .withKind(ExportKind.Service) - .withName("myservice") - .build()); - ExportsController controller = new ExportsController(client); - controller.reconcileAnyState(addressSpace); - - Service service = client.services().inNamespace(addressSpace.getMetadata().getNamespace()).withName("myservice").get(); - assertNotNull(service); - assertEquals("ExternalName", service.getSpec().getType()); - assertEquals("messaging.svc.cluster.local", service.getSpec().getExternalName()); - assertEquals(2, service.getSpec().getPorts().size()); - - addressSpace.getStatus().getEndpointStatuses().get(0).setServiceHost("messaging2.svc"); - controller.reconcileAnyState(addressSpace); - - service = client.services().inNamespace(addressSpace.getMetadata().getNamespace()).withName("myservice").get(); - assertNotNull(service); - assertEquals("messaging2.svc.cluster.local", service.getSpec().getExternalName()); - } - - private AddressSpace createTestSpace(ExportSpec ... exports) { - return new AddressSpaceBuilder() - - .withApiVersion("enmasse.io/v1beta1") - .withKind("AddressSpace") - .withNewMetadata() - .withName("myspace") - .withNamespace("ns") - .withUid("1234") - .endMetadata() - - .withNewSpec() - .withType("type1") - .withPlan("plan1") - .addToEndpoints(new EndpointSpecBuilder() - .withName("messaging") - .withService("messaging") - .withExports(exports) - .build()) - .endSpec() - .editOrNewStatus() - .addToEndpointStatuses(new EndpointStatusBuilder() - .withName("messaging") - .withServiceHost("messaging.svc") - .withExternalHost("messaging.example.com") - .addToServicePorts("amqp", 5672) - .addToServicePorts("amqps", 5671) - .addToExternalPorts("amqps", 443) - .build()) - .withCaCert("mycert") - - .endStatus() - - .build(); - } -} diff --git a/address-space-controller/src/test/java/io/enmasse/controller/MessagingUserFinalizerControllerTest.java b/address-space-controller/src/test/java/io/enmasse/controller/MessagingUserFinalizerControllerTest.java deleted file mode 100644 index 156ecf84ea4..00000000000 --- a/address-space-controller/src/test/java/io/enmasse/controller/MessagingUserFinalizerControllerTest.java +++ /dev/null @@ -1,134 +0,0 @@ -/* - * Copyright 2019, EnMasse authors. - * License: Apache License 2.0 (see the file LICENSE or http://apache.org/licenses/LICENSE-2.0.html). - */ -package io.enmasse.controller; - -import io.enmasse.address.model.AddressSpace; -import io.enmasse.address.model.AddressSpaceBuilder; -import io.enmasse.config.AnnotationKeys; -import io.enmasse.controller.common.Kubernetes; -import io.enmasse.model.CustomResourceDefinitions; -import io.enmasse.user.model.v1.DoneableUser; -import io.enmasse.user.model.v1.Operation; -import io.enmasse.user.model.v1.User; -import io.enmasse.user.model.v1.UserAuthenticationType; -import io.enmasse.user.model.v1.UserBuilder; -import io.enmasse.user.model.v1.UserCrd; -import io.enmasse.user.model.v1.UserList; -import io.fabric8.kubernetes.client.KubernetesClientException; -import io.fabric8.kubernetes.client.NamespacedKubernetesClient; -import io.fabric8.kubernetes.client.dsl.MixedOperation; -import io.fabric8.kubernetes.client.dsl.Resource; -import io.fabric8.kubernetes.client.server.mock.KubernetesServer; -import org.junit.jupiter.api.AfterEach; -import org.junit.jupiter.api.BeforeAll; -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Test; - -import static org.junit.jupiter.api.Assertions.assertFalse; -import static org.junit.jupiter.api.Assertions.assertNotNull; -import static org.junit.jupiter.api.Assertions.assertNull; -import static org.junit.jupiter.api.Assertions.assertTrue; -import static org.mockito.ArgumentMatchers.any; -import static org.mockito.ArgumentMatchers.eq; -import static org.mockito.Mockito.doThrow; -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.verify; - -public class MessagingUserFinalizerControllerTest { - - private MessagingUserFinalizerController controller; - private NamespacedKubernetesClient client; - private KubernetesServer server; - private MixedOperation> userClient; - - @BeforeAll - public static void init() { - CustomResourceDefinitions.registerAll(); - } - - @BeforeEach - public void setup() { - server = new KubernetesServer(false, true); - server.before(); - client = server.getClient(); - userClient = client.customResources(UserCrd.messagingUser(), User.class, UserList.class, DoneableUser.class); - this.controller = new MessagingUserFinalizerController(client); - } - - @AfterEach - public void teardown() { - client.close(); - server.after(); - } - - @Test - public void testFinalizerSuccess() { - userClient.inNamespace("test").createOrReplace(createTestUser("myspace", "test")); - assertNotNull(userClient.inNamespace("test").withName("myspace.user").get()); - AbstractFinalizerController.Result result = controller.processFinalizer(createTestSpace()); - assertNotNull(result); - assertTrue(result.isFinalized()); - assertNull(userClient.inNamespace("test").withName("myspace.user").get()); - } - - @Test - public void testFinalizerFilterNamespace() { - userClient.inNamespace("test2").createOrReplace(createTestUser("myspace", "test2")); - AbstractFinalizerController.Result result = controller.processFinalizer(createTestSpace()); - assertNotNull(result); - assertTrue(result.isFinalized()); - assertNotNull(userClient.inNamespace("test2").withName("myspace.user").get()); - } - - @Test - public void testFinalizerFilterAddressSpace() { - userClient.inNamespace("test").createOrReplace(createTestUser("myspace2", "test")); - AbstractFinalizerController.Result result = controller.processFinalizer(createTestSpace()); - assertNotNull(result); - assertTrue(result.isFinalized()); - assertNotNull(userClient.inNamespace("test").withName("myspace2.user").get()); - } - - @Test - public void testFinalizerFailed() { - var mockClient = mock(MixedOperation.class); - MessagingUserFinalizerController controller = new MessagingUserFinalizerController(mockClient); - doThrow(new KubernetesClientException("ERROR")).when(mockClient).inNamespace(any()); - AbstractFinalizerController.Result result = controller.processFinalizer(createTestSpace()); - assertNotNull(result); - assertFalse(result.isFinalized()); - } - - private static User createTestUser(String addressSpace, String namespace) { - return new UserBuilder() - .editOrNewMetadata() - .withName(addressSpace + ".user") - .withNamespace(namespace) - .endMetadata() - .editOrNewSpec() - .withUsername("user") - .withNewAuthentication() - .withType(UserAuthenticationType.serviceaccount) - .endAuthentication() - .addNewAuthorization() - .addToOperations(Operation.manage) - .endAuthorization() - .endSpec() - .build(); - } - - private static AddressSpace createTestSpace() { - return new AddressSpaceBuilder() - .editOrNewMetadata() - .withName("myspace") - .withNamespace("test") - .endMetadata() - .editOrNewSpec() - .withType("standard") - .withPlan("standard") - .endSpec() - .build(); - } -} diff --git a/address-space-controller/src/test/java/io/enmasse/controller/MetricsReporterTest.java b/address-space-controller/src/test/java/io/enmasse/controller/MetricsReporterTest.java deleted file mode 100644 index 1b03bdb30c7..00000000000 --- a/address-space-controller/src/test/java/io/enmasse/controller/MetricsReporterTest.java +++ /dev/null @@ -1,81 +0,0 @@ -/* - * Copyright 2019, EnMasse authors. - * License: Apache License 2.0 (see the file LICENSE or http://apache.org/licenses/LICENSE-2.0.html). - */ -package io.enmasse.controller; - -import io.enmasse.address.model.AddressSpace; -import io.enmasse.address.model.AddressSpaceBuilder; -import io.enmasse.address.model.Phase; -import io.enmasse.controller.common.Kubernetes; -import io.enmasse.metrics.api.Metric; -import io.enmasse.metrics.api.MetricSnapshot; -import io.enmasse.metrics.api.Metrics; -import org.junit.jupiter.api.Test; -import static org.mockito.Mockito.mock; - -import java.util.Arrays; -import java.util.List; - -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertNotNull; - -public class MetricsReporterTest { - @Test - public void testController() throws Exception { - Metrics metrics = new Metrics(); - Kubernetes kubernetes = mock(Kubernetes.class); - - MetricsReporterController controller = new MetricsReporterController(metrics, "1.0", kubernetes); - - controller.reconcileAll(Arrays.asList( - createAddressSpace("s1", true), - createAddressSpace("s2", true), - createAddressSpace("s3", false))); - - List metricList = metrics.getMetrics(); - assertEquals(15, metricList.size()); - MetricSnapshot numReady = createSnapshot("address_space_status_ready", metricList); - assertNotNull(numReady); - assertEquals(3, numReady.getValues().size()); - - MetricSnapshot numNotReady = createSnapshot("address_space_status_not_ready", metricList); - assertNotNull(numNotReady); - assertEquals(3, numNotReady.getValues().size()); - - MetricSnapshot total = createSnapshot("address_spaces_total", metricList); - assertNotNull(total); - assertEquals(3, total.getValues().iterator().next().getValue()); - - MetricSnapshot numActive = createSnapshot("address_spaces_active_total", metricList); - assertNotNull(numActive); - assertEquals(Long.valueOf(2), numActive.getValues().iterator().next().getValue()); - } - - private MetricSnapshot createSnapshot(String name, List metricList) { - for (Metric metric : metricList) { - if (name.equals(metric.getName())) { - return metric.getSnapshot(); - } - } - return null; - } - - private AddressSpace createAddressSpace(String name, boolean isReady) { - Phase phase = isReady ? Phase.Active : Phase.Configuring; - return new AddressSpaceBuilder() - .editOrNewMetadata() - .withName(name) - .endMetadata() - .editOrNewSpec() - .withType("standard") - .withPlan("plan1") - .endSpec() - .editOrNewStatus() - .withReady(isReady) - .withPhase(phase) - .endStatus() - .build(); - - } -} diff --git a/address-space-controller/src/test/java/io/enmasse/controller/NetworkPolicyControllerTest.java b/address-space-controller/src/test/java/io/enmasse/controller/NetworkPolicyControllerTest.java deleted file mode 100644 index 615b88a5436..00000000000 --- a/address-space-controller/src/test/java/io/enmasse/controller/NetworkPolicyControllerTest.java +++ /dev/null @@ -1,229 +0,0 @@ -/* - * Copyright 2016-2018, EnMasse authors. - * License: Apache License 2.0 (see the file LICENSE or http://apache.org/licenses/LICENSE-2.0.html). - */ -package io.enmasse.controller; - -import static junit.framework.TestCase.assertNotNull; -import static org.hamcrest.CoreMatchers.hasItem; -import static org.hamcrest.MatcherAssert.assertThat; -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertNull; - -import java.util.Collections; - -import org.junit.jupiter.api.AfterEach; -import org.junit.jupiter.api.BeforeAll; -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Test; - -import com.fasterxml.jackson.core.JsonProcessingException; -import com.fasterxml.jackson.databind.ObjectMapper; - -import io.enmasse.address.model.AddressSpace; -import io.enmasse.address.model.AddressSpaceBuilder; -import io.enmasse.address.model.KubeUtil; -import io.enmasse.admin.model.v1.InfraConfig; -import io.enmasse.admin.model.v1.NetworkPolicy; -import io.enmasse.admin.model.v1.NetworkPolicyBuilder; -import io.enmasse.admin.model.v1.StandardInfraConfigBuilder; -import io.enmasse.config.AnnotationKeys; -import io.enmasse.config.LabelKeys; -import io.enmasse.k8s.util.JULInitializingTest; -import io.enmasse.model.CustomResourceDefinitions; -import io.fabric8.kubernetes.api.model.networking.NetworkPolicyEgressRuleBuilder; -import io.fabric8.kubernetes.api.model.networking.NetworkPolicyIngressRuleBuilder; -import io.fabric8.openshift.client.OpenShiftClient; -import io.fabric8.openshift.client.server.mock.OpenShiftServer; - -public class NetworkPolicyControllerTest extends JULInitializingTest { - private static final ObjectMapper mapper = new ObjectMapper(); - private OpenShiftClient client; - - public OpenShiftServer openShiftServer = new OpenShiftServer(false, true); - - @BeforeAll - public static void init() { - CustomResourceDefinitions.registerAll(); - } - - @BeforeEach - public void setup() { - openShiftServer.before(); - client = openShiftServer.getOpenshiftClient(); - } - - @AfterEach - void tearDown() { - openShiftServer.after(); - } - - @Test - public void testCreateFromInfraConfig() throws Exception { - InfraConfig infraConfig = createTestInfra(createTestPolicy("my", "label")); - AddressSpace addressSpace = createTestSpace(infraConfig, null); - - NetworkPolicyController controller = new NetworkPolicyController(client); - controller.reconcileAnyState(addressSpace); - - assertEquals(1, client.network().networkPolicies().list().getItems().size()); - io.fabric8.kubernetes.api.model.networking.NetworkPolicy networkPolicy = client.network().networkPolicies().withName(KubeUtil.getNetworkPolicyName(addressSpace)).get(); - assertNotNull(networkPolicy); - assertEquals("enmasse", networkPolicy.getMetadata().getLabels().get(LabelKeys.APP)); - assertEquals("1234", networkPolicy.getMetadata().getLabels().get(LabelKeys.INFRA_UUID)); - assertThat(networkPolicy.getSpec().getPolicyTypes(), hasItem("Ingress")); - assertEquals("label", networkPolicy.getSpec().getIngress().get(0).getFrom().get(0).getPodSelector().getMatchLabels().get("my")); - } - - @Test - public void testCreateFromAddressSpaceConfig() throws Exception { - InfraConfig infraConfig = createTestInfra(null); - AddressSpace addressSpace = createTestSpace(infraConfig, createTestPolicy("my", "label")); - - NetworkPolicyController controller = new NetworkPolicyController(client); - controller.reconcileAnyState(addressSpace); - - assertEquals(1, client.network().networkPolicies().list().getItems().size()); - io.fabric8.kubernetes.api.model.networking.NetworkPolicy networkPolicy = client.network().networkPolicies().withName(KubeUtil.getNetworkPolicyName(addressSpace)).get(); - assertNotNull(networkPolicy); - assertEquals("enmasse", networkPolicy.getMetadata().getLabels().get(LabelKeys.APP)); - assertEquals("1234", networkPolicy.getMetadata().getLabels().get(LabelKeys.INFRA_UUID)); - assertThat(networkPolicy.getSpec().getPolicyTypes(), hasItem("Ingress")); - assertEquals("label", networkPolicy.getSpec().getIngress().get(0).getFrom().get(0).getPodSelector().getMatchLabels().get("my")); - } - - @Test - public void testAddressSpaceOverridesInfra() throws Exception { - InfraConfig infraConfig = createTestInfra(createTestPolicy("my", "label")); - AddressSpace addressSpace = createTestSpace(infraConfig, createTestPolicy("my", "overridden")); - - NetworkPolicyController controller = new NetworkPolicyController(client); - controller.reconcileAnyState(addressSpace); - - assertEquals(1, client.network().networkPolicies().list().getItems().size()); - io.fabric8.kubernetes.api.model.networking.NetworkPolicy networkPolicy = client.network().networkPolicies().withName(KubeUtil.getNetworkPolicyName(addressSpace)).get(); - assertNotNull(networkPolicy); - assertEquals("enmasse", networkPolicy.getMetadata().getLabels().get(LabelKeys.APP)); - assertEquals("1234", networkPolicy.getMetadata().getLabels().get(LabelKeys.INFRA_UUID)); - assertEquals("type1", networkPolicy.getMetadata().getLabels().get(LabelKeys.INFRA_TYPE)); - assertThat(networkPolicy.getSpec().getPolicyTypes(), hasItem("Ingress")); - assertEquals("overridden", networkPolicy.getSpec().getIngress().get(0).getFrom().get(0).getPodSelector().getMatchLabels().get("my")); - } - - @Test - public void testUpdatesWhenChanged() throws Exception { - InfraConfig infraConfig = createTestInfra(null); - AddressSpace addressSpace = createTestSpace(infraConfig, - createTestPolicy("my", "label1")); - - NetworkPolicyController controller = new NetworkPolicyController(client); - controller.reconcileAnyState(addressSpace); - - assertEquals(1, client.network().networkPolicies().list().getItems().size()); - io.fabric8.kubernetes.api.model.networking.NetworkPolicy networkPolicy = client.network().networkPolicies().withName(KubeUtil.getNetworkPolicyName(addressSpace)).get(); - assertNotNull(networkPolicy); - assertThat(networkPolicy.getSpec().getPolicyTypes(), hasItem("Ingress")); - assertEquals("label1", networkPolicy.getSpec().getIngress().get(0).getFrom().get(0).getPodSelector().getMatchLabels().get("my")); - - addressSpace = createTestSpace(infraConfig, createTestPolicy("my", "label2")); - controller.reconcileAnyState(addressSpace); - - assertEquals(1, client.network().networkPolicies().list().getItems().size()); - networkPolicy = client.network().networkPolicies().withName(KubeUtil.getNetworkPolicyName(addressSpace)).get(); - assertNotNull(networkPolicy); - assertThat(networkPolicy.getSpec().getPolicyTypes(), hasItem("Ingress")); - assertEquals("label2", networkPolicy.getSpec().getIngress().get(0).getFrom().get(0).getPodSelector().getMatchLabels().get("my")); - - addressSpace = createTestSpace(infraConfig, createTestPolicy("my", "label2", "other", "label3")); - controller.reconcileAnyState(addressSpace); - networkPolicy = client.network().networkPolicies().withName(KubeUtil.getNetworkPolicyName(addressSpace)).get(); - assertNotNull(networkPolicy); - assertThat(networkPolicy.getSpec().getPolicyTypes(), hasItem("Ingress")); - assertThat(networkPolicy.getSpec().getPolicyTypes(), hasItem("Egress")); - assertEquals("label2", networkPolicy.getSpec().getIngress().get(0).getFrom().get(0).getPodSelector().getMatchLabels().get("my")); - assertEquals("label3", networkPolicy.getSpec().getEgress().get(0).getTo().get(0).getPodSelector().getMatchLabels().get("other")); - } - - @Test - public void testDeletesWhenRemoved() throws Exception { - InfraConfig infraConfig = createTestInfra(null); - AddressSpace addressSpace = createTestSpace(infraConfig, createTestPolicy("my", "label")); - - NetworkPolicyController controller = new NetworkPolicyController(client); - controller.reconcileAnyState(addressSpace); - - assertEquals(1, client.network().networkPolicies().list().getItems().size()); - io.fabric8.kubernetes.api.model.networking.NetworkPolicy networkPolicy = client.network().networkPolicies().withName(KubeUtil.getNetworkPolicyName(addressSpace)).get(); - assertNotNull(networkPolicy); - assertThat(networkPolicy.getSpec().getPolicyTypes(), hasItem("Ingress")); - assertEquals("label", networkPolicy.getSpec().getIngress().get(0).getFrom().get(0).getPodSelector().getMatchLabels().get("my")); - - addressSpace = createTestSpace(infraConfig, null); - controller.reconcileAnyState(addressSpace); - assertEquals(0, client.network().networkPolicies().list().getItems().size()); - networkPolicy = client.network().networkPolicies().withName(KubeUtil.getNetworkPolicyName(addressSpace)).get(); - assertNull(networkPolicy); - } - - private NetworkPolicy createTestPolicy(String labelKey, String labelValue) { - return new NetworkPolicyBuilder() - .withIngress(Collections.singletonList(new NetworkPolicyIngressRuleBuilder() - .addNewFrom() - .withNewPodSelector() - .addToMatchLabels(labelKey, labelValue) - .endPodSelector() - .endFrom() - .build())) - .build(); - } - - private NetworkPolicy createTestPolicy(String ingressLabelKey, String ingressLabelValue, String egressLabelKey, String egressLabelValue) { - return new NetworkPolicyBuilder() - .withIngress(Collections.singletonList(new NetworkPolicyIngressRuleBuilder() - .addNewFrom() - .withNewPodSelector() - .addToMatchLabels(ingressLabelKey, ingressLabelValue) - .endPodSelector() - .endFrom() - .build())) - .withEgress(Collections.singletonList(new NetworkPolicyEgressRuleBuilder() - .addNewTo() - .withNewPodSelector() - .addToMatchLabels(egressLabelKey, egressLabelValue) - .endPodSelector() - .endTo() - .build())) - .build(); - } - - private InfraConfig createTestInfra(NetworkPolicy networkPolicy) throws JsonProcessingException { - return new StandardInfraConfigBuilder() - .withNewMetadata() - .withName("test") - .endMetadata() - - .withNewSpec() - .withNetworkPolicy(networkPolicy) - .endSpec() - .build(); - } - - private AddressSpace createTestSpace(InfraConfig infraConfig, NetworkPolicy networkPolicy) throws JsonProcessingException { - return new AddressSpaceBuilder() - - .withNewMetadata() - .withName("myspace") - .withNamespace("ns") - .addToAnnotations(AnnotationKeys.INFRA_UUID, "1234") - .addToAnnotations(AnnotationKeys.APPLIED_INFRA_CONFIG, mapper.writeValueAsString(infraConfig)) - .endMetadata() - - .withNewSpec() - .withType("type1") - .withPlan("plan1") - .withNetworkPolicy(networkPolicy) - .endSpec() - - .build(); - } -} diff --git a/address-space-controller/src/test/java/io/enmasse/controller/RealmFinalizerControllerTest.java b/address-space-controller/src/test/java/io/enmasse/controller/RealmFinalizerControllerTest.java deleted file mode 100644 index ffa0ab02449..00000000000 --- a/address-space-controller/src/test/java/io/enmasse/controller/RealmFinalizerControllerTest.java +++ /dev/null @@ -1,140 +0,0 @@ -/* - * Copyright 2019, EnMasse authors. - * License: Apache License 2.0 (see the file LICENSE or http://apache.org/licenses/LICENSE-2.0.html). - */ -package io.enmasse.controller; - -import io.enmasse.address.model.AddressSpace; -import io.enmasse.address.model.AddressSpaceBuilder; -import io.enmasse.admin.model.v1.AuthenticationService; -import io.enmasse.admin.model.v1.AuthenticationServiceBuilder; -import io.enmasse.admin.model.v1.AuthenticationServiceType; -import io.enmasse.config.AnnotationKeys; -import io.enmasse.k8s.api.AuthenticationServiceRegistry; -import io.enmasse.model.CustomResourceDefinitions; -import io.enmasse.user.api.RealmApi; -import org.junit.jupiter.api.BeforeAll; -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Test; - -import java.util.Optional; -import java.util.Set; - -import static org.junit.jupiter.api.Assertions.assertNotNull; -import static org.junit.jupiter.api.Assertions.assertTrue; -import static org.mockito.ArgumentMatchers.any; -import static org.mockito.ArgumentMatchers.eq; -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.never; -import static org.mockito.Mockito.verify; -import static org.mockito.Mockito.when; - -public class RealmFinalizerControllerTest { - - private RealmFinalizerController controller; - private RealmApi realmApi; - private AuthenticationServiceRegistry authenticationServiceRegistry; - - @BeforeAll - public static void init() { - CustomResourceDefinitions.registerAll(); - } - - @BeforeEach - public void setup() { - this.realmApi = mock(RealmApi.class); - this.authenticationServiceRegistry = mock(AuthenticationServiceRegistry.class); - this.controller = new RealmFinalizerController(realmApi, authenticationServiceRegistry); - } - - @Test - public void testFinalizerSuccess() throws Exception { - AuthenticationService authenticationService = createAuthService(AuthenticationServiceType.standard, null); - when(authenticationServiceRegistry.findAuthenticationService(any())).thenReturn(Optional.of(authenticationService)); - when(realmApi.getRealmNames(eq(authenticationService))).thenReturn(Set.of("realm1", "realm2", "myrealm")); - AbstractFinalizerController.Result result = controller.processFinalizer(createTestSpace("standard", "myrealm")); - assertNotNull(result); - assertTrue(result.isFinalized()); - verify(realmApi).deleteRealm(eq(authenticationService), eq("myrealm")); - } - - @Test - public void testFinalizerAlreadyDeleted() throws Exception { - AuthenticationService authenticationService = createAuthService(AuthenticationServiceType.standard, null); - when(authenticationServiceRegistry.findAuthenticationService(any())).thenReturn(Optional.of(authenticationService)); - when(realmApi.getRealmNames(eq(authenticationService))).thenReturn(Set.of("realm1", "realm2")); - AbstractFinalizerController.Result result = controller.processFinalizer(createTestSpace("standard", "myrealm")); - assertNotNull(result); - assertTrue(result.isFinalized()); - verify(realmApi, never()).deleteRealm(eq(authenticationService), eq("myrealm")); - } - - @Test - public void testFinalizerAuthServiceNotConfigured() throws Exception { - AuthenticationService authenticationService = createAuthService(AuthenticationServiceType.standard, null); - authenticationService.setStatus(null); - when(authenticationServiceRegistry.findAuthenticationService(any())).thenReturn(Optional.of(authenticationService)); - when(realmApi.getRealmNames(eq(authenticationService))).thenReturn(Set.of("realm1", "realm2", "myrealm")); - AbstractFinalizerController.Result result = controller.processFinalizer(createTestSpace("standard", "myrealm")); - assertNotNull(result); - assertTrue(result.isFinalized()); - verify(realmApi, never()).deleteRealm(eq(authenticationService), eq("myrealm")); - } - - @Test - public void testMissingAuthService() { - AbstractFinalizerController.Result result = controller.processFinalizer(createTestSpace("unknown", "myrealm")); - assertNotNull(result); - assertTrue(result.isFinalized()); - } - - @Test - public void testAuthServiceRealmSet() { - when(authenticationServiceRegistry.findAuthenticationService(any())).thenReturn(Optional.of(createAuthService(AuthenticationServiceType.standard, "myrealm"))); - AbstractFinalizerController.Result result = controller.processFinalizer(createTestSpace("standard", "myrealm")); - assertNotNull(result); - assertTrue(result.isFinalized()); - } - - @Test - public void testAuthServiceType() { - when(authenticationServiceRegistry.findAuthenticationService(any())).thenReturn(Optional.of(createAuthService(AuthenticationServiceType.none, null))); - AbstractFinalizerController.Result result = controller.processFinalizer(createTestSpace("standard", "myrealm")); - assertNotNull(result); - assertTrue(result.isFinalized()); - } - - - - - private static AuthenticationService createAuthService(AuthenticationServiceType type, String realm) { - return new AuthenticationServiceBuilder() - .editOrNewMetadata() - .withName("standard") - .endMetadata() - .editOrNewSpec() - .withType(type) - .withRealm(realm) - .endSpec() - .editOrNewStatus() - .endStatus() - .build(); - } - - private static AddressSpace createTestSpace(String authenticationServiceName, String realm) { - return new AddressSpaceBuilder() - .editOrNewMetadata() - .withName("myspace") - .withNamespace("test") - .addToAnnotations(AnnotationKeys.REALM_NAME, realm) - .endMetadata() - .editOrNewSpec() - .editOrNewAuthenticationService() - .withName(authenticationServiceName) - .endAuthenticationService() - .withType("standard") - .withPlan("standard") - .endSpec() - .build(); - } -} diff --git a/address-space-controller/src/test/java/io/enmasse/controller/RouterConfigControllerTest.java b/address-space-controller/src/test/java/io/enmasse/controller/RouterConfigControllerTest.java deleted file mode 100644 index af959042063..00000000000 --- a/address-space-controller/src/test/java/io/enmasse/controller/RouterConfigControllerTest.java +++ /dev/null @@ -1,453 +0,0 @@ -/* - * Copyright 2019, EnMasse authors. - * License: Apache License 2.0 (see the file LICENSE or http://apache.org/licenses/LICENSE-2.0.html). - */ -package io.enmasse.controller; - -import io.enmasse.address.model.AddressSpace; -import io.enmasse.address.model.AddressSpaceBuilder; -import io.enmasse.address.model.AddressSpaceStatusConnector; -import io.enmasse.admin.model.v1.*; -import io.enmasse.config.AnnotationKeys; -import io.enmasse.controller.router.config.*; -import io.enmasse.k8s.api.AuthenticationServiceRegistry; -import io.enmasse.k8s.api.LogEventLogger; -import io.enmasse.model.CustomResourceDefinitions; -import io.fabric8.kubernetes.api.model.ConfigMap; -import io.fabric8.kubernetes.api.model.Secret; -import io.fabric8.kubernetes.client.NamespacedKubernetesClient; -import io.fabric8.kubernetes.client.server.mock.KubernetesServer; -import org.junit.jupiter.api.BeforeAll; -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Test; - -import java.time.Duration; -import java.util.Collections; -import java.util.List; -import java.util.Optional; - -import static org.junit.jupiter.api.Assertions.*; -import static org.mockito.ArgumentMatchers.any; -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.when; - -public class RouterConfigControllerTest { - - private NamespacedKubernetesClient client; - - public KubernetesServer kubernetesServer = new KubernetesServer(false, true); - - private AuthenticationServiceRegistry authenticationServiceRegistry; - - @BeforeAll - public static void init() { - CustomResourceDefinitions.registerAll(); - } - - @BeforeEach - public void setup() { - kubernetesServer.before(); - client = kubernetesServer.getClient(); - authenticationServiceRegistry = mock(AuthenticationServiceRegistry.class); - when(authenticationServiceRegistry.findAuthenticationService(any())).thenReturn(Optional.of( - new AuthenticationServiceBuilder() - .editOrNewMetadata() - .withName("test") - .endMetadata() - .editOrNewSpec() - .withType(AuthenticationServiceType.standard) - .withRealm("myrealm") - .endSpec() - .editOrNewStatus() - .withHost("auth.example.com") - .withPort(5671) - .endStatus() - .build())); - } - - @Test - public void testReconcile() throws Exception { - RouterConfigController configController = new RouterConfigController( - client, - "test", - new AuthenticationServiceResolver(authenticationServiceRegistry), - new RouterStatusCache(new LogEventLogger(), Duration.ofSeconds(100), mock(NamespacedKubernetesClient.class), "test", Duration.ofSeconds(100), Duration.ofSeconds(100))); - - StandardInfraConfig appliedConfig = new StandardInfraConfigBuilder() - .editOrNewMetadata() - .withName("test") - .endMetadata() - .editOrNewSpec() - .editOrNewRouter() - .withIdleTimeout(2) - .withLinkCapacity(50) - .withHandshakeTimeout(20) - .withNewPolicy() - .withMaxConnections(30) - .withMaxConnectionsPerHost(10) - .withMaxConnectionsPerUser(10) - .withMaxReceiversPerConnection(2) - .withMaxSendersPerConnection(3) - .withMaxSessionsPerConnection(4) - .endPolicy() - .endRouter() - .endSpec() - .build(); - - AddressSpace addressSpace = new AddressSpaceBuilder() - .editOrNewMetadata() - .withName("myspace") - .addToAnnotations(AnnotationKeys.INFRA_UUID, "1234") - .endMetadata() - .editOrNewSpec() - .withType("type1") - .withPlan("plan1") - .withNewAuthenticationService() - .withName("test") - .endAuthenticationService() - .endSpec() - .build(); - InfraConfigs.setCurrentInfraConfig(addressSpace, appliedConfig); - - configController.reconcileAnyState(addressSpace); - - ConfigMap routerConfigMap = client.configMaps().inNamespace("test").withName("qdrouterd-config.1234").get(); - assertNotNull(routerConfigMap); - - RouterConfig actual = RouterConfig.fromMap(routerConfigMap.getData()); - - assertEquals("${HOSTNAME}", actual.getRouter().getId()); - assertEquals(3, actual.getSslProfiles().size()); - assertEquals(8, actual.getListeners().size()); - assertEquals(2, actual.getLinkRoutes().size()); - assertEquals(1, actual.getAutoLinks().size()); - assertEquals(5, actual.getAddresses().size()); - assertEquals(1, actual.getConnectors().size()); - assertEquals(1, actual.getPolicies().size()); - assertEquals(2, actual.getVhosts().size()); - assertEquals(1, actual.getAuthServicePlugins().size()); - - Listener amqpPublic = getListenerOnPort(5672, actual.getListeners()); - assertNotNull(amqpPublic); - assertEquals(2, amqpPublic.getIdleTimeoutSeconds()); - assertEquals(20, amqpPublic.getInitialHandshakeTimeoutSeconds()); - assertEquals(50, amqpPublic.getLinkCapacity()); - - VhostPolicy internal = getPolicyForHostname("$default", actual.getVhosts()); - assertNotNull(internal); - assertNull(internal.getMaxConnections()); - assertNull(internal.getMaxConnectionsPerHost()); - assertNull(internal.getMaxConnectionsPerUser()); - assertNull(internal.getGroups().get("$default").getMaxSessions()); - assertNull(internal.getGroups().get("$default").getMaxSenders()); - assertNull(internal.getGroups().get("$default").getMaxReceivers()); - - VhostPolicy pub = getPolicyForHostname("public", actual.getVhosts()); - assertNotNull(pub); - assertEquals(30, pub.getMaxConnections()); - assertEquals(10, pub.getMaxConnectionsPerUser()); - assertEquals(10, pub.getMaxConnectionsPerHost()); - assertEquals(4, pub.getGroups().get("$default").getMaxSessions()); - assertEquals(3, pub.getGroups().get("$default").getMaxSenders()); - assertEquals(2, pub.getGroups().get("$default").getMaxReceivers()); - - - appliedConfig.getSpec().getRouter().setIdleTimeout(20); - appliedConfig.getSpec().getRouter().getPolicy().setMaxConnectionsPerUser(300); - InfraConfigs.setCurrentInfraConfig(addressSpace, appliedConfig); - - configController.reconcileAnyState(addressSpace); - - routerConfigMap = client.configMaps().inNamespace("test").withName("qdrouterd-config.1234").get(); - assertNotNull(routerConfigMap); - - actual = RouterConfig.fromMap(routerConfigMap.getData()); - amqpPublic = getListenerOnPort(5672, actual.getListeners()); - assertNotNull(amqpPublic); - assertEquals(20, amqpPublic.getIdleTimeoutSeconds()); - assertEquals(20, amqpPublic.getInitialHandshakeTimeoutSeconds()); - assertEquals(50, amqpPublic.getLinkCapacity()); - - pub = getPolicyForHostname("public", actual.getVhosts()); - assertNotNull(pub); - assertEquals(30, pub.getMaxConnections()); - assertEquals(300, pub.getMaxConnectionsPerUser()); - } - - @Test - public void testReconcileConnector() throws Exception { - RouterStatusCache routerStatusCache = new RouterStatusCache(new LogEventLogger(), Duration.ofSeconds(100), mock(NamespacedKubernetesClient.class), "test", Duration.ofSeconds(100), Duration.ofSeconds(100)); - RouterConfigController configController = new RouterConfigController( - client, - "test", - new AuthenticationServiceResolver(authenticationServiceRegistry), - routerStatusCache); - - - StandardInfraConfig appliedConfig = new StandardInfraConfigBuilder() - .editOrNewMetadata() - .withName("test") - .endMetadata() - .build(); - - AddressSpace addressSpace = new AddressSpaceBuilder() - .editOrNewMetadata() - .withName("myspace") - .withNamespace("space") - .addToAnnotations(AnnotationKeys.INFRA_UUID, "1234") - .endMetadata() - .editOrNewSpec() - .withType("standard") - .withPlan("plan1") - .withNewAuthenticationService() - .withName("test") - .endAuthenticationService() - .addNewConnector() - .withName("remote1") - .addNewEndpointHost() - .withHost("messaging.example.com") - .withPort(5671) - .endEndpointHost() - .addNewEndpointHost() - .withHost("messaging2.example.com") - .endEndpointHost() - .withIdleTimeout(12000) - .withMaxFrameSize(12345) - .withRole("normal") - - .withNewTls() - .withNewCaCert() - .withNewValueFromSecret("ca.crt", "remote-certs", false) - .endCaCert() - .withNewClientCert() - .withNewValueFromSecret("tls.crt", "remote-certs", false) - .endClientCert() - .withNewClientKey() - .withNewValueFromSecret("tls.key", "remote-certs", false) - .endClientKey() - .endTls() - .withNewCredentials() - .withNewUsername() - .withValue("test") - .endUsername() - .withNewPassword() - .withValue("test") - .endPassword() - .endCredentials() - - .addNewAddress() - .withName("pat1") - .withPattern("foo*") - .endAddress() - .endConnector() - .endSpec() - .build(); - - - InfraConfigs.setCurrentInfraConfig(addressSpace, appliedConfig); - - - routerStatusCache.reconcileAll(Collections.singletonList(addressSpace)); - routerStatusCache.checkRouterStatus(a -> Collections.singletonList(new RouterStatus("r1", - new RouterConnections(Collections.singletonList("messaging.example.com:5671"), Collections.singletonList(true), Collections.singletonList("up")), - Collections.emptyList(), - 0))); - - /* - client.apps().statefulSets().inNamespace("test").createOrReplaceWithNew() - .editOrNewMetadata() - .withName(KubeUtil.getRouterSetName(addressSpace)) - .withNamespace("test") - .endMetadata() - .editOrNewSpec() - .withReplicas(1) - .editOrNewTemplate() - .editOrNewSpec() - .addNewContainer() - .withName("router") - .addNewVolumeMount() - .withName("external-connector-old") - .endVolumeMount() - .endContainer() - .addNewVolume() - .withName("external-connector-old") - .withNewSecret() - .withSecretName("external-connector-old") - .endSecret() - .endVolume() - .endSpec() - .endTemplate() - .endSpec() - .done(); - */ - - configController.reconcileAnyState(addressSpace); - - AddressSpaceStatusConnector status = addressSpace.getStatus().getConnectors().get(0); - assertNotNull(status); - assertEquals("remote1", status.getName()); - assertFalse(status.isReady()); - assertTrue(status.getMessages().contains("Unable to locate value or secret for caCert")); - assertTrue(status.getMessages().contains("Unable to locate value or secret for clientCert")); - assertTrue(status.getMessages().contains("Unable to locate value or secret for clientKey")); - - - ConfigMap routerConfigMap = client.configMaps().inNamespace("test").withName("qdrouterd-config.1234").get(); - assertNotNull(routerConfigMap); - - RouterConfig actual = RouterConfig.fromMap(routerConfigMap.getData()); - - SslProfile profile = getSslProfile("connector_remote1_settings", actual.getSslProfiles()); - assertNull(profile); - - Connector remote = getConnectorForHost("messaging.example.com", actual.getConnectors()); - assertNull(remote); - - status.setReady(true); - status.clearMessages(); - - client.secrets().inNamespace("space").createOrReplaceWithNew() - .editOrNewMetadata() - .withName("remote-certs") - .endMetadata() - .addToData("tls.crt", "cert") - .addToData("tls.key", "key") - .addToData("ca.crt", "ca") - .done(); - - configController.reconcileAnyState(addressSpace); - - routerConfigMap = client.configMaps().inNamespace("test").withName("qdrouterd-config.1234").get(); - assertNotNull(routerConfigMap); - - actual = RouterConfig.fromMap(routerConfigMap.getData()); - - profile = getSslProfile("connector_remote1_settings", actual.getSslProfiles()); - assertNotNull(profile); - assertEquals("connector_remote1_settings", profile.getName()); - assertEquals("/etc/enmasse-connectors/remote1/ca.crt", profile.getCaCertFile()); - assertEquals("/etc/enmasse-connectors/remote1/tls.crt", profile.getCertFile()); - assertEquals("/etc/enmasse-connectors/remote1/tls.key", profile.getPrivateKeyFile()); - - remote = getConnectorForHost("messaging.example.com", actual.getConnectors()); - assertNotNull(remote); - assertEquals("amqps://messaging2.example.com:5671", remote.getFailoverUrls()); - assertEquals("connector_remote1_settings", remote.getSslProfile()); - assertEquals("EXTERNAL PLAIN", remote.getSaslMechanisms()); - assertEquals(12000, remote.getIdleTimeoutSeconds()); - assertEquals(12345, remote.getMaxFrameSize()); - assertEquals("normal", remote.getRole().toValue()); - assertEquals(5671, remote.getPort()); - assertEquals("test", remote.getSaslUsername()); - assertEquals("test", remote.getSaslPassword()); - - LinkRoute lrIn = getLinkRoute("override.connector.remote1.pat1.in", actual.getLinkRoutes()); - assertNotNull(lrIn); - assertEquals("remote1/foo*", lrIn.getPattern()); - assertEquals(LinkDirection.in, lrIn.getDirection()); - assertEquals("remote1", lrIn.getConnection()); - - LinkRoute lrOut = getLinkRoute("override.connector.remote1.pat1.out", actual.getLinkRoutes()); - assertNotNull(lrOut); - assertEquals("remote1/foo*", lrOut.getPattern()); - assertEquals(LinkDirection.out, lrOut.getDirection()); - assertEquals("remote1", lrOut.getConnection()); - - - status = addressSpace.getStatus().getConnectors().get(0); - assertNotNull(status); - assertEquals("remote1", status.getName()); - assertTrue(status.isReady(), String.join(",", status.getMessages())); - - Secret certs = client.secrets().inNamespace("test").withName("external-connector-1234-remote1").get(); - assertNotNull(certs); - assertEquals("ca", certs.getData().get("ca.crt")); - assertEquals("key", certs.getData().get("tls.key")); - assertEquals("cert", certs.getData().get("tls.crt")); - - /* - StatefulSet router = client.apps().statefulSets().inNamespace("test").withName("qdrouterd-1234").get(); - assertNotNull(router); - */ - } - - - @Test - public void testVhostPolicyGen() { - RouterPolicySpec policySpec = new RouterPolicySpecBuilder() - .withMaxConnections(1000) - .withMaxConnectionsPerHost(10) - .withMaxConnectionsPerUser(10) - .withMaxSendersPerConnection(5) - .withMaxReceiversPerConnection(5) - .withMaxSessionsPerConnection(5) - .build(); - - List policyList = RouterConfigController.createVhostPolices(policySpec); - - assertEquals(2, policyList.size()); - - VhostPolicy internal = getPolicyForHostname("$default", policyList); - assertNotNull(internal); - assertNull(internal.getMaxConnections()); - assertNull(internal.getMaxConnectionsPerHost()); - assertNull(internal.getMaxConnectionsPerUser()); - assertNull(internal.getGroups().get("$default").getMaxSessions()); - assertNull(internal.getGroups().get("$default").getMaxSenders()); - assertNull(internal.getGroups().get("$default").getMaxReceivers()); - - VhostPolicy pub = getPolicyForHostname("public", policyList); - assertNotNull(pub); - assertEquals(1000, pub.getMaxConnections()); - assertEquals(10, pub.getMaxConnectionsPerUser()); - assertEquals(10, pub.getMaxConnectionsPerHost()); - assertEquals(5, pub.getGroups().get("$default").getMaxSessions()); - assertEquals(5, pub.getGroups().get("$default").getMaxSenders()); - assertEquals(5, pub.getGroups().get("$default").getMaxReceivers()); - } - - private Listener getListenerOnPort(int port, List listeners) { - for (Listener listener : listeners) { - if (port == listener.getPort()) { - return listener; - } - } - return null; - } - - private VhostPolicy getPolicyForHostname(String hostname, List vhostPolicies) { - for (VhostPolicy vhostPolicy : vhostPolicies) { - if (hostname.equals(vhostPolicy.getHostname())) { - return vhostPolicy; - } - } - return null; - } - - private SslProfile getSslProfile(String name, List sslProfiles) { - for (SslProfile sslProfile : sslProfiles) { - if (name.equals(sslProfile.getName())) { - return sslProfile; - } - } - return null; - } - - private Connector getConnectorForHost(String hostname, List connectors) { - for (Connector connector : connectors) { - if (hostname.equals(connector.getHost())) { - return connector; - } - } - return null; - } - - private LinkRoute getLinkRoute(String name, List linkRoutes) { - for (LinkRoute lr : linkRoutes) { - if (name.equals(lr.getName())) { - return lr; - } - } - return null; - } -} diff --git a/address-space-controller/src/test/java/io/enmasse/controller/RouterStatusCacheTest.java b/address-space-controller/src/test/java/io/enmasse/controller/RouterStatusCacheTest.java deleted file mode 100644 index 3ff1587a5b4..00000000000 --- a/address-space-controller/src/test/java/io/enmasse/controller/RouterStatusCacheTest.java +++ /dev/null @@ -1,87 +0,0 @@ -/* - * Copyright 2020, EnMasse authors. - * License: Apache License 2.0 (see the file LICENSE or http://apache.org/licenses/LICENSE-2.0.html). - */ -package io.enmasse.controller; - -import io.enmasse.address.model.AddressSpace; -import io.enmasse.address.model.AddressSpaceBuilder; -import io.enmasse.k8s.api.LogEventLogger; -import io.fabric8.kubernetes.client.NamespacedKubernetesClient; -import io.fabric8.kubernetes.client.server.mock.KubernetesServer; -import org.junit.jupiter.api.AfterEach; -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Test; - -import java.time.Duration; -import java.util.Collections; -import java.util.List; - -import static org.junit.jupiter.api.Assertions.assertNotNull; -import static org.junit.jupiter.api.Assertions.assertNull; - -public class RouterStatusCacheTest { - - private KubernetesServer server = new KubernetesServer(true, true); - private NamespacedKubernetesClient client; - - @BeforeEach - public void setup() { - server.before(); - client = server.getClient(); - } - - @AfterEach - public void teardown() { - server.after(); - } - - @Test - public void testResetCache() { - AddressSpace a1 = new AddressSpaceBuilder() - .editOrNewMetadata() - .withName("a1") - .withNamespace("n") - .endMetadata() - .editOrNewSpec() - .withType("standard") - .withPlan("small") - .endSpec() - .build(); - - AddressSpace a2 = new AddressSpaceBuilder() - .editOrNewMetadata() - .withName("a2") - .withNamespace("n") - .endMetadata() - .editOrNewSpec() - .withType("standard") - .withPlan("small") - .endSpec() - .build(); - - RouterStatusCache cache = new RouterStatusCache(new LogEventLogger(), Duration.ofDays(1), client, "n", Duration.ofSeconds(1), Duration.ofSeconds(1)); - cache.reconcileAll(List.of(a1, a2)); - - assertNull(cache.getLatestResult(a1)); - assertNull(cache.getLatestResult(a2)); - - cache.checkRouterStatus(addressSpace -> Collections.singletonList(new RouterStatus("r1", - new RouterConnections(Collections.singletonList("example.com"), Collections.singletonList(true), Collections.singletonList("up")), - Collections.emptyList(), - 0))); - - assertNotNull(cache.getLatestResult(a1)); - assertNotNull(cache.getLatestResult(a2)); - - cache.reconcileAll(List.of(a1, a2)); - - assertNotNull(cache.getLatestResult(a1)); - assertNotNull(cache.getLatestResult(a2)); - - cache.reconcileAll(List.of(a1)); - - assertNotNull(cache.getLatestResult(a1)); - assertNull(cache.getLatestResult(a2)); - } -} diff --git a/address-space-controller/src/test/java/io/enmasse/controller/StatusControllerTest.java b/address-space-controller/src/test/java/io/enmasse/controller/StatusControllerTest.java deleted file mode 100644 index 26213b728cc..00000000000 --- a/address-space-controller/src/test/java/io/enmasse/controller/StatusControllerTest.java +++ /dev/null @@ -1,87 +0,0 @@ -/* - * Copyright 2016-2018, EnMasse authors. - * License: Apache License 2.0 (see the file LICENSE or http://apache.org/licenses/LICENSE-2.0.html). - */ -package io.enmasse.controller; - -import io.enmasse.address.model.AddressSpace; -import io.enmasse.address.model.AddressSpaceBuilder; -import io.enmasse.address.model.AuthenticationService; -import io.enmasse.address.model.AuthenticationServiceType; -import io.enmasse.config.AnnotationKeys; -import io.enmasse.controller.common.Kubernetes; -import io.enmasse.k8s.api.AuthenticationServiceRegistry; -import io.enmasse.k8s.api.LogEventLogger; -import io.fabric8.kubernetes.api.model.apps.Deployment; -import io.fabric8.kubernetes.api.model.apps.DeploymentBuilder; -import io.fabric8.kubernetes.client.NamespacedKubernetesClient; -import org.junit.jupiter.api.Test; - -import java.time.Duration; -import java.util.Collections; -import java.util.Optional; - -import static org.junit.jupiter.api.Assertions.assertFalse; -import static org.mockito.ArgumentMatchers.any; -import static org.mockito.ArgumentMatchers.eq; -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.when; - -public class StatusControllerTest { - - @Test - public void testStatusControllerSetsNotReady() throws Exception { - InfraResourceFactory infraResourceFactory = mock(InfraResourceFactory.class); - Kubernetes kubernetes = mock(Kubernetes.class); - - Deployment deployment = new DeploymentBuilder() - .withNewMetadata() - .withName("mydepl1") - .endMetadata() - .withNewStatus() - .withUnavailableReplicas(1) - .withAvailableReplicas(0) - .endStatus() - .build(); - - when(kubernetes.getReadyDeployments(new AddressSpaceBuilder() - .withNewMetadata() - .withName("a") - .withNamespace("b") - .addToAnnotations(AnnotationKeys.INFRA_UUID, "1234") - .endMetadata() - - .withNewSpec() - .withPlan("c") - .withType("d") - .endSpec() - - .build())) - .thenReturn(Collections.emptySet()); - - AuthenticationServiceRegistry authenticationServiceRegistry = mock(AuthenticationServiceRegistry.class); - when(authenticationServiceRegistry.findAuthenticationService(any())).thenReturn(Optional.empty()); - StatusController controller = new StatusController(kubernetes, new TestSchemaProvider(), infraResourceFactory, authenticationServiceRegistry, null, - new RouterStatusCache(new LogEventLogger(), Duration.ofSeconds(100), mock(NamespacedKubernetesClient.class), "test", Duration.ofSeconds(100), Duration.ofSeconds(100))); - - AuthenticationService authenticationService = new AuthenticationService(); - authenticationService.setType(AuthenticationServiceType.NONE); - AddressSpace addressSpace = new AddressSpaceBuilder() - .withNewMetadata() - .withName("myspace") - .endMetadata() - - .withNewSpec() - .withType("type1") - .withPlan("myplan") - .withAuthenticationService(authenticationService) - .endSpec() - .build(); - - when(infraResourceFactory.createInfraResources(eq(addressSpace), any(), any())).thenReturn(Collections.singletonList(deployment)); - - assertFalse(addressSpace.getStatus().isReady()); - controller.reconcileAnyState(addressSpace); - assertFalse(addressSpace.getStatus().isReady()); - } -} diff --git a/address-space-controller/src/test/java/io/enmasse/controller/TemplateInfraResourceFactoryTest.java b/address-space-controller/src/test/java/io/enmasse/controller/TemplateInfraResourceFactoryTest.java deleted file mode 100644 index 4b574565435..00000000000 --- a/address-space-controller/src/test/java/io/enmasse/controller/TemplateInfraResourceFactoryTest.java +++ /dev/null @@ -1,237 +0,0 @@ -/* - * Copyright 2017-2018, EnMasse authors. - * License: Apache License 2.0 (see the file LICENSE or http://apache.org/licenses/LICENSE-2.0.html). - */ -package io.enmasse.controller; - -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertNotNull; -import static org.mockito.ArgumentMatchers.any; -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.when; - -import java.io.File; -import java.util.Collections; -import java.util.List; -import java.util.Map; -import java.util.Optional; - -import io.enmasse.address.model.*; -import io.enmasse.admin.model.v1.*; -import io.enmasse.admin.model.v1.AuthenticationService; -import io.enmasse.admin.model.v1.AuthenticationServiceBuilder; -import io.enmasse.admin.model.v1.AuthenticationServiceType; -import io.enmasse.k8s.api.SchemaProvider; -import io.fabric8.kubernetes.api.model.*; -import io.fabric8.kubernetes.api.model.apps.Deployment; -import io.fabric8.kubernetes.api.model.apps.StatefulSet; -import io.fabric8.kubernetes.client.NamespacedKubernetesClient; -import io.fabric8.kubernetes.client.server.mock.KubernetesServer; -import org.junit.jupiter.api.AfterEach; -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Test; - -import io.enmasse.k8s.api.AuthenticationServiceRegistry; -import io.enmasse.config.AnnotationKeys; -import io.enmasse.controller.common.KubernetesHelper; -import io.enmasse.k8s.util.JULInitializingTest; - -public class TemplateInfraResourceFactoryTest extends JULInitializingTest { - - private KubernetesServer kubeServer = new KubernetesServer(false, true); - - private TemplateInfraResourceFactory resourceFactory; - private NamespacedKubernetesClient client; - private AuthenticationServiceResolver authenticationServiceResolver; - - @AfterEach - void tearDown() { - kubeServer.after(); - } - - @BeforeEach - public void setup() { - kubeServer.before(); - client = kubeServer.getClient(); - client.secrets().createNew().editOrNewMetadata().withName("certs").endMetadata().addToData("tls.crt", "cert").done(); - AuthenticationServiceRegistry authenticationServiceRegistry = mock(AuthenticationServiceRegistry.class); - SchemaProvider schemaProvider = mock(SchemaProvider.class); - when(schemaProvider.getSchema()).thenReturn(mock(Schema.class)); - AuthenticationService authenticationService = new AuthenticationServiceBuilder() - .withNewMetadata() - .withName("standard") - .endMetadata() - .withNewSpec() - .withType(AuthenticationServiceType.none) - .endSpec() - .withNewStatus() - .withHost("example") - .withPort(5671) - .withCaCertSecret(new SecretReferenceBuilder().withName("certs").build()) - .endStatus() - .build(); - when(authenticationServiceRegistry.findAuthenticationService(any())).thenReturn(Optional.of(authenticationService)); - - authenticationServiceResolver = new AuthenticationServiceResolver(authenticationServiceRegistry); - - resourceFactory = new TemplateInfraResourceFactory( - new KubernetesHelper("test", - client, - new File("src/test/resources/templates"), - true), - Collections.emptyMap(), schemaProvider); - } - - @Test - public void testGenerateStandard() { - AddressSpace addressSpace = new AddressSpaceBuilder() - .withNewMetadata() - .withName("myspace") - .withNamespace("myproject") - .addToAnnotations(AnnotationKeys.INFRA_UUID, "1234") - .endMetadata() - - .withNewSpec() - .withType("standard") - .withPlan("standard-unlimited") - .addToEndpoints(new EndpointSpecBuilder() - .withName("messaging") - .withService("messaging") - .withCert(new CertSpec("selfsigned", "messaging-secret", null, null)) - .build()) - .addToEndpoints(new EndpointSpecBuilder() - .withName("console") - .withService("console") - .withCert(new CertSpec("selfsigned", "console-secret", null, null)) - .build()) - .endSpec() - - .build(); - - PodTemplateSpec routerTemplateSpec = createTemplateSpec(Collections.singletonMap("mylabel", "router"), "myrnode", "myrkey", "myrClass"); - PodTemplateSpec adminTemplateSpec = createTemplateSpec(Collections.singletonMap("mylabel", "broker"), "mybnode", "mybkey", "mybClass"); - StandardInfraConfig infraConfig = new StandardInfraConfigBuilder() - .withNewMetadata() - .withName("test") - .endMetadata() - - .withNewSpec() - .withVersion("master") - .withAdmin(new StandardInfraConfigSpecAdminBuilder() - .editOrNewResources() - .withMemory("2Mi") - .endResources() - .withPodTemplate(adminTemplateSpec) - .build()) - .withBroker(new StandardInfraConfigSpecBrokerBuilder() - .editOrNewResources() - .withMemory("2Mi") - .withStorage("1Gi") - .endResources() - .withAddressFullPolicy("FAIL") - .build()) - .withRouter(new StandardInfraConfigSpecRouterBuilder() - .editOrNewResources() - .withMemory("2Mi") - .endResources() - .withLinkCapacity(22) - .withPodTemplate(routerTemplateSpec) - .build()) - .endSpec() - .build(); - List items = resourceFactory.createInfraResources(addressSpace, infraConfig, authenticationServiceResolver.resolve(addressSpace)); - assertEquals(3, items.size()); - ConfigMap map = findItem(ConfigMap.class, "ConfigMap", "mymap", items); - assertEquals("FAIL", map.getData().get("key")); - - StatefulSet routerSet = findItem(StatefulSet.class, "StatefulSet", "qdrouterd-1234", items); - assertTemplateSpec(routerSet.getSpec().getTemplate(), routerTemplateSpec); - - Deployment adminDeployment = findItem(Deployment.class, "Deployment", "admin.1234", items); - assertTemplateSpec(adminDeployment.getSpec().getTemplate(), adminTemplateSpec); - } - - public static PodTemplateSpec createTemplateSpec(Map labels, String nodeAffinityValue, String tolerationKey, String priorityClassName) { - PodTemplateSpecBuilder builder = new PodTemplateSpecBuilder(); - if (labels != null) { - builder.editOrNewMetadata() - .withLabels(labels) - .endMetadata(); - } - - if (nodeAffinityValue != null) { - builder.editOrNewSpec() - .editOrNewAffinity() - .editOrNewNodeAffinity() - .addToPreferredDuringSchedulingIgnoredDuringExecution(new PreferredSchedulingTermBuilder() - .withNewPreference() - .addToMatchExpressions(new NodeSelectorRequirementBuilder() - .addToValues(nodeAffinityValue) - .build()) - .endPreference() - .build()) - .endNodeAffinity() - .endAffinity() - .endSpec(); - } - - if (tolerationKey != null) { - builder.editOrNewSpec() - .addNewToleration() - .withKey(tolerationKey) - .withOperator("Exists") - .withEffect("NoSchedule") - .endToleration() - .endSpec(); - } - - if (priorityClassName != null) { - builder.editOrNewSpec() - .withPriorityClassName(priorityClassName) - .endSpec(); - } - - return builder.build(); - } - - - private void assertTemplateSpec(PodTemplateSpec pod, PodTemplateSpec templateSpec) { - if (templateSpec.getMetadata().getLabels() != null) { - for (Map.Entry labelPair : templateSpec.getMetadata().getLabels().entrySet()) { - assertEquals(labelPair.getValue(), pod.getMetadata().getLabels().get(labelPair.getKey()), "Labels do not match"); - } - } - - if (templateSpec.getSpec().getAffinity() != null) { - assertEquals(templateSpec.getSpec().getAffinity(), pod.getSpec().getAffinity(), "Affinity rules do not match"); - } - - if (templateSpec.getSpec().getPriorityClassName() != null) { - assertEquals(templateSpec.getSpec().getPriorityClassName(), pod.getSpec().getPriorityClassName(), "Priority class names do not match"); - } - - if (templateSpec.getSpec().getTolerations() != null) { - assertEquals(templateSpec.getSpec().getTolerations(), pod.getSpec().getTolerations(), "List of tolerations does not match"); - } - - for (Container expectedContainer : templateSpec.getSpec().getContainers()) { - for (Container actualContainer : pod.getSpec().getContainers()) { - if (expectedContainer.getName().equals(actualContainer.getName())) { - assertEquals(expectedContainer.getResources(), actualContainer.getResources()); - } - } - } - } - - private T findItem(Class clazz, String kind, String name, List items) { - T found = null; - for (HasMetadata item : items) { - if (kind.equals(item.getKind()) && name.equals(item.getMetadata().getName())) { - found = clazz.cast(item); - break; - } - } - assertNotNull(found); - return found; - } -} diff --git a/address-space-controller/src/test/java/io/enmasse/controller/TestSchemaProvider.java b/address-space-controller/src/test/java/io/enmasse/controller/TestSchemaProvider.java deleted file mode 100644 index 024a21ca7f8..00000000000 --- a/address-space-controller/src/test/java/io/enmasse/controller/TestSchemaProvider.java +++ /dev/null @@ -1,18 +0,0 @@ -/* - * Copyright 2016-2018, EnMasse authors. - * License: Apache License 2.0 (see the file LICENSE or http://apache.org/licenses/LICENSE-2.0.html). - */ -package io.enmasse.controller; - -import io.enmasse.address.model.Schema; -import io.enmasse.k8s.api.SchemaProvider; -import io.enmasse.k8s.api.TestSchemaApi; - -public class TestSchemaProvider implements SchemaProvider { - public TestSchemaApi api = new TestSchemaApi(); - - @Override - public Schema getSchema() { - return api.getSchema(); - } -} diff --git a/address-space-controller/src/test/java/io/enmasse/controller/auth/CertBundleCertProviderTest.java b/address-space-controller/src/test/java/io/enmasse/controller/auth/CertBundleCertProviderTest.java deleted file mode 100644 index 349d59d878d..00000000000 --- a/address-space-controller/src/test/java/io/enmasse/controller/auth/CertBundleCertProviderTest.java +++ /dev/null @@ -1,122 +0,0 @@ -/* - * Copyright 2018, EnMasse authors. - * License: Apache License 2.0 (see the file LICENSE or http://apache.org/licenses/LICENSE-2.0.html). - */ -package io.enmasse.controller.auth; - -import io.enmasse.address.model.AddressSpace; -import io.enmasse.address.model.AddressSpaceBuilder; -import io.enmasse.address.model.CertSpec; -import io.enmasse.address.model.CertSpecBuilder; -import io.enmasse.k8s.util.JULInitializingTest; -import io.enmasse.model.validation.DefaultValidator; -import io.fabric8.kubernetes.api.model.Secret; -import io.fabric8.openshift.client.OpenShiftClient; -import io.fabric8.openshift.client.server.mock.OpenShiftServer; -import org.junit.jupiter.api.AfterEach; -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Test; - -import static org.hamcrest.CoreMatchers.is; -import static org.hamcrest.MatcherAssert.assertThat; -import static org.junit.jupiter.api.Assertions.*; - -import javax.validation.ValidationException; - -public class CertBundleCertProviderTest extends JULInitializingTest { - - public OpenShiftServer server = new OpenShiftServer(true, true); - - private OpenShiftClient client; - private CertProvider certProvider; - - @AfterEach - void tearDown() { - server.after(); - } - - @BeforeEach - public void setup() { - server.before(); - client = server.getOpenshiftClient(); - - certProvider = new CertBundleCertProvider(client); - } - - @Test - public void testProvideCertNoService() { - - AddressSpace space = new AddressSpaceBuilder() - .withNewMetadata() - .withName("myspace") - .endMetadata() - - .withNewSpec() - .withPlan("myplan") - .withType("standard") - .endSpec() - .build(); - - CertSpec spec = new CertSpecBuilder() - .withProvider("certBundle") - .withSecretName("mycerts") - .build(); - - certProvider.provideCert(space, new EndpointInfo("messaging", spec)); - - Secret cert = client.secrets().withName("mycerts").get(); - assertNull(cert); - } - - @Test - public void testProvideCert() { - AddressSpace space = new AddressSpaceBuilder() - .withNewMetadata() - .withName("myspace") - .endMetadata() - - .withNewSpec() - .withPlan("myplan") - .withType("standard") - .endSpec() - .build(); - - CertSpec spec = new CertSpecBuilder() - .withProvider("certBundle") - .withSecretName("mycerts") - .withTlsKey("aGVsbG8=") - .withTlsCert("d29ybGQ=") - .build(); - - DefaultValidator.validate(space); - - certProvider.provideCert(space, new EndpointInfo("messaging", spec)); - - Secret cert = client.secrets().withName("mycerts").get(); - assertNotNull(cert); - assertThat(cert.getData().get("tls.key"), is(spec.getTlsKey())); - assertThat(cert.getData().get("tls.crt"), is(spec.getTlsCert())); - } - - @Test - public void testValidateBadKey() { - assertThrows(ValidationException.class, () -> DefaultValidator.validate( - new CertSpecBuilder() - .withProvider("certBundle") - .withSecretName("mycerts") - .withTlsKey("/%^$lkg") - .withTlsCert("d29ybGQ=") - .build())); - } - - @Test - public void testValidateBadCert() { - assertThrows(ValidationException.class, () -> DefaultValidator.validate( - new CertSpecBuilder() - .withProvider("certBundle") - .withSecretName("mycerts") - .withTlsKey("d29ybGQ=") - .withTlsCert("/%^$lkg") - .build())); - } -} diff --git a/address-space-controller/src/test/java/io/enmasse/controller/auth/OpenShiftCertProviderTest.java b/address-space-controller/src/test/java/io/enmasse/controller/auth/OpenShiftCertProviderTest.java deleted file mode 100644 index b821f4924d6..00000000000 --- a/address-space-controller/src/test/java/io/enmasse/controller/auth/OpenShiftCertProviderTest.java +++ /dev/null @@ -1,100 +0,0 @@ -/* - * Copyright 2018, EnMasse authors. - * License: Apache License 2.0 (see the file LICENSE or http://apache.org/licenses/LICENSE-2.0.html). - */ -package io.enmasse.controller.auth; - -import io.enmasse.address.model.AddressSpace; -import io.enmasse.address.model.AddressSpaceBuilder; -import io.enmasse.address.model.CertSpec; -import io.enmasse.address.model.CertSpecBuilder; -import io.enmasse.config.AnnotationKeys; -import io.enmasse.config.LabelKeys; -import io.enmasse.k8s.util.JULInitializingTest; -import io.fabric8.kubernetes.api.model.Secret; -import io.fabric8.kubernetes.api.model.Service; -import io.fabric8.kubernetes.api.model.ServiceBuilder; -import io.fabric8.openshift.client.OpenShiftClient; -import io.fabric8.openshift.client.server.mock.OpenShiftServer; -import org.junit.jupiter.api.AfterEach; -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Test; - -import static junit.framework.TestCase.assertNotNull; -import static org.junit.jupiter.api.Assertions.assertNull; - -public class OpenShiftCertProviderTest extends JULInitializingTest { - public OpenShiftServer server = new OpenShiftServer(true, true); - - private OpenShiftClient client; - private CertProvider certProvider; - - @BeforeEach - public void setup() { - server.before(); - client = server.getOpenshiftClient(); - - certProvider = new OpenshiftCertProvider(client); - } - - @AfterEach - void tearDown() { - server.after(); - } - - @Test - public void testProvideCertNoService() { - - AddressSpace space = new AddressSpaceBuilder() - .withNewMetadata() - .withName("myspace") - .endMetadata() - - .withNewSpec() - .withPlan("myplan") - .withType("standard") - .endSpec() - .build(); - - CertSpec spec = new CertSpecBuilder().withProvider("openshift").withSecretName("mycerts").build(); - certProvider.provideCert(space, new EndpointInfo("messaging", spec)); - - Secret cert = client.secrets().withName("mycerts").get(); - assertNull(cert); - } - - @Test - public void testProvideCert() { - AddressSpace space = new AddressSpaceBuilder() - .withNewMetadata() - .withName("myspace") - .addToAnnotations(AnnotationKeys.INFRA_UUID, "1234") - .endMetadata() - - .withNewSpec() - .withPlan("myplan") - .withType("standard") - .endSpec() - - .build(); - - client.services().inNamespace("test").create(new ServiceBuilder() - .editOrNewMetadata() - .withName("messaging-1234") - .addToLabels(LabelKeys.INFRA_UUID, "1234") - .endMetadata() - .editOrNewSpec() - .endSpec() - .build()); - - CertSpec spec = new CertSpecBuilder().withProvider("openshift").withSecretName("mycerts").build(); - certProvider.provideCert(space, new EndpointInfo("messaging", spec)); - - Secret cert = client.secrets().withName("mycerts").get(); - assertNull(cert); - - Service service = client.services().inNamespace("test").withName("messaging-1234").get(); - assertNotNull(service); - // Should verify that annotation is set, but bug in mock-server seems to not set it - } -} diff --git a/address-space-controller/src/test/java/io/enmasse/controller/auth/WildcardCertProviderTest.java b/address-space-controller/src/test/java/io/enmasse/controller/auth/WildcardCertProviderTest.java deleted file mode 100644 index 5a64dd1c4dd..00000000000 --- a/address-space-controller/src/test/java/io/enmasse/controller/auth/WildcardCertProviderTest.java +++ /dev/null @@ -1,92 +0,0 @@ -/* - * Copyright 2018, EnMasse authors. - * License: Apache License 2.0 (see the file LICENSE or http://apache.org/licenses/LICENSE-2.0.html). - */ -package io.enmasse.controller.auth; - -import io.enmasse.address.model.AddressSpace; -import io.enmasse.address.model.AddressSpaceBuilder; -import io.enmasse.address.model.CertSpec; -import io.enmasse.address.model.CertSpecBuilder; -import io.enmasse.k8s.util.JULInitializingTest; -import io.fabric8.kubernetes.api.model.Secret; -import io.fabric8.kubernetes.api.model.SecretBuilder; -import io.fabric8.kubernetes.client.KubernetesClient; -import io.fabric8.kubernetes.client.server.mock.KubernetesServer; -import org.junit.jupiter.api.AfterEach; -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Test; - -import static org.hamcrest.CoreMatchers.is; -import static org.hamcrest.MatcherAssert.assertThat; -import static org.junit.jupiter.api.Assertions.assertThrows; - - -public class WildcardCertProviderTest extends JULInitializingTest { - public KubernetesServer server = new KubernetesServer(true, true); - - private KubernetesClient client; - private CertProvider certProvider; - - @BeforeEach - public void setup() { - server.before(); - client = server.getClient(); - String wildcardCert = "wildcardcert"; - - certProvider = new WildcardCertProvider(client, wildcardCert); - } - - @AfterEach - void tearDown() { - server.after(); - } - - @Test - public void testUnknownWildcardSecret() { - - AddressSpace space = new AddressSpaceBuilder() - .withNewMetadata() - .withName("myspace") - .endMetadata() - - .withNewSpec() - .withType("standard") - .withPlan("myplan") - .endSpec() - .build(); - CertSpec spec = new CertSpecBuilder().withProvider("wildcard").withSecretName("mycerts").build(); - - assertThrows(IllegalStateException.class, () -> certProvider.provideCert(space, new EndpointInfo("messaging", spec))); - } - - @Test - public void testProvideCert() { - - AddressSpace space = new AddressSpaceBuilder() - .withNewMetadata() - .withName("myspace") - .endMetadata() - - .withNewSpec() - .withPlan("myplan") - .withType("standard") - .endSpec() - .build(); - - client.secrets().create(new SecretBuilder() - .editOrNewMetadata() - .withName("wildcardcert") - .endMetadata() - .addToData("tls.key", "mykey") - .addToData("tls.crt", "myvalue") - .build()); - - CertSpec spec = new CertSpecBuilder().withProvider("wildcard").withSecretName("mycerts").build(); - certProvider.provideCert(space, new EndpointInfo("messaging", spec)); - - Secret cert = client.secrets().withName("mycerts").get(); - assertThat(cert.getData().get("tls.key"), is("mykey")); - assertThat(cert.getData().get("tls.crt"), is("myvalue")); - } -} diff --git a/address-space-controller/src/test/java/io/enmasse/controller/common/KubernetesHelperTest.java b/address-space-controller/src/test/java/io/enmasse/controller/common/KubernetesHelperTest.java deleted file mode 100644 index 75c9db6c1b5..00000000000 --- a/address-space-controller/src/test/java/io/enmasse/controller/common/KubernetesHelperTest.java +++ /dev/null @@ -1,107 +0,0 @@ -/* - * Copyright 2016-2018, EnMasse authors. - * License: Apache License 2.0 (see the file LICENSE or http://apache.org/licenses/LICENSE-2.0.html). - */ - -package io.enmasse.controller.common; - -public class KubernetesHelperTest { - - /* - @Test - public void testListClusters() { - - MixedOperation dOp = mock(MixedOperation.class); - MixedOperation pvcOp = mock(MixedOperation.class); - MixedOperation mapOp = mock(MixedOperation.class); - - MixedOperation rcOp = mock(MixedOperation.class); - ExtensionsAPIGroupDSL extensions = mock(ExtensionsAPIGroupDSL.class); - - OpenShiftClient mockClient = mock(OpenShiftClient.class); - KubernetesHelper helper = new KubernetesHelper(AddressSpaceId.withId("myinstance"), mockClient, new File("src/test/resources/templates")); - - when(mockClient.extensions()).thenReturn(extensions); - when(extensions.deployments()).thenReturn(dOp); - when(mockClient.persistentVolumeClaims()).thenReturn(pvcOp); - when(mockClient.configMaps()).thenReturn(mapOp); - when(mockClient.replicationControllers()).thenReturn(rcOp); - - when(dOp.inNamespace(anyString())).thenReturn(dOp); - when(pvcOp.inNamespace(anyString())).thenReturn(pvcOp); - when(mapOp.inNamespace(anyString())).thenReturn(mapOp); - when(rcOp.inNamespace(anyString())).thenReturn(rcOp); - - when(pvcOp.withLabel(anyString(), anyString())).thenReturn(pvcOp); - when(dOp.withLabel(anyString(), anyString())).thenReturn(dOp); - when(mapOp.withLabels(any())).thenReturn(mapOp); - - Deployment config = - new DeploymentBuilder() - .withMetadata(new ObjectMetaBuilder() - .withName("testdc") - .addToLabels(LabelKeys.CLUSTER_ID, "mygroup") - .addToLabels(LabelKeys.ADDRESS_CONFIG, "address-config-mygroup") - .build()) - .build(); - - - - ConfigMap map1 = helper.createAddressConfig(new Destination("myqueue", "mygroup", true, false, Optional.of("vanilla"), Optional.empty(), status)); - ConfigMap map2 = helper.createAddressConfig(new Destination("myqueue2", "mygroup", true, false, Optional.of("vanilla"), Optional.empty(), status)); - - when(rcOp.withLabel(anyString(), anyString())).thenReturn(rcOp); - when(pvcOp.list()).thenReturn(new PersistentVolumeClaimListBuilder().build()); - when(dOp.list()).thenReturn(new DeploymentListBuilder().addToItems(config).build()); - when(mapOp.list()).thenReturn(new ConfigMapListBuilder().addToItems(map1, map2).build()); - when(rcOp.list()).thenReturn(new ReplicationControllerListBuilder().build()); - - PlanManager flavorManager = new PlanManager(); - flavorManager.flavorsUpdated(Collections.singletonMap("vanilla", new Flavor.Builder("vanilla", "test").build())); - List clusters = helper.listClusters(); - assertThat(clusters.size(), is(1)); - - AddressCluster cluster = clusters.get(0); - Set group = cluster.getDestinations(); - assertThat(group.size(), is(2)); - assertDestination(group, "myqueue", true, false, Optional.of("vanilla")); - assertDestination(group, "myqueue2", true, false, Optional.of("vanilla")); - } - - @Test - public void testProcessTemplate() { - KubernetesHelper helper = new KubernetesHelper(AddressSpaceId.withId("myinstance"), new DefaultOpenShiftClient(), new File("src/test/resources/templates")); - KubernetesList list = helper.processTemplate("test", new ParameterValue("MYPARAM", "value"), new ParameterValue("SECONDPARAM", "")); - assertThat(list.getItems().size(), is(1)); - } - - @Test - public void testCreateAddressConfig() { - OpenShiftClient mockClient = mock(OpenShiftClient.class); - KubernetesHelper helper = new KubernetesHelper(AddressSpaceId.withId("myinstance"), mockClient, new File("src/test/resources/templates")); - Destination destination = new Destination("queue1", "group1", true, false, Optional.of("vanilla"), Optional.empty(), status); - - ConfigMap addressConfig = helper.createAddressConfig(destination); - - assertTrue(addressConfig.getMetadata().getLabels().containsKey(LabelKeys.CLUSTER_ID)); - assertThat(addressConfig.getMetadata().getLabels().get(LabelKeys.CLUSTER_ID), is("group1")); - assertThat(addressConfig.getData().size(), is(5)); - assertThat(addressConfig.getData().get(AddressConfigKeys.ADDRESS), is("queue1")); - } - - private void assertDestination(Set destinations, String address, boolean storeAndForward, boolean multicast, Optional flavor) { - Destination actual = null; - for (Destination destination : destinations) { - if (destination.address().equals(address)) { - actual = destination; - break; - } - } - assertNotNull(actual); - assertThat(actual.address(), is(address)); - assertThat(actual.storeAndForward(), is(storeAndForward)); - assertThat(actual.multicast(), is(multicast)); - assertThat(actual.flavor(), is(flavor)); - } - */ -} diff --git a/address-space-controller/src/test/java/io/enmasse/controller/keycloak/RealmControllerTest.java b/address-space-controller/src/test/java/io/enmasse/controller/keycloak/RealmControllerTest.java deleted file mode 100644 index ccb138efdcc..00000000000 --- a/address-space-controller/src/test/java/io/enmasse/controller/keycloak/RealmControllerTest.java +++ /dev/null @@ -1,139 +0,0 @@ -/* - * Copyright 2017-2018, EnMasse authors. - * License: Apache License 2.0 (see the file LICENSE or http://apache.org/licenses/LICENSE-2.0.html). - */ -package io.enmasse.controller.keycloak; - -import io.enmasse.address.model.*; -import io.enmasse.admin.model.v1.AuthenticationService; -import io.enmasse.admin.model.v1.AuthenticationServiceBuilder; -import io.enmasse.config.AnnotationKeys; -import io.enmasse.k8s.api.AuthenticationServiceRegistry; -import io.enmasse.user.api.RealmApi; -import io.fabric8.kubernetes.api.model.ObjectMetaBuilder; -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Test; - -import java.util.*; - -import static org.junit.jupiter.api.Assertions.*; -import static org.mockito.ArgumentMatchers.eq; -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.when; - -public class RealmControllerTest { - - private RealmController manager; - private Set realms; - private AuthenticationServiceRegistry mockAuthenticationServiceRegistry; - - @BeforeEach - public void setup() { - realms = new HashSet<>(); - AuthenticationService authenticationService = new AuthenticationServiceBuilder() - .withNewMetadata() - .withName("standard") - .endMetadata() - .withNewSpec() - .withType(io.enmasse.admin.model.v1.AuthenticationServiceType.standard) - .endSpec() - .withNewStatus() - .withHost("example.com") - .withPort(5671) - .endStatus() - .build(); - mockAuthenticationServiceRegistry = mock(AuthenticationServiceRegistry.class); - io.enmasse.address.model.AuthenticationService standardSvc = new io.enmasse.address.model.AuthenticationServiceBuilder() - .withName("standard") - .build(); - when(mockAuthenticationServiceRegistry.findAuthenticationService(standardSvc)).thenReturn(Optional.of(authenticationService)); - when(mockAuthenticationServiceRegistry.findAuthenticationServiceByType(eq(io.enmasse.admin.model.v1.AuthenticationServiceType.standard))).thenReturn(Arrays.asList(authenticationService)); - when(mockAuthenticationServiceRegistry.listAuthenticationServices()).thenReturn(Collections.singletonList(authenticationService)); - - manager = new RealmController(new RealmApi() { - @Override - public Set getRealmNames(AuthenticationService auth) { - return new HashSet<>(realms); - } - - @Override - public void createRealm(AuthenticationService auth, String namespace, String realmName) { - realms.add(realmName); - } - - @Override - public void deleteRealm(AuthenticationService auth, String realmName) { - realms.remove(realmName); - } - }, mockAuthenticationServiceRegistry); - } - - @Test - public void testAddAddressSpace() throws Exception { - manager.reconcileAll(Collections.singletonList(createAddressSpace("a1", AuthenticationServiceType.NONE))); - assertTrue(realms.isEmpty()); - - manager.reconcileAll(Arrays.asList(createAddressSpace("a1", AuthenticationServiceType.NONE), createAddressSpace("a2", AuthenticationServiceType.STANDARD))); - assertTrue(realms.contains("a2")); - - manager.reconcileAll(Arrays.asList(createAddressSpace("a1", AuthenticationServiceType.NONE), createAddressSpace("a2", AuthenticationServiceType.STANDARD), createAddressSpace("a3", AuthenticationServiceType.STANDARD))); - assertTrue(realms.contains("a2")); - assertTrue(realms.contains("a3")); - assertEquals(2, realms.size()); - } - - @Test - public void testRemoveAddressSpace() throws Exception { - manager.reconcileAll(Arrays.asList(createAddressSpace("a1", AuthenticationServiceType.STANDARD), createAddressSpace("a2", AuthenticationServiceType.STANDARD), createAddressSpace("a3", AuthenticationServiceType.STANDARD))); - manager.reconcileAll(Arrays.asList(createAddressSpace("a1", AuthenticationServiceType.STANDARD), createAddressSpace("a3", AuthenticationServiceType.STANDARD))); - - assertTrue(realms.contains("a1")); - assertFalse(realms.contains("a2")); - assertTrue(realms.contains("a3")); - assertEquals(2, realms.size()); - } - - @Test - public void testAuthTypeChanged() throws Exception { - manager.reconcileAll(Arrays.asList(createAddressSpace("a1", AuthenticationServiceType.STANDARD))); - assertTrue(realms.contains("a1")); - assertEquals(1, realms.size()); - - manager.reconcileAll(Arrays.asList(createAddressSpace("a1", AuthenticationServiceType.NONE))); - assertFalse(realms.contains("a1")); - assertEquals(0, realms.size()); - } - - private AddressSpace createAddressSpace(String name, AuthenticationServiceType authType) { - return new AddressSpaceBuilder() - .withMetadata(new ObjectMetaBuilder() - .withName(name) - .withNamespace("myns") - .addToAnnotations(AnnotationKeys.CREATED_BY, "developer") - .addToAnnotations(AnnotationKeys.REALM_NAME, name) - .build()) - - .withNewSpec() - .withPlan("myplan") - .withType("standard") - - .addToEndpoints(new EndpointSpecBuilder() - .withName("console") - .withService("console") - .build()) - .withAuthenticationService(new io.enmasse.address.model.AuthenticationServiceBuilder().withName(authType.getName()).build()) - .endSpec() - - .withNewStatus() - .withReady(true) - .addToEndpointStatuses(new EndpointStatusBuilder() - .withName("console") - .withServiceHost("console.svc") - .withExternalPorts(Collections.singletonMap("http", 443)) - .withExternalHost("console.example.com") - .build()) - .endStatus() - - .build(); - } -} diff --git a/address-space-controller/src/test/java/io/enmasse/controller/router/config/RouterConfigTest.java b/address-space-controller/src/test/java/io/enmasse/controller/router/config/RouterConfigTest.java deleted file mode 100644 index e4c1193c036..00000000000 --- a/address-space-controller/src/test/java/io/enmasse/controller/router/config/RouterConfigTest.java +++ /dev/null @@ -1,47 +0,0 @@ -/* - * Copyright 2019, EnMasse authors. - * License: Apache License 2.0 (see the file LICENSE or http://apache.org/licenses/LICENSE-2.0.html). - */ -package io.enmasse.controller.router.config; - -import org.junit.jupiter.api.Test; - -import java.io.IOException; -import java.nio.charset.StandardCharsets; -import java.util.Collections; - -import static org.junit.jupiter.api.Assertions.assertEquals; - -public class RouterConfigTest { - @Test - public void testSerialize() throws IOException { - RouterConfig config = new RouterConfig( - new Router(), - Collections.singletonList(new SslProfile()), - Collections.singletonList(new AuthServicePlugin()), - Collections.singletonList(new Listener()), - Collections.singletonList(new Policy()), - Collections.singletonList(new Connector()), - Collections.singletonList(new AutoLink()), - Collections.singletonList(new LinkRoute()), - Collections.singletonList(new Address()), - Collections.singletonList(new VhostPolicy())); - - - byte[] serialized = config.asJson(); - System.out.println(new String(serialized, StandardCharsets.UTF_8)); - RouterConfig deser = RouterConfig.fromJson(serialized); - - assertEquals(deser.getRouter(), config.getRouter()); - assertEquals(deser.getAddresses(), config.getAddresses()); - assertEquals(deser.getAuthServicePlugins(), config.getAuthServicePlugins()); - assertEquals(deser.getConnectors(), config.getConnectors()); - assertEquals(deser.getAutoLinks(), config.getAutoLinks()); - assertEquals(deser.getLinkRoutes(), config.getLinkRoutes()); - assertEquals(deser.getListeners(), config.getListeners()); - assertEquals(deser.getPolicies(), config.getPolicies()); - assertEquals(deser.getVhosts(), config.getVhosts()); - - assertEquals(deser, config); - } -} diff --git a/address-space-controller/src/test/resources/flavors.json b/address-space-controller/src/test/resources/flavors.json deleted file mode 100644 index f767f2c058f..00000000000 --- a/address-space-controller/src/test/resources/flavors.json +++ /dev/null @@ -1,15 +0,0 @@ -[ - { - "name": "test-queue", - "templateName": "queue-persisted", - "templateParameters": { - "STORAGE_CAPACITY": "2Gi" - } - }, - { - "name": "descriptive-queue", - "templateName": "queue-inmemory", - "type": "queue", - "description": "This is a simple queue" - } -] diff --git a/address-space-controller/src/test/resources/logback-test.xml b/address-space-controller/src/test/resources/logback-test.xml deleted file mode 100644 index db3cde4dacb..00000000000 --- a/address-space-controller/src/test/resources/logback-test.xml +++ /dev/null @@ -1,17 +0,0 @@ - - - - - - - %d{yyyy-MM-dd'T'HH:mm:ss.SSS'Z',GMT} %-5p [%c{0}] %m%n - - - - - - - - - - \ No newline at end of file diff --git a/address-space-controller/src/test/resources/templates/standard-space-infra.yaml b/address-space-controller/src/test/resources/templates/standard-space-infra.yaml deleted file mode 100644 index 9ac3d2da07c..00000000000 --- a/address-space-controller/src/test/resources/templates/standard-space-infra.yaml +++ /dev/null @@ -1,46 +0,0 @@ -apiVersion: v1 -kind: Template -metadata: - name: standard-space-infra -objects: - - kind: "ConfigMap" - apiVersion: "v1" - metadata: - name: "mymap" - data: - key: "${BROKER_ADDRESS_FULL_POLICY}" - - kind: "Deployment" - apiVersion: "apps/v1" - metadata: - name: "admin.1234" - spec: - replicas: 1 - template: - metadata: - labels: - component: "admin" - spec: - containers: - - name: admin - - - kind: "StatefulSet" - apiVersion: "apps/v1" - metadata: - name: "qdrouterd-1234" - spec: - replicas: 1 - template: - metadata: - labels: - component: "router" - spec: - containers: - - name: router -parameters: - - name: "BROKER_ADDRESS_FULL_POLICY" - description: "desc" - - name: "SECONDPARAM" - description: "desc" - - - diff --git a/address-space-controller/src/test/resources/templates/test.json b/address-space-controller/src/test/resources/templates/test.json deleted file mode 100644 index 7a3b6b3b10a..00000000000 --- a/address-space-controller/src/test/resources/templates/test.json +++ /dev/null @@ -1,29 +0,0 @@ -{ - "apiVersion": "v1", - "kind": "Template", - "metadata": { - "name": "test" - }, - "objects": [ - { - "kind": "ConfigMap", - "apiVersion": "v1", - "metadata": { - "name": "mymap" - }, - "data": { - "key": "${MYPARAM}" - } - } - ], - "parameters": [ - { - "name": "MYPARAM", - "description": "desc" - }, - { - "name": "SECONDPARAM", - "description": "desc" - } - ] -} diff --git a/agent/.dockerignore b/agent/.dockerignore deleted file mode 100644 index e4e5f6c8b2d..00000000000 --- a/agent/.dockerignore +++ /dev/null @@ -1 +0,0 @@ -*~ \ No newline at end of file diff --git a/agent/.gitignore b/agent/.gitignore deleted file mode 100644 index 68f403db583..00000000000 --- a/agent/.gitignore +++ /dev/null @@ -1,11 +0,0 @@ -*~ -.idea -*.iml - -#Autogenerated build files -node/ -node_modules/ -package-lock.json -www/favicon.ico -www/help.html -www/tooltips.json diff --git a/agent/Dockerfile b/agent/Dockerfile deleted file mode 100644 index e583d97e0e5..00000000000 --- a/agent/Dockerfile +++ /dev/null @@ -1,15 +0,0 @@ -FROM quay.io/enmasse/nodejs-base:10-1 - -RUN mkdir -p /opt/app-root/src/ -WORKDIR /opt/app-root/src/ - -ARG version -ARG maven_version -ARG revision -ENV VERSION=${version} REVISION=${revision} MAVEN_VERSION=${maven_version} - -ADD target/agent-${maven_version}-dist.tar.gz /opt/app-root/src/ - -EXPOSE 56720 8080 - -CMD ["/opt/app-root/src/bin/launch_node.sh", "/opt/app-root/src/bin/agent.js"] diff --git a/agent/LICENSE b/agent/LICENSE deleted file mode 100644 index 8dada3edaf5..00000000000 --- a/agent/LICENSE +++ /dev/null @@ -1,201 +0,0 @@ - Apache License - Version 2.0, January 2004 - http://www.apache.org/licenses/ - - TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION - - 1. Definitions. - - "License" shall mean the terms and conditions for use, reproduction, - and distribution as defined by Sections 1 through 9 of this document. - - "Licensor" shall mean the copyright owner or entity authorized by - the copyright owner that is granting the License. - - "Legal Entity" shall mean the union of the acting entity and all - other entities that control, are controlled by, or are under common - control with that entity. For the purposes of this definition, - "control" means (i) the power, direct or indirect, to cause the - direction or management of such entity, whether by contract or - otherwise, or (ii) ownership of fifty percent (50%) or more of the - outstanding shares, or (iii) beneficial ownership of such entity. - - "You" (or "Your") shall mean an individual or Legal Entity - exercising permissions granted by this License. - - "Source" form shall mean the preferred form for making modifications, - including but not limited to software source code, documentation - source, and configuration files. - - "Object" form shall mean any form resulting from mechanical - transformation or translation of a Source form, including but - not limited to compiled object code, generated documentation, - and conversions to other media types. - - "Work" shall mean the work of authorship, whether in Source or - Object form, made available under the License, as indicated by a - copyright notice that is included in or attached to the work - (an example is provided in the Appendix below). - - "Derivative Works" shall mean any work, whether in Source or Object - form, that is based on (or derived from) the Work and for which the - editorial revisions, annotations, elaborations, or other modifications - represent, as a whole, an original work of authorship. For the purposes - of this License, Derivative Works shall not include works that remain - separable from, or merely link (or bind by name) to the interfaces of, - the Work and Derivative Works thereof. - - "Contribution" shall mean any work of authorship, including - the original version of the Work and any modifications or additions - to that Work or Derivative Works thereof, that is intentionally - submitted to Licensor for inclusion in the Work by the copyright owner - or by an individual or Legal Entity authorized to submit on behalf of - the copyright owner. For the purposes of this definition, "submitted" - means any form of electronic, verbal, or written communication sent - to the Licensor or its representatives, including but not limited to - communication on electronic mailing lists, source code control systems, - and issue tracking systems that are managed by, or on behalf of, the - Licensor for the purpose of discussing and improving the Work, but - excluding communication that is conspicuously marked or otherwise - designated in writing by the copyright owner as "Not a Contribution." - - "Contributor" shall mean Licensor and any individual or Legal Entity - on behalf of whom a Contribution has been received by Licensor and - subsequently incorporated within the Work. - - 2. Grant of Copyright License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - copyright license to reproduce, prepare Derivative Works of, - publicly display, publicly perform, sublicense, and distribute the - Work and such Derivative Works in Source or Object form. - - 3. Grant of Patent License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - (except as stated in this section) patent license to make, have made, - use, offer to sell, sell, import, and otherwise transfer the Work, - where such license applies only to those patent claims licensable - by such Contributor that are necessarily infringed by their - Contribution(s) alone or by combination of their Contribution(s) - with the Work to which such Contribution(s) was submitted. If You - institute patent litigation against any entity (including a - cross-claim or counterclaim in a lawsuit) alleging that the Work - or a Contribution incorporated within the Work constitutes direct - or contributory patent infringement, then any patent licenses - granted to You under this License for that Work shall terminate - as of the date such litigation is filed. - - 4. Redistribution. You may reproduce and distribute copies of the - Work or Derivative Works thereof in any medium, with or without - modifications, and in Source or Object form, provided that You - meet the following conditions: - - (a) You must give any other recipients of the Work or - Derivative Works a copy of this License; and - - (b) You must cause any modified files to carry prominent notices - stating that You changed the files; and - - (c) You must retain, in the Source form of any Derivative Works - that You distribute, all copyright, patent, trademark, and - attribution notices from the Source form of the Work, - excluding those notices that do not pertain to any part of - the Derivative Works; and - - (d) If the Work includes a "NOTICE" text file as part of its - distribution, then any Derivative Works that You distribute must - include a readable copy of the attribution notices contained - within such NOTICE file, excluding those notices that do not - pertain to any part of the Derivative Works, in at least one - of the following places: within a NOTICE text file distributed - as part of the Derivative Works; within the Source form or - documentation, if provided along with the Derivative Works; or, - within a display generated by the Derivative Works, if and - wherever such third-party notices normally appear. The contents - of the NOTICE file are for informational purposes only and - do not modify the License. You may add Your own attribution - notices within Derivative Works that You distribute, alongside - or as an addendum to the NOTICE text from the Work, provided - that such additional attribution notices cannot be construed - as modifying the License. - - You may add Your own copyright statement to Your modifications and - may provide additional or different license terms and conditions - for use, reproduction, or distribution of Your modifications, or - for any such Derivative Works as a whole, provided Your use, - reproduction, and distribution of the Work otherwise complies with - the conditions stated in this License. - - 5. Submission of Contributions. Unless You explicitly state otherwise, - any Contribution intentionally submitted for inclusion in the Work - by You to the Licensor shall be under the terms and conditions of - this License, without any additional terms or conditions. - Notwithstanding the above, nothing herein shall supersede or modify - the terms of any separate license agreement you may have executed - with Licensor regarding such Contributions. - - 6. Trademarks. This License does not grant permission to use the trade - names, trademarks, service marks, or product names of the Licensor, - except as required for reasonable and customary use in describing the - origin of the Work and reproducing the content of the NOTICE file. - - 7. Disclaimer of Warranty. Unless required by applicable law or - agreed to in writing, Licensor provides the Work (and each - Contributor provides its Contributions) on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or - implied, including, without limitation, any warranties or conditions - of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A - PARTICULAR PURPOSE. You are solely responsible for determining the - appropriateness of using or redistributing the Work and assume any - risks associated with Your exercise of permissions under this License. - - 8. Limitation of Liability. In no event and under no legal theory, - whether in tort (including negligence), contract, or otherwise, - unless required by applicable law (such as deliberate and grossly - negligent acts) or agreed to in writing, shall any Contributor be - liable to You for damages, including any direct, indirect, special, - incidental, or consequential damages of any character arising as a - result of this License or out of the use or inability to use the - Work (including but not limited to damages for loss of goodwill, - work stoppage, computer failure or malfunction, or any and all - other commercial damages or losses), even if such Contributor - has been advised of the possibility of such damages. - - 9. Accepting Warranty or Additional Liability. While redistributing - the Work or Derivative Works thereof, You may choose to offer, - and charge a fee for, acceptance of support, warranty, indemnity, - or other liability obligations and/or rights consistent with this - License. However, in accepting such obligations, You may act only - on Your own behalf and on Your sole responsibility, not on behalf - of any other Contributor, and only if You agree to indemnify, - defend, and hold each Contributor harmless for any liability - incurred by, or claims asserted against, such Contributor by reason - of your accepting any such warranty or additional liability. - - END OF TERMS AND CONDITIONS - - APPENDIX: How to apply the Apache License to your work. - - To apply the Apache License to your work, attach the following - boilerplate notice, with the fields enclosed by brackets "{}" - replaced with your own identifying information. (Don't include - the brackets!) The text should be enclosed in the appropriate - comment syntax for the file format. We also recommend that a - file or class name and description of purpose be included on the - same "printed page" as the copyright notice for easier - identification within third-party archives. - - Copyright {yyyy} {name of copyright owner} - - 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. diff --git a/agent/Makefile b/agent/Makefile deleted file mode 100644 index 27e8bca799a..00000000000 --- a/agent/Makefile +++ /dev/null @@ -1 +0,0 @@ -include ../Makefile.java.mk diff --git a/agent/README.md b/agent/README.md deleted file mode 100644 index 2cbd12bceed..00000000000 --- a/agent/README.md +++ /dev/null @@ -1,26 +0,0 @@ -# EnMasse agent - -Generic EnMasse agent running within each address space. - -## brokerd address space controller - -Controller for managing addresses in a brokered address space. - -## console-server [WIP] - -A patternfly based GUI for monitoring EnMasse instances. The console -can be accessed on port 8080. - -For development purposes, it can be run outside of the local openshift -cluster, connecting to the messaging service and the config and -podsense daemons within the admin service by setting the -MESSAGING_SERVICE_HOST, ADDRESS_CONTROLLER_SERVICE_HOST and -ADMIN_SERVICE_HOST appropriately (e.g. with $(oc get service | awk -'/messaging/{print $2}'), $(oc get service | awk -'/address-space-controller/{print $2}') and $(oc get service | awk -'/admin/{print $2}') respectively). - -## coming soon... - -ragent and subserv will be moved into this repo for simpler sharing of -common code. The standard address space controller logic will also be moved here. diff --git a/agent/bin/agent.js b/agent/bin/agent.js deleted file mode 100644 index a6054562c14..00000000000 --- a/agent/bin/agent.js +++ /dev/null @@ -1,106 +0,0 @@ -/* - * Copyright 2016 Red Hat Inc. - * - * 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. - */ -'use strict'; - -var v8 = require('v8'); -var log = require("../lib/log.js").logger(); -var AddressSource = require('../lib/internal_address_source.js'); -var AddressSpacePlanSource = require('../lib/internal_addressspaceplan_source.js'); -var AddressPlansSource = require('../lib/internal_addressplan_source.js'); -var ConsoleServer = require('../lib/console_server.js'); -var kubernetes = require('../lib/kubernetes.js'); -var Ragent = require('../lib/ragent.js'); -var tls_options = require('../lib/tls_options.js'); - -function bind_event(source, event, target, method) { - source.on(event, target[method || event].bind(target)); -} - -function start(env) { - kubernetes.is_openshift().then((openshift) => { - kubernetes.get_messaging_route_hostname(env).then(function (result) { - if (result !== undefined) env.MESSAGING_ROUTE_HOSTNAME = result; - env.ADDRESS_SPACE_PREFIX = env.ADDRESS_SPACE + "."; - var address_space_plan_source = new AddressSpacePlanSource(env); - var address_plans_source = new AddressPlansSource(env); - var address_source = new AddressSource(env); - - var console_server = new ConsoleServer(env, openshift); - bind_event(address_source, 'addresses_defined', console_server.addresses); - - var server_promise = console_server.listen(env); - server_promise.then(() => { - if (env.ADDRESS_SPACE_TYPE === 'brokered') { - bind_event(address_source, 'addresses_defined', console_server.metrics); - console_server.listen_health(env); - var event_logger = env.ENABLE_EVENT_LOGGER === 'true' ? kubernetes.post_event : undefined; - var bc = require('../lib/broker_controller.js').create_agent(event_logger); - bind_event(bc, 'address_stats_retrieved', console_server.addresses, 'update_existing'); - bind_event(bc, 'connection_stats_retrieved', console_server.connections, 'set'); - bind_event(bc, 'address_stats_retrieved', address_source, 'check_status'); - bind_event(address_source, 'addresses_defined', bc); - bind_event(address_plans_source, 'addressplans_defined', address_source, 'check_address_plans'); - bind_event(address_space_plan_source, 'addressspaceplan_defined', address_source, 'check_address_plans'); - bc.connect(tls_options.get_client_options({ - host: env.BROKER_SERVICE_HOST, port: env.BROKER_SERVICE_PORT, username: 'console', - idle_time_out: 'AMQP_IDLE_TIMEOUT' in process.env ? process.env.AMQP_IDLE_TIMEOUT : 300000 - })); - - address_space_plan_source.start(); - address_plans_source.start(address_space_plan_source); - } else { - //assume standard address space for now - var StandardStats = require('../lib/standard_stats.js'); - var stats = new StandardStats(); - stats.init(console_server); - - var ragent = new Ragent(); - ragent.disable_connectivity = true; - bind_event(address_source, 'addresses_ready', ragent, 'sync_addresses'); - ragent.start_listening(env); - ragent.listen_health({HEALTH_PORT:8888}); - } - - address_source.start(address_plans_source); - - process.on('SIGTERM', function () { - log.info('Shutdown started'); - var exitHandler = function () { - process.exit(0); - }; - var timeout = setTimeout(exitHandler, 2000); - - console_server.close(function() { - log.info("Console server closed"); - clearTimeout(timeout); - exitHandler(); - }); - }); - - setInterval(() => { - log.info("Heap statistics : %j", v8.getHeapStatistics()); - }, 60000); - }).catch((e) => {log.error("Failed to listen ", e)}) - - }); - }).catch((e) => {log.error("Failed to check for openshift", e)}); -} - -if (require.main === module) { - start(process.env); -} else { - module.exports.start = start; -} diff --git a/agent/bin/launch_node.sh b/agent/bin/launch_node.sh deleted file mode 100755 index ad257fdd2dc..00000000000 --- a/agent/bin/launch_node.sh +++ /dev/null @@ -1,34 +0,0 @@ -#!/usr/bin/env bash - -OLD_SPACE_SIZE_PERCENT=${OLD_SPACE_SIZE_PERCENT:-80} -CGROUP_FILE='/sys/fs/cgroup/memory/memory.limit_in_bytes' - -# Ensure that we have a suitable node interpreter. This ought to be declared declaratively in the package.json, within an engines section, -# but we currently don't install the package so this wouldn't be enforced. -REQUIRED_NODE_MAJOR=6 -NODE_MAJOR=$(node -p 'process.version.match("^v?(\\d+)\.")[1]') -if [[ ${NODE_MAJOR} -lt ${REQUIRED_NODE_MAJOR} ]]; then - 2>&1 echo "Node major version ${NODE_MAJOR} [$(node --version)] too low - require ${REQUIRED_NODE_MAJOR}" - exit 1 -fi - -if [[ ! -z "${_NODE_OPTIONS}" ]]; then - set -- ${_NODE_OPTIONS} "${@}" -fi - -# Set max_old_space_size w.r.t the container's memory, unless caller has supplied --max_old_space_size -for arg; do - if [[ "${arg}" =~ ^--max_old_space_size.* ]]; then - OLD_SPACE_SIZE_PERCENT=0 - break - fi -done - -if [[ -f "${CGROUP_FILE}" && "${OLD_SPACE_SIZE_PERCENT}" -gt 0 ]]; then - CONTAINTER_BYTES=$(cat ${CGROUP_FILE}) - MAX_OLD_SPACE_SIZE=$(( ${CONTAINTER_BYTES} / 100 * ${OLD_SPACE_SIZE_PERCENT} )) - MAX_OLD_SPACE_SIZE_MI=$(( ${MAX_OLD_SPACE_SIZE} / ( 1024 * 1024 ) )) - set -- "--max_old_space_size=${MAX_OLD_SPACE_SIZE_MI}" "${@}" -fi - -exec node "${@}" diff --git a/agent/bin/ragent.js b/agent/bin/ragent.js deleted file mode 100644 index 71d6e5c2dad..00000000000 --- a/agent/bin/ragent.js +++ /dev/null @@ -1,23 +0,0 @@ -/* - * Copyright 2018 Red Hat Inc. - * - * 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. - */ -'use strict'; - -var log = require('../lib/log.js').logger(); -var Ragent = require('../lib/ragent.js'); - -var ragent = new Ragent(); -ragent.start_listening(process.env); -ragent.listen_health(process.env); diff --git a/agent/bin/subserv.js b/agent/bin/subserv.js deleted file mode 100644 index 7e0843c940d..00000000000 --- a/agent/bin/subserv.js +++ /dev/null @@ -1,230 +0,0 @@ -/* - * Copyright 2016 Red Hat Inc. - * - * 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. - */ -'use strict'; - -var log = require('../lib/log.js').logger(); -var util = require('util'); -var amqp = require('rhea').create_container(); -var create_topic = require('../lib/topic.js'); -var topic_tracker = require('../lib/topic_tracker.js'); -var tls_options = require('../lib/tls_options.js'); - -var topics = {}; - -var SUBCTRL = '$subctrl'; - -function get_topic(message) { - if (message.application_properties && message.application_properties.root_address) { - var root = message.application_properties.root_address; - var topic = topics[root]; - if (topic !== undefined) { - return topic; - } else { - throw Error('subscription control message specified unrecognised root_address: ' + root + ' [' + Object.getOwnPropertyNames(topics) + ']'); - } - } else { - throw Error('subscription control message must specify root_address in application properties'); - } -} - -function combine(objects) { - var r = {}; - objects.forEach(function (o) { - Object.keys(o).forEach( function(key) { r[key] = o[key]; } ); - }); - return r; -} - -function list(subscription_id, key) { - return topics[key].controller.list(subscription_id); -} - -function close(subscription_id, key) { - return topics[key].controller.close(subscription_id); -} - -function subscribe(subscription_id, request) { - return request.topic.controller.subscribe(subscription_id, request.addresses); -} - -function unsubscribe(subscription_id, request) { - return request.topic.controller.unsubscribe(subscription_id, request.addresses); -} - -function request_string(message) { - var params = message.body ? [message.correlation_id, message.body] : [message.correlation_id]; - return message.subject + '(' + params.join() + ')'; -} - -function get_separator(address) { - return address.indexOf('/') >= 0 ? '/' : '.'; -} - -function get_root(address) { - if (topics[address]) { - return address; - } else { - var separator = get_separator(address); - var root = address; - for (var end = root.lastIndexOf(separator); end > 0; end = root.lastIndexOf(separator)) { - root = root.substr(0, end); - if (topics[root]) return root; - } - } - throw Error('Unrecognised topic: ' + address); -} - -function subreqs(input) { - var grouped = {}; - if (util.isArray(input)) { - input.forEach(function (address) { - var root = get_root(address); - if (grouped[root] === undefined) { - grouped[root] = {}; - } - grouped[root][address] = undefined; - }); - } else if ((typeof input) === 'string') { - var addresses = {}; - addresses[input] = undefined; - grouped[get_root(input)] = addresses; - } else { - //assume map - for (var address in input) { - var root = get_root(address); - if (grouped[root] === undefined) { - grouped[root] = {}; - } - grouped[root][address] = input[address]; - } - } - log.debug('in subreqs(' + input + '): type=' + (typeof input) + ', grouped=' + JSON.stringify(grouped)); - return Object.keys(grouped).map( - function(key) { - return { - topic: topics[key], - addresses: grouped[key] - }; - } - ); -} - -function handle_control_message(context) { - log.debug('received message: ' + context.message); - if (context.message.to === SUBCTRL || (context.receiver.target && context.receiver.target.address === SUBCTRL)) { - var subscription_id = context.message.correlation_id; - var accept = function () { - log.info(request_string(context.message) + ' succeeded'); - context.delivery.accept(); - }; - var reject = function (e, code) { - log.info(request_string(context.message) + ' failed: ' + e); - context.delivery.reject({condition: code || 'amqp:internal-error', description: '' + e}); - }; - var reply = function (type, value) { - if (sender) { - sender.send({to:context.message.reply_to, subject:type, correlation_id:subscription_id, body:value}); - } - accept(); - }; - - log.info(request_string(context.message)); - try { - if (context.message.subject === 'list') { - Promise.all(Object.keys(topics).map(list.bind(null, subscription_id))).then( - function (results) { - reply('subscriptions', combine(results)); - } - ).catch(reject); - } else if (context.message.subject === 'close') { - Promise.all(Object.keys(topics).map(close.bind(null, subscription_id))).then(accept).catch(reject); - } else if (context.message.subject === 'subscribe') { - Promise.all(subreqs(context.message.body).map(subscribe.bind(null, subscription_id))).then(accept).catch(reject); - } else if (context.message.subject === 'unsubscribe') { - Promise.all(subreqs(context.message.body).map(unsubscribe.bind(null, subscription_id))).then(accept).catch(reject); - } else if (context.message.subject === 'list_topics') { - reply('topics', Object.keys(topics)); - } else { - reject('unrecognised subject ' + context.message.subject, 'amqp:not-implemented'); - } - } catch (e) { - reject(e, 'amqp:precondition-failed'); - } - } -} - -amqp.on('message', handle_control_message); - -amqp.on('connection_open', function (context) { - log.debug('connected ' + context.connection.container_id + ' [' + context.connection.options.id + ']'); -}); -amqp.on('disconnected', function (context) { - log.debug('disconnected ' + context.connection.container_id + ' [' + context.connection.options.id + ']'); -}); -amqp.on('error', function (e) { - log.error(JSON.stringify(e)); -}); - -var connection_properties = {product:'subserv', container_id:process.env.HOSTNAME}; - -log.info("Starting subserv"); - -var options; -var server; -try { - options = tls_options.get_server_options({port:5672, properties:connection_properties}); -} catch (error) { - options = {port:5672, properties:connection_properties}; - log.warn('Error setting TLS options ' + error + ' using ' + options); -} -amqp.sasl_server_mechanisms.enable_anonymous(); -server = amqp.listen(options); -server.on('listening', function(server) { - log.info("Subserv listening on %d", options.port); -}); - -var sender; -if (process.env.MESSAGING_SERVICE_HOST) { - var client_options = {host:process.env.MESSAGING_SERVICE_HOST, port:process.env.MESSAGING_SERVICE_PORT_AMQPS_NORMAL, properties:connection_properties, id:'messaging-service'}; - try { - client_options = tls_options.get_client_options(client_options); - } catch (error) { - log.warn('Error setting TLS options for client ' + error + ' using ' + options); - } - var conn = amqp.connect(client_options); - conn.open_receiver({autoaccept: false, source:SUBCTRL, target:SUBCTRL}); - sender = conn.open_sender({target:{}}); - conn.on('sender_open', function (context) { - log.debug('opened anonymous sender'); - }); -} - -var pod_watcher = require('../lib/pod_watcher.js').watch('role=broker,addresstype=topic'); -pod_watcher.on('updated', topic_tracker(topics, create_topic)); - -process.on('SIGTERM', function () { - log.info('Subserv shutdown started'); - var exitHandler = function () { - process.exit(0); - }; - var timeout = setTimeout(exitHandler, 5000); - - server.close(() => { - log.info('Subserv shutdown completed'); - clearTimeout(timeout); - exitHandler(); - }); -}); diff --git a/agent/lib/address_ctrl.js b/agent/lib/address_ctrl.js deleted file mode 100644 index 7fe3cc3fa0a..00000000000 --- a/agent/lib/address_ctrl.js +++ /dev/null @@ -1,159 +0,0 @@ -/* - * Copyright 2016 Red Hat Inc. - * - * 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. - */ -'use strict'; - -var fs = require("fs"); - -var AddressCtrl = function (host, port, ca, auth_string, rejectUnauthorized) { - this.host = host; - this.port = port; - this.address_space = process.env.ADDRESS_SPACE - this.http = ca ? require('https') : require('http'); - this.ca = ca; - this.auth_string = auth_string; - - this.addr_path = "/apis/enmasse.io/v1/addresses/"; - if (this.address_space) { - this.addr_path += this.address_space + "/"; - } - this.rejectUnauthorized = rejectUnauthorized; -} - -AddressCtrl.prototype.request = function (addr_path, method, headers, body, handler) { - var self = this; - return new Promise(function (resolve, reject) { - var options = { - hostname: self.host, - port: self.port - }; - options.path = addr_path; - options.method = method || 'GET'; - options.headers = headers || {}; - if (self.rejectUnauthorized !== undefined) options.rejectUnauthorized = self.rejectUnauthorized; - - if (self.auth_string) { - options.headers['Authorization'] = self.auth_string - } - - if (self.ca) { - options.ca = self.ca; - } - - var req = self.http.request(options, function(res) { - if (res.statusCode === 200) { - if (handler) { - var text = ''; - res.setEncoding('utf8'); - res.on('data', function (chunk) { - text += chunk; - }); - res.on('end', function () { - try { - resolve(handler(text)); - } catch (e) { - reject(e); - } - }); - } else { - resolve(); - } - } else { - var text = 'Error: ' + res.statusCode + ' for ' + options.method + ' on ' + options.path; - console.error(text); - text += ' '; - res.setEncoding('utf8'); - res.on('data', function (chunk) { - text += chunk; - }); - res.on('end', function () { - reject(new Error(text)); - }); - } - }); - if (body) { - req.write(body); - } - req.on('error', function (error) { reject(error); }); - req.end(); - }); -} - -AddressCtrl.prototype.create_address = function (address) { - var data = { - "apiVersion": "enmasse.io/v1", - "kind": "AddressList", - "items" : [ - { - "metadata": { - "name": address.address - }, - "spec": { - "address": address.address, - "type": address.type, - "plan": address.plan - } - } - ] - }; - return this.request(this.addr_path, 'POST', {'content-type': 'application/json'}, JSON.stringify(data)); -} - -AddressCtrl.prototype.delete_address = function(address) { - return this.request(this.addr_path + encodeURIComponent(address.address), 'DELETE'); -} - -function index(list) { - return list.reduce(function (map, item) { map[item.name] = item; return map; }, {}); -} - -function address_types(text) { - var object = JSON.parse(text); - if (object.kind === 'Schema') { - var address_space_type = process.env.ADDRESS_SPACE_TYPE || 'standard'; - return index(object.spec.addressSpaceTypes)[address_space_type].addressTypes; - } else { - throw new Error('Unexpected object kind: ' + object.kind); - } -} - -AddressCtrl.prototype.get_address_types = function () { - return this.request('/apis/enmasse.io/v1/schema/', 'GET', undefined, undefined, address_types); -} - -module.exports.create = function (env) { - var host = 'localhost'; - var port; - var rejectUnauthorized; - if (env.ADDRESS_SPACE_SERVICE_HOST) { - host = env.ADDRESS_SPACE_SERVICE_HOST; - } else if (env.ADDRESS_CONTROLLER_SERVICE_HOST) { - host = env.ADDRESS_CONTROLLER_SERVICE_HOST; - rejectUnauthorized = false; - } - if (env.ADDRESS_CONTROLLER_SERVICE_HOST && env.ADDRESS_CONTROLLER_SERVICE_PORT_HTTPS) { - port = env.ADDRESS_CONTROLLER_SERVICE_PORT_HTTPS; - } - var ca = undefined; - if (env.ADDRESS_CONTROLLER_CA) { - ca = fs.readFileSync(env.ADDRESS_CONTROLLER_CA); - if (port === undefined) port = 443; - } - if (port === undefined) port = 8080; - - var auth_string = 'Bearer ' + (env.KUBERNETES_TOKEN || fs.readFileSync('/var/run/secrets/kubernetes.io/serviceaccount/token')); - - return new AddressCtrl(host, port, ca, auth_string, rejectUnauthorized); -}; diff --git a/agent/lib/address_list.js b/agent/lib/address_list.js deleted file mode 100644 index 3684028acb1..00000000000 --- a/agent/lib/address_list.js +++ /dev/null @@ -1,35 +0,0 @@ -/* - * Copyright 2016 Red Hat Inc. - * - * 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. - */ -'use strict'; - -var util = require('util'); -var Registry =require('./registry.js'); - -function Addresses() { - Registry.call(this); -} - -util.inherits(Addresses, Registry); - -Addresses.prototype.update_stats = function (name, stats) { - this.update_if_exists(name, stats); -}; - -Addresses.prototype.addresses_defined = function (addresses) { - this.set(addresses.reduce(function (map, a) { map[a.address] = a; return map; }, {})); -}; - -module.exports = Addresses; diff --git a/agent/lib/admin_service.js b/agent/lib/admin_service.js deleted file mode 100644 index f564eaed3d9..00000000000 --- a/agent/lib/admin_service.js +++ /dev/null @@ -1,65 +0,0 @@ -/* - * Copyright 2016 Red Hat Inc. - * - * 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. - */ -'use strict'; - -var log = require("./log.js").logger(); -var path = require('path'); -var tls_options = require('./tls_options.js'); - -function hostport(service_name, defaults) { - var result = defaults || {host: 'localhost'}; - if (process.env.ADMIN_SERVICE_HOST) result.host = process.env.ADMIN_SERVICE_HOST; - if (process.env['ADMIN_SERVICE_PORT_' + service_name]) result.port = process.env['ADMIN_SERVICE_PORT_' + service_name]; - if (process.env[service_name + '_SERVICE_HOST']) { - result.host = process.env[service_name + '_SERVICE_HOST']; - if (process.env[service_name + '_SERVICE_PORT']) { - result.port = process.env[service_name + '_SERVICE_PORT']; - } - } - return result; -}; - -var counter = 1; - -function connect_service(container, service_name, defaults) { - var options = hostport(service_name, defaults); - return connect(container, options, service_name); -}; - -function connect(container, options, service_name) { - try { - options = tls_options.get_client_options(options); - } catch(error) { - // likely certs not present, will attempt to use non-TLS connection instead - } - if (options.host) { - var self = path.basename(process.argv[1], '.js'); - if (options.properties === undefined) options.properties = {}; - if (options.properties.product === undefined) options.properties.product = self; - if (options.container_id === undefined) options.container_id = process.env.HOSTNAME; - if (options.username === undefined) options.username = self; - if (options.id === undefined) options.id = self + '-' + counter++; - log.info('Connecting to ' + service_name + ' service with host: ' + options.host + ', port: ' + options.port + ', container: ' + options.container_id + ', id: ' + options.id); - return container.connect(options); - } else { - return undefined; - } -}; - -module.exports.hostport = hostport; -module.exports.connect_service = connect_service; -module.exports.connect = connect; - diff --git a/agent/lib/artemis.js b/agent/lib/artemis.js deleted file mode 100644 index 2f961a03f0e..00000000000 --- a/agent/lib/artemis.js +++ /dev/null @@ -1,741 +0,0 @@ -/* - * Copyright 2016 Red Hat Inc. - * - * 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. - */ -'use strict'; - -var util = require('util'); -var log = require('./log.js').logger(); -var myutils = require('./utils.js'); -var amqp = require('rhea').create_container(); - -var Artemis = function (connection) { - this.connection = connection; - this.sender = connection.open_sender('activemq.management'); - this.sender.on('sender_open', this.sender_open.bind(this)); - this.sender.on('sendable', this._send_pending_requests.bind(this)); - connection.on('receiver_open', this.receiver_ready.bind(this)); - connection.on('message', this.incoming.bind(this)); - connection.on('receiver_error', this.on_receiver_error.bind(this)); - connection.on('sender_error', this.on_sender_error.bind(this)); - var self = this; - connection.on('connection_open', function (context) { - let previous = self.id; - self.id = context.connection.container_id; - if (previous !== undefined && previous !== self.id) { - log.info('[%s] connection opened (was %s)', self.id, previous); - } else { - log.info('[%s] connection opened', self.id); - } - }); - connection.on('connection_error', this.on_connection_error.bind(this)); - connection.on('connection_close', this.on_connection_close.bind(this)); - connection.on('disconnected', this.disconnected.bind(this)); - connection.on('error', this.on_error.bind(this)); - this.handlers = []; - this.requests = []; - this.pushed = 0; - this.popped = 0; -}; - -Artemis.prototype.log_info = function (context) { - if (this.id) { - log.info('[%s] artemis requests pending: %d, made:%d, completed: %d, ready: %d', this.id, this.handlers.length, this.pushed, this.popped, this.address !== undefined); - } -}; - -Artemis.prototype.sender_open = function () { - var tmp_reply_address = 'activemq.management.tmpreply.' + amqp.generate_uuid(); - log.debug('[%s] sender ready, creating reply to address: %s', this.connection.container_id, tmp_reply_address); - var self = this; - this.sender.once('accepted', () => { - self.connection.open_receiver({source:{address: tmp_reply_address}}); - }); - this._create_temp_response_queue(tmp_reply_address); -} - -Artemis.prototype.receiver_ready = function (context) { - this.address = context.receiver.remote.attach.source.address; - log.info('[%s] ready to send requests, reply to address: %s', this.connection.container_id, this.address); - this._send_pending_requests(); -}; - -Artemis.prototype.as_handler = function(resolve, reject) { - var self = this; - return function (context) { - var message = context.message || context; - if (message.application_properties && message.application_properties._AMQ_OperationSucceeded) { - try { - if (message.body) { - resolve(JSON.parse(message.body)[0]); - } else { - resolve(true); - } - } catch (e) { - log.info('[%s] Error parsing message body: %s : %s', self.connection.container_id, message, e); - reject(message.body) - } - } else { - log.debug('[%s] Response did not signal _AMQ_OperationSucceeded : %j', self.connection.container_id, message); - reject(message.body); - } - }; -} - -Artemis.prototype.incoming = function (context) { - var message = context.message; - log.debug('[%s] recv: %j', this.id || context.connection.container_id, message); - var handler = this.handlers.shift(); - if (handler) { - this.popped++; - handler(context); - } -}; - -Artemis.prototype.disconnected = function (context) { - log.info('[%s] disconnected', this.id || context.connection.container_id); - this.address = undefined; - this.abort_requests('disconnected'); -}; - -Artemis.prototype.abort_requests = function (error) { - while (this.handlers.length > 0) { - var handler = this.handlers.shift(); - if (handler) { - this.popped++; - handler(error); - } - } -} - -Artemis.prototype.on_sender_error = function (context) { - var error = this.connection.container_id + ' sender error ' + JSON.stringify(context.sender.error); - log.info('[' + this.connection.container_id + '] ' + error); - this.abort_requests(error); -}; - -Artemis.prototype.on_receiver_error = function (context) { - var error = this.connection.container_id + ' receiver error ' + JSON.stringify(context.receiver.error); - log.info('[' + this.connection.container_id + '] ' + error); - this.abort_requests(error); -}; - -Artemis.prototype.on_connection_error = function (context) { - var error = this.connection.container_id + ' connection error ' + JSON.stringify(context.connection.error); - log.info('[' + this.connection.container_id + '] connection error: ' + JSON.stringify(context.connection.error)); - this.abort_requests(error); -}; - -Artemis.prototype.on_connection_close = function (context) { - var error = this.connection.container_id + ' connection closed'; - log.info('[' + this.connection.container_id + '] connection closed'); - this.abort_requests(error); -}; - -Artemis.prototype.on_error = function (error) { - log.error('[%s] socket error: %s', this.connection.container_id, JSON.stringify(error)); -}; - -Artemis.prototype._send_pending_requests = function () { - if (this.address === undefined) return false; - - var i = 0; - while (i < this.requests.length && this.sender.sendable()) { - this._send_request(this.requests[i++]); - } - this.requests.splice(0, i); - return this.requests.length === 0 && this.sender.sendable(); -} - -Artemis.prototype._send_request = function (request, withReply = true) { - if (withReply) { - request.application_properties.JMSReplyTo = this.address; - request.reply_to = this.address; - if (process.env.AGENT_CORRELATE_MGMT === 'true') { - // Aids debug, has no functional effect - request.correlation_id = amqp.generate_uuid(); - } - } - this.sender.send(request); - log.debug('[%s] sent: %j', this.id, request); -} - -Artemis.prototype._create_request_message = function(resource, operation, parameters) { - var request = {application_properties: {'_AMQ_ResourceName': resource, '_AMQ_OperationName': operation}}; - request.body = JSON.stringify(parameters); - return request; -} - -Artemis.prototype._request = function (resource, operation, parameters) { - var request = this._create_request_message(resource, operation, parameters); - if (this._send_pending_requests()) { - this._send_request(request); - } else { - this.requests.push(request); - } - var stack = this.handlers; - var self = this; - return new Promise(function (resolve, reject) { - self.pushed++; - stack.push(self.as_handler(resolve, reject)); - }); -} - -// No response requested -Artemis.prototype._create_temp_response_queue = function (name) { - var request = this._create_request_message('broker', 'createQueue', [name/*address*/, 'ANYCAST', name/*queue name*/, null/*filter*/, false/*durable*/, - 1/*max consumers*/, true/*purgeOnNoConsumers*/, true/*autoCreateAddress*/]); - this._send_request(request, false); -} - -Artemis.prototype.createSubscription = function (name, address, maxConsumers) { - return this._request('broker', 'createQueue', [address, 'MULTICAST', name/*queue name*/, null/*filter*/, true/*durable*/, - maxConsumers/*max consumers*/, false/*purgeOnNoConsumers*/, false/*autoCreateAddress*/]); -} - -Artemis.prototype.createQueue = function (name) { - return this._request('broker', 'createQueue', [name/*address*/, 'ANYCAST', name/*queue name*/, null/*filter*/, true/*durable*/, - -1/*max consumers*/, false/*purgeOnNoConsumers*/, true/*autoCreateAddress*/]); -} - -Artemis.prototype.destroyQueue = function (name) { - return this._request('broker', 'destroyQueue', [name, true, true]); -} - -Artemis.prototype.purgeQueue = function (name) { - return this._request('queue.'+name, 'removeMessages', [null]); -} - -Artemis.prototype.getQueueNames = function () { - return this._request('broker', 'getQueueNames', []); -} - -var queue_attributes = { - temporary: 'isTemporary', - durable: 'isDurable', - messages: 'getMessageCount', - consumers: 'getConsumerCount', - enqueued: 'getMessagesAdded', - delivering: 'getDeliveringCount', - acknowledged: 'getMessagesAcknowledged', - expired: 'getMessagesExpired', - killed: 'getMessagesKilled' -}; - -function add_queue_method(name) { - Artemis.prototype[name] = function (queue) { - return this._request('queue.'+queue, name, []); - }; -} - -for (var key in queue_attributes) { - add_queue_method(queue_attributes[key]); -} - -var extra_queue_attributes = { - address: 'getAddress', - routing_type: 'getRoutingType' -} - -for (var key in extra_queue_attributes) { - add_queue_method(extra_queue_attributes[key]); -} - -var queue_attribute_aliases = { - 'isTemporary': 'temporary', - 'isDurable': 'durable', - 'messageCount': 'messages', - 'consumerCount': 'consumers', - 'messagesAdded': 'enqueued', - 'deliveringCount': 'delivering', - 'messagesAcked': 'acknowledged', - 'messagesExpired': 'expired', - 'messagesKilled': 'killed' -}; - -function correct_type(o) { - var i = Number(o); - if (!Number.isNaN(i)) return i; - else if (o === 'true') return true; - else if (o === 'false') return false; - else if (o === null) return undefined; - else return o; -} - -function set_queue_aliases(q) { - for (var f in queue_attribute_aliases) { - var a = queue_attribute_aliases[f]; - if (q[f] !== undefined) { - q[a] = q[f]; - delete q[f]; - } - } -} - -function fix_types(o) { - for (var k in o) { - o[k] = correct_type(o[k]); - } -} - -function process_queue_stats(q) { - fix_types(q); - set_queue_aliases(q); -} - -Artemis.prototype.listQueues = function () { - return this._request('broker', 'listQueues', ['{"field":"","operation":"","value":"","sortOrder":"","sortBy":"","sortColumn":""}', 1, 2147483647/*MAX_INT*/]).then(function (result) { - var queues = JSON.parse(result).data; - queues.forEach(process_queue_stats); - return queues; - }); -}; - -function routing_type_to_type(t) { - if (t === 'MULTICAST') return 'topic'; - if (t === 'ANYCAST') return 'queue'; - return t; -} - -function routing_types_to_type(t) { - if (t.indexOf('MULTICAST') >= 0) { - return 'topic'; - } else if (t.indexOf('ANYCAST') >= 0) { - return 'queue'; - } else { - return undefined; - } -} - -function address_to_queue_or_topic(a) { - return { - name: a.name, - type: routing_types_to_type(a.routingTypes) - }; -} - -Artemis.prototype.getAddresses = function () { - return this._request('broker', 'listAddresses', ['{"field":"","operation":"","value":"","sortOrder":"","sortBy":"","sortColumn":""}', 1, 2147483647/*MAX_INT*/]).then(function (result) { - return JSON.parse(result).data.map(address_to_queue_or_topic); - }); -}; - -Artemis.prototype.getQueueDetails = function (name, attribute_list) { - var attributes = attribute_list || Object.keys(queue_attributes); - var agent = this; - return Promise.all( - attributes.map(function (attribute) { - var method_name = queue_attributes[attribute]; - if (method_name) { - return agent[method_name](name); - } - }) - ).then(function (results) { - var q = {'name':name}; - for (var i = 0; i < results.length; i++) { - q[attributes[i]] = results[i]; - } - return q; - }); -} - -function initialise_topic_stats(a) { - a.enqueued = 0; - a.messages = 0; - a.subscriptions = []; - a.subscription_count = 0; - a.durable_subscription_count = 0; - a.inactive_durable_subscription_count = 0; -} - -function update_topic_stats(a, q) { - a.enqueued += q.enqueued; - a.messages += q.messages; - a.subscriptions.push(q); - a.subscription_count++; - if (q.durable) { - a.durable_subscription_count++; - if (q.consumers === 0) { - a.inactive_durable_subscription_count++; - } - } -} - -function queues_to_addresses(addresses, queues, include_topic_stats, include_reverse_index) { - var index = {}; - var reverse_index = include_reverse_index ? {} : undefined; - for (var i = 0; i < addresses.length; i++) { - var a = addresses[i]; - var b = index[a.name]; - if (b === undefined) { - index[a.name] = a; - if (include_topic_stats && a.type === 'topic') { - initialise_topic_stats(a); - } - } else { - log.warn('Duplicate address: %s (%s)', a.name, a.type); - } - } - for (var i = 0; i < queues.length; i++) { - var q = queues[i]; - if (reverse_index) { - reverse_index[q.name] = q.address; - } - var a = index[q.address]; - if (q.routingType === 'MULTICAST') { - if (a === undefined) { - log.warn('Missing address %s for topic queue %s', q.address, q.name); - a = { - name: q.address, - type: 'topic' - }; - index[q.address] = a; - if (include_topic_stats) { - initialise_topic_stats(a); - } - } else if (a.type !== 'topic') { - log.warn('Unexpected address type: queue %s has type %s, address %s has type %s', q.name, q.routingType, q.address, a.type); - } - if (q.durable && !q.purgeOnNoConsumers) { - //treat as subscription - index[q.name] = myutils.merge(q, { - name: q.name, - type: 'subscription' - }); - } - if (include_topic_stats) { - update_topic_stats(a, q); - } - } else if (q.routingType === 'ANYCAST') { - if (a === undefined) { - a = { - name: q.address, - type: 'queue' - }; - index[q.address] = a; - log.warn('Missing address %s for queue %s', q.address, q.name); - } else if (q.name !== q.address) { - log.warn('Mismatched address %s for queue %s', q.address, q.name); - } else if (a.routingType !== undefined) { - log.warn('Duplicate queue for address %s: %j %j', q.address, a, q); - } - myutils.merge(a, q); - } else { - log.error('Unknown routingType: %s', q.routingType); - } - } - return { - addresses: addresses, - queues: queues, - index: index, - reverse_index: reverse_index - }; -} - -Artemis.prototype._get_address_data = function (include_topic_stats, include_reverse_index) { - return Promise.all([this.getAddresses(), this.listQueues()]).then(function (results) { - return queues_to_addresses(results[0], results[1], include_topic_stats, include_reverse_index); - }); -}; - -Artemis.prototype.listAddresses = function () { - return this._get_address_data().then(function (data) { - return data.index; - }); -}; - -Artemis.prototype.getAllAddressData = function () { - return this._get_address_data(true, true).then(function (data) { - return data; - }); -}; - -Artemis.prototype.getAllQueuesAndTopics = function () { - return this._get_address_data(true, false).then(function (data) { - return data.index; - }); -}; - -Artemis.prototype.getAddressNames = function () { - return this._request('broker', 'getAddressNames', []); -} - -Artemis.prototype.createAddress = function (name, type) { - var routing_types = []; - if (type.anycast || type.queue) routing_types.push('ANYCAST'); - if (type.multicast || type.topic) routing_types.push('MULTICAST'); - return this._request('broker', 'createAddress', [name, routing_types.join(',')]); -}; - -Artemis.prototype.deleteAddress = function (name) { - return this._request('broker', 'deleteAddress', [name]); -}; - -var address_settings_order = [ - 'DLA', - 'expiryAddress', - 'expiryDelay', - 'lastValueQueue', - 'maxDeliveryAttempts', - 'maxSizeBytes', - 'pageSizeBytes', - 'pageCacheMaxSize', - 'redeliveryDelay', - 'redeliveryMultiplier', - - - 'maxRedeliveryDelay', - 'redistributionDelay', - 'sendToDLAOnNoRoute', - 'addressFullMessagePolicy', - 'slowConsumerThreshold', - 'slowConsumerCheckPeriod', - 'slowConsumerPolicy', - - 'autoCreateJmsQueues', - 'autoDeleteJmsQueues', - 'autoCreateJmsTopics', - 'autoDeleteJmsTopics', - 'autoCreateQueues', - 'autoDeleteQueues', - 'autoCreateAddresses', - 'autoDeleteAddresses', - - 'configDeleteQueues', - 'configDeleteAddresses', - - 'maxSizeBytesRejectThreshold', - 'defaultLastValueKey', - 'defaultNonDestructive', - 'defaultExclusiveQueue', - 'defaultGroupRebalance', - 'defaultGroupBuckets', - 'defaultGroupFirstKey', - 'defaultMaxConsumers', - - 'defaultPurgeOnNoConsumers', - 'defaultConsumersBeforeDispatch', - 'defaultDelayBeforeDispatch', - 'defaultQueueRoutingType', - 'defaultAddressRoutingType', - 'defaultConsumerWindowSize', - 'defaultRingSize', - - 'autoDeleteCreatedQueues', - 'autoDeleteQueuesDelay', - 'autoDeleteQueuesMessageCount', - 'autoDeleteAddressesDelay', - - 'redeliveryCollisionAvoidanceFactor', - 'retroactiveMessageCount', - 'autoCreateDeadLetterResources', - 'deadLetterQueuePrefix', - 'deadLetterQueueSuffix', - 'autoCreateExpiryResources', - 'expiryQueuePrefix', - 'expiryQueueSuffix', - 'minExpiryDelay', - 'maxExpiryDelay', - 'enableMetrics']; - -Artemis.prototype.addAddressSettings = function (match, settings) { - var args = [match]; - address_settings_order.forEach((name) => { - var v = settings[name]; - args.push(v); - }); - return this._request('broker', 'addAddressSettings', args); -}; - -Artemis.prototype.removeAddressSettings = function (match) { - return this._request('broker', 'removeAddressSettings', [match]); -}; - -Artemis.prototype.getAddressSettings = function (match) { - return this._request('broker', 'getAddressSettingsAsJSON', [match]).then(function (result) { - return JSON.parse(result); - }); -}; - -Artemis.prototype.deleteAddressAndBindings = function (address) { - var self = this; - return this.deleteBindingsFor(address).then(function () { - return self.deleteAddress(address); - }); -}; - -Artemis.prototype.deleteBindingsFor = function (address) { - var self = this; - return this.getBoundQueues(address).then(function (results) { - return Promise.all(results.map(function (q) { return self.destroyQueue(q); })); - }); -}; - -Artemis.prototype.getBoundQueues = function (address) { - return this._request('address.'+address, 'getQueueNames', []); -}; - -Artemis.prototype.createDivert = function (name, source, target) { - return this._request('broker', 'createDivert', [name, name, source, target, false, null, null]); -} - -Artemis.prototype.destroyDivert = function (name) { - return this._request('broker', 'destroyDivert', [name]); -} - -Artemis.prototype.getDivertNames = function () { - return this._request('broker', 'getDivertNames', []); -} - -/** - * Create divert if one does not already exist. - */ -Artemis.prototype.ensureDivert = function (name, source, target) { - var broker = this; - return broker.findDivert(name).then( - function (found) { - if (!found) { - return broker.createDivert(name, source, target); - } - } - ); -}; - -Artemis.prototype.findDivert = function (name) { - return this.getDivertNames().then( - function (results) { - return results.indexOf(name) >= 0; - } - ); -}; - -Artemis.prototype.createConnectorService = function (connector) { - var parameters = { - "host": process.env.MESSAGING_SERVICE_HOST, - "port": process.env.MESSAGING_SERVICE_PORT_AMQPS_BROKER, - "clusterId": connector.clusterId - }; - - if (connector.containerId !== undefined) { - parameters.containerId = connector.containerId; - } - if (connector.linkName !== undefined) { - parameters.linkName = connector.linkName; - } - if (connector.targetAddress !== undefined) { - parameters.targetAddress = connector.targetAddress; - } - if (connector.sourceAddress !== undefined) { - parameters.sourceAddress = connector.sourceAddress; - } - if (connector.direction !== undefined) { - parameters.direction = connector.direction; - } - return this._request('broker', 'createConnectorService', [connector.name, "org.apache.activemq.artemis.integration.amqp.AMQPConnectorServiceFactory", parameters]); -}; - - -Artemis.prototype.destroyConnectorService = function (name) { - return this._request('broker', 'destroyConnectorService', [name]); -} - -Artemis.prototype.getConnectorServices = function () { - return this._request('broker', 'getConnectorServices', []); -} - -Artemis.prototype.listConnections = function () { - return this._request('broker', 'listConnectionsAsJSON', []).then(function (result) { - return JSON.parse(result); - }); -} - -Artemis.prototype.listSessionsForConnection = function (connection_id) { - return this._request('broker', 'listSessionsAsJSON', [connection_id]).then(function (result) { - return JSON.parse(result); - }); -} - -Artemis.prototype.listConnectionsWithSessions = function () { - var self = this; - return this.listConnections().then(function (conns) { - return Promise.all(conns.map(function (c) { return self.listSessionsForConnection(c.connectionID)})).then(function (sessions) { - for (var i in conns) { - conns[i].sessions = sessions[i]; - } - return conns; - }); - }); -} - -Artemis.prototype.closeConnection = function (connection_id) { - return this._request('broker', 'closeConnectionWithID', [connection_id]).then(function (result) { - return JSON.parse(result); - }); -} - -Artemis.prototype.listConsumers = function () { - return this._request('broker', 'listAllConsumersAsJSON', []).then(function (result) { - return JSON.parse(result); - }); -} - -Artemis.prototype.listProducers = function () { - return this._request('broker', 'listProducersInfoAsJSON', []).then(function (result) { - return JSON.parse(result); - }); -} - -Artemis.prototype.getGlobalMaxSize = function () { - return this._request('broker', 'getGlobalMaxSize', []); -}; - -/** - * Create connector service if one does not already exist. - */ -Artemis.prototype.ensureConnectorService = function (connector) { - var broker = this; - return broker.findConnectorService(connector.name).then( - function (found) { - if (!found) { - return broker.createConnectorService(connector); - } - } - ); -}; - -Artemis.prototype.findConnectorService = function (name) { - return this.getConnectorServices().then( - function (results) { - return results.indexOf(name) >= 0; - } - ); -}; - -Artemis.prototype.close = function () { - if (this.connection) { - var connection = this.connection; - return new Promise(function (resolve) { - connection.on('connection_close', resolve); - connection.on('connection_error', resolve); - connection.close(); - }); - } else { - return Promise.resolve(); - } -} - -module.exports.Artemis = Artemis; -module.exports.connect = function (options) { - return new Artemis(amqp.connect(options)); -} diff --git a/agent/lib/authz.js b/agent/lib/authz.js deleted file mode 100644 index d6d8a103c1f..00000000000 --- a/agent/lib/authz.js +++ /dev/null @@ -1,64 +0,0 @@ -/* - * Copyright 2016 Red Hat Inc. - * - * 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. - */ -'use strict'; - -function TrustAllPolicy() {}; - -// We no longer use derive permissions from keycloak groups. - -TrustAllPolicy.prototype.has_permission = function (required, actual) { - return true; -}; - -TrustAllPolicy.prototype.access_console = function (request) { - return request.authz_props && request.authz_props.console; -}; - -TrustAllPolicy.prototype.is_admin = function (connection) { - return connection.options && connection.options.admin; -}; - -TrustAllPolicy.prototype.set_admin = function (connection) { - if (!connection.options) { - connection.options = {}; - } - connection.options.admin = true; -}; - -TrustAllPolicy.prototype.address_filter = function (connection) { - return undefined; -}; - -TrustAllPolicy.prototype.connection_filter = function (connection) { - return undefined; -}; - -TrustAllPolicy.prototype.can_publish = function (sender, message) { - return true; -}; - -TrustAllPolicy.prototype.get_access_token = function (connection) { - return connection.options && connection.options.token ? connection.options.token.getAccessToken() : null; -}; - -TrustAllPolicy.prototype.get_user = function (connection) { - return connection.options && connection.options.username ? connection.options.username : null; -}; - - -module.exports.policy = function (env) { - return new TrustAllPolicy(); -}; diff --git a/agent/lib/broker_controller.js b/agent/lib/broker_controller.js deleted file mode 100644 index cc7ccb7dd49..00000000000 --- a/agent/lib/broker_controller.js +++ /dev/null @@ -1,819 +0,0 @@ -/* - * Copyright 2016 Red Hat Inc. - * - * 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. - */ -'use strict'; - -var log = require("./log.js").logger(); -var util = require('util'); -var events = require('events'); -var rhea = require('rhea'); -var artemis = require('./artemis.js'); -var myevents = require('./events.js'); -var myutils = require('./utils.js'); -var plimit = require('p-limit'); -var crypto = require('crypto'); -var uuidv5 = require('uuid/v5'); -var clone = require('clone'); - -function BrokerController(event_sink, config) { - events.EventEmitter.call(this); - this.check_in_progress = false; - this.post_event = event_sink || function (event) { log.debug('event: %j', event); }; - this.serial_sync = myutils.serialize(this._sync_addresses_and_forwarders.bind(this)); - this.addresses_synchronized = false; - this.busy_count = 0; - this.retrieve_count = 0; - this.last_retrieval = undefined; - this.excluded_types = undefined; - this.config = config ? config : process.env; - this.global_max_size = this.read_global_max_size(); - this.root_address_settings = {}; -}; - -util.inherits(BrokerController, events.EventEmitter); - -BrokerController.prototype.start_polling = function (poll_frequency) { - this.polling = setInterval(this.check_broker_addresses.bind(this), poll_frequency || 5000);//poll broker stats every 5 secs by default -}; - -function type_filter(ignored) { - return function (type) { - return ignored.indexOf(type) >= 0; - } -} - -BrokerController.prototype.connect = function (options) { - this.excluded_types = type_filter(['subscription']); - var container = rhea.create_container(); - container.on('connection_open', this.on_connection_open.bind(this)); - return container.connect(options); -}; - -BrokerController.prototype.close = function () { - if (this.polling) { - clearInterval(this.polling); - this.polling = undefined; - } - return this.broker.close(); -}; - -BrokerController.prototype.on_connection_open = function (context) { - var self = this; - this.broker = new artemis.Artemis(context.connection); - this.id = context.connection.container_id; - self.broker.getAddressSettings('#').then((root_address_settings) => { - self.root_address_settings = root_address_settings; - self.check_broker_addresses().then(() => { - log.info('[%s] broker controller ready', self.id); - self.emit('ready'); - }).catch((e) => { - log.error("[%s] failed to check_broker_addresses", self.id, e); - }); - }).catch((e) => { - log.error("[%s] failed to get root settings", self.id, e); - }); -}; - -BrokerController.prototype.set_connection = function (connection) { - var self = this; - this.broker = new artemis.Artemis(connection); - this.id = connection.container_id; - log.info('[%s] broker controller ready', this.id); - - return new Promise((resolve, reject) => { - self.broker.getAddressSettings('#').then((root_address_settings) => { - self.root_address_settings = root_address_settings; - resolve(); - }).catch(reject); - }); -} - -BrokerController.prototype.addresses_defined = function (addresses) { - this.addresses = addresses.reduce(function (map, a) { map[a.address] = a; return map; }, {}); - return this.check_broker_addresses(); -}; - -BrokerController.prototype.sync_addresses = function (addresses) { - this.addresses = addresses.reduce(function (map, a) { map[a.address] = a; return map; }, {}); - return this.serial_sync(); -}; - -BrokerController.prototype.sync_addresssettings = function (addresses) { - var limit = plimit(250); - let update_address_setting_fn = limit.bind(null, this.update_address_setting.bind(this)); - - return Promise.all(addresses.map(update_address_setting_fn)); -}; - -BrokerController.prototype.close_connection = function (connection) { - return this.broker.closeConnection(connection.id); -}; - -function total() { - var result = 0; - for (var i in arguments) { - if (arguments[i]) result += arguments[i]; - } - return result; -} - -function transform_queue_stats(queue) { - return { - receivers: queue.consumers, - senders: 0,/*add this later*/ - depth: queue.messages, - messages_in: queue.enqueued, - messages_out: total(queue.acknowledged, queue.expired, queue.killed), - propagated: 100, - shards: [queue], - outcomes: { - egress: { - links: [], - accepted: queue.acknowledged, - unsettled: queue.delivered, - rejected: queue.killed - }, - ingress: { - links: [], - accepted: queue.enqueued - } - } - }; -} - -function sum(field, list) { - return util.isArray(list) ? list.map(function (o) { return o[field] || 0; }).reduce(function (a, b) { return a + b; }, 0) : 0; -} - -function transform_topic_stats(topic) { - return { - receivers: topic.subscription_count, - senders: 0,/*add this later*/ - depth: topic.enqueued, - /*this isn't really correct; what we want is the total number of messages sent to the topic (independent of the number of subscribers)*/ - messages_in: sum('enqueued', topic.subscriptions), - messages_out: sum('acknowledged', topic.subscriptions), - propagated: 100, - shards: [topic], - outcomes: { - egress: { - links: [], - accepted: sum('acknowledged', topic.subscriptions), - unsettled: sum('delivered', topic.subscriptions), - rejected: sum('killed', topic.subscriptions) - }, - ingress: { - links: [], - accepted: topic.enqueued - } - } - }; -} - -function transform_address_stats(address) { - return address.type === 'queue' ? transform_queue_stats(address) : transform_topic_stats(address); -} - -BrokerController.prototype.transform_connection_stats = function(raw) { - var addressSpace = process.env.ADDRESS_SPACE; - var addressSpaceNamespace = process.env.ADDRESS_SPACE_NAMESPACE; - var addressSpaceType = process.env.ADDRESS_SPACE_TYPE; - - var uuid = generateStableUuid(addressSpaceNamespace, addressSpace, raw.connectionID, raw.clientAddress); - - return { - id: raw.connectionID, - addressSpace: addressSpace, - addressSpaceNamespace: addressSpaceNamespace, - addressSpaceType: addressSpaceType, - uuid: uuid, - host: raw.clientAddress, - container: 'not available', - creationTimestamp: raw.creationTime ? Math.floor(raw.creationTime / 1000) : 0, - user: raw.sessions.length ? raw.sessions[0].principal : '', - senders: [], - receivers: [], - - close: () => {return this.broker.closeConnection(raw.connectionID)} - }; -} - -function generateStableUuid() { - var hash = crypto.createHash('sha256'); - for (var i = 0, j = arguments.length; i < j; i++){ - var argument = arguments[i]; - if (argument) { - hash.update(argument); - } - } - var ba = []; - ba.push(...hash.digest().slice(0, 16)); - const ns = "3751f842-240e-48b9-89b5-5b47f04e931b"; - return uuidv5(ns, ba); -} - - -function transform_producer_stats(raw) { - return { - uuid: generateStableUuid(raw.connectionID, raw.sessionID, raw.destination), - name: undefined, - connection_id: raw.connectionID, - address: raw.destination, - deliveries: raw.msgSent, - deliveryCount: raw.msgSent, - lastUpdated: Date.now() - }; -} - -function transform_consumer_stats(raw) { - return { - uuid: generateStableUuid(raw.connectionID, raw.sessionID, raw.consumerID), - name: raw.consumerID, - connection_id: raw.connectionID, - address: raw.queueName,//mapped to address in later step - deliveries: 0,//TODO: not yet available from broker - - lastUpdated: Date.now() - }; -} - -function index_by(list, field) { - return list.reduce(function (map, item) { map[item[field]] = item; return map; }, {}); -} - -function collect_by_connection(conns, links, name) { - links.forEach(function (link) { - var conn = conns[link.connection_id]; - if (conn) { - conn[name].push(link); - } - }); -} - -function collect_by_address(addresses, links, name, count) { - links.forEach(function (link) { - var address = addresses[link.address]; - if (address) { - address.outcomes[name].links.push(link); - if (count) address[count] = address.outcomes[name].links.length; - } - }); -} - -function is_not_internal(conn) { - return conn.user !== undefined && conn.user.match('^agent.[a-z0-9]+$') == null; //Can't get properties or anything else on which to base decision yet -} - -function compare_address_settings(orig_address_settings, new_address_settings) { - for (var name in orig_address_settings) { - log.debug('comparing %s: %s %s', name, orig_address_settings[name], new_address_settings[name]) - if (new_address_settings[name]) { - var compare = myutils.string_compare(orig_address_settings[name], new_address_settings[name]); - if (compare !== 0) { - return compare; - } - } else { - //Ignoring settings that are from the broker & not in the new calculated set. - } - } - return 0; -} - -BrokerController.prototype.retrieve_stats = function () { - var self = this; - if (this.broker !== undefined) { - return Promise.all([ - this.broker.getAllAddressData(), - this.broker.listConnectionsWithSessions(), - this.broker.listProducers(), - this.broker.listConsumers()]).then(function (results) { - var address_stats = {}; - for (var name in results[0].index) { - address_stats[name] = transform_address_stats(results[0].index[name]); - } - var connection_stats = index_by(results[1].map(self.transform_connection_stats, self).filter(is_not_internal), 'id'); - var senders = results[2].map(transform_producer_stats); - var receivers = results[3].map(transform_consumer_stats); - receivers.forEach(function (r) { - var q = results[0].reverse_index[r.address]; - if (q) { - r.address = q; - } - }); - - collect_by_connection(connection_stats, senders, 'senders'); - collect_by_connection(connection_stats, receivers, 'receivers'); - - collect_by_address(address_stats, senders, 'ingress', 'senders'); - collect_by_address(address_stats, receivers, 'egress'); - - for(var c in connection_stats) { - connection_stats[c].messages_in = connection_stats[c].senders.reduce(function (total, sender) { return total + sender.deliveries; }, 0); - // there is currently insufficient data exposed by the consumer to compute a messages_out - } - - self.retrieve_count++; - var now = Date.now(); - if (self.last_retrieval === undefined) { - log.info('[%s] broker stats retrieved (%d)', self.id, self.retrieve_count); - } else { - var interval = now - self.last_retrieval; - if (interval >= 60000) { - log.info('[%s] broker stats retrieved %d times in last %d secs', self.id, self.retrieve_count, Math.round(interval/1000)); - self.retrieve_count = 0; - } - } - self.emit('address_stats_retrieved', address_stats); - self.emit('connection_stats_retrieved', connection_stats); - }).catch(function (error) { - log.error('[%s] error retrieving stats: %s', self.id, error); - }); - } else { - return Promise.resolve(); - } -}; - -function same_address(a, b) { - return b !== undefined && a.address === b.address && a.type === b.type; -} - -function difference(a, b, equivalent) { - var diff = {}; - for (var k in a) { - if (!equivalent(a[k], b[k])) { - diff[k] = a[k]; - } - } - return diff; -} - -function address_and_type(object) { - return {address: object.address, type: object.type}; -} - -function values(map) { - return Object.keys(map).map(function (key) { return map[key]; }); -} - -function exclude_subscriptions(type) { - return type === 'subscription'; -} - -function excluded_addresses(address) { - return address === '!!GLOBAL_DLQ' || address === 'DLQ' || address === 'ExpiryQueue' || address === 'activemq.notifications' || address === 'activemq.management' || address.startsWith("!!HEALTH_CHECK_BROKER"); -} - -function is_temp_queue(a) { - return a.type === 'queue' && - (a.temporary || (a.name.startsWith('activemq.management.tmpreply') && a.durable === false && - a.maxConsumers === a.consumers && a.consumers > 0 /* ignore in-use response queues */)) -} - -function is_wildcard_sub(a) { - return a.type === 'topic' && (a.name.includes("#") || a.name.includes("+") || a.name.includes("/")); -} - -function toplevel_address_exists(a, addresses_in) { - for (var name in addresses_in) { - if (a.name.startsWith(name)) { - return true; - } - } - return false; -} - -function is_durable_subscription(a) { - return a.durable && !a.purgeOnNoConsumers; -} - -/** - * Translate from the address details we get back from artemis to the - * structure used for the definition, for easier comparison. - */ -function translate(addresses_in, excluded_names, excluded_types) { - var addresses_out = {}; - for (var name in addresses_in) { - if (excluded_names && excluded_names(name)) continue; - var a = addresses_in[name]; - if (excluded_types && excluded_types(a.type)) continue; - if (is_temp_queue(a)) { - log.debug('ignoring temp queue %s', a.name); - continue; - } - if (is_wildcard_sub(a) && toplevel_address_exists(a, addresses_in)) { - log.debug("Ignoring wildcard sub"); - continue; - } - if (a.name) { - addresses_out[name] = {address:a.name, type: a.type}; - } else { - log.warn('Skipping address with no name: %j', a); - } - } - return addresses_out; -} - -BrokerController.prototype.update_address_setting = function (a) { - var self = this; - var name = a.address; - return self.broker.getAddressSettings(name).then(function (orig_settings) { - return self.get_address_settings(a).then(function (new_settings) { - if (new_settings) { - if (compare_address_settings(orig_settings, new_settings) !== 0) { - log.info('[%s] Adding/updating address settings: %s', self.id, name); - return self.broker.addAddressSettings(name, new_settings); - } else { - log.debug('[%s] Address settings match for %s: not updating', self.id, name); - return Promise.resolve(); - } - } else if (compare_address_settings(orig_settings, self.root_address_settings) !== 0) { - // Settings were previously applied but now no longer required. To remove them first we - // reset the address settings to the root values then remove the record. - log.info('[%s] Removing address settings: %s', self.id, name); - return self.broker.addAddressSettings(name, self.root_address_settings).finally(() => { - return self.broker.removeAddressSettings(name); - }); - } else { - // No settings required (and none currently applied). - return Promise.resolve(); - } - }).catch(function (error) { - log.error('[%s] Failed to create new address setting %s: %s', self.id, name, error); - return Promise.reject(error); - }); - }).catch(function (error) { - log.error('[%s] Failed to retrieve broker address setting: %s', self.id, name, error); - return Promise.reject(error); - }); -} - -BrokerController.prototype.delete_address = function (a) { - var self = this; - if (a.type === 'queue') { - log.info('[%s] Deleting queue "%s"...', self.id, a.address); - return self.broker.destroyQueue(a.address).then(function () { - log.info('[%s] Deleted queue "%s"', self.id, a.address); - self.post_event(myevents.address_delete(a)); - }).catch(function (error) { - log.error('[%s] Failed to delete queue %s: %s', self.id, a.address, error); - return self.broker.deleteAddress(a.address).then(function () { - log.info('[%s] Deleted anycast address %s', self.id, a.address); - }).catch(function (error) { - log.error('[%s] Failed to delete queue address %s: %s', self.id, a.address, error); - self.post_event(myevents.address_failed_delete(a, error)); - }); - }); - } else if (a.type === 'topic') { - log.info('[%s] Deleting topic "%s"...', self.id, a.address); - return self.broker.deleteAddressAndBindings(a.address).then(function () { - log.info('[%s] Deleted topic "%s"', self.id, a.address); - self.post_event(myevents.address_delete(a)); - }).catch(function (error) { - log.error('[%s] Failed to delete topic %s: %s', self.id, a.address, error); - self.post_event(myevents.address_failed_delete(a, error)); - }); - } else if (a.type === 'subscription') { - log.info('[%s] Deleting subscription "%s"...', self.id, a.address); - return self.broker.destroyQueue(a.address).then(function () { - log.info('[%s] Deleted subscription "%s"', self.id, a.address); - self.post_event(myevents.address_delete(a)); - }).catch(function (error) { - log.error('[%s] Failed to delete subscription %s: %s', self.id, a.address, error); - }); - } -}; - -BrokerController.prototype.delete_address_and_settings = function (a) { - var self = this; - log.info('[%s] Deleting address-settings for "%s"...', self.id, a.address); - return self.broker.removeAddressSettings(a.address).then(function() { - log.info('[%s] Deleted address-settings for "%s"', self.id, a.address); - return self.delete_address(a); - }).catch(function (error) { - log.error('[%s] Failed to delete address setting for "%s": %s', self.id, a.address, error); - return self.delete_address(a); - }); -}; - -BrokerController.prototype.delete_addresses = function (addresses) { - var limit = plimit(250); - let delete_fn = limit.bind(null, this.delete_address_and_settings.bind(this)); - return Promise.all(addresses.map(delete_fn)); -}; - -BrokerController.prototype.create_address = function (a) { - var self = this; - if (a.type === 'queue') { - log.info('[%s] Creating queue "%s"...', self.id, a.address); - return self.broker.createQueue(a.address).then(function () { - log.info('[%s] Created queue "%s"', self.id, a.address); - self.post_event(myevents.address_create(a)); - }).catch(function (error) { - log.error('[%s] Failed to create queue "%s": %s', self.id, a.address, error); - self.post_event(myevents.address_failed_create(a, error)); - }); - } else if (a.type === 'topic') { - log.info('[%s] Creating topic "%s"', self.id, a.address); - return self.broker.createAddress(a.address, {multicast:true}).then(function () { - log.info('[%s] Created topic "%s"', self.id, a.address); - self.post_event(myevents.address_create(a)); - }).catch(function (error) { - log.error('[%s] Failed to create topic "%s": %s', self.id, a.address, error); - self.post_event(myevents.address_failed_create(a, error)); - }); - } else if (a.type === 'subscription') { - var maxConsumers = 1; - if (a.subscription !== undefined && a.subscription.maxConsumers !== undefined) { - maxConsumers = a.subscription.maxConsumers; - } - log.info('[%s] Creating subscription "%s" on "%s" with max consumers "%d"...', self.id, a.address, a.topic, maxConsumers); - return self.broker.createSubscription(a.address, a.topic, maxConsumers).then(function () { - log.info('[%s] Created subscription "%s" on "%s" with max consumers "%d"', self.id, a.address, a.topic, maxConsumers); - self.post_event(myevents.address_create(a)); - }).catch(function (error) { - log.error('[%s] Failed to create subscription "%s" on "%s": %s', self.id, a.address, a.topic, error); - self.post_event(myevents.address_failed_create(a, error)); - }); - } -}; - -BrokerController.prototype.create_addresses = function (addresses) { - var limit = plimit(250); - let create_fn = limit.bind(null, this.create_address.bind(this)); - return Promise.all(addresses.map(create_fn)); -}; - -BrokerController.prototype.check_broker_addresses = function () { - if (this.broker !== undefined && this.addresses !== undefined) { - if (!this.check_in_progress) { - this.busy_count = 0; - this.check_in_progress = true; - var self = this; - return this._sync_broker_addresses().then(function () { - return self.retrieve_stats().then(function () { - self.check_in_progress = false; - }).catch( function (error) { - log.error('[%s] error retrieving stats: %s', self.id, error); - self.check_in_progress = false; - }); - }).catch (function (error) { - log.error('[%s] error syncing addresses: %s', self.id, error); - self.check_in_progress = false; - }); - } else { - if (++(this.busy_count) % 10 === 0) { - log.info('Unable to check broker, check already in progress (%d)', this.busy_count); - } - return Promise.resolve(); - } - } else { - log.info('Unable to check broker (broker %s defined, addresses %s defined)', this.broker === undefined ? 'is not' : 'is', this.addresses === undefined ? 'is not' : 'is'); - return Promise.resolve(); - } -}; - -// Only used in tests -BrokerController.prototype._sync_addresses = function (addresses) { - this.addresses = addresses.reduce(function (map, a) { map[a.address] = a; return map; }, {}); - return this._sync_broker_addresses(); -}; - -BrokerController.prototype._set_sync_status = function (stale, missing) { - var b = (stale === 0 && missing === 0); - if (this.addresses_synchronized !== b) { - if (b) { - log.info('[%s] addresses are synchronized', this.id); - this.emit('synchronized'); - } else { - log.info('[%s] addresses synchronizing (%d to delete, %d to create)', this.id, stale, missing); - } - } - this.addresses_synchronized = b; -} - -BrokerController.prototype._sync_broker_addresses = function (retry) { - var self = this; - return this.broker.listAddresses().then(function (results) { - var addrSettings = self.sync_addresssettings(values(self.addresses).filter((o) => o.type === 'subscription' || o.type === 'queue' || o.type === 'topic')); - var actual = translate(results, excluded_addresses, self.excluded_types); - var stale = values(difference(actual, self.addresses, same_address)); - var missing = values(difference(self.addresses, actual, same_address)); - log.debug('[%s] checking addresses, desired=%j, actual=%j => delete %j and create %j', self.id, values(self.addresses).map(address_and_type), values(actual), - stale.map(address_and_type), missing.map(address_and_type)); - self._set_sync_status(stale.length, missing.length); - return addrSettings.then(() => { - return self.delete_addresses(stale).then( - function () { - return self.create_addresses(missing.filter(function (o) { - return o.type !== 'subscription' - })).then(function () { - return self.create_addresses(missing.filter(function (o) { - return o.type === 'subscription' - })).then(function () { - return retry ? true : self._sync_broker_addresses(true); - }); - }); - }); - - - } - ); - - }).catch(function (e) { - log.error('[%s] failed to retrieve addresses: %s', self.id, e); - throw e; - }); -}; - -BrokerController.prototype.destroy_connector = function (connector) { - var self = this; - log.info('[%s] Deleting connector for "%s"...', self.id, connector.name); - return self.broker.destroyConnectorService(connector.name).then(function() { - log.info('[%s] Deleted connector for "%s"', self.id, connector.name); - }).catch(function (error) { - log.error('[%s] Failed to delete connector for "%s": %s', self.id, connector.name, error); - }); -}; - -BrokerController.prototype.create_connector = function (connector) { - var self = this; - log.info('[%s] Creating connector "%j"...', self.id, connector); - return self.broker.createConnectorService(connector).then(function () { - log.info('[%s] Created connector for "%s"', self.id, connector.name); - }).catch(function (error) { - log.error('[%s] Failed to create connector for "%s": %s', self.id, connector.name, error); - }); -}; - -BrokerController.prototype.destroy_connectors = function (connectors) { - return Promise.all(connectors.map(this.destroy_connector.bind(this))); -}; - -BrokerController.prototype.create_connectors = function (connectors) { - return Promise.all(connectors.map(this.create_connector.bind(this))); -}; - -function connectors_of_interest(name) { - return !name.startsWith("$override"); -} - -function connector_compare(a, b) { - return myutils.string_compare(a.name, b.name); -} - -BrokerController.prototype._sync_broker_forwarders = function () { - var self = this; - var desired = []; - for (var address in this.addresses) { - var address_type = this.addresses[address].type; - // We will only check queues and subscriptions for forwarders - if (address_type === "queue" || address_type === "subscription") { - var address_name = this.addresses[address].name; - var forwarders = this.addresses[address].forwarders; - if (forwarders !== undefined) { - for (var fwdidx in forwarders) { - var forwarder = forwarders[fwdidx]; - var forwarder_name = address_name + "." + forwarder.name + "." + forwarder.direction; - // Only create forwarder for subscriptions if the direction is outward - if (address_type === "queue" || (address_type === "subscription" && forwarder.direction === "out")) { - var sourceAddress = forwarder.direction === "out" ? (address_type === "subscription" ? this.addresses[address].topic + "::" + address : address) : forwarder.remoteAddress; - var targetAddress = forwarder.direction === "out" ? forwarder.remoteAddress : address; - var forwarder_entry = { - name: forwarder_name, - clusterId: address_name, - containerId: address_name, - linkName: forwarder_name, - targetAddress: targetAddress, - sourceAddress: sourceAddress, - direction: forwarder.direction - }; - desired.push(forwarder_entry); - } - } - } - } - } - log.debug("desired connectors: %j", desired); - return this.broker.getConnectorServices().then(function (results) { - var actual = results.filter(connectors_of_interest); - actual.sort(); - actual = actual.map(function (name) { - return {name: name} - }); - - var difference = myutils.changes(actual, desired, connector_compare); - if (difference) { - log.info('[%s] %d connectors missing, %d to be removed', self.id, difference.added.length, difference.removed.length); - return self.destroy_connectors(difference.removed).then(function () { - return self.create_connectors(difference.added); - }); - } else { - log.info('[%s] all connectors exist', self.id); - } - }).catch(function (e) { - log.error('[%s] failed to retrieve connectors: %s', self.id, e); - throw e; - }); -}; - -BrokerController.prototype._sync_addresses_and_forwarders = function () { - return this._sync_broker_addresses().then(this._sync_broker_forwarders()); -}; - - -BrokerController.prototype.generate_address_settings = function (address, global_max_size) { - if (address.status) { - var addressSettings = clone(this.root_address_settings); - var upd = 0; - if (address.status.planStatus && address.status.planStatus.resources && address.status.planStatus.resources.broker > 0 && (address.type === 'subscription' || address.type === 'queue')) { - var planStatus = address.status.planStatus; - - var r = planStatus.resources.broker; - var p = planStatus.partitions; - var allocation = (r && p) ? (r / p) : (r) ? r : undefined; - if (allocation) { - var maxSizeBytes = Math.round(allocation * global_max_size); - if ('PAGE' === addressSettings.addressFullMessagePolicy && addressSettings.pageSizeBytes > 0) { - // Artemis business rule that maxSizeBytes >= pageSizeBytes - maxSizeBytes = Math.max(maxSizeBytes, addressSettings.pageSizeBytes); - } else { - addressSettings.pageSizeBytes = -1; - } - addressSettings.maxSizeBytes = maxSizeBytes; - upd++; - } - } - if (address.status.messageTtl && (address.type === 'topic' || address.type === 'queue')) { - if (address.status.messageTtl.minimum) { - addressSettings.minExpiryDelay = address.status.messageTtl.minimum; - upd++; - } - if (address.status.messageTtl.maximum) { - addressSettings.maxExpiryDelay = address.status.messageTtl.maximum; - upd++; - } - } - return upd ? addressSettings : undefined; - } -}; - -BrokerController.prototype.get_address_settings = function (address) { - return this.get_address_settings_async(address, /* this.broker.getGlobalMaxSize()*/ Promise.resolve()); -}; - -BrokerController.prototype.read_global_max_size = function () { - return this.config.BROKER_GLOBAL_MAX_SIZE ? myutils.parseToBytes(this.config.BROKER_GLOBAL_MAX_SIZE) : 0; -}; - -BrokerController.prototype.get_address_settings_async = function (address, global_max_size_promise) { - var self = this; - if (self.global_max_size > 0) { - return Promise.resolve(self.generate_address_settings(address, self.global_max_size)); - } else if (global_max_size_promise) { - return global_max_size_promise.then(function (global_max_size) { - return Promise.resolve(self.generate_address_settings(address, global_max_size)); - }).catch(function (e) { - log.debug('no global max, therefore not applying address settings %s', e); - return Promise.reject(e); - }); - } else { - log.debug('no global max, therefore not applying address settings'); - return Promise.resolve(); - } -}; - -module.exports.create_controller = function (connection, event_sink) { - var rcg = connection.properties['qd.route-container-group']; - var bc = new BrokerController(event_sink); - if (rcg === 'sharded-topic') { - //only control subscriptions - bc.excluded_types = type_filter(['queue', 'topic']); - log.info('excluding types %j controller for %s (%s)', bc.excluded_types, connection.container_id, rcg); - } - - return new Promise((resolve, reject) => { - bc.set_connection(connection).then(() => { - resolve(bc); - }).catch(reject); - }); -}; - -module.exports.create_agent = function (event_sink, polling_frequency) { - var bc = new BrokerController(event_sink); - bc.start_polling(polling_frequency); - return bc; -}; - -module.exports.BrokerController = BrokerController; // testing \ No newline at end of file diff --git a/agent/lib/broker_stats.js b/agent/lib/broker_stats.js deleted file mode 100644 index 60b8105b9e3..00000000000 --- a/agent/lib/broker_stats.js +++ /dev/null @@ -1,87 +0,0 @@ -/* - * Copyright 2016 Red Hat Inc. - * - * 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. - */ -'use strict'; - -var create_podgroup = require('./podgroup.js'); -var pod_watcher = require('./pod_watcher.js'); - -function is_pod_ready(pod) { - return pod.ready === 'True' && pod.phase === 'Running'; -} - -function list_addresses(broker) { - return broker.getAllQueuesAndTopics(); -} - -function get_stats_for_address(stats, address) { - var s = stats[address]; - if (s === undefined) { - s = {depth:0, dlq_depth: 0, shards:[]}; - stats[address] = s; - } - return s; -} - -function merge() { - var c = {}; - for (var i = 0; i < arguments.length; i++) { - for (var k in arguments[i]) { - c[k] = arguments[i][k]; - } - } - return c; -} - -function BrokerStats (env) { - this.queues = {}; - this.brokers = create_podgroup(); - this.watcher = pod_watcher.watch("role=broker", env); - this.watcher.on('updated', this.on_updated.bind(this)); -} - -BrokerStats.prototype.on_updated = function(pods) { - this.brokers.update(pods.filter(is_pod_ready)); -}; - -BrokerStats.prototype.retrieve = function(addresses) { - return this._retrieve().then(function (stats) { - for (var s in stats) { - addresses.update_stats(s, stats[s]); - } - }).catch(function (error) { - console.error('Failed to retrieve broker stats: %s', error); - }); -}; - -BrokerStats.prototype._retrieve = function() { - var brokers = this.brokers.broker_list(); - return Promise.all(brokers.map(list_addresses)).then(function (results) { - var stats = {}; - for (var i = 0; i < results.length; i++) { - for (var name in results[i]) { - var s = get_stats_for_address(stats, name); - var dlq = results[i]["!!GLOBAL_DLQ"]; - var shard = merge(results[i][name], {name:brokers[i].connection.container_id}); - if (dlq !== undefined && dlq.messages) s.dlq_depth += dlq.messages; - if (shard.messages) s.depth += shard.messages; - s.shards.push(shard); - } - } - return stats; - }); -}; - -module.exports = BrokerStats; diff --git a/agent/lib/buffered_sender.js b/agent/lib/buffered_sender.js deleted file mode 100644 index 2998c7bd2c0..00000000000 --- a/agent/lib/buffered_sender.js +++ /dev/null @@ -1,62 +0,0 @@ -/* - * Copyright 2018 Red Hat Inc. - * - * 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. - */ -'use strict'; - -function BufferedSender(sender, key_function) { - this.sender = sender; - this.key_function = key_function; - this.buffer = []; - this.index = {}; - this.sender.on('sendable', this._send_pending.bind(this)); -} - -BufferedSender.prototype.send = function (message) { - if (this._send_pending()) { - this.sender.send(message); - } else { - this.buffer.push(message); - } -}; - -BufferedSender.prototype._get_previous = function (message) { - if (this.key_function) { - var key = this.key_function(message); - if (key) { - return this.index[key]; - } - } - return undefined; -}; - -BufferedSender.prototype._add_pending = function (message) { - var previous = this._get_previous(message); - if (previous) { - //replace previous - this.buffer[previous] = message; - } else { - this.buffer.push(message); - } -}; - -BufferedSender.prototype._send_pending = function () { - while (this.sender.sendable() && this.buffer.length > 0) { - this.sender.send(this.buffer.shift()); - } - return this.buffer.length === 0 && this.sender.sendable(); -}; - -module.exports = BufferedSender; - diff --git a/agent/lib/console_server.js b/agent/lib/console_server.js deleted file mode 100644 index b8489965c88..00000000000 --- a/agent/lib/console_server.js +++ /dev/null @@ -1,378 +0,0 @@ -/* - * Copyright 2016 Red Hat Inc. - * - * 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. - */ -'use strict'; - -var log = require("./log.js").logger(); -var http = require('http'); -var url = require('url'); -var rhea = require('rhea'); -var AddressList = require('./address_list.js'); -var BufferedSender = require('./buffered_sender.js'); -var Registry = require('./registry.js'); -var tls_options = require('./tls_options.js'); -var Metrics = require('./metrics.js'); -var queue = require('../lib/queue.js'); -var kubernetes = require('./kubernetes.js'); -var sasl = require('./sasl.js'); - -const agentData = "agent_data"; -const agentCmd = "agent_command"; -const agentCmdResponse = "agent_command_response"; - -function ConsoleServer(env, openshift) { - this.addresses = new AddressList(); - this.metrics = new Metrics(env.ADDRESS_SPACE_NAMESPACE, env.ADDRESS_SPACE); - this.connections = new Registry(); - this.listeners = {}; - this.openshift = openshift; - var self = this; - this.addresses.on('updated', function (address) { - self.publish({subject:'address',body:address}); - - }); - this.addresses.on('deleted', function (address) { - log.debug('address %s has been deleted, notifying clients...', address.address); - self.publish({subject:'address_deleted',body:address.address}); - }); - this.addresses.on('purged', function (address) { - log.info('address %s has been purged, notifying clients...', address.address); - self.publish({subject:'address_purged',body:address.address}); - }); - this.connections.on('updated', function (conn) { - self.publish({subject:'connection',body:conn}); - }); - this.connections.on('deleted', function (conn) { - log.debug('connection %s has been deleted, notifying clients...', conn.host); - self.publish({subject:'connection_deleted',body:conn}); - }); - this.amqp_container = rhea.create_container({autoaccept:false}); - - this.amqp_container.on('sender_open', function (context) { - var source = context.sender.source ? context.sender.source : {}; - var sourceAddress = source.address ? source.address : ""; - - if (sourceAddress === agentCmdResponse) { - context.sender.set_source({address: agentCmdResponse}); - } else if (sourceAddress === agentData) { - context.sender.set_source({address: agentData}); - self.subscribe(context.connection.remote.open.container_id, context.sender); - } - }); - this.amqp_container.on('receiver_open', function (context) { - var target = context.receiver.target ? context.receiver.target : {}; - var targetAddress = target.address ? target.address : ""; - - - if (targetAddress === agentCmd) { - context.receiver.set_target({address: agentCmd}); - } - }); - function unsubscribe (context) { - if (context.connection.remote.open) { - self.unsubscribe(context.connection.remote.open.container_id); - } - } - this.amqp_container.on('sender_close', unsubscribe); - this.amqp_container.on('connection_open', function (context) { - var connection = context.connection; - if (connection.sasl_transport && - connection.sasl_transport.mechanism) { - var mechanism = connection.sasl_transport.mechanism; - if (!connection.options.username) { - connection.options.username = mechanism.username; - } - if (mechanism.admin) { - self.authz.set_admin(connection); - } - } - log.info("[%s] Console connection open [%j]", connection.options.username, connection.properties); - }); - this.amqp_container.on('connection_close', function(context) { - var connection = context.connection; - log.info("[%s] Console connection closed [%j]", connection.options.username, connection.properties); - unsubscribe(context); - }); - this.amqp_container.on('disconnected', unsubscribe); - this.amqp_container.on('message', function (context) { - var accept = function () { - context.delivery.accept(); - }; - var reject = function (e, code) { - context.delivery.reject({condition: code || 'amqp:internal-error', description: '' + e}); - }; - var sendResponse = function (sender, message, outcome, error) { - sender.send({ - subject: message.subject + "_response", - correlation_id: message.message_id, - application_properties: {outcome: outcome, error: error} - } - ); - }; - var extractServerResponse = function (e) { - if (e && e.body) { - try { - // Might be a Kubernetes Status response, if so use its message. - var status = JSON.parse(e.body); - if (status.message) { - return "" + e.statusCode + " : " + status.message; - } - } catch (ignored) { - return "" + e.statusCode + " : " + e.body; - } - } - return e - }; - - if (!context.message.reply_to) { - reject("reply_to mandatory", 'amqp:precondition-failed'); - } - - if (!context.message.message_id) { - reject("message_id mandatory", 'amqp:precondition-failed'); - } - - var responseSender = context.connection.find_sender((s) => s.source.address === context.message.reply_to); - if (!responseSender) { - reject("can't find sender for reply-to : " + context.message.reply_to, 'amqp:precondition-failed'); - } - - var user = self.authz.get_user(context.connection); - if (!self.authz.is_admin(context.connection)) { - reject(context, 'amqp:unauthorized-access', 'not authorized'); - } else if (context.message.subject === 'purge_address') { - accept(); - var name = context.message.body.address; - log.info('[%s] purging address %s', user, name); - - var a = name.split(".", 2); - if (a.length !== 2) { - sendResponse(responseSender, context.message, false, "unexpected address name : " + name); - } else { - var addr = self.addresses.get(a[1]); - if (addr) { - queue(addr).purge().then(purged => { - log.info("[%s] Purged %d message(s) from address %s", user, purged, context.message.body.address); - sendResponse(responseSender, context.message, true); - }).catch((e) => { - sendResponse(responseSender, context.message, false, extractServerResponse(e)); - }); - } else { - sendResponse(responseSender, context.message, false, "could not find address with name : " + name); - } - } - } else if (context.message.subject === 'close_connection') { - accept(); - var connectionUid = context.message.body.connectionUid; - log.info('[%s] closing connection %s', user, connectionUid); - var closePromise = null; - self.connections.first((c) => { - try { - closePromise = c.close() || Promise.resolve(); - } catch (e) { - closePromise = Promise.reject(e); - } - }, c => c.uuid === connectionUid); - if (!closePromise) { - closePromise = Promise.reject("failed to find connection with uuid " + connectionUid); - } - closePromise.then(() => { - log.info("[%s] closed connection %s", user, connectionUid); - sendResponse(responseSender, context.message, true); - }).catch((e) =>{ - log.warn("[%s] failed to close connection %s", user, connectionUid, e); - sendResponse(responseSender, context.message, false, e); - }); - } else { - reject('ignoring message: ' + context.message); - } - }); -} - - -ConsoleServer.prototype.close = function (callback) { - var self = this; - return new Promise(function (resolve, reject) { - }).then(function () { - new Promise(function (resolve, reject) { - if (self.server) { - server.close(resolve); - } else { - resolve(); - } - }); - }).then(callback); -}; - -function shouldImpersonate(user) { - return user !== undefined && user != null && user != ""; -} - -function authorize(options, namespace) { - return new Promise((resolve, reject) => { - try { - kubernetes.self_subject_access_review(options, namespace, - "list", "enmasse.io", "addresses").then(({allowed: allowed_list, reason: reason_list}) => { - if (allowed_list) { - kubernetes.self_subject_access_review(options, namespace, - "create", "enmasse.io", "addresses").then(({allowed: allowed_create, reason: reason_create}) => { - if (allowed_create) { - resolve({admin: allowed_create}); - } else { - kubernetes.self_subject_access_review(options, namespace, - "delete", "enmasse.io", "addresses").then(({allowed: allowed_delete, reason: reason_delete}) => { - resolve({admin: allowed_delete}); - if (!allowed_delete) { - log.info("User has neither create nor delete address permission, not granting admin permission. [%j, %j]", - reason_create, reason_delete); - } - }).catch(reject); - } - }).catch(reject); - } else { - log.warn("User does not have list address permission, granting no access. [%j]", reason_list); - resolve({admin: false}); - } - }).catch(reject); - } catch (e) { - reject(e); - } - }); -}; - -ConsoleServer.prototype.listen = function (env, callback) { - this.authz = require('./authz.js').policy(env); - var self = this; - - return new Promise((resolve, reject) => { - var port = env.port === undefined ? 56710 : env.port; - var opts = tls_options.get_console_server_options({port: port}, env); - const namespace = env.ADDRESS_SPACE_NAMESPACE; - - self.amqp_container.sasl_server_mechanisms['XOAUTH2'] = function () { - return { - outcome: undefined, - start: function (response, hostname) { - var resp = sasl.parseXOAuth2Reponse(response); - if (!"token" in resp) { - this.connection.sasl_failed('Unexpected response in XOAUTH2, no token part found'); - } - - var self = this; - function authenticate(token) { - return kubernetes.whois(token).then((user) => { - log.info("Authenticated as user : %s ", user.username); - if (shouldImpersonate(resp.user)) { - log.info("Impersonating user " + resp.user); - self.username = resp.user; - } else { - self.username = user.username; - } - return user.authenticated; - }).catch((e) => { - log.error("Failed to authenticate using token", e); - return false; - }) - } - - return Promise.resolve(authenticate(resp.token, hostname)) - .then((result) => { - if (result) { - var options = {"token": resp.token}; - if (shouldImpersonate(resp.user)) { - options.impersonateUser = resp.user; - } - return Promise.resolve(authorize(options, namespace)) - .then((results) => { - self.outcome = true; - self.admin = results.admin; - }) - .catch((e) => { - self.outcome = false; - }); - } else { - self.outcome = false; - } - }); - }, - }; - }; - - self.server = self.amqp_container.listen(opts); - log.info("AMQP server listening on port %d", port); - resolve(self.server); - }); -}; - -ConsoleServer.prototype.listen_health = function (env, callback) { - if (env.HEALTH_PORT !== undefined) { - var self = this; - var health = http.createServer(function (req, res) { - var pathname = url.parse(req.url).pathname; - if (pathname === "/metrics") { - var data = self.metrics.format_prometheus(new Date().getTime()); - res.writeHead(200, {'Content-Type': 'text/html'}); - res.end(data); - } else { - res.writeHead(200, {'Content-Type': 'text/plain'}); - res.end('OK'); - } - }); - return health.listen(env.HEALTH_PORT, callback); - } -}; - -function indexer(message) { - if (message.subject === 'address' && message.body) { - return message.body.address; - } else if (message.subject === 'address_deleted') { - return message.body; - } else if (message.subject === 'address_purged') { - return message.body; - } else if (message.subject === 'connection' && message.body) { - return message.body.id; - } else if (message.subject === 'connection_deleted') { - return message.body; - } else { - return undefined; - } -} - -ConsoleServer.prototype.subscribe = function (name, sender) { - - var buffered_sender = new BufferedSender(sender, indexer); - this.listeners[name] = buffered_sender; - this.addresses.for_each(function (address) { - buffered_sender.send({subject:'address', body:address}); - }, this.authz.address_filter(sender.connection)); - this.connections.for_each(function (conn) { - buffered_sender.send({subject:'connection', body:conn}); - }, this.authz.connection_filter(sender.connection)); -}; - -ConsoleServer.prototype.unsubscribe = function (name) { - delete this.listeners[name]; -}; - -ConsoleServer.prototype.publish = function (message) { - for (var name in this.listeners) { - var sender = this.listeners[name]; - if (this.authz.can_publish(sender.sender, message)) { - sender.send(message); - } - } -}; - -module.exports = ConsoleServer; diff --git a/agent/lib/events.js b/agent/lib/events.js deleted file mode 100644 index 1a1bfc2c5fd..00000000000 --- a/agent/lib/events.js +++ /dev/null @@ -1,63 +0,0 @@ -/* - * Copyright 2017 Red Hat Inc. - * - * 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. - */ - -var util = require('util'); -var myutils = require('./utils.js'); - -function event (component, reason, message, type, kind, name) { - var event_id = util.format('%s.%s', component, myutils.hash([kind, reason, name, message].join())); - var now = new Date().toISOString(); - return { - kind: 'Event', - metadata: { - name: event_id - }, - count: 1, - reason: reason, - message: message, - type: type, - firstTimestamp: now, - lastTimestamp: now, - source: { - component: component - }, - involvedObject: { - kind: kind, - name: name - } - }; -} - -module.exports.address_create = function (address) { - return event('agent', 'AddressCreated', util.format('%s %s created', address.type, address.address), 'Normal', 'Address', address.address); -}; - -module.exports.address_failed_create = function (address, error) { - return event('agent', 'AddressCreateFailed', util.format('Failed to create %s %s: %s', address.type, address.address, error), 'Warning', 'Address', address.address); -}; - -module.exports.address_delete = function (address) { - return event('agent', 'AddressDeleted', util.format('%s %s deleted', address.type, address.address), 'Normal', 'Address', address.address); -}; - -module.exports.address_failed_delete = function (address, error) { - return event('agent', 'AddressDeleteFailed', util.format('Failed to delete %s %s: %s', address.type, address.address, error), 'Warning', 'Address', address.address); -}; - -module.exports.equivalent = function (a, b) { - return a.name === b.name && a.reason === b.reason && a.involvedObject.kind === b.involvedObject.kind && a.involvedObject.name === b.involvedObject.name - && a.message === b.message; -} diff --git a/agent/lib/external_address_source.js b/agent/lib/external_address_source.js deleted file mode 100644 index 465dccc3873..00000000000 --- a/agent/lib/external_address_source.js +++ /dev/null @@ -1,48 +0,0 @@ -/* - * Copyright 2016 Red Hat Inc. - * - * 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. - */ -'use strict'; - -var log = require("./log.js").logger(); -var util = require('util'); -var events = require('events'); -var rhea = require('rhea'); - -function AddressSource(connection) { - var self = this; - events.EventEmitter.call(this); - var configserv = connection || require('./admin_service.js').connect_service(rhea, 'CONFIGURATION'); - configserv.open_receiver('v1/addresses').on('message', function (context) { - if (context.message.subject === 'enmasse.io/v1/AddressList') { - if (context.message.body && context.message.body.length) { - try { - var content = JSON.parse(context.message.body); - var defs = content.items ? content.items.map(function (address) { return address.spec; }) : []; - self.emit('addresses_defined', defs); - } catch (e) { - log.info('ERROR: failed to parse addresses: ' + e + '; ' + context.message.body); - } - } else { - self.emit('addresses_defined', []); - } - } else { - log.info('WARN: unexpected subject: ' + context.message.subject); - } - }); -} - -util.inherits(AddressSource, events.EventEmitter); - -module.exports = AddressSource; diff --git a/agent/lib/internal_address_source.js b/agent/lib/internal_address_source.js deleted file mode 100644 index 7b05f808e15..00000000000 --- a/agent/lib/internal_address_source.js +++ /dev/null @@ -1,417 +0,0 @@ -/* - * Copyright 2016 Red Hat Inc. - * - * 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. - */ -'use strict'; - -var http = require('http'); -var util = require('util'); -var events = require('events'); -var kubernetes = require('./kubernetes.js'); -var log = require('./log.js').logger(); -var myutils = require('./utils.js'); -var clone = require('clone'); - -function extract_spec(def, env) { - if (def.spec === undefined) { - console.error('no spec found on %j', def); - } - var o = myutils.merge({}, def.spec, {status:def.status}); - - o.name = def.metadata ? def.metadata.name : def.address; - o.addressSpace = def.metadata && def.metadata.annotations && def.metadata.annotations['addressSpace'] ? def.metadata.annotations['addressSpace'] : process.env.ADDRESS_SPACE; - o.addressSpaceNamespace = def.metadata ? def.metadata.namespace : process.env.ADDRESS_SPACE_NAMESPACE; - - if (def.status && def.status.brokerStatuses) { - o.allocated_to = clone(def.status.brokerStatuses); - } - return o; -} - -function is_defined (addr) { - return addr !== undefined; -} - -function ready (addr) { - return addr && addr.status && addr.status.phase !== 'Terminating' && addr.status.phase !== 'Pending'; -} - -function same_allocation(a, b) { - if (a === b) { - return true; - } else if (a == null || b == null || a.length !== b.length) { - return false; - } - for (var i in a) { - var equal = false; - for (var j in b) { - if (a[i].containerId === b[j].containerId && a[i].clusterId === b[j].clusterId && a[i].state === b[j].state) { - equal = true; - break; - } - } - if (!equal) { - return false; - } - } - return true; -} - -function same_address_definition_and_allocation(a, b) { - if (a.address === b.address && a.type === b.type && !same_allocation(a.allocated_to, b.allocated_to)) { - log.info('allocation changed for %s %s: %s <-> %s', a.type, a.address, JSON.stringify(a.allocated_to), JSON.stringify(b.allocated_to)); - } - return same_address_definition(a, b) - && same_allocation(a.allocated_to, b.allocated_to) - && same_plan_status(a.status ? a.status.planStatus : undefined, b.status ? b.status.planStatus : undefined) - && myutils.same_ttl(a.status ? a.status.messageTtl : undefined, b.status ? b.status.messageTtl : undefined); -} - -function same_address_definition(a, b) { - if (a === b) return true; - return a - && b - && a.address === b.address - && a.type === b.type - && a.plan === b.plan - && myutils.same_ttl(a.messageTtl, b.messageTtl); -} - -function same_address_status(a, b) { - if (a === b) return true; - return a - && b - && a.isReady === b.isReady - && a.phase === b.phase - && myutils.same_status_messages(a.messages, b.messages) - && same_plan_status(a.planStatus, b.planStatus) - && myutils.same_ttl(a.messageTtl, b.messageTtl); -} - -function same_address_definition_and_status(a, b) { - return same_address_definition(a, b) && same_address_status(a.status, b.status); -} - -function same_plan_status(a, b) { - if (a === b) return true; - return a && b && a.name === b.name && a.partitions === b.partitions && same_addressplan_resources(a.resources, b.resources); -} - -function same_addressplan_resources(a, b) { - if (a === b) return true; - return a && b && a.broker === b.broker && a.router === b.router; -} - -function address_compare(a, b) { - return myutils.string_compare(a.name, b.name); -} - -function by_address(a) { - return a.address; -} - -function description(list) { - return myutils.description(list, by_address); -} - -function AddressSource(config) { - this.config = config || {}; - this.selector = ""; - events.EventEmitter.call(this); -} - -AddressSource.prototype.start = function (addressplanssource) { - var options = myutils.merge({selector: this.selector, namespace: this.config.ADDRESS_SPACE_NAMESPACE}, this.config); - if (addressplanssource) { - addressplanssource.on('addressplans_defined', this.updated_plans.bind(this)) - } - - this.watcher = kubernetes.watch('addresses', options); - this.watcher.on('updated', this.updated.bind(this)); - this.readiness = {}; - this.last = {}; - this.plans = []; -}; - -util.inherits(AddressSource, events.EventEmitter); - -AddressSource.prototype.get_changes = function (name, addresses, unchanged) { - var c = myutils.changes(this.last[name], addresses, address_compare, unchanged, description); - this.last[name] = clone(addresses); - return c; -}; - -AddressSource.prototype.dispatch = function (name, addresses, description) { - log.info('%s: %s', name, description); - this.emit(name, addresses); -}; - -AddressSource.prototype.dispatch_if_changed = function (name, addresses, unchanged) { - var changes = this.get_changes(name, addresses, unchanged); - if (changes) { - this.dispatch(name, addresses, changes.description); - } -}; - -AddressSource.prototype.add_readiness_record = function (definition) { - var record = this.readiness[definition.address]; - if (record === undefined) { - record = {ready: false, address: definition.address, name: definition.name}; - this.readiness[definition.address] = record; - } -}; - -AddressSource.prototype.update_readiness_record = function (definition) { - if (this.readiness[definition.address] !== undefined) { - this.readiness[definition.address].ready = false; - } -}; - -AddressSource.prototype.delete_readiness_record = function (definition) { - delete this.readiness[definition.address]; -}; - -AddressSource.prototype.update_readiness = function (changes) { - if (changes.added.length > 0) { - changes.added.forEach(this.add_readiness_record.bind(this)); - } - if (changes.removed.length > 0) { - changes.removed.forEach(this.delete_readiness_record.bind(this)); - } - if (changes.modified.length > 0) { - changes.modified.forEach(this.update_readiness_record.bind(this)); - } -}; - -AddressSource.prototype.updated_plans = function (plans) { - this.plans = plans; -}; - -AddressSource.prototype.updated = function (objects) { - log.debug('addresses updated: %j', objects); - var self = this; - var addresses = objects.filter(is_defined).filter(function (address) { - return (self.config.ADDRESS_SPACE_PREFIX === undefined) || address.metadata.name.startsWith(self.config.ADDRESS_SPACE_PREFIX); - }).map(extract_spec); - var changes = this.get_changes('addresses_defined', addresses, same_address_definition_and_status); - if (changes) { - this.update_readiness(changes); - this.dispatch('addresses_defined', addresses, changes.description); - // used by standard - this.dispatch_if_changed('addresses_ready', addresses.filter(ready), same_address_definition_and_allocation); - } -}; - -AddressSource.prototype.update_status = function (record, ready) { - var self = this; - - function sanitizeTtlValue(ttl) { - return ttl && ttl < 1 ? undefined : ttl; - } - - function update(address) { - var updated = 0; - var messages = []; - if (address.status === undefined) { - address.status = {}; - } - - var planDef; - for (var p in self.plans) { - if (self.plans[p].metadata.name === address.spec.plan) { - planDef = self.plans[p]; - break - } - } - if (planDef && planDef.spec && planDef.spec.addressType === address.spec.type) { - if (address.status.planStatus === undefined) { - address.status.planStatus = {}; - } - if (planDef.metadata.name !== address.status.planStatus.name) { - address.status.planStatus.name = planDef.metadata.name; - updated++; - } - - planDef.spec.partitions = 1; // Always 1 for brokered - if (planDef.spec.partitions !== address.status.planStatus.partitions) { - address.status.planStatus.partitions = planDef.spec.partitions; - updated++; - } - if (planDef.spec.resources) { - address.status.planStatus.resources = clone(planDef.spec.resources); - updated++; - } else if (address.status.planStatus.resources) { - delete address.status.planStatus.resources; - updated++; - } - - var minimumTtl; - var maximumTtl; - if (planDef.spec && planDef.spec.messageTtl && planDef.spec.messageTtl.minimum) { - minimumTtl = sanitizeTtlValue(planDef.spec.messageTtl.minimum); - } - if (planDef.spec && planDef.spec.messageTtl && planDef.spec.messageTtl.maximum) { - maximumTtl = sanitizeTtlValue(planDef.spec.messageTtl.maximum); - } - if (maximumTtl && minimumTtl && minimumTtl > maximumTtl) { - maximumTtl = undefined; - minimumTtl = undefined; - } - - if (address.spec.messageTtl && address.spec.messageTtl.minimum && (minimumTtl === undefined || address.spec.messageTtl.minimum > minimumTtl)) { - minimumTtl = address.spec.messageTtl.minimum; - } - if (address.spec.messageTtl && address.spec.messageTtl.maximum && (maximumTtl === undefined || address.spec.messageTtl.maximum < maximumTtl)) { - maximumTtl = address.spec.messageTtl.maximum; - } - if (maximumTtl && minimumTtl && minimumTtl > maximumTtl) { - maximumTtl = undefined; - minimumTtl = undefined; - } - - if (minimumTtl || maximumTtl) { - address.status.messageTtl = {}; - if (address.status.messageTtl.minimum !== minimumTtl) { - address.status.messageTtl.minimum = minimumTtl; - updated++; - } - if (address.status.messageTtl.maximum !== maximumTtl) { - address.status.messageTtl.maximum = maximumTtl; - updated++; - } - } else { - delete address.status.messageTtl; - updated++; - } - } else { - ready = false; - messages.push("Unknown address plan '" + address.spec.plan + "'"); - delete address.status.planStatus; - updated++; - } - - if (address.status.isReady !== ready) { - address.status.isReady = ready; - updated++; - } - - var phase = ready ? 'Active' : 'Pending'; - if (address.status.phase !== phase) { - address.status.phase = phase; - updated++; - } - - if (!myutils.same_status_messages(address.status.messages, messages)) { - address.status.messages = messages; - updated++; - } - - if (!("annotations" in address.metadata) || !("enmasse.io/version" in address.metadata.annotations) || (address.metadata.annotations['enmasse.io/version'] !== self.config.VERSION)) { - address.metadata.annotations = myutils.merge({"enmasse.io/version": self.config.VERSION}, address.metadata.annotations); - updated++; - } - return updated ? address : undefined; - } - var options = {namespace: this.config.ADDRESS_SPACE_NAMESPACE}; - Object.assign(options, this.config); - return kubernetes.update('addresses/' + record.name, update, options).then(function (result) { - if (result === 200) { - record.ready = ready; - log.info('updated status for %s to %s: %s', record.address, record.ready, result); - } else if (result === 304) { - record.ready = ready; - log.debug('no need to update status for %j: %s', record, result); - } else { - log.error('failed to update status for %j: %s', record, result); - } - }).catch(function (error) { - log.error('failed to update status for %j: %j', record, error); - }); -}; - -AddressSource.prototype.check_status = function (address_stats) { - var results = []; - for (var address in this.readiness) { - var record = this.readiness[address]; - var stats = address_stats[address]; - if (stats === undefined) { - log.info('no stats supplied for %s (%s)', address, record.ready); - } else { - if (!record.ready) { - if (stats.propagated === 100) { - log.info('%s is now ready', address); - results.push(this.update_status(record, true)); - } - } else { - if (stats.propagated !== 100) { - log.info('%s is no longer ready', address); - results.push(this.update_status(record, false)); - } - } - } - } - return Promise.all(results); -}; - -AddressSource.prototype.check_address_plans = function () { - log.info('Address space plan or address plan(s) updated'); - // Change the readiness to false so the next check_status will cause the status to be updated. - for (var address in this.readiness) { - var record = this.readiness[address]; - record.ready = false; - } -}; - -AddressSource.prototype.create_address = function (definition, access_token) { - var address_name = this.config.ADDRESS_SPACE + "." + myutils.kubernetes_name(definition.address); - var address = { - apiVersion: 'enmasse.io/v1beta1', - kind: 'Address', - metadata: { - name: address_name, - namespace: this.config.ADDRESS_SPACE_NAMESPACE, - addressSpace: this.config.ADDRESS_SPACE - }, - spec: { - address: definition.address, - type: definition.type, - plan: definition.plan - } - }; - if (definition.type === 'subscription') { - address.spec.topic = definition.topic; - } - - var options = {token : access_token, - namespace: this.config.ADDRESS_SPACE_NAMESPACE}; - Object.assign(options, this.config); - return kubernetes.post('addresses', address, options).then(function (result, error) { - if (result >= 300) { - log.error('failed to create address for %j [%d %s]: %s', address, result, http.STATUS_CODES[result], error); - return Promise.reject(new Error(util.format('Failed to create address %j: %d %s %s', definition, result, http.STATUS_CODES[result], error))); - } else { - return Promise.resolve(); - } - }); -}; - -AddressSource.prototype.delete_address = function (definition, access_token) { - var address_name = definition.name; - var options = {token : access_token, - namespace: this.config.ADDRESS_SPACE_NAMESPACE}; - Object.assign(options, this.config); - return kubernetes.delete_resource('addresses/' + address_name, options); -}; - -module.exports = AddressSource; diff --git a/agent/lib/internal_addressplan_source.js b/agent/lib/internal_addressplan_source.js deleted file mode 100644 index 4a887f2dae8..00000000000 --- a/agent/lib/internal_addressplan_source.js +++ /dev/null @@ -1,108 +0,0 @@ -/* - * Copyright 2020 Red Hat Inc. - * - * 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. - */ -'use strict'; - -var util = require('util'); -var events = require('events'); -var kubernetes = require('./kubernetes.js'); -var log = require('./log.js').logger(); -var myutils = require('./utils.js'); -var clone = require('clone'); - -function AddressPlanSource(config) { - this.config = config || {}; - events.EventEmitter.call(this); -} - -AddressPlanSource.prototype.start = function (addressspaceplansource) { - var self = this; - var options = myutils.merge({api: '/apis/admin.enmasse.io/v1beta2/'}, this.config); - var started = false; - addressspaceplansource.on('addressspaceplan_defined', (addressspaceplan) => { - if (!started) { - self.watcher = kubernetes.watch('addressplans', options); - self.watcher.on('updated', self.updated.bind(self)); - started = true; - } - self.addressspaceplan = addressspaceplan; - if ('addressplans_defined' in self.last) { - self.updated(self.last['addressplans_defined']); - } else { - self.updated([]); - } - }); - this.last = {}; -}; - -util.inherits(AddressPlanSource, events.EventEmitter); - -AddressPlanSource.prototype.get_changes = function (name, addressplans, unchanged) { - var c = myutils.changes(this.last[name], addressplans, addressplan_compare, unchanged, description); - this.last[name] = clone(addressplans); - return c; -}; - -AddressPlanSource.prototype.dispatch = function (name, addresses, description) { - log.info('%s: %s', name, description); - this.emit(name, addresses); -}; - -AddressPlanSource.prototype.updated = function (objects) { - log.debug('addressplans updated: %j', objects); - var self = this; - var addressplans = objects.filter(ap => ap.metadata - && self.addressspaceplan - && self.addressspaceplan.spec - && self.addressspaceplan.spec.addressPlans - && self.addressspaceplan.spec.addressPlans.includes(ap.metadata.name)); - var changes = this.get_changes('addressplans_defined', addressplans, same_addressplan_definition_and_status); - if (changes) { - this.dispatch('addressplans_defined', addressplans, changes.description); - } -}; - -function same_addressplan_definition_and_status(a, b) { - return same_addressplan_definition(a.spec, b.spec) && same_addressplan_status(a.status, b.status); -} - -function same_addressplan_definition(a, b) { - if (a === b) return true; - return a && b && a.addressType === b.addressType && same_addressplan_resources(a.resources, b.resources) && myutils.same_ttl(a.messageTtl, b.messageTtl); -} - -function same_addressplan_resources(a, b) { - if (a === b) return true; - return a && b && a.broker === b.broker && a.router === b.router; -} - -function same_addressplan_status(a, b) { - if (a === b) return true; - return a && b && a.isReady === b.isReady && a.phase === b.phase && myutils.same_status_messages(a.messages, b.messages) && myutils.same_ttl(a.messageTtl, b.messageTtl); -} - -function addressplan_compare(a, b) { - return myutils.string_compare(a.name, b.name); -} - -function by_addressplan_name(a) { - return a.metadata.name; -} - -function description(list) { - return myutils.description(list, by_addressplan_name); -} - -module.exports = AddressPlanSource; diff --git a/agent/lib/internal_addressspaceplan_source.js b/agent/lib/internal_addressspaceplan_source.js deleted file mode 100644 index b8f764e7dd2..00000000000 --- a/agent/lib/internal_addressspaceplan_source.js +++ /dev/null @@ -1,107 +0,0 @@ -/* - * Copyright 2020 Red Hat Inc. - * - * 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. - */ -'use strict'; - -var util = require('util'); -var events = require('events'); -var kubernetes = require('./kubernetes.js'); -var log = require('./log.js').logger(); -var myutils = require('./utils.js'); -var clone = require('clone'); - - -function AddressSpacePlanSource(config) { - this.config = config || {}; - this.selector = ""; - events.EventEmitter.call(this); -}; - -AddressSpacePlanSource.prototype.start = function () { - this.addressSpacePlan = this.config.ADDRESS_SPACE_PLAN; - var options = myutils.merge({api: '/apis/admin.enmasse.io/v1beta2/'}, this.config); - this.watcher = kubernetes.watch('addressspaceplans', options); - this.watcher.on('updated', this.updated.bind(this)); - - this.last = {}; -}; - -util.inherits(AddressSpacePlanSource, events.EventEmitter); - -AddressSpacePlanSource.prototype.get_changes = function (name, addressspaceplan, unchanged) { - var c = myutils.changes(this.last[name], addressspaceplan, addressspaceplan_compare, unchanged, description); - this.last[name] = clone(addressspaceplan); - return c; -}; - -AddressSpacePlanSource.prototype.dispatch = function (name, addresses, description) { - log.info('%s: %s', name, description); - this.emit(name, addresses); -}; - -AddressSpacePlanSource.prototype.updated = function (objects) { - log.debug('addressspaceplans updated: %j', objects); - var self = this; - var addressspaceplans = objects.filter(asp => asp.metadata && self.addressSpacePlan === asp.metadata.name); - var changes = this.get_changes('addressspaceplan_defined', addressspaceplans, same_addressspaceplan_spec_and_status); - if (changes) { - this.dispatch('addressspaceplan_defined', addressspaceplans[0], changes.description); - } -}; - -function same_addressspaceplan_spec_and_status(a, b) { - return same_addressspaceplan_definition(a.spec, b.spec) && same_addressspaceplan_status(a.status, b.status); -} - -function same_addressspaceplan_definition(a, b) { - return same_addressplans(a.addressPlans, b.addressPlans); -} - -function same_addressplans(a, b) { - if (a === b) return true; - if (a == null || b == null || a.length !== b.length) return false; - for (var i = 0; i < a.length; i++) { - var found = false; - for (var j = 0; j < b.length; j++) { - if (b[j] === a[i]) { - found = true; - break - } - } - if (!found) { - return false; - } - } - return true; -} - -function same_addressspaceplan_status(a, b) { - if (a === b) return true; - return a && b && a.isReady === b.isReady && a.phase === b.phase && myutils.same_status_messages(a.messages, b.messages); -} - -function addressspaceplan_compare(a, b) { - return myutils.string_compare(a.name, b.name); -} - -function by_addressspaceplan_name(a) { - return a.metadata.name; -} - -function description(list) { - return myutils.description(list, by_addressspaceplan_name); -} - -module.exports = AddressSpacePlanSource; diff --git a/agent/lib/kubernetes.js b/agent/lib/kubernetes.js deleted file mode 100644 index b5b4da7cffa..00000000000 --- a/agent/lib/kubernetes.js +++ /dev/null @@ -1,505 +0,0 @@ -/* - * Copyright 2017 Red Hat Inc. - * - * 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. - */ -var log = require('./log.js').logger(); -var https = require('https'); -var fs = require('fs'); -var util = require('util'); -var events = require('events'); -var querystring = require('querystring'); -var set = require('./set.js'); -var myutils = require('./utils.js'); -var myevents = require('./events.js'); - -function watch_handler(collection) { - var partial = undefined; - return function (msg) { - var content = partial ? partial + msg : msg; - var start = 0; - for (var end = content.indexOf('\n', start); end > 0; start = end + 1, end = start < content.length ? content.indexOf('\n', start) : -1) { - var line = content.substring(start, end); - var event; - try { - event = JSON.parse(line); - } catch (e) { - log.warn('Could not parse message as JSON (%s), assuming incomplete: %s', e, line); - break; - } - collection[event.type.toLowerCase()](event.object); - } - partial = content.substring(start); - } -} - -var cache = {}; - -function read(file) { - if (cache[file] === undefined) { - cache[file] = fs.readFileSync(file); - setTimeout(function () { cache[file] = undefined; }, 1000*60*5);//force refresh every 5 minutes - } - return cache[file]; -} - -function get_options(options, path) { - var opts = { - hostname: options.host || process.env.KUBERNETES_SERVICE_HOST, - port: options.port || process.env.KUBERNETES_SERVICE_PORT, - rejectUnauthorized: false, - path: options.path || path, - headers: { - 'Authorization': 'Bearer ' + (options.token || process.env.KUBERNETES_TOKEN || read('/var/run/secrets/kubernetes.io/serviceaccount/token')), - 'Accept': 'application/json', - 'Content-Type': 'application/json', - } - }; - if (options.impersonateUser !== undefined) { - opts.headers['Impersonate-User'] = options.impersonateUser; - } - return opts; -} - -function get_path(base, resource, options, timeout) { - var namespace = options.namespace || process.env.KUBERNETES_NAMESPACE || read('/var/run/secrets/kubernetes.io/serviceaccount/namespace'); - var path = base + namespace + '/' + resource; - var queryParams = {}; - if (options.selector) { - queryParams.labelSelector = options.selector; - } - if (timeout) { - queryParams.timeoutSeconds = timeout; - } - if (Object.keys(queryParams).length > 0) { - path += '?' + querystring.stringify(queryParams); - } - return path; -} - -function build_api_from_resource(resource) { - return resource.startsWith("address") ? '/apis/enmasse.io/v1beta1/' : '/api/v1/'; -} - -function list_options(resource, baseOptions) { - let base = ("api" in baseOptions ? baseOptions.api : build_api_from_resource(resource)) + "namespaces/"; - let path = get_path(base, resource, baseOptions); - var options = get_options(baseOptions, path); - return myutils.merge({requestTimeout: baseOptions.requestTimeout || process.env.REQUEST_TIMEOUT || 120}, options); -} - -function watch_options(resource, baseOptions) { - var base = ("api" in baseOptions ? baseOptions.api : build_api_from_resource(resource)) + "watch/namespaces/"; - var timeout = baseOptions.requestTimeout || process.env.RESYNC_INTERVAL; - var options = get_options(baseOptions, get_path(base, resource, baseOptions, timeout)); - return myutils.merge({requestTimeout: timeout}, options); -} - -function apply_timeout(opts, request) { - if (opts.requestTimeout !== undefined) { - request.setTimeout(opts.requestTimeout * 1000, function () { - log.info('response %s timeout (%ds)', opts.path, opts.requestTimeout); - request.abort(); - }); - } -} - -function do_get_with_options(opts) { - return new Promise(function (resolve, reject) { - var request = https.get(opts, function(response) { - log.info('GET %s => %s ', opts.path, response.statusCode); - response.setEncoding('utf8'); - var data = ''; - response.on('data', function (chunk) { data += chunk; }); - response.on('end', function () { - if (response.statusCode === 200) { - try { - resolve(JSON.parse(data)); - } catch (e) { - reject(new Error(util.format('Could not parse message as JSON (%s): %s', e, data))); - } - } else { - var error = new Error(util.format('Failed to retrieve %s: %s %s', opts.path, response.statusCode, data)); - error.statusCode = response.statusCode; - reject(error); - } - }); - }); - request.on('error', reject); - apply_timeout(opts, request); - }); -}; - -function do_get(resource, options) { - var opts = list_options(resource, options || {}); - return do_get_with_options(opts); -}; - -function do_get_raw(path, options) { - var opts = get_options(options, path); - return do_get_with_options(opts); -} - -function do_request(method, input, opts, return_data = false) { - return new Promise(function (resolve, reject) { - opts.method = method; - var request = https.request(opts, function (response) { - var body = ''; - response.on('data', function (chunk) { - body += chunk; - }); - response.on('end', function () { - var statusCode = response.statusCode; - log.info('%s %s => %s', opts.method, opts.path, statusCode); - var statusObject = { - statusCode: statusCode, - body: body, - }; - if (statusCode >= 400) { - reject(statusObject); - } else { - resolve(return_data ? statusObject : statusCode); - } - }); - }); - request.on('error', reject); - apply_timeout(opts, request); - if (input) request.write(input); - request.end(); - }); -}; - -function do_post(resource, object, options) { - var opts = list_options(resource, options || {}); - return do_request('POST', JSON.stringify(object), opts); -}; - -function do_put(resource, object, options) { - var opts = list_options(resource, options || {}); - return do_request('PUT', JSON.stringify(object), opts); -}; - -function do_delete(resource, options) { - var opts = list_options(resource, options || {}); - return do_request('DELETE', undefined, opts); -}; - -function do_is_openshift() { - var opts = get_options({}, "/apis/user.openshift.io"); - return new Promise(function (resolve, reject) { - do_get_with_options(opts) - .then(() => {resolve(true)}) - .catch((e) => { - if (e.statusCode === 404) { - resolve(false); - } else { - reject(e); - }}); - }); -} - -function name_compare(a, b) { - return myutils.string_compare(a.metadata.name, b.metadata.name); -}; - -function Watcher (resource, options) { - events.EventEmitter.call(this); - this.closed = false; - this.resource = resource; - this.options = options; - this.set = set.sorted_object_set(name_compare); - this.delay = 0; - this.notify = myutils.coalesce(this._notify.bind(this), 100, 5000); -} - -util.inherits(Watcher, events.EventEmitter); - -Watcher.prototype._notify = function () { - var self = this; - setImmediate( function () { - self.emit('updated', self.set.to_array()); - }); -}; - -Watcher.prototype.list = function () { - var self = this; - do_get(this.resource, this.options).then(function (result) { - self.delay = 0; - self.set.reset(result.items); - self.notify(); - if (!self.closed) { - log.debug('list retrieved; watching...'); - self.watch(); - } else { - self.emit('closed'); - } - }).catch(function (error) { - log.error('failed to retrieve %s: %s (retry in %d seconds)', self.resource, error, self.delay); - setTimeout(self.list.bind(self), self.delay * 1000); - self.delay = Math.min(30, self.delay + 1); - }); -}; - -Watcher.prototype.watch = function () { - var self = this; - var opts = watch_options(this.resource, this.options); - var request = https.get(opts, function(response) { - log.info('GET %s => %s ', opts.path, response.statusCode); - response.setEncoding('utf8'); - response.on('data', watch_handler(self)); - }); - request.on('error', function(e) { - log.error('error on watch %s: %s', opts.path, e); - }); - request.on('close', function () { - if (!self.closed) { - log.debug('response %s ended; reconnecting...', opts.path); - self.list(); - } else { - self.emit('closed'); - } - }); - apply_timeout(opts, request); -}; - -function matcher(object) { - return function (o) { return o.metadata.name === object.metadata.name; }; -}; - -Watcher.prototype.added = function (object) { - if (this.set.insert(object)) { - this.notify(); - return true; - } else { - return false; - } -}; - -Watcher.prototype.modified = function (object) { - if (this.set.replace(object)) { - this.notify(); - return true; - } else { - return false; - } -}; - -Watcher.prototype.deleted = function (object) { - if (this.set.remove(object)) { - this.notify(); - return true; - } else { - return false; - } -}; - -Watcher.prototype.close = function () { - this.closed = true; - var self = this; - return new Promise(function (resolve) { - self.once('closed', function () { - resolve(); - }); - }); -}; - -module.exports.get_path = get_path; -module.exports.get_raw = do_get_raw; -module.exports.is_openshift = do_is_openshift; - -module.exports.get = function (resource, options) { - return do_get(resource, options); -}; - -module.exports.post = function (resource, object, options) { - return do_post(resource, object, options); -}; - -module.exports.delete_resource = function(resource, options) { - return do_delete(resource, options); -} - -module.exports.watch = function (resource, options) { - var w = new Watcher(resource, options); - w.list(); - return w; -}; - -module.exports.update = function (resource, transform, options) { - return do_get(resource, options).then(function (original) { - var updated = transform(original); - if (updated !== undefined) { - return do_put(resource, updated, options); - } else { - return 304; - } - }); -}; - -function post_new_event(event) { - return do_post('events', event).then(function (code, message) { - if (code >= 400) log.warn('failed to post new event: %j %s %s', event, code, message); - else log.debug('posted new event: %j %s %s', event, code); - }).catch(function (error) { - log.warn(' failed to post event: %j %s', event, error); - }); -} - -module.exports.post_event = function (event) { - event.involvedObject.namespace = process.env.KUBERNETES_NAMESPACE || read('/var/run/secrets/kubernetes.io/serviceaccount/namespace').toString(); - return do_get(util.format('events/%s', event.metadata.name)).then(function (original) { - if (myevents.equivalent(event, original)) { - original.count++; - original.lastTimestamp = event.lastTimestamp; - return do_put(util.format('events/%s', event.metadata.name), original).then(function (code, message) { - log.debug('updated existing event: %j %s %s', event, code, message); - }); - } else { - return post_new_event(event); - } - }).catch(function () { - return post_new_event(event); - }); -}; - - -module.exports.get_messaging_route_hostname = function (options) { - if (options.MESSAGING_ROUTE_HOSTNAME === undefined && options.KUBERNETES_SERVICE_HOST !== undefined) { - var messaging_route_name = options.MESSAGING_ROUTE_NAME || 'messaging-' + options.INFRA_UUID; - var opts = get_options(options, get_path('/oapi/v1/namespaces/', 'routes/' + messaging_route_name, options)); - return do_get_with_options(opts).then(function (definition) { - return definition.spec.host; - }).catch(function () { - log.info('could not retrieve messaging route hostname'); - return undefined; - }); - } else { - return Promise.resolve(options.MESSAGING_ROUTE_HOSTNAME); - } -}; - -module.exports.self_subject_access_review = function (options, namespace, verb, group, resource) { - - var opts = get_options(options, "/apis/authorization.k8s.io/v1/selfsubjectaccessreviews"); - var object = { - kind: "SelfSubjectAccessReview", - apiVersion: "authorization.k8s.io/v1", - spec: { - resourceAttributes: { - namespace: namespace, - verb: verb, - group: group, - resource: resource - } - }, - status: {allowed: false} - }; - - return new Promise(function (resolve, reject) { - do_request('POST', JSON.stringify(object), opts, true).then(({statusCode, body}) => - { - try { - if (body) { - var review = JSON.parse(body); - if (review && "status" in review) { - var status = review["status"]; - var allowed = status["allowed"]; - var reason = status["reason"]; - resolve({allowed, reason}); - return; - } - reject(new Error("Unexpectedly formed SelfSubjectAccessReview response : " + body)); - } else { - reject(new Error("Unexpectedly SelfSubjectAccessReview response status code : " + statusCode + " response body" + body)); - } - } catch (e) { - reject(e); - } - }).catch((e) => { - reject(e); - }); - }); -}; - -module.exports.whois = function (token) { - var opts = get_options({}, "/apis/authentication.k8s.io/v1/tokenreviews"); - var object = { - kind: "TokenReview", - apiVersion: "authentication.k8s.io/v1", - spec: { - token: token - }, - status: {authenticated: false} - }; - - return new Promise(function (resolve, reject) { - do_request('POST', JSON.stringify(object), opts, true).then(({statusCode, body}) => - { - try { - if (body) { - var result = JSON.parse(body); - if (result && "status" in result) { - var status = result["status"]; - var authenticated = status["authenticated"]; - var user = status["user"]; - var reason = status["error"]; - if (!authenticated) { - reject(new Error(reason)); - } else { - var response = { - username: user.username, - authenticated: authenticated, - reason: reason - }; - resolve(response); - } - return; - } - reject(new Error("Unexpectedly formed TokenReview response : " + body)); - } else { - reject(new Error("Unexpectedly TokenReview response status code : " + statusCode + " response body" + body)); - } - } catch (e) { - reject(e); - } - }).catch((e) => { - reject(e); - }); - }); -} - -module.exports.whoami = function (options) { - var opts = get_options(options, "/apis/user.openshift.io/v1/users/~"); - return new Promise(function (resolve, reject) { - do_request('GET', null, opts, true).then(({statusCode, body}) => - { - try { - if (body) { - var user = JSON.parse(body); - if (user && "metadata" in user) { - resolve({username: user.metadata.name}); - return; - } - reject(new Error("Unexpectedly formed User response : " + body)); - } else { - reject(new Error("Unexpectedly User response status code : " + statusCode + " response body" + body)); - } - } catch (e) { - reject(e); - } - }).catch((e) => { - reject(e); - }); - }); -}; - diff --git a/agent/lib/log.js b/agent/lib/log.js deleted file mode 100644 index 8dff65efbf7..00000000000 --- a/agent/lib/log.js +++ /dev/null @@ -1,67 +0,0 @@ -/* - * Copyright 2017 Red Hat Inc. - * - * 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. - */ -'use strict'; - -var path = require('path'); - -var levels = { - "error": 0, - "warn": 1, - "info": 2, - "debug": 3 -}; - -function Logger (name) { - this.logger = require('debug')(name); - this.level = 2; - this.name = name; - - if (process.env.LOGLEVEL) { - var desired_level = levels[process.env.LOGLEVEL]; - if (desired_level !== undefined) { - this.level = desired_level; - } - } -} - -function formatLevel(level, spaces) { - var nchars = level.length; - while (nchars < spaces) { - level += " "; - nchars++; - } - return level.toUpperCase(); -} - -Logger.prototype.log = function (level, str) { - var args = Array.prototype.slice.call(arguments, 2); - if (levels[level] !== undefined && levels[level] <= this.level) { - this.logger.apply(this.logger, [formatLevel(level, 5) + " " + str].concat(args)); - } -} - -module.exports.logger = function() { - var name = path.basename(process.argv[1], ".js"); - if (!process.env.DEBUG) { - process.env.DEBUG = name + "*"; - } - var logger = new Logger(name); - logger.debug = logger.log.bind(logger, "debug"); - logger.info = logger.log.bind(logger, "info"); - logger.warn = logger.log.bind(logger, "warn"); - logger.error = logger.log.bind(logger, "error"); - return logger; -} diff --git a/agent/lib/metrics.js b/agent/lib/metrics.js deleted file mode 100644 index aef1a8e5504..00000000000 --- a/agent/lib/metrics.js +++ /dev/null @@ -1,113 +0,0 @@ -/* - * Copyright 2016 Red Hat Inc. - * - * 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. - */ -'use strict'; - -var log = require("./log.js").logger(); -var util = require('util'); -var events = require('events'); - -function Metrics(address_space_namespace, address_space) { - this.address_space_namespace = address_space_namespace; - this.address_space = address_space; - this.addresses_total = 0; - this.addresses_ready_total = 0; - this.addresses_not_ready_total = 0; - - this.addresses_pending_total = 0; - this.addresses_configuring_total = 0; - this.addresses_active_total = 0; - this.addresses_failed_total = 0; - this.addresses_terminating_total = 0; - events.EventEmitter.call(this); -} - -util.inherits(Metrics, events.EventEmitter); - -Metrics.prototype.addresses_defined = function (addresses) { - this.addresses_total = 0; - this.addresses_ready_total = 0; - this.addresses_not_ready_total = 0; - - this.addresses_pending_total = 0; - this.addresses_configuring_total = 0; - this.addresses_active_total = 0; - this.addresses_failed_total = 0; - this.addresses_terminating_total = 0; - - for (var i = 0; i < addresses.length; i++) { - var address = addresses[i]; - if (address.status !== undefined) { - var phase = address.status.phase; - if (phase == 'Pending') { - this.addresses_pending_total++; - } else if (phase == 'Configuring') { - this.addresses_configuring_total++; - } else if (phase == 'Active') { - this.addresses_active_total++; - } else if (phase == 'Failed') { - this.addresses_failed_total++; - } else if (phase == 'Terminating') { - this.addresses_terminating_total++; - } - if (address.status.isReady) { - this.addresses_ready_total++; - } else { - this.addresses_not_ready_total++; - } - } - this.addresses_total++; - } - -} - -Metrics.prototype.format_prometheus = function(timestamp) { - var data = ""; - data += "# HELP addresses_total Total number of addresses\n"; - data += "# TYPE addresses_total gauge\n"; - data += "addresses_total{addressspace=\"" + this.address_space + "\",namespace=\"" + this.address_space_namespace + "\"} " + this.addresses_total + " " + timestamp + "\n"; - - data += "# HELP addresses_ready_total Total number of addresses with status ready\n"; - data += "# TYPE addresses_ready_total gauge\n"; - data += "addresses_ready_total{addressspace=\"" + this.address_space + "\",namespace=\"" + this.address_space_namespace + "\"} " + this.addresses_ready_total + " " + timestamp + "\n"; - - data += "# HELP addresses_not_ready_total Total number of addresses with status not ready\n"; - data += "# TYPE addresses_not_ready_total gauge\n"; - data += "addresses_not_ready_total{addressspace=\"" + this.address_space + "\",namespace=\"" + this.address_space_namespace + "\"} " + this.addresses_not_ready_total + " " + timestamp + "\n"; - - data += "# HELP addresses_pending_total Total number of addresses in pending state\n"; - data += "# TYPE addresses_pending_total gauge\n"; - data += "addresses_pending_total{addressspace=\"" + this.address_space + "\",namespace=\"" + this.address_space_namespace + "\"} " + this.addresses_pending_total + " " + timestamp + "\n"; - - data += "# HELP addresses_configuring_total Total number of addresses in configuring state\n"; - data += "# TYPE addresses_configuring_total gauge\n"; - data += "addresses_configuring_total{addressspace=\"" + this.address_space + "\",namespace=\"" + this.address_space_namespace + "\"} " + this.addresses_configuring_total + " " + timestamp + "\n"; - - data += "# HELP addresses_active_total Total number of addresses in active state\n"; - data += "# TYPE addresses_active_total gauge\n"; - data += "addresses_active_total{addressspace=\"" + this.address_space + "\",namespace=\"" + this.address_space_namespace + "\"} " + this.addresses_active_total + " " + timestamp + "\n"; - - data += "# HELP addresses_failed_total Total number of addresses in failed state\n"; - data += "# TYPE addresses_failed_total gauge\n"; - data += "addresses_failed_total{addressspace=\"" + this.address_space + "\",namespace=\"" + this.address_space_namespace + "\"} " + this.addresses_failed_total + " " + timestamp + "\n"; - - data += "# HELP addresses_terminating_total Total number of addresses in terminating state\n"; - data += "# TYPE addresses_terminating_total gauge\n"; - data += "addresses_terminating_total{addressspace=\"" + this.address_space + "\",namespace=\"" + this.address_space_namespace + "\"} " + this.addresses_terminating_total + " " + timestamp + "\n"; - - return data; -} - -module.exports = Metrics; diff --git a/agent/lib/pod_watcher.js b/agent/lib/pod_watcher.js deleted file mode 100644 index dfc96532ad3..00000000000 --- a/agent/lib/pod_watcher.js +++ /dev/null @@ -1,87 +0,0 @@ -/* - * Copyright 2017 Red Hat Inc. - * - * 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. - */ -'use strict'; - -var log = require("./log.js").logger(); -var util = require('util'); -var events = require('events'); -var kubernetes = require('./kubernetes.js'); - -function extract_pod_definition (object) { - var pod = { - name: object.metadata.name, - host: object.status.podIP, - phase: object.status.phase, - annotations: object.metadata.annotations - }; - for (var i in object.status.conditions) { - if (object.status.conditions[i].type === 'Ready') { - pod.ready = object.status.conditions[i].status; - } - } - if (object.spec.containers) { - pod.ports = {}; - for (var i in object.spec.containers) { - var c = object.spec.containers[i]; - if (c.ports) { - pod.ports[c.name] = {}; - for (var j in c.ports) { - var p = c.ports[j]; - pod.ports[c.name][p.name] = p.containerPort; - } - } - } - } - return pod; -}; - -function PodWatcher (options) { - events.EventEmitter.call(this); - this.watcher = kubernetes.watch('pods', options); - this.watcher.on('updated', this.on_update.bind(this)); -} - -util.inherits(PodWatcher, events.EventEmitter); - -PodWatcher.prototype.on_update = function (pods) { - this.emit('updated', pods.map(extract_pod_definition)); -}; - -PodWatcher.prototype.close = function () { - this.watcher.close(); -}; - -module.exports.watch = function (selector, env) { - var options = env || {}; - if (selector) { - options.selector = selector - } - - if (process.env.INFRA_UUID) { - var infraSelector = "infraUuid=" + process.env.INFRA_UUID; - if (options.selector) { - options.selector += "," + infraSelector; - } else { - options.selector = infraSelector; - } - } - - return new PodWatcher(options); -}; - -module.exports.get_pod_definition = function (object) { - return extract_pod_definition(object); -}; \ No newline at end of file diff --git a/agent/lib/podgroup.js b/agent/lib/podgroup.js deleted file mode 100644 index 5a7833387e2..00000000000 --- a/agent/lib/podgroup.js +++ /dev/null @@ -1,122 +0,0 @@ -/* - * Copyright 2016 Red Hat Inc. - * - * 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. - */ -'use strict'; - -var log = require("./log.js").logger(); -var path = require('path'); -var fs = require('fs'); -var artemis = require('./artemis.js'); -var tls_options = require('./tls_options.js'); -var utils = require('./utils'); - -function get_broker_port(pod) { - return utils.get(pod.ports, ['broker', 'amqp'], 5673); -}; - -function BrokerPod(pod) { - this.name = pod.name; - var options = { - id: pod.name, - host: pod.host, - port: get_broker_port(pod) - }; - try { - options = tls_options.get_client_options(options); - options.username = 'anonymous'; - } catch (error) { - log.error(error); - } - this.broker = artemis.connect(options); -}; - -BrokerPod.prototype.close = function () { - this.broker.close(); -}; - -function PodGroup(constructor) { - this.pods = {}; - this.constructor = constructor || BrokerPod; -}; - -PodGroup.prototype.update = function (latest) { - var changed = false; - var indexed = {}; - for (var i in latest) { - var pod = latest[i]; - indexed[pod.name] = pod; - if (this.pods[pod.name] === undefined) { - this.added(pod); - changed = true; - log.info('added pod ' + JSON.stringify(pod)); - } - } - for (var name in this.pods) { - if (indexed[name] === undefined) { - this.removed_by_name(name) - changed = true; - log.info('removed pod named ' + name); - } - } - return changed; -} - -PodGroup.prototype.added = function (pod) { - this.pods[pod.name] = new this.constructor(pod); -}; - -PodGroup.prototype.removed_by_name = function (podname) { - if (this.pods[podname].close) this.pods[podname].close(); - delete this.pods[podname]; -}; - -PodGroup.prototype.removed = function (pod) { - this.remove_by_name(pod.name); -}; - -PodGroup.prototype.pod_list = function () { - var list = []; - for (var i in this.pods) { - list.push(this.pods[i]); - } - return list; -}; - -PodGroup.prototype.broker_list = function () { - var list = []; - for (var i in this.pods) { - list.push(this.pods[i].broker); - } - return list; -}; - -PodGroup.prototype.empty = function () { - return Object.keys(this.pods).length === 0; -}; - -PodGroup.prototype.close = function () { - var self = this; - Object.keys(this.pods).forEach(function (n) { - self.removed_by_name(n); - }); -}; - -PodGroup.prototype.get_port_from_pod_definition = function (pod, container, port_name) { - return utils.get(pod.ports, [container, port_name || 'amqp']); -}; - -module.exports = function (constructor) { - return new PodGroup(constructor); -} diff --git a/agent/lib/qdr.js b/agent/lib/qdr.js deleted file mode 100644 index c3d1c6c74d9..00000000000 --- a/agent/lib/qdr.js +++ /dev/null @@ -1,376 +0,0 @@ -/* - * Copyright 2016 Red Hat Inc. - * - * 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. - */ -'use strict'; - -var log = require("./log.js").logger(); -var util = require('util'); - -var MAX_OUTSTANDING = process.env.MAX_OUTSTANDING_QDR_REQUESTS || 250; - -var Router = function (connection, router, agent) { - if (router) { - this.target = agent; - this.connection = router.connection; - this.counter = router.counter; - this.handlers = router.handlers; - this.requests = router.requests; - this.address = router.address; - this.sender = router.sender; - this.tracking = router.tracking; - } else { - this.target = '$management'; - this.sender = connection.open_sender(); - this.sender.on('sendable', this._send_pending_requests.bind(this)); - this.connection = connection; - this.counter = 0; - this.handlers = {}; - this.requests = []; - connection.open_receiver({source:{dynamic:true}}); - connection.on('message', this.incoming.bind(this)); - connection.on('connection_close', this.closed.bind(this)); - this.tracking = { - sent: 0, - recv: 0, - outstanding: 0, - unexpected_responses: 0 - }; - this.last_status = undefined; - var self = this; - var interval = process.env.STATUS_LOG_INTERVAL || 300000; - this.timer = setInterval(function () { - self.health_check(); - }, interval); - } - - this.connection.setMaxListeners(0); - this.connection.on('receiver_open', this.ready.bind(this)); - this.connection.on('disconnected', this.disconnected.bind(this)); - this.connection.on('sender_error', this.on_sender_error.bind(this)); -}; - -Router.prototype.get_id = function () { - if (this.name) { - return this.name + ':' + this.connection.container_id; - } else { - return this.connection.container_id; - } -}; - -Router.prototype.log_info = function () { - log.info('[%s] qdr handlers: %d, requests pending: %d, requests sent: %d, responses received: %d, unexpected response: %d, ready: %s', - this.get_id(), Object.keys(this.handlers).length, this.requests.length, - this.tracking.sent, this.tracking.recv, this.tracking.unexpected_responses, (this.address !== undefined)); -}; - -Router.prototype.health_check = function () { - var current = this._get_status(); - var last = this.last_status; - if (this.last_status === undefined) { - this.last_status = current; - } else { - var ready_state; - if (current.ready) { - if (last.ready) { - ready_state = 'remains active'; - } else { - ready_state = 'transitioned to active'; - } - } else { - if (last.ready) { - ready_state = 'transitioned to inactive'; - } else { - ready_state = 'remains inactive'; - } - } - - var messages = []; - var still_outstanding = current.outstanding.filter(function (id) { return last.outstanding.indexOf(id) >=0; }); - if (still_outstanding.length > 0) { - messages.push(util.format('still waiting for responses for %j', still_outstanding)); - } - - if (current.unsent > last.unsent && current.sent === last.sent) { - messages.push('sending of requests appears to be blocked'); - } - - if (current.unexpected_responses > last.unexpected_responses) { - messages.push(util.format('%d unexpected responses since last status', current.unexpected_responses - last.unexpected_responses)); - } - - if (messages.length) { - log.warn('[%s] %s (%s)', this.get_id(), messages.join(', '), ready_state); - } else if (current.sent !== last.sent || current.recv != last.recv) { - log.info('[%s] %s sent %d requests and received %d responses since last status; %d outstanding requests issued', this.get_id(), - ready_state, current.sent - last.sent, current.recv - last.recv, current.outstanding); - } else { - log.info('[%s] %s no requests/responses since last status', this.get_id(), ready_state); - } - last = current; - } -}; - -Router.prototype._get_status = function () { - return { - sent: this.tracking.sent, - recv: this.tracking.recv, - unexpected_responses: this.tracking.unexpected_responses, - unsent: this.requests.length, - outstanding: Object.keys(this.handlers), - ready: (this.address !== undefined) - }; -}; - -Router.prototype.closed = function (context) { - if (context.connection.error) { - log.error('[%s] ERROR: router closed connection with %s', this.get_id(), context.connection.error.description); - } - log.info('[%s] router closed %s, abort_idle: %s', this.get_id(), this.target, context.connection.abort_idle); - this.address = undefined; - this._abort_requests('closed'); - this._clear_timer(); -}; - -Router.prototype._abort_requests = function (error) { - log.info('[%s] aborting pending requests: %s', this.get_id(), error); - for (var h in this.handlers) { - try { - this.handlers[h](error); - } catch (e) { - log.warn("[%s] ignoring handler error while aborting requests: %s", this.get_id(), e) - } - delete this.handlers[h]; - } - while (this.requests.length > 0) { this.requests.shift(); }; -} - -Router.prototype.on_sender_error = function (context) { - log.info('[%s] sender error %s', this.get_id(), context.sender.error); -}; - -Router.prototype.disconnected = function (context) { - this.address = undefined; - log.info('[%s] router disconnected %s, abort_idle: %s', this.get_id(), this.target, context.connection.abort_idle); - this._abort_requests('disconnected'); -} - -Router.prototype.ready = function (context) { - log.info('[%s] router ready', this.get_id()); - this.address = context.receiver.source.address; - this._send_pending_requests(); -}; - -function create_record(names, values) { - var record = {}; - for (var j = 0; j < names.length; j++) { - record[names[j]] = values[j]; - } - return record; -} - -function extract_records(body) { - return body.results ? body.results.map(create_record.bind(null, body.attributeNames)) : body; -} - -function as_handler(resolve, reject) { - return function (context) { - if (context == null) { - reject(null); - return; - } - try { - var message = context.message === undefined ? context : context.message; - if (message.application_properties) { - if (message.application_properties.statusCode >= 200 && message.application_properties.statusCode < 300) { - if (message.body) resolve(extract_records(message.body)); - else resolve({ - code: message.application_properties.statusCode, - description: message.application_properties.statusDescription - }); - } else { - reject({ - code: message.application_properties.statusCode, - description: message.application_properties.statusDescription - }); - } - } else { - reject(message.toString()); - } - } catch (e) { - reject(e); - } - }; -} - -Router.prototype._sendable = function () { - return this.sender.sendable() && this.tracking.outstanding < MAX_OUTSTANDING; -} - -Router.prototype._send_pending_requests = function () { - if (this.address === undefined) return false; - - var i = 0; - while (i < this.requests.length && this._sendable()) { - this._send_request(this.requests[i++]); - } - this.requests.splice(0, i); - return this.requests.length === 0 && this._sendable(); -} - -Router.prototype._send_request = function (request) { - request.reply_to = this.address; - this.sender.send(request); - this.tracking.sent++; - this.tracking.outstanding++; - log.debug('sent: %j', request); -} - -Router.prototype.request = function (operation, properties, body) { - var id = this.target + this.counter.toString(); - this.counter++; - var req = {correlation_id:id, to:this.target}; - req.application_properties = properties || {}; - req.application_properties.operation = operation; - req.body = body; - - if (this._send_pending_requests()) { - this._send_request(req); - } else { - this.requests.push(req); - } - var handlers = this.handlers; - return new Promise(function (resolve, reject) { - handlers[id] = as_handler(resolve, reject); - }); -} - -Router.prototype.query = function (type, options) { - return this.request('QUERY', {entityType:type}, options || {attributeNames:[]}); -}; - -Router.prototype.create_entity = function (type, name, attributes) { - return this.request('CREATE', {'type':type, 'name':name}, attributes || {}); -}; - -Router.prototype.update_entity = function (type, identity, attributes) { - return this.request('UPDATE', {'type':type, 'identity':identity}, attributes || {}); -}; - -Router.prototype.delete_entity = function (type, name) { - return this.request('DELETE', {'type':type, 'name':name}, {}); -}; - -Router.prototype.get_mgmt_nodes = function () { - return this.request('GET-MGMT-NODES', {}, {}); -}; - -Router.prototype._get_all_routers = function () { - var self = this; - return this.get_mgmt_nodes().then( - function (agents) { - return agents.map(function (agent) { return new Router(undefined, self, agent); }); - } - ); -}; - -Router.prototype.get_all_routers = function (current) { - if (current === undefined || current.length === 0) { - return this._get_all_routers(); - } else { - var self = this; - return this.get_mgmt_nodes().then( - function (results) { - var agents = {}; - results.forEach(function (name) { agents[name] = true; }); - var routers = []; - for (var i = 0; i < current.length; i++) { - if (agents[current[i].target] !== undefined) { - delete agents[current[i].target]; - routers.push(current[i]); - } - } - return routers.concat( Object.keys(agents).map(function (agent) { return new Router(undefined, self, agent); }) ); - } - ); - } -}; - -Router.prototype.incoming = function (context) { - log.debug('recv: %j', context.message); - this.tracking.recv++ - this.tracking.outstanding--; - var message = context.message; - var handler = this.handlers[message.correlation_id]; - if (handler) { - delete this.handlers[message.correlation_id]; - handler(context); - } else { - this.tracking.unexpected_responses++; - log.warn('WARNING: unexpected response: ' + message.correlation_id + ' [' + JSON.stringify(message) + ']'); - } - this._send_pending_requests(); -}; - -Router.prototype._clear_timer = function () { - if (this.timer) { - clearInterval(this.timer); - this.timer = null; - } -}; - -Router.prototype.close = function () { - if (this.connection) this.connection.close(); - this._clear_timer(); -}; - -function add_resource_type (crudFlags, name, typename, plural) { - var resource_type = typename || name; - var plural_name = plural || name + 's'; - if (crudFlags.indexOf("C") > -1) { - Router.prototype['create_' + name] = function (o) { - return this.create_entity(resource_type, o.name, o); - } - } - if (crudFlags.indexOf("R") > -1) { - Router.prototype['get_' + plural_name] = function (options) { - return this.query(resource_type, options); - } - } - if (crudFlags.indexOf("U") > -1) { - Router.prototype['update_' + name] = function (o, attributes) { - return this.update_entity(resource_type, o.identity, attributes); - } - } - if (crudFlags.indexOf("D") > -1) { - Router.prototype['delete_' + name] = function (o) { - return this.delete_entity(resource_type, o.name); - } - } -} - -add_resource_type('CRD', 'connector'); -add_resource_type('CRD', 'listener'); -add_resource_type('CRD', 'address', 'org.apache.qpid.dispatch.router.config.address', 'addresses'); -add_resource_type('CRD', 'link_route', 'org.apache.qpid.dispatch.router.config.linkRoute'); - -add_resource_type('RU', 'connection', 'org.apache.qpid.dispatch.connection'); -add_resource_type('R', 'link', 'org.apache.qpid.dispatch.router.link'); -add_resource_type('R', 'address_stat', 'org.apache.qpid.dispatch.router.address'); - -var amqp = require('rhea').create_container(); -module.exports.Router = Router; -module.exports.connect = function (options) { - return new Router(amqp.connect(options)); -} diff --git a/agent/lib/queue.js b/agent/lib/queue.js deleted file mode 100644 index a7d3fe76d4b..00000000000 --- a/agent/lib/queue.js +++ /dev/null @@ -1,153 +0,0 @@ -/* - * Copyright 2017 Red Hat Inc. - * - * 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. - */ -'use strict'; - -var kubernetes = require('./kubernetes.js'); -var log = require('./log.js').logger(); -var pod_watcher = require('./pod_watcher'); -var tls_options = require('./tls_options.js'); -var artemis = require('./artemis.js'); -var utils = require('./utils'); - -function Queue (addr) { - this.addr = addr; - this.name = addr.address; -}; - -function get_current_pods () { - var options = { - selector: 'role=broker' - }; - - if (process.env.INFRA_UUID) { - var infraSelector = "infraUuid=" + process.env.INFRA_UUID; - if (options.selector) { - options.selector += "," + infraSelector; - } - } - return kubernetes.get('pods', options); -} - -function get_pods (pods) { - var options = { - selector: 'name in ('+pods.join()+')' - }; - - if (process.env.INFRA_UUID) { - var infraSelector = "infraUuid=" + process.env.INFRA_UUID; - if (options.selector) { - options.selector += "," + infraSelector; - } - } - return kubernetes.get('pods', options); -} - -function get_broker_port(pod) { - return utils.get(pod.ports, ['broker', 'amqp'], 5673); -}; - -function create_brokers(podDefs, address_space) { - return podDefs.map(function (podDef){ - - var options; - if (address_space === 'brokered') { - options = { - username: 'console', - id: podDef.name, - host: process.env.BROKER_SERVICE_HOST, - port: process.env.BROKER_SERVICE_PORT - }; - } else { - options = { - username:'anonymous', - host: podDef.host, - port: get_broker_port(podDef), - id: podDef.name, - }; - } - try { - options = tls_options.get_client_options(options); - } catch (error) { - log.error(error); - } - - return artemis.connect(options); - }); -}; - -function purge_brokered_queue(queueName) { - return get_current_pods().then( - function (currentPods) { - - var podDefs = currentPods.items.map(function (pod) { - return pod_watcher.get_pod_definition(pod); - }); - - var broker_cons = create_brokers(podDefs, 'brokered'); - var purgedQueues = broker_cons.map(broker => broker.purgeQueue(queueName)); - - //for the brokered address space, there will never be more then 1 broker. - return Promise.all(purgedQueues) - .then(sum_total_purged) - .finally(() => { - Promise.all(broker_cons.map(c => c.close())) - .catch((e) => log.warn("Failed to close purge connection", e)); - }); - }); -}; - -function sum_total_purged(results) { - var totalPurged = 0; - results.forEach((c) => totalPurged += c); - log.debug("Purged %d message(s) from %d shard(s)", totalPurged, results.length); - return totalPurged; -} - -Queue.prototype.purge = function () { - var queueName = this.name; - var addr = this.addr; - - if (process.env.ADDRESS_SPACE_TYPE === 'brokered') { - return purge_brokered_queue(queueName); - } else { - var brokerStatuses = 'status' in addr && 'brokerStatuses' in addr.status ? addr.status.brokerStatuses : []; - var brokerNames = brokerStatuses.map(function (brokerStatus) { - return brokerStatus.clusterId; - }); - return get_pods(brokerNames).then( - function (brokerPods) { - var podDefs = brokerPods.items.map(function (pod) { - return pod_watcher.get_pod_definition(pod); - }); - - var broker_cons = create_brokers(podDefs, 'standard'); - var purgedQueues = broker_cons.map(broker => broker.purgeQueue(queueName)); - - return Promise.all(purgedQueues) - .then(sum_total_purged) - .finally(() => { - Promise.all(broker_cons.map(c => c.close())) - .catch((e) => log.warn("Failed to close purge connection", e)); - }); - - }); - } -}; - - -module.exports = function (name) { - return new Queue(name); -}; diff --git a/agent/lib/ragent.js b/agent/lib/ragent.js deleted file mode 100644 index 81f3892517c..00000000000 --- a/agent/lib/ragent.js +++ /dev/null @@ -1,458 +0,0 @@ -/* - * Copyright 2016 Red Hat Inc. - * - * 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. - */ -'use strict'; - -var events = require('events'); -var http = require('http'); -var util = require('util'); -var amqp = require('rhea'); -var AddressSource = require('./internal_address_source.js'); -var log = require("./log.js").logger(); -var rtr = require('./router.js'); -var pod_watcher = require('./pod_watcher.js'); -var tls_options = require('./tls_options.js'); -var Artemis = require('./artemis.js'); -var broker_controller = require('./broker_controller.js'); - -function wrap_known_routers (routers) { - var data = {}; - for (var k in routers) { - data[k] = routers[k].listeners; - } - return data; -} - -function unwrap_known_routers (data) { - var result = {}; - for (var k in data) { - result[k] = rtr.known(k, data[k]); - } - return result; -} - -function get_product (connection) { - if (connection.properties) { - return connection.properties.product; - } else { - return undefined; - } -} - -function Ragent() { - this.known_routers = {}; - this.connected_routers = {}; - this.connected_brokers = {}; - this.addresses = []; - this.subscribers = {}; - this.clients = {}; - this.container = amqp.create_container(); - this.container.sasl_server_mechanisms.enable_anonymous(); - this.configure_handlers(); - this.status = new events.EventEmitter(); - this.disable_connectivity = false; - this.addresses_initialised = false; -} - -Ragent.prototype.subscribe = function (context) { - this.subscribers[context.connection.container_id] = context.sender; - //send initial routers: - var payload = wrap_known_routers(this.connected_routers); - context.sender.send({subject:'routers', body:payload}); -}; - -Ragent.prototype.unsubscribe = function (context) { - delete this.subscribers[context.connection.container_id]; -} - -Ragent.prototype.add_client = function (context) { - var id = amqp.generate_uuid(); - context.sender.set_source({address:id}); - this.clients[context.connection.container_id] = context.sender; -} - -Ragent.prototype.remove_client = function (context) { - delete this.clients[context.connection.container_id]; -} - -Ragent.prototype.get_all_routers = function () { - var all_routers = {}; - for (var g in this.known_routers) { - var group = this.known_routers[g]; - for (var key in group) { - all_routers[key] = group[key]; - } - } - for (var r in this.connected_routers) { - all_routers[r] = this.connected_routers[r]; - } - - return all_routers; -} - -Ragent.prototype.check_connectivity = function () { - if (!this.disable_connectivity) { - var all_routers = this.get_all_routers(); - - for (var r in this.connected_routers) { - this.check_router_connectors(this.connected_routers[r], all_routers); - } - } -} - -Ragent.prototype.check_router_connectors = function (router, all_routers) { - if (!this.disable_connectivity) { - try { - if (router.is_ready_for_connectivity_check()) { - log.info('checking connectivity for ' + router.container_id); - router.check_connectors(all_routers || this.get_all_routers()); - } else { - log.info(router.container_id + ' not ready for connectivity check: ' + router.initial_provisioning_completed + ' ' + (router.connectors !== undefined)); - } - } catch (error) { - log.error('connectivity check failed for %s: %s', router.container_id, error); - } - } -} - -Ragent.prototype.connected_routers_updated = function (router) { - this.check_connectivity(); - for (var id in this.subscribers) { - var sender = this.subscribers[id]; - var payload = wrap_known_routers(this.connected_routers); - sender.send({subject:'routers', body:payload}); - } -} - -Ragent.prototype.router_disconnected = function (context) { - delete this.connected_routers[context.connection.container_id]; - log.info('router ' + context.connection.container_id + ' disconnected'); - //update now, or wait a bit? - //connected_routers_updated(); -} - -Ragent.prototype.addresses_updated = function () { - for (var b in this.connected_brokers) { - this.sync_broker(this.connected_brokers[b]); - } - for (var r in this.connected_routers) { - this.sync_router_addresses(this.connected_routers[r], Object.keys(this.connected_brokers)); - } -} - -function is_valid_address_definition(def) { - return def.address && def.type; -} - -Ragent.prototype.sync_addresses = function (updated) { - this.addresses_initialised = true; - this.addresses = updated.filter(is_valid_address_definition); - log.debug('addresses updated: %j', this.addresses); - this.addresses_updated(); -} - -Ragent.prototype.sync_router_addresses = function (router, brokers) { - router.sync_addresses(this.addresses, brokers); -} - -Ragent.prototype.verify_addresses = function (expected) { - for (var r in this.connected_routers) { - if (!this.connected_routers[r].verify_addresses(expected)) { - return false; - } - } - return true; -} - -Ragent.prototype.on_router_agent_disconnect = function (context) { - delete this.known_routers[context.connection.container_id]; -} - -Ragent.prototype.on_broker_disconnect = function (context) { - log.info('broker disconnected: %s', context.connection.container_id); - if (this.connected_brokers[context.connection.container_id]) { - this.connected_brokers[context.connection.container_id].close(); - delete this.connected_brokers[context.connection.container_id]; - this.on_synchronized(); - } -} - -Ragent.prototype.are_brokers_synchronized = function () { - for (var b in this.connected_brokers) { - if (!this.connected_brokers[b].addresses_synchronized) { - return false; - } - } - return true; -}; - -Ragent.prototype.are_routers_synchronized = function () { - for (var r in this.connected_routers) { - if (!this.connected_routers[r].is_synchronized()) { - return false; - } - } - return true; -}; - -Ragent.prototype.are_routers_connected = function () { - if (Object.keys(this.connected_routers).length === 1) { - return true; - } else { - for (var r in this.connected_routers) { - if (!this.connected_routers[r].fully_connected) { - return false; - } - } - return true; - } -}; - -Ragent.prototype.is_synchronized = function () { - return this.are_routers_synchronized() && this.are_brokers_synchronized() && this.are_routers_connected(); -}; - -Ragent.prototype.on_synchronized = function () { - this.status.emit('synchronized'); -}; - -Ragent.prototype._check_stable = function (addresses, routers, brokers, all_known_routers) { - try { - var result = (addresses === undefined || addresses === Object.keys(this.addresses).length) - && (routers === undefined || routers === Object.keys(this.connected_routers).length) - && (brokers === undefined || brokers === Object.keys(this.connected_brokers).length) - && this.is_synchronized(); - return result; - } catch (e) { - console.error(e.stack); - return false; - } -} - - -Ragent.prototype.wait_for_stable = function (addresses, routers, brokers) { - var self = this; - if (this._check_stable(addresses, routers, brokers)) { - return true; - } else { - return new Promise(function (resolve, reject) { - function do_test() { - if (self._check_stable(addresses, routers, brokers)) { - resolve(); - } else { - self.status.once('synchronized', do_test); - } - } - self.status.once('synchronized', do_test); - }); - } -}; - - -function if_allocated_to (id) { - return function (a) { - for (var i in a.allocated_to) { - if (a.allocated_to[i].containerId === id) { - return true; - } - } - return false; - }; -} - -function get_address (a) { return a.address; } - -Ragent.prototype.sync_broker = function (broker) { - var allocated = this.addresses.filter(if_allocated_to(broker.id)); - log.debug('syncing broker %s with %j', broker.id, allocated.map(get_address)); - broker.sync_addresses(allocated); -} - -var connection_properties = {product:'ragent', container_id:process.env.HOSTNAME}; - -// watch for- and connect to- other router agents to share knowledge of -// all routers -Ragent.prototype.watch_pods = function (env) { - var parent = this; - var agents; - function RagentPod(pod) { - this.name = pod.name; - var options = { - host:pod.host, - port:agents.get_port_from_pod_definition(pod, 'ragent'), - id:pod.name, - properties:connection_properties - }; - log.info('connecting to new agent %s', options); - this.agent = parent.container.connect(options); - this.agent.open_receiver('routers'); - var agent = this.agent; - this.close = function () { - log.info('disconnecting from agent ' + pod.name); - agent.close(); - } - } - agents = require('./podgroup.js')(RagentPod); - this.watcher = pod_watcher.watch('name=admin', env); - this.watcher.on('updated', agents.update.bind(agents)); -} - -Ragent.prototype.on_message = function (context) { - if (context.message.subject === 'routers') { - this.known_routers[context.connection.container_id] = unwrap_known_routers (context.message.body); - this.check_connectivity(); - } else if (context.message.subject === 'health-check') { - var request = context.message; - var content = JSON.parse(request.body); - var reply_to = request.reply_to; - var response = {to: reply_to}; - response.correlation_id = request.correlation_id; - response.body = this.verify_addresses(content); - var sender = this.clients[context.connection.container_id]; - if (sender) { - sender.send(response); - } - } else { - log.info('ERROR: unrecognised subject ' + context.message.subject); - } -} - -Ragent.prototype.configure_handlers = function () { - var self = this; - this.container.on('connection_open', function(context) { - var product = get_product(context.connection); - - if (product === 'qpid-dispatch-router') { - var r = rtr.connected(context.connection); - log.info('Router connected from ' + r.container_id); - self.connected_routers[r.container_id] = r; - context.connection.on('disconnected', self.router_disconnected.bind(self));//todo: wait for a bit? - context.connection.on('connection_close', self.router_disconnected.bind(self));//todo: wait for a bit? - r.on('ready', function (router) { - router.retrieve_listeners(); - router.retrieve_connectors(); - router.on('synchronized', self.on_synchronized.bind(self)); - if (self.addresses_initialised) { - router.sync_addresses(self.addresses, Object.keys(self.connected_brokers)); - } - router.on('listeners_updated', self.connected_routers_updated.bind(self));//advertise only once have listeners - router.on('connectors_updated', self.check_router_connectors.bind(self)); - router.on('provisioned', self.check_router_connectors.bind(self)); - }); - } else if (product === 'apache-activemq-artemis') { - broker_controller.create_controller(context.connection, self.event_sink).then((broker) => { - self.connected_brokers[broker.id] = broker; - log.info('broker %s connected', broker.id); - if (self.addresses_initialised) { - self.sync_broker(broker); - for (var r in self.connected_routers) { - self.sync_router_addresses(self.connected_routers[r], Object.keys(self.connected_brokers)); - } - } - broker.on('synchronized', self.on_synchronized.bind(self)); - context.connection.on('disconnected', self.on_broker_disconnect.bind(self)); - context.connection.on('connection_close', self.on_broker_disconnect.bind(self)); - - }).catch((e) => { - log.error("Failed to create_controller", e); - }); - } else { - if (product === 'ragent') { - context.connection.on('disconnected', self.on_router_agent_disconnect.bind(self)); - context.connection.on('connection_close', self.on_router_agent_disconnect.bind(self)); - } - context.connection.on('message', self.on_message.bind(self)); - } - }); - - this.container.on('sender_open', function(context) { - if (context.sender.source.address === 'routers') { - self.subscribe(context); - context.session.on('session_closed', self.unsubscribe.bind(self)); - context.sender.on('sender_closed', self.unsubscribe.bind(self)); - context.connection.on('connection_close', self.unsubscribe.bind(self)); - context.connection.on('disconnected', self.unsubscribe.bind(self)); - } else { - if (context.sender.source.dynamic) { - self.add_client(context); - context.session.on('session_closed', self.remove_client.bind(self)); - context.sender.on('sender_closed', self.remove_client.bind(self)); - context.connection.on('connection_close', self.remove_client.bind(self)); - context.connection.on('disconnected', self.remove_client.bind(self)); - } - } - }); - - this.container.on('receiver_open', function(context) { - if (context.receiver.target.address === 'health-check') { - context.receiver.set_target({address:context.receiver.remote.attach.target.address}); - } - }); - -}; - -Ragent.prototype.listen = function (options) { - this.server = this.container.listen(options); - return this.server; -} - -Ragent.prototype.subscribe_to_addresses = function (env) { - var address_source = new AddressSource(env); - address_source.on('addresses_ready', this.sync_addresses.bind(this)); - address_source.start(); - return address_source.watcher; -}; - -Ragent.prototype.listen_health = function (env) { - if (env.HEALTH_PORT !== undefined) { - var health = http.createServer(function (req, res) { - res.writeHead(200, {'Content-Type': 'text/plain'}); - res.end('OK'); - }); - return health.listen(env.HEALTH_PORT); - } -}; - -Ragent.prototype.start_listening = function (env, callback) { - var options = { - properties: connection_properties, - idle_time_out: 'AMQP_IDLE_TIMEOUT' in process.env ? process.env.AMQP_IDLE_TIMEOUT : 300000 - }; - try { - options = tls_options.get_server_options(options, env); - options.port = env.AMQP_PORT === undefined ? 55671 : env.AMQP_PORT; - } catch (error) { - options.port = env.AMQP_PORT === undefined ? 55672 : env.AMQP_PORT; - log.warn('Error setting TLS options ' + error + ' using ' + JSON.stringify(options)); - } - var server = this.listen(options); - server.on('listening', function() { - log.info("Router agent listening on " + server.address().port); - if (callback) callback(server.address().port); - }); -}; - -Ragent.prototype.run = function (env, callback) { - this.start_listening(env, callback); - var watcher = this.subscribe_to_addresses(env); - this.listen_health(env); - return watcher; -}; - -if (require.main === module) { - new Ragent().run(process.env); -} else { - module.exports = Ragent; -} diff --git a/agent/lib/registry.js b/agent/lib/registry.js deleted file mode 100644 index 559610e2862..00000000000 --- a/agent/lib/registry.js +++ /dev/null @@ -1,122 +0,0 @@ -/* - * Copyright 2016 Red Hat Inc. - * - * 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. - */ -'use strict'; - -var log = require("./log.js").logger(); -var util = require('util'); -var events = require('events'); - -function Registry() { - events.EventEmitter.call(this); - this.objects = {}; -} - -util.inherits(Registry, events.EventEmitter); - -Registry.prototype.deleted = function (key) { - var a = this.objects[key] - delete this.objects[key]; - this.emit('deleted', a); -}; - -Registry.prototype.get = function (key) { - return this.objects[key]; -}; - -Registry.prototype.set = function (known) { - for (var key in known) { - if (this.objects[key] === undefined) { - this.objects[key] = known[key]; - this.emit('updated', known[key]); - } else { - this.update(key, known[key]); - } - } - for (var key in this.objects) { - if (known[key] === undefined) { - this.deleted(key); - } - } -}; - -function equals(a, b) { - return JSON.stringify(a) === JSON.stringify(b); -} - -Registry.prototype.update = function (id, latest) { - var current = this.objects[id]; - if (current === undefined) { - this.objects[id] = latest; - log.debug('setting ' + id + ' to ' + JSON.stringify(latest)); - this.emit('updated', latest); - } else { - var changed = false; - for (var s in latest) { - if (!equals(current[s], latest[s])) { - log.debug('changing ' + s + ' on ' + id + ' from ' + JSON.stringify(current[s]) + ' to ' + JSON.stringify(latest[s])); - current[s] = latest[s]; - changed = true; - } - } - if (changed) { - this.emit('updated', current); - } - } -}; - -Registry.prototype.update_if_exists = function (id, latest) { - var current = this.objects[id]; - if (current) { - var changed = false; - for (var s in latest) { - if (!equals(current[s], latest[s])) { - log.debug('changing ' + s + ' on ' + id + ' from ' + JSON.stringify(current[s]) + ' to ' + JSON.stringify(latest[s])); - current[s] = latest[s]; - changed = true; - } - } - if (changed) { - this.emit('updated', current); - } - return true; - } else { - return false; - } -}; - -Registry.prototype.update_existing = function (updates) { - for (var k in updates) { - this.update_if_exists(k, updates[k]); - } -}; - -Registry.prototype.for_each = function (action, filter) { - for (var key in this.objects) { - if (filter === undefined || filter(this.objects[key])) { - action(this.objects[key]); - } - } -}; - -Registry.prototype.first = function (action, filter) { - for (var key in this.objects) { - if (filter === undefined || filter(this.objects[key])) { - action(this.objects[key]); - } - } -}; - -module.exports = Registry; diff --git a/agent/lib/router.js b/agent/lib/router.js deleted file mode 100644 index cb5473a2baf..00000000000 --- a/agent/lib/router.js +++ /dev/null @@ -1,261 +0,0 @@ -/* - * Copyright 2016 Red Hat Inc. - * - * 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. - */ -'use strict'; - -var util = require("util"); -var events = require("events"); -var qdr = require("./qdr.js"); -var myutils = require("./utils.js"); -var router_config = require("./router_config.js"); -var log = require("./log.js").logger(); - -const ID_QUALIFIER = 'ragent' - -function has_inter_router_role (record) { - return record.role === 'inter-router'; -} - -function matches_qualifier (record) { - return record.name && record.name.indexOf(ID_QUALIFIER) === 0; -} - -function to_host_port (record) { - return record.host + ':' + record.port; -} - -/** - * A KnownRouter instance represents routers this process knows about - * but is not directly connected to (or responsible for). - */ -var KnownRouter = function (container_id, listeners) { - this.container_id = container_id; - this.listeners = listeners; -}; - -/** - * A ConnectedRouter represents a router this process is connected to - * and is therefore responsible for configuring. - */ -var ConnectedRouter = function (connection) { - events.EventEmitter.call(this); - this.container_id = connection.container_id; - this.listeners = undefined; - this.connectors = undefined; - this.fully_connected = false; - this.addresses = {}; - this.initial_provisioning_completed = false; - this.addresses_synchronized = false; - this.realise_address_definitions = myutils.serialize(this._realise_address_definitions.bind(this)); - - this.router_mgmt = new qdr.Router(connection); - this.router_mgmt.name = 'mgmt'; - var self = this; - connection.on('receiver_open', function () { - self.emit('ready', self); - }); -}; - -util.inherits(ConnectedRouter, events.EventEmitter); - -function remove_current(set) { - if (this.listeners) { - this.listeners.forEach(function (l) { delete set[l]; }); - } -} - -function has_listener(host_port) { - return this.listeners && this.listeners.indexOf(host_port) !== -1; -} - -ConnectedRouter.prototype.has_connector_to = function (router) { - return this.connectors && this.connectors.some(has_listener.bind(router)); -}; - -ConnectedRouter.prototype.expects_connector_to = function (router) { - return router.container_id < this.container_id && router.listeners && router.listeners.length > 0; -}; - -ConnectedRouter.prototype.is_missing_connector_to = function (router) { - return this.expects_connector_to(router) && !this.has_connector_to(router); -}; - -ConnectedRouter.prototype.is_ready_for_connectivity_check = function () { - return this.initial_provisioning_completed && this.connectors !== undefined; -} - -ConnectedRouter.prototype.check_connectors = function (routers) { - var missing = []; - var stale = myutils.index(this.connectors); - for (var r in routers) { - var router = routers[r]; - if (router === this) continue; - if (this.is_missing_connector_to(router)) { - missing.push(router.listeners[0]); - } - remove_current.call(router, stale); - } - stale = Object.keys(stale); - - var num_connectors = 1; - if (process.env.ROUTER_NUM_CONNECTORS) { - num_connectors = process.env.ROUTER_NUM_CONNECTORS; - } - var do_create = this.forall_connectors.bind(this, num_connectors, this.create_connector.bind(this)); - var do_delete = this.forall_connectors.bind(this, num_connectors, this.delete_connector.bind(this)); - var work = missing.map(do_create).concat(stale.filter(matches_qualifier).map(do_delete)); - var self = this; - if (work.length) { - this.fully_connected = false; - log.info('[%s] checking connectors on router, missing=%j, stale=%j', this.container_id, missing, stale); - //if made changes, requery when they are complete - Promise.all(work).then(function () { - self.retrieve_connectors(); - }).catch(function (error) { - log.warn('[%s] error on updating connectors: %s', self.container_id, error); - self.retrieve_connectors(); - }); - //prevent any updates to connectors until we have re-retrieved - //them from router after updates: - this.connectors = undefined; - } else { - log.info('[%s] fully connected', this.container_id); - this.fully_connected = true; - this.emit('synchronized'); - } -}; - -ConnectedRouter.prototype.verify_addresses = function (expected) { - if (!expected || this.actual === undefined) { - return false; - } - - for (var i = 0; i < expected.length; i++) { - var address = expected[i]; - if (address["store_and_forward"] && !address["multicast"]) { - if (this.actual[address.name] === undefined) { - return false; - } - } - } - return true; -} - -ConnectedRouter.prototype.forall_connectors = function (num_connectors, connector_operation, host_port) { - var futures = []; - for (var i = 0; i < num_connectors; i++) { - var connector_name = ID_QUALIFIER + '-' + host_port + '-' + i; - futures.push(connector_operation(host_port, connector_name)); - } - return Promise.all(futures); -} - -ConnectedRouter.prototype.create_connector = function (host_port, connector_name) { - log.info('[%s] creating connector %s to %s', this.container_id, connector_name, host_port); - var parts = host_port.split(':'); - return this.create_entity('connector', connector_name, {role:'inter-router', host:parts[0], port:parts[1], - sslProfile:'ssl_internal_details', verifyHostName:'no'}); -}; - -ConnectedRouter.prototype.delete_connector = function (host_port, connector_name) { - log.info('[%s] deleting connector %s to %s', this.container_id, connector_name, host_port); - return this.delete_entity('connector', connector_name); -}; - -ConnectedRouter.prototype.retrieve_listeners = function () { - var self = this; - return this.query('listener', {attributeNames:['identity', 'name', 'host', 'port', 'role']}).then(function (results) { - self.listeners = results.filter(has_inter_router_role).map(to_host_port); - log.debug('[%s] retrieved listeners: %j', self.container_id, self.listeners); - self.emit('listeners_updated', self); - }).catch(function (error) { - log.warn('[%s] failed to retrieve listeners: %s', self.container_id, error); - return self.retrieve_listeners(); - }); -}; - -ConnectedRouter.prototype.retrieve_connectors = function () { - var self = this; - return this.query('connector', {attributeNames:['identity', 'name', 'host', 'port', 'role']}).then(function (results) { - self.connectors = results.filter(has_inter_router_role).map(to_host_port); - log.debug('[%s] retrieved connectors: %j', self.container_id, self.connectors); - self.emit('connectors_updated', self); - }).catch(function (error) { - log.warn('[%s] failed to retrieve connectors: %s', self.container_id, error); - return self.retrieve_connectors(); - }); -}; - -ConnectedRouter.prototype.query = function (type, options) { - return this.router_mgmt.query(type, options); -}; - -ConnectedRouter.prototype.create_entity = function (type, name, attributes) { - return this.router_mgmt.create_entity(type, name, attributes); -}; - -ConnectedRouter.prototype.delete_entity = function (type, name) { - return this.router_mgmt.delete_entity(type, name); -}; - -ConnectedRouter.prototype.sync_addresses = function (desired, brokers) { - this.desired = desired; - this.brokers = brokers; - log.info('[%s] sync %d addresses for %d brokers', this.container_id, desired.length, brokers.length); - this.realise_address_definitions(); -}; - -ConnectedRouter.prototype._realise_address_definitions = function () { - var self = this; - return router_config.realise_address_definitions(this.desired, this.router_mgmt, this.brokers).then(function (result) { - self.actual = result; - log.info('[%s] addresses synchronized', self.container_id); - if (self.initial_provisioning_completed !== true) { - self.initial_provisioning_completed = true; - self.emit('provisioned', self); - } - self.emit('synchronized', self); - }).catch(function (error) { - console.error('[%s] error while synchronizing addresses: %s', self.container_id, error); - log.error('[%s] error while synchronizing addresses: %s', self.container_id, error); - throw error; - }); -}; - -ConnectedRouter.prototype.is_synchronized = function () { - if (this.actual === undefined) return false; - for (var k in this.desired) { - var desired = this.desired[k]; - var actual = this.actual[desired.address]; - if (actual === undefined) { - log.info('[%s] not synchronized, missing %s %s', this.container_id, desired.type, desired.address); - return false; - } else if (actual.type !== desired.type) { - log.info('[%s] not synchronized, %s of wrong type expected %s got %s', this.container_id, desired.address, desired.type, actual.type); - return false; - } - } - if (Object.keys(this.actual).length !== (this.desired.length + (this.brokers !== undefined ? this.brokers.length : 0))) { - log.info('[%s] not synchronized, %s of wrong type expected %s got %s', this.container_id, desired.address, desired.type, actual.type); - log.info('[%s] not synchronized, have extra addresses', this.container_id); - return false; - } - return true; -} - -module.exports = { - connected: function (conn) { return new ConnectedRouter(conn); }, - known: function (container_id, listeners) { return new KnownRouter(container_id, listeners); } -}; diff --git a/agent/lib/router_config.js b/agent/lib/router_config.js deleted file mode 100644 index c208f02314a..00000000000 --- a/agent/lib/router_config.js +++ /dev/null @@ -1,477 +0,0 @@ -/* - * Copyright 2018 Red Hat Inc. - * - * 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. - */ -'use strict'; - -var util = require('util'); -var qdr = require('./qdr.js'); -var myutils = require('./utils.js'); -var log = require('./log.js').logger(); -var plimit = require('p-limit'); - -const ID_QUALIFIER = 'ragent-'; -const MAX_RETRIES = 3; - -function matches_qualifier (record) { - return record.name && record.name.indexOf(ID_QUALIFIER) === 0; -} - -function address_compare (a, b) { - return myutils.string_compare(a.prefix, b.prefix); -} - -function same_address_definition (a, b) { - return a.prefix === b.prefix && a.distribution === b.distribution && a.waypoint === b.waypoint; -} - -function address_describe (a) { - return 'address ' + a.prefix; -} - -function autolink_compare (a, b) { - return myutils.string_compare(a.address, b.address) || myutils.string_compare(a.direction, b.direction) || myutils.string_compare(a.containerId, b.containerId); -} - -function is_not_defined (a) { - return a === null || a === '' || a === undefined; -} - -function equivalent_container_id(a, b) { - // empty string, null & undefined are all considered equivalent - // for containerId - return (is_not_defined(a) && is_not_defined(b)) || a === b; -} - -function same_autolink_definition (a, b) { - return a.address === b.address && a.direction === b.direction && equivalent_container_id(a.containerId, b.containerId); -} - -function autolink_describe (a) { - return 'autolink ' + a.name + ' (dir: ' + a.direction + ', address: ' + a.address + ')'; -} - -function linkroute_compare (a, b) { - var result = myutils.string_compare(a.prefix, b.prefix); - if (result === 0) { - result = myutils.string_compare(a.direction, b.direction); - } - if (result === 0) { - result = myutils.string_compare(a.containerId, b.containerId); - } - return result; -} - -function same_linkroute_definition (a, b) { - return a.prefix === b.prefix && a.direction === b.direction && equivalent_container_id(a.containerId, b.containerId); -} - -function linkroute_describe (a) { - return 'linkroute ' + a.direction + ' ' + a.prefix; -} - -function listener_compare(a, b) { - return myutils.string_compare(a.host, b.host) || myutils.string_compare(a.port, b.port); -} - -function same_listener_definition(a, b) { - // NOTE: Special comparsion needed for legacy probe as schema for listener changed between - // router versions - if (a.port === '56711' && b.port === '56711') { - return a.host === b.host && - a.port === b.port && - a.authenticatePeer === b.authenticatePeer; - } else { - return a.host === b.host && - a.port === b.port && - a.authenticatePeer === b.authenticatePeer && - a.metrics === b.metrics && - a.healthz === b.healthz && - a.http === b.http && - a.websockets === b.websockets && - a.httpRootDir === b.httpRootDir; - } -} - -function listener_describe (a) { - return 'listener ' + a.name + ' (' + a.host + ':' + a.port + ')'; -} - -const entities = [ - { - name:'addresses', - comparator:address_compare, - equality:same_address_definition, - describe:address_describe, - type:'org.apache.qpid.dispatch.router.config.address', - singular:'address', - filter: matches_qualifier - }, - { - name:'autolinks', - comparator:autolink_compare, - equality:same_autolink_definition, - describe:autolink_describe, - type:'org.apache.qpid.dispatch.router.config.autoLink', - singular:'autolink', - filter: matches_qualifier - }, - { - name:'linkroutes', - comparator:linkroute_compare, - equality:same_linkroute_definition, - describe:linkroute_describe, - type:'org.apache.qpid.dispatch.router.config.linkRoute', - singular:'linkroute', - filter: matches_qualifier - }, - { - name:'listeners', - comparator:listener_compare, - equality:same_listener_definition, - describe:listener_describe, - type:'org.apache.qpid.dispatch.listener', - singular:'listener', - filter: matches_qualifier - } -]; - -const directions = ['in', 'out']; - -function RouterConfig(prefix) { - this.prefix = prefix; - this.autolinks = []; - this.addresses = []; - this.linkroutes = []; - this.listeners = []; -} - -RouterConfig.prototype.add_address = function (a) { - this.addresses.push(myutils.merge({name:this.prefix + a.prefix}, a)); -}; - -RouterConfig.prototype.add_autolink = function (a) { - this.autolinks.push(myutils.merge({name: this.prefix + a.address + '-' + a.containerId}, a)); -}; - -RouterConfig.prototype.add_listener = function (a) { - this.listeners.push(myutils.merge({name:this.prefix + a.host + '-' + a.port}, a)); -}; - -RouterConfig.prototype.add_linkroute = function (l) { - this.linkroutes.push(myutils.merge({name:this.prefix + l.prefix + '-' + l.containerId}, l)); -}; - -function distinct_container_per_direction(props) { - if (props.containerId) { - props.containerId = props.containerId + '-' + props.direction; - } - return props; -} - -RouterConfig.prototype.add_autolink_pair = function (def) { - for (let i = 0; i < directions.length; i++) { - this.add_autolink(distinct_container_per_direction(myutils.merge({direction:directions[i]}, def))); - } -}; - -RouterConfig.prototype.add_autolink_in = function (def) { - this.add_autolink(distinct_container_per_direction(myutils.merge({direction:'in'}, def))); -} - -RouterConfig.prototype.add_linkroute_pair = function (def) { - for (let i = 0; i < directions.length; i++) { - this.add_linkroute(distinct_container_per_direction(myutils.merge({direction:directions[i]}, def))); - } -}; - -function get_router_id (router) { - return router.connection ? router.connection.container_id : 'unknown-router'; -} - -function sort_config (config) { - entities.forEach(function (entity) { - config[entity.name].sort(entity.comparator); - }); -}; - -function delete_config_element(router, entity, element) { - let router_id = get_router_id(router); - log.debug('deleting %s on %s', entity.describe(element), router_id); - return router.delete_entity(entity.type, element.name).then(function () { - log.info('deleted %s on %s', entity.describe(element), router_id); - return true; - }).catch(function (error) { - log.error('deleting %s on %s => %s', entity.describe(element), router_id, error.description); - return false; - }); -} - -function create_config_element(router, entity, element) { - let router_id = get_router_id(router); - log.debug('creating %s on %s', entity.describe(element), router_id); - return router.create_entity(entity.type, element.name, element).then(function () { - log.info('created %s on %s', entity.describe(element), router_id); - return true; - }).catch(function (error) { - log.error('creating %s on %s => %s', entity.describe(element), router_id, error.description); - return false; - }); -} - -function retrieve_elements(entity, router) { - let router_id = get_router_id(router); - return router.query(entity.type).then(function (results) { - if (Array.isArray(results)) { - log.debug('retrieved %s from %s', entity.name, router_id); - if (entity.filter) { - results = results.filter(r => entity.filter(r)); - } - results.sort(entity.comparator); - return results; - } else { - log.warn('unexpected result from retrieving %s from %s: %j', entity.name, router_id, results); - return []; - } - }).catch(function (error) { - log.error('error retrieving %s from %s: %s', entity.name, router_id, error); - throw error; - }); - -} - -function print_list(prefix, list) { - log.info(' %s', prefix); - list.forEach(function (o) { - log.info(' %j', o); - }); -} - -function is_false(v) { return v === false; } - -function debug_failures(entity, targets, results, actual) { - for (let i = 0; i < results.length; i++) { - if (!results[i]) { - if (actual.some(entity.equality.bind(null, targets[i]))) { - log.info('%s IS in retrieved list', entity.describe(targets[i])); - } else { - log.info('%s IS NOT in retrieved list', entity.describe(targets[i])); - } - } - } -} - -function report(entity, targets, results, actual, operation) { - if (results.some(is_false)) { - log.info('had %d %s, %s %d of which %d failed:', actual.length, entity.name, operation, targets.length, results.filter(is_false).length); - debug_failures(entity, targets, results, actual); - } else if (targets.length) { - log.info('had %d %s, %s %d', actual.length, entity.name, operation, targets.length); - } -} - -function ensure_elements(entity, desired, router, collected) { - let router_id = get_router_id(router); - return retrieve_elements(entity, router).then(function (actual) { - var delta = myutils.changes(actual, desired, entity.comparator, entity.equality); - if (delta) { - log.debug('on %s, have %j, want %j => %s', router_id, actual, desired, delta.description); - let stale = delta.removed.concat(delta.modified); - let missing = delta.added.concat(delta.modified); - - if (stale.length || missing.length) { - var limit = plimit(250); - let delete_fn = limit.bind(null, delete_config_element.bind(null, router, entity)); - let create_fn = limit.bind(null, create_config_element.bind(null, router, entity)); - return Promise.all(stale.map(delete_fn)).then( - function (deletions) { - report(entity, stale, deletions, actual, 'deleted'); - return Promise.all(missing.map(create_fn)).then( - function (creations) { - report(entity, missing, creations, actual, 'created'); - return false;//recheck when changed - } - ).catch(function (error) { - log.error('Failed to create required %s: %s', entity.name, error); - }); - }).catch(function (error) { - log.error('Failed to delete stale %s: %s', entity.name, error); - }); - } else { - log.info('%s up to date on %s (ignoring %d elements)', entity.name, router_id, delta.removed.length); - collected[entity.name] = actual; - return true; - } - } else { - log.info('%s up to date on %s', entity.name, router_id); - collected[entity.name] = actual; - return true; - } - }).catch(function (error) { - log.error('error retrieving %s from %s: %s', entity.name, router_id, error); - return false; - }); -} - -function apply_config(desired, router, count) { - let iteration = count || 1; - let router_id = get_router_id(router); - log.info('checking configuration of %s', router_id); - log.debug('applying %j to %s', desired, router_id); - var actual = {}; - let promise = Promise.resolve(true); - for (let i = 0; i < entities.length; i++) { - let entity = entities[i]; - promise = promise.then(function (result_a) { - return ensure_elements(entity, desired[entity.name], router, actual).then(function (result_b) { - return result_a && result_b; - }); - }); - } - return promise.then(function (ok) { - if (ok) { - log.info('configuration of %s is up to date', router_id); - return actual; - } else { - log.error('configuration update for %s not up to date (attempt %d of %d)', router_id, iteration, MAX_RETRIES); - if (iteration < MAX_RETRIES) { - return apply_config(desired, router, iteration + 1); - } else { - log.error('Unable to apply desired configuration; gave up after %d attempts', iteration); - throw new Error(util.format('Unable to apply desired configuration; gave up after %d attempts', iteration)); - } - } - }).catch(function (error) { - log.error('error while applying configuration to %s, retrying: %j', router_id, error); - if (iteration < MAX_RETRIES) { - return apply_config(desired, router, iteration + 1); - } else { - log.error('Unable to apply desired configuration; gave up after %d attempts (%s)', iteration, error); - throw new Error(util.format('Unable to apply desired configuration; gave up after %d attempts (%s)', iteration, error)); - } - }); -} - -function desired_address_config(high_level_address_definitions, brokers) { - var config = new RouterConfig(ID_QUALIFIER); - // Addresses and autolinks for broker health checks - for (var i in brokers) { - var address = "!!HEALTH_CHECK_BROKER_" + brokers[i]; - config.add_address({prefix:address, distribution:'balanced', waypoint:true}); - config.add_autolink_pair({address:address, containerId: brokers[i]}); - } - for (var i in high_level_address_definitions) { - var def = high_level_address_definitions[i]; - if (def.type === 'queue') { - config.add_address({prefix:def.address, distribution:'balanced', waypoint:true}); - if (def.allocated_to) { - log.debug("Constructing config for queue %s allocated to: %j", def.address, def.allocated_to); - for (var j in def.allocated_to) { - var brokerStatus = def.allocated_to[j]; - if (brokerStatus.state === 'Active') { - config.add_autolink_pair({address:def.address, containerId: brokerStatus.containerId}); - } else if (brokerStatus.state === 'Migrating') { - config.add_autolink_pair({address:def.address, containerId: brokerStatus.containerId}); - } else if (brokerStatus.state === 'Draining') { - config.add_autolink_in({address:def.address, containerId: brokerStatus.containerId}); - } - } - } else { - log.debug("Constructing old config for queue %s", def.address); - config.add_autolink_pair({address:def.address, containerId: def.address}); - } - } else if (def.type === 'topic') { - if (def.allocated_to) { - for (var j in def.allocated_to) { - var brokerStatus = def.allocated_to[j]; - config.add_linkroute_pair({prefix:def.address, containerId: brokerStatus.containerId}); - // TODO: Handle Draining? - } - } else { - config.add_linkroute_pair({prefix:def.address, containerId: def.address}); - } - } else if (def.type === 'subscription') { - if (def.allocated_to) { - for (var j in def.allocated_to) { - var brokerStatus = def.allocated_to[j]; - config.add_linkroute(distinct_container_per_direction({prefix:def.topic+'::'+def.address, containerId: brokerStatus.containerId, direction:'out'})); - // TODO: Handle Draining? - } - } else { - log.warn('subscription %s not allocated to broker', def.address); - } - } else if (def.type === 'anycast') { - config.add_address({prefix:def.address, distribution:'balanced', waypoint:false}); - } else if (def.type === 'multicast') { - config.add_address({prefix:def.address, distribution:'multicast', waypoint:false}); - } - } - config.add_listener({host:'0.0.0.0', port: '8080', authenticatePeer: false, metrics: true, healthz: true, http: true, websockets: false, httpRootDir: 'invalid'}) - // NOTE: The following listener exists in order to be able to proceed with upgrade from 0.26.x - // to master as the statefulset controller will not start rollout if probe is failing - config.add_listener({host:'0.0.0.0', port: '56711', authenticatePeer: true}); - sort_config(config); - log.debug('mapped %j => %j', high_level_address_definitions, config); - return config; -} - -function deduce_type(address_config) { - if (address_config.distribution === 'balanced') { - if (address_config.waypoint) return 'queue'; - else return 'anycast'; - } else if (address_config.distribution === 'multicast') { - return 'multicast'; - } else { - return undefined; - } -} - -function deduce_definition(actual_config) { - var definition = {}; - var linkroutes = {}; - for (var i in actual_config.addresses) { - var a = actual_config.addresses[i]; - definition[a.prefix] = {address:a.prefix, type:deduce_type(a)}; - }; - for (var i in actual_config.linkroutes) { - var l = actual_config.linkroutes[i]; - var e = linkroutes[l.prefix]; - if (e === undefined) { - e = {}; - linkroutes[l.prefix] = e; - } - e[l.direction] = true; - }; - for (var i in linkroutes) { - var l = linkroutes[i]; - if (l['in'] && l['out']) { - definition[i] = {address:i, type:'topic'}; - } else if (l['out']) { - var s = i.indexOf('::'); - if (s > 0) { - var name = i.substr(s+2); - definition[name] = {address:name, type:'subscription'}; - } - } - } - return definition; -} - -module.exports = { - realise_address_definitions: function (high_level_address_definitions, router, brokers) { - return apply_config(desired_address_config(high_level_address_definitions, brokers), router).then(function (actual) { - return deduce_definition(actual); - }); - } -}; diff --git a/agent/lib/router_stats.js b/agent/lib/router_stats.js deleted file mode 100644 index 80d3706a9df..00000000000 --- a/agent/lib/router_stats.js +++ /dev/null @@ -1,394 +0,0 @@ -/* - * Copyright 2016 Red Hat Inc. - * - * 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. - */ -'use strict'; - -var log = require("./log.js").logger(); -var rhea = require('rhea'); -var path = require('path'); -var fs = require('fs'); -var Router = require('./qdr.js').Router; -var crypto = require('crypto'); -var uuidv5 = require('uuid/v5'); - - - -function RouterStats(connection) { - var options = { host: process.env.MESSAGING_SERVICE_HOST, port: process.env.MESSAGING_SERVICE_PORT_AMQPS_NORMAL}; - - //TODO: fix admin_service to be more sensibly generic - var conn = connection || require('./admin_service.js').connect(rhea, options, 'MESSAGING'); - this.router = new Router(conn); - this.router.name = 'stats'; - - var self = this; - this.router.get_all_routers().then(function (routers) { - self.routers = routers; - log.info('routers: ' + self.routers.map(function (r) { return r.target; })); - }); -} - -function strip_topic_prefix(clean) { - var i = clean.indexOf('::'); - if (i > 0) { - return clean.substring(0, i); - } else { - return clean; - } -} - -function clean_address(address) { - if (!address) { - return address; - } else if (address.charAt(0) === 'M') { - return strip_topic_prefix(address.substring(2)); - } else { - return strip_topic_prefix(address.substring(1)); - } -} - -function address_phase(address) { - if (address && address.charAt(0) === 'M') { - return parseInt(address.substr(1, 1)); - } else { - return undefined; - } -} - -var defined_outcomes = ['accepted', 'released', 'rejected', 'modified', 'unsettled', 'presettled', 'undelivered']; -function backlog (link_stats) { - var backlog = 0 - if (link_stats.undeliveredCount) backlog += link_stats.undeliveredCount - if (link_stats.unsettledCount) backlog += link_stats.unsettledCount - return backlog -} -function routerName (link_stats, router) { - return router && router.target ? router.target.split('/')[3] : undefined; -} -function clientName (link_stats, router, connection) { - return connection ? connection.container : undefined; -} -var defined_linkDetails = ['identity', 'name', 'operStatus', 'adminStatus', 'deliveryCount', 'capacity', backlog, routerName, clientName]; -defined_linkDetails.push.apply(defined_linkDetails, defined_outcomes.map((d) => d+'Count')) - -function init_outcomes(outcomes) { - defined_outcomes.forEach(function (name) { - outcomes[name] = 0; - }); - outcomes.links = [] - return outcomes; -} - -function update_outcomes(outcomes, link_stats, router, connection) { - if (link_stats) { - defined_outcomes.forEach(function (name) { - if (link_stats[name + 'Count']) outcomes[name] += link_stats[name + 'Count']; - }); - var link_details = {} - defined_linkDetails.forEach(function (name) { - if (typeof name === 'function') - link_details[name.name] = name(link_stats, router, connection) - else if (link_stats[name] !== undefined) link_details[name] = link_stats[name] - }); - link_details.lastUpdated = Date.now(); - outcomes.links.push(link_details) - } - return outcomes; -} - -function get_stats_for_address(stats, address) { - var s = stats[address]; - if (s === undefined) { - s = { - senders: 0, receivers: 0, propagated: 0, - messages_in: 0, messages_out: 0, - outcomes: { - ingress: init_outcomes({}), - egress: init_outcomes({}) - } - }; - stats[address] = s; - } - return s; -} - -function collect_by_address(links, stats, router, connections, index) { - for (var l in links) { - var link = links[l]; - if (link.linkType === 'endpoint' && link.owningAddr && connections[link.connectionId + '-' + index]) { - var connection = connections[link.connectionId + '-' + index]; - var address = clean_address(link.owningAddr); - - var counts = get_stats_for_address(stats, address); - if (link.name.indexOf('qdlink.') !== 0) { - if (link.linkDir === 'in') { - counts.senders++; - update_outcomes(counts.outcomes.ingress, link, router, connection); - } else if (link.linkDir === 'out') { - counts.receivers++; - update_outcomes(counts.outcomes.egress, link, router, connection); - } - } - } - } -} - -function collect_by_connection(links, connections, router, index) { - links.forEach(function (link) { - var connection = connections[link.connectionId + '-' + index]; - if (connection) { - var l = update_outcomes(init_outcomes({address:clean_address(link.owningAddr),name:link.name}), link); - l.deliveries = link.deliveryCount; - l.uuid = l.name; // this is the router assigned link id - if (link.linkDir === 'in') { - connection.senders.push(l); - update_outcomes(connection.outcomes.ingress, link, router, connection); - connection.messages_in += l.deliveries; - } else if (link.linkDir === 'out') { - connection.receivers.push(l); - update_outcomes(connection.outcomes.egress, link, router, connection); - connection.messages_out += l.deliveries; - } - } - }); -} - -function log_error(error) { - if (error.message) log.error('ERROR: ' + error.message); - else log.error('ERROR: ' + JSON.stringify(error)); - -} - -function same_list(a, b, comparator) { - var equal = comparator || function (x, y) { return x === y; }; - if (a === undefined || b === undefined || a.length !== b.length) { - return false; - } else { - for (var i = 0; i < a.length; i++) { - if (!equal(a[i], b[i])) return false; - } - return true; - } -} - -function same_routers(a, b) { - return same_list(a, b, function (x, y) { return x.target === y.target; }); -} - -RouterStats.prototype.update_routers = function () { - var self = this; - return this.router.get_all_routers(this.routers).then(function (routers) { - if (routers === undefined) { - log.info('no routers found'); - return []; - } else { - if (!same_routers(routers, self.routers)) { - log.info('routers changed: ' + routers.map(function (r) { return r.target; })); - } - self.routers = routers; - return self.routers; - } - }); -} - -function check_link_routes (link_routes) { - var by_address = {}; - link_routes.forEach(function (link_route) { - if (link_route.name.indexOf('override') !== 0) { - var lr = by_address[link_route.prefix]; - if (lr === undefined) { - lr = {}; - by_address[link_route.prefix] = lr; - } - lr[link_route.dir] = true; - } - }); - var results = []; - for (var a in by_address) { - if (by_address[a]['out'] && (by_address[a]['in'] || a.indexOf('::') > 0)) { - results.push(a); - } - } - return results; -} - -function is_role_normal (c) { - return c.role === 'normal'; -} - -var internal_identifiers = ['address-space-controller', 'standard-controller', 'agent', 'ragent', 'qdconfigd', 'subserv', 'lwt-service', 'standard-controller-healthcheck']; - -function is_internal_identifier (s) { - return internal_identifiers.indexOf(s) >= 0; -} - -function is_internal (c) { - return (c.properties && is_internal_identifier(c.properties.product)) || is_internal_identifier(c.container); -} - -function is_application_connection (c) { - return is_role_normal(c) && !is_internal(c); -} - - -function generateStableUuid() { - var hash = crypto.createHash('sha256'); - for (var i = 0, j = arguments.length; i < j; i++){ - var argument = arguments[i]; - if (argument) { - hash.update(argument); - } - } - var ba = []; - ba.push(...hash.digest().slice(0, 16)); - const ns = "3751f842-240e-48b9-89b5-5b47f04e931b"; - return uuidv5(ns, ba); -} - -function get_normal_connections (results) { - var connections = {}; - results.forEach(function (stats, i) { - stats.filter(is_application_connection).forEach(function (c) { - var qualified_id = c.identity + '-' + i; - if (connections[qualified_id]) { - log.warn('overwriting connection details for %s', qualified_id); - } - var addressSpace = process.env.ADDRESS_SPACE; - var addressSpaceNamespace = process.env.ADDRESS_SPACE_NAMESPACE; - var addressSpaceType = process.env.ADDRESS_SPACE_TYPE; - var uuid = generateStableUuid(addressSpaceNamespace, addressSpace, c.container, c.host); - connections[qualified_id] = { - id: c.identity, - addressSpace: addressSpace, - addressSpaceNamespace: addressSpaceNamespace, - addressSpaceType: addressSpaceType, - uuid: uuid, - host: c.host, - container: c.container, - properties: c.properties, - encrypted: c.isEncrypted, - sasl_mechanism: c.isAuthenticated ? c.sasl : 'none', - user: c.user, - messages_in: 0, - messages_out: 0, - outcomes: { - ingress: init_outcomes({}), - egress: init_outcomes({}) - }, - senders: [], - receivers: [], - creationTimestamp: Math.floor(Date.now() / 1000) - c.uptimeSeconds, - - close: c.close - }; - }); - }); - return connections; -} - -RouterStats.prototype.close = function () { - this.router.close(); -} - -RouterStats.prototype.retrieve = function (addresses, connection_registry) { - return this._retrieve().then(function (results) { - if (results) { - connection_registry.set(results.connections); - for (var a in results.addresses) { - var i = a.indexOf('::'); - if (i > 0) { - var s = a.substring(i+2); - addresses.update_stats(s, results.addresses[a]); - } else { - addresses.update_stats(a, results.addresses[a]); - } - } - } - }).catch(function (error) { - console.error('Failed to retrieve router stats: %s', error); - }); -}; - -function aggregate_delivery_count(link_details) { - return link_details.map(function (l) { return l.deliveryCount; }).reduce(function (a, b) { return a + b}, 0); -} - -RouterStats.prototype._retrieve = function () { - return this.update_routers().then(function (routers) { - return Promise.all(routers.map(function (router) { - return router.get_connections().then((routerConns) => { - routerConns.forEach((c) => { - c.close = () => {return router.update_connection({identity: c.identity}, {adminStatus : 'deleted'})}; - }); - return Promise.resolve(routerConns); - }).catch((e) => { - return Promise.reject(e); - });})).then(function (connection_results) { - var connections = get_normal_connections(connection_results); - return Promise.all(routers.map(function (router) { return router.get_links(); })).then(function (results) { - var address_stats = {}; - results.forEach(function (links, i) { - collect_by_address(links, address_stats, routers[i], connections, i); - collect_by_connection(links, connections, routers[i], i); - }); - return Promise.all(routers.map(function (router) { return router.get_addresses(); })).then(function (results) { - results.forEach(function (configured) { - configured.forEach(function (address) { - var s = get_stats_for_address(address_stats, address.prefix); - s.propagated++; - if (address.waypoint) s.waypoint = true; - }); - }); - - return Promise.all(routers.map(function (router) { return router.get_link_routes(); } )).then(function (results) { - results.forEach(function (lrs) { - check_link_routes(lrs).forEach(function (a) { - var s = get_stats_for_address(address_stats, a); - s.messages_in += aggregate_delivery_count(s.outcomes.ingress.links); - s.messages_out += aggregate_delivery_count(s.outcomes.egress.links); - s.propagated++; - }); - }); - //convert propagated to a percentage of all routers - for (var a in address_stats) { - address_stats[a].propagated = (address_stats[a].propagated / routers.length) * 100; - } - - return Promise.all(routers.map(function (router) { - return router.get_address_stats(); - })).then(function (results) { - results.forEach(function (configured) { - configured.forEach(function (address) { - var s = get_stats_for_address(address_stats, clean_address(address.name)); - if (s.waypoint) { - var phase = address_phase(address.name); - if (phase === 0) s.messages_in += address.deliveriesIngress; - else if (phase === 1) s.messages_out += address.deliveriesEgress; - } else { - s.messages_in += address.deliveriesIngress; - s.messages_out += address.deliveriesEgress; - } - }); - }); - return {addresses: address_stats, connections: connections}; - }).catch(log_error); - }).catch(log_error); - }).catch(log_error); - }).catch(log_error); - }).catch(log_error); - }); -}; - -module.exports = RouterStats; diff --git a/agent/lib/sasl.js b/agent/lib/sasl.js deleted file mode 100644 index 3c86a6b86cc..00000000000 --- a/agent/lib/sasl.js +++ /dev/null @@ -1,40 +0,0 @@ -/* - * Copyright 2018 Red Hat Inc. - * - * 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. - */ -'use strict'; - -module.exports.parseXOAuth2Reponse = function (buffer) { - - var i = 0; - var start = 0; - var results = {}; - while (i < buffer.length) { - if (buffer[i] === 0x01) { - if (i > start) { - var keyvalue = buffer.toString('utf8', start, i); - var sep = keyvalue.split("=", 2) - results[sep[0]] = sep[1]; - } - start = ++i; - } else { - ++i; - } - } - - if ("auth" in results) { - results.token = results.auth.replace(/^bearer +/i, ""); - } - return results; -}; diff --git a/agent/lib/set.js b/agent/lib/set.js deleted file mode 100644 index a7314d1c895..00000000000 --- a/agent/lib/set.js +++ /dev/null @@ -1,83 +0,0 @@ -/* - * Copyright 2018 Red Hat Inc. - * - * 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. - */ -'use strict'; - -var myutils = require('./utils.js'); - -function SortedObjectSet(comparator) { - this.comparator = comparator; - this.objects = []; -} - -SortedObjectSet.prototype.find = function(object) { - var m = 0; - var n = this.objects.length - 1; - while (m <= n) { - var k = (n + m) >> 1; - var cmp = this.comparator(object, this.objects[k]); - if (cmp > 0) { - m = k + 1; - } else if(cmp < 0) { - n = k - 1; - } else { - return k; - } - } - return -m - 1; -} - -SortedObjectSet.prototype.insert = function (object) { - var i = this.find(object); - if (i < 0) { - this.objects.splice(~i, 0, object); - return true; - } else { - return false; - } -}; - -SortedObjectSet.prototype.remove = function (object) { - var i = this.find(object); - if (i < 0) { - return false; - } else { - this.objects.splice(i, 1); - return true; - } -}; - -SortedObjectSet.prototype.replace = function (object) { - var i = this.find(object); - if (i < 0) { - return false; - } else { - this.objects[i] = object; - return true; - } -}; - -SortedObjectSet.prototype.reset = function (objects) { - this.objects = objects; - this.objects.sort(this.comparator); -}; - -SortedObjectSet.prototype.to_array = function () { - return this.objects; -}; - -module.exports.sorted_object_set = function (comparator) { - return new SortedObjectSet(comparator); -}; diff --git a/agent/lib/standard_stats.js b/agent/lib/standard_stats.js deleted file mode 100644 index 93b8db341d7..00000000000 --- a/agent/lib/standard_stats.js +++ /dev/null @@ -1,42 +0,0 @@ -/* - * Copyright 2016 Red Hat Inc. - * - * 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. - */ -'use strict'; - -var RouterStats = require('../lib/router_stats.js'); -var BrokerStats = require('../lib/broker_stats.js'); -var log = require("./log.js").logger(); - -function StandardStats () { - this.router_stats = new RouterStats(); - this.broker_stats = new BrokerStats(); -} - -StandardStats.prototype.init = function (console_server) { - var self = this; - setInterval(function () { - log.debug('triggering router stats retrieval'); - self.router_stats.retrieve(console_server.addresses, console_server.connections); - log.debug('router stats retrieval triggered'); - }, 10000); - - setInterval(function () { - log.debug('triggering broker stats retrieval'); - self.broker_stats.retrieve(console_server.addresses); - log.debug('broker stats retrieval triggered'); - }, 10000); -}; - -module.exports = StandardStats; diff --git a/agent/lib/subctrl.js b/agent/lib/subctrl.js deleted file mode 100644 index cb27d0370e4..00000000000 --- a/agent/lib/subctrl.js +++ /dev/null @@ -1,202 +0,0 @@ -/* - * Copyright 2016 Red Hat Inc. - * - * 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. - */ -'use strict'; - -var log = require('./log.js').logger(); - -function SubscriptionControl(pods) { - this.pods = pods; -} - -/** - * Find a queue with the given name if it exists on the list of pods - * provided. Returns a promise whose value is an object with fields - * 'found' and 'pod'. The 'found' field indicates whether the queue - * was found or not. If the queue was found, the 'pod' field indicates - * the pod on which the broker that has the queue lives. If the queue - * was not found, the 'pod' field is the pod whose broker has the - * fewest queues. - */ -function find_queue (name, pods) { - log.info('looking for ' + name + ' in ' + pods.length + ' pods'); - return Promise.all(pods.map(function (pod) { return pod.broker.getQueueNames(); } )).then( - function (results) { - var found = false; - var pod = undefined; - var min = undefined; - for (var i = 0; !found && i < results.length; i++) { - if (results[i].indexOf(name) >= 0) { - pod = pods[i]; - found = true; - } else if (min === undefined || min > results[i].length) { - pod = pods[i]; - } - } - return {'found':found, 'pod':pod}; - } - ); -}; - -/** - * Create queue if one does not already exist. Returns a promise whose - * value on resolution is the pod containing the broker on which the - * queue exists. - */ -function ensure_queue (name, pods) { - log.info('ensuring queue ' + name + ' in ' + pods.length + " pods"); - pods.map(function (pod) { - log.info('podmap ' + pod.name + " with broker undefined: " + (pod.broker === undefined)); - }); - - return find_queue(name, pods).then( - function (result) { - if (result.found) { - return result.pod; - } else { - return result.pod.broker.createQueue(name).then( - function () { - return result.pod; - } - ); - } - } - ); -}; - -/** - * Returns a map of topic name to optional tag (as passed to subscribe - * request). If no tag was specified, the value of the map is always - * true. - */ -SubscriptionControl.prototype.list = function (subscription_id) { - return find_queue(subscription_id, this.pods.pod_list()).then(function (result) { - if (result.found) { - return result.pod.broker.getDivertNames().then( - function (names) { - return names.filter( - function (n) { return n.indexOf(subscription_id) === 0; } - ).reduce( - function (a, b) { - var parts = b.split('|'); - a[parts[1]] = parts[2] || true; - return a; - }, - {} - ); - } - ); - } else { - return {}; - } - }); -}; - -function get_divert_name(subscription_id, topic, tag) { - return subscription_id + '|' + topic + '|' + (tag || ''); -} - -SubscriptionControl.prototype.subscribe = function (subscription_id, topics) { - log.debug('subscribing to ' + JSON.stringify(topics)); - var pods = this.pods.pod_list(); - if (pods.length == 0) { - return Promise.reject('No brokers available for subscribing to topics ' + JSON.stringify(Object.keys(topics))); - } - return ensure_queue(subscription_id, pods).then( - function (pod) { - log.info('after ensure looking at pod ' + JSON.stringify(pod.name) + " with broker undefined " + (pod.broker === undefined)); - var connector = { - name: "$override." + subscription_id, - clusterId: subscription_id, - linkName: subscription_id, - sourceAddress: subscription_id, - targetAddress: subscription_id, - containerId: subscription_id, - direction: 'out' - }; - return pod.broker.ensureConnectorService(connector).then( - function () { - return Promise.all(Object.keys(topics).map( - function (topic) { - var name = get_divert_name(subscription_id, topic, topics[topic]); - return pod.broker.ensureDivert(name, topic, subscription_id); - } - )); - } - ); - } - ); -}; - -function delete_diverts(broker, prefix) { - function matches(n) { return n.indexOf(prefix) === 0; }; - function destroy(n) { return broker.destroyDivert(n); } - return broker.getDivertNames().then( - function (names) { - return Promise.all(names.filter(matches).map(destroy)); - } - ); -}; - -SubscriptionControl.prototype.unsubscribe = function (subscription_id, topics) { - return find_queue(subscription_id, this.pods.pod_list()).then( - function(result) { - if (result.found) { - return Promise.all(Object.keys(topics).map( - function (topic) { - var name = get_divert_name(subscription_id, topic); - return delete_diverts(result.pod.broker, name); - //TODO: could delete queue and connector *if* there are no longer any diverts - } - )); - } - } - ); -}; - -SubscriptionControl.prototype.close = function (subscription_id) { - return find_queue(subscription_id, this.pods.pod_list()).then( - function(result) { - if (result.found) { - return delete_diverts(result.pod.broker, subscription_id).then( - function () { - return result.pod.broker.destroyConnectorService('$override.' + subscription_id).then( - function () { - //delete queue last as that is how we - //find the right broker to cleanup - return result.pod.broker.destroyQueue(subscription_id); - } - ); - } - ); - } - } - ); -}; - - -/** - * Returns a new SubscriptionControl object with the following public methods: - * - * pod_added - * pod_removed - * subscribe - * unsubscribe - * close - * - */ -module.exports = function (pods) { - return new SubscriptionControl(pods); -}; diff --git a/agent/lib/tls_options.js b/agent/lib/tls_options.js deleted file mode 100644 index 72f5edfdf24..00000000000 --- a/agent/lib/tls_options.js +++ /dev/null @@ -1,63 +0,0 @@ -/* - * Copyright 2017 Red Hat Inc. - * - * 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. - */ -'use strict'; - -var path = require('path'); -var fs = require('fs'); - -function get_paths(env) { - var opts = env || process.env; - var cert_dir = opts.CERT_DIR || '/etc/enmasse-certs'; - var paths = {}; - paths.ca = opts.CA_PATH || path.resolve(cert_dir, 'ca.crt'); - paths.cert = opts.CERT_PATH || path.resolve(cert_dir, 'tls.crt'); - paths.key = opts.KEY_PATH || path.resolve(cert_dir, 'tls.key'); - return paths; -} - -function get_client_options(config, env) { - var options = config || {}; - var paths = get_paths(env); - options.ca = [fs.readFileSync(paths.ca)]; - options.key = fs.readFileSync(paths.key); - options.cert = fs.readFileSync(paths.cert); - options.enable_sasl_external = true; - options.transport = 'tls'; - options.rejectUnauthorized = false; - return options; -} - -function get_server_options(config, env) { - var options = config || {}; - get_client_options(options, env); - options.requestCert = true; - options.rejectUnauthorized = true; - return options; -} - -function get_console_server_options(config, env) { - var options = config || {}; - var paths = get_paths(env); - options.key = fs.readFileSync(paths.key); - options.cert = fs.readFileSync(paths.cert); - options.transport = 'tls'; - return options; -} - -module.exports.get_client_options = get_client_options; -module.exports.get_server_options = get_server_options; -module.exports.get_console_server_options = get_console_server_options; -module.exports.get_paths = get_paths; diff --git a/agent/lib/topic.js b/agent/lib/topic.js deleted file mode 100644 index 4cf464a803e..00000000000 --- a/agent/lib/topic.js +++ /dev/null @@ -1,38 +0,0 @@ -/* - * Copyright 2017 Red Hat Inc. - * - * 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. - */ -'use strict'; - -var create_podgroup = require('./podgroup.js'); -var create_controller = require('./subctrl.js'); -var log = require('./log.js').logger(); - -function Topic (name) { - this.name = name; - this.pods = create_podgroup(); - this.controller = create_controller(this.pods); -}; - -Topic.prototype.close = function () { - this.pods.close(); -} - -Topic.prototype.empty = function () { - return this.pods.empty(); -} - -module.exports = function (name) { - return new Topic(name); -} diff --git a/agent/lib/topic_tracker.js b/agent/lib/topic_tracker.js deleted file mode 100644 index 55d210c9cbf..00000000000 --- a/agent/lib/topic_tracker.js +++ /dev/null @@ -1,52 +0,0 @@ -/* - * Copyright 2018 Red Hat Inc. - * - * 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. - */ -'use strict'; - -function is_pod_ready(pod) { - return pod.ready === 'True' && pod.phase === 'Running'; -} - -module.exports = function (topics, create_topic) { - return function (pods) { - var by_topic = {}; - pods.filter(is_pod_ready).forEach(function (pod) { - var key = pod.annotations.address; - var list = by_topic[key]; - if (list === undefined) { - list = []; - by_topic[key] = list; - } - list.push(pod); - }); - for (var name in by_topic) { - var topic = topics[name]; - if (topic === undefined) { - topic = create_topic(name); - topics[name] = topic; - } - topic.pods.update(by_topic[name]); - } - for (var name in topics) { - if (by_topic[name] === undefined) { - topics[name].close(); - } - if (topics[name].empty()) { - delete topics[name]; - } - } - }; -}; - diff --git a/agent/lib/utils.js b/agent/lib/utils.js deleted file mode 100644 index 6b382ab9ffd..00000000000 --- a/agent/lib/utils.js +++ /dev/null @@ -1,345 +0,0 @@ -/* - * Copyright 2017 Red Hat Inc. - * - * 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. - */ -'use strict'; - -var rhea = require('rhea'); -var util = require('util'); -var log = require("./log.js").logger(); - -module.exports.remove = function (list, predicate) { - var count = 0; - for (var i = 0; i < list.length;) { - if (predicate(list[i])) { - list.splice(i, 1); - count++; - } else { - i++; - } - } - return count; -}; - -module.exports.replace = function (list, object, match) { - for (var i = 0; i < list.length; i++) { - if (match(list[i])) { - list[i] = object; - return true; - } - } - return false; -}; - -module.exports.merge = function () { - return Array.prototype.slice.call(arguments).reduce(function (a, b) { - for (var key in b) { - a[key] = b[key]; - } - return a; - }); -} - -module.exports.basic_auth = function (request) { - if (request.headers.authorization) { - var parts = request.headers.authorization.split(' '); - if (parts.length === 2 && parts[0].toLowerCase() === 'basic') { - parts = new Buffer(parts[1], 'base64').toString().split(':'); - return { username: parts[0], password: parts[1] }; - } else { - throw new Error('Cannot handle authorization header ' + request.headers.authorization); - } - } -} - -function self(o) { - return o; -} - -module.exports.index = function (a, key, value) { - var fk = key || self; - var fv = value || self; - var m = {}; - a.forEach(function (i) { m[fk(i)] = fv(i); }); - return m; -} - -module.exports.values = function (map) { - var v = []; - for (var k in map) { - v.push(map[k]); - } - return v; -} - -module.exports.separate = function (map, predicate, a, b) { - for (var k in map) { - var v = map[k]; - if (predicate(v)) { - a[k] = v; - } else { - b[k] = v; - } - } -} - -module.exports.difference = function (a, b, equivalent) { - var diff = {}; - for (var k in a) { - if (!equivalent(b[k], a[k])) { - diff[k] = a[k]; - } - } - return diff; -} - -module.exports.match_source_address = function (link, address) { - return link && link.local && link.local.attach && link.local.attach.source - && link.local.attach.source.value[0].toString() === address; -} - -function hash(s) { - var h = 0; - for (var i = 0; i < s.length; i++) { - h = ((h << 5) - h) + s.charCodeAt(i) | 0; - } - return h; -}; - -module.exports.hash = hash; - -const MAX_KUBE_NAME = 63/*max allowed*/ - 3/*needed for kube to add stateful set qualifier*/; -module.exports.kubernetes_name = function (name) { - var clean = name.toLowerCase().replace(/[^a-z0-9\-\.]/g, ''); - if (clean.length > MAX_KUBE_NAME) clean = clean.substring(0, MAX_KUBE_NAME); - if (clean.charAt(0) === '-' || clean.charAt(0) === '.') clean = clean.substring(1); - if (clean.charAt(clean.length-1) === '-' || clean.charAt(clean.length-1) === '.') clean = clean.substring(0,clean.length-1); - - var qualifier = rhea.generate_uuid(); - clean += '.' + qualifier; - return clean; -} - -module.exports.serialize = function (f, retry_timeout = 5000) { - var in_progress = false; - var pending = false; - var timeout = null; - function doIt() { - if (in_progress) { - pending = true; - } else { - in_progress = true; - execute(); - } - } - function scheduleRetry (error) { - if (retry_timeout) { - log.warn("Rescheduling failed serialized func (%s) in %dms.", error, retry_timeout); - timeout = setTimeout(doIt, retry_timeout); - } - } - function cancelRetry() { - if (timeout) { - clearTimeout(timeout); - timeout = null; - } - } - function execute() { - try { - f().then(function () { - if (pending) { - next(); - } else { - in_progress = false; - } - }).catch(function(error) { - in_progress = false; - scheduleRetry(error); - }); - } catch (error) { - in_progress = false; - scheduleRetry(error); - } - } - function next() { - pending = false; - setImmediate(execute); - }; - return function() { - cancelRetry(); - doIt(); - } -}; - -module.exports.string_compare = function (a, b) { - if (a === b) return 0; - else if (a < b) return -1; - else return 1; -}; - -//return changes between two sorted lists -module.exports.changes = function (last, current, compare, unchanged, stringify) { - let description = stringify || JSON.stringify; - if (last === undefined) { - return { - added: current, - removed: [], - modified: [], - description: util.format('initial %s', description(current)) - }; - } else { - let d = { - added: [], - removed: [], - modified: [] - }; - let i = 0, j = 0; - while (i < last.length && j < current.length) { - switch (compare(last[i], current[j])) { - case 0: - //same address, has it changed? - if (unchanged && !unchanged(last[i], current[j])) { - d.modified.push(current[j]); - } - i++; j++; - break; - case 1: - //current[j] comes before last[i], therefore it is not in last - d.added.push(current[j++]); - break; - case -1: - //last[i] comes before current[j], therefore it is not in current - d.removed.push(last[i++]); - break; - } - } - while (i < last.length) { - //remaining items were in last but not in current - d.removed.push(last[i++]); - } - while (j < current.length) { - //remaining items are in current but not in last - d.added.push(current[j++]); - } - if (d.added.length || d.removed.length || d.modified.length) { - var parts = []; - for (let k in d) { - if (d[k].length) { - parts.push(util.format('%s %s', k, description(d[k]))); - } - } - d.description = parts.join(', '); - return d; - } else { - return undefined; - } - } -}; - -module.exports.coalesce = function (f, delay, max_delay) { - var start, scheduled, timeout = undefined; - var timeout = undefined; - - function fire() { - start = undefined; - timeout = undefined; - f(); - } - - function can_delay() { - return start && scheduled < (start + max_delay); - } - - function schedule() { - timeout = setTimeout(fire, delay); - scheduled = Date.now() + delay; - } - - return function () { - if (timeout) { - if (can_delay()) { - clearTimeout(timeout); - schedule(); - } // else just wait for previously scheduled call - } else { - start = Date.now(); - schedule(); - } - } -}; - -module.exports.get = function (object, fields, default_value) { - var o = object; - for (var i = 0; o && i < fields.length; i++) { - o = o[fields[i]]; - } - return o || default_value; -}; - -var multipliers = {}; -multipliers.B = 1; -multipliers.KB = ( multipliers.B * 1024 ); -multipliers.MB = ( multipliers.KB * 1024 ); -multipliers.GB = ( multipliers.MB * 1024 ); -multipliers.TB = ( multipliers.GB * 1024 ); -multipliers.PB = ( multipliers.TB * 1024 ); -multipliers.EB = ( multipliers.PB * 1024 ); -multipliers.ZB = ( multipliers.EB * 1024 ); - -module.exports.parseToBytes = function (input) { - var unit = input.toUpperCase().match(/([KMGTPEZ]I?)?B$/); - - if (!unit) { - log.warn( "parseToBytes input does not contain a supported unit of measurement. Using 'B': %s", input ); - unit = "B"; - } - unit = unit[ 0 ].replace( /i/i, "" ); - - var value = parseFloat(input); - if (isNaN(value)) { - log.error( "parseToBytes input does not contain a numeric value: %s",input ); - return 0; - } - - return( value * multipliers[unit] ); -}; - -module.exports.same_status_messages = function(a, b) { - if (a === b) { - return true; - } else if (a == null || b == null || a.length !== b.length) { - return false; - } - - for (var i in a) { - if (!b.includes(a[i])) { - return false; - } - } - return true; -}; - -module.exports.same_ttl = function (a, b) { - if (a === b) return true; - return a && b && a.minimum === b.minimum && a.maximum === b.maximum; -}; - -module.exports.description = function description(list, name) { - const max = 5; - if (list.length > max) { - return list.slice(0, max).map(name).join(', ') + ' and ' + (list.length - max) + ' more'; - } else { - return JSON.stringify(list.map(name)); - } -}; diff --git a/agent/package.json b/agent/package.json deleted file mode 100644 index 2df9f329fc6..00000000000 --- a/agent/package.json +++ /dev/null @@ -1,27 +0,0 @@ -{ - "name": "agent", - "version": "0.0.1", - "description": "controller agent and console for enmasse", - "homepage": "http://github.com/enmasseproject", - "license": "Apache-2.0", - "repository": { - "type": "git", - "url": "http://github.com/enmasseproject/enmasse" - }, - "dependencies": { - "debug": "^3.1.*", - "clone": "^2.1.2", - "p-limit": "^2.2.0", - "rhea": "^1.0.13", - "uuid": "^3.3.3" - }, - "devDependencies": { - "eslint": "5.16.0", - "mocha": "^6.1.4", - "mocha-junit-reporter": "^1.22.0" - }, - "scripts": { - "lint": "eslint lib/*.js", - "test": "mocha ${MOCHA_ARGS} --exit" - } -} diff --git a/agent/pom.xml b/agent/pom.xml deleted file mode 100644 index 9f2ac137cb8..00000000000 --- a/agent/pom.xml +++ /dev/null @@ -1,215 +0,0 @@ - - - - io.enmasse - enmasse - 0.32-SNAPSHOT - - 4.0.0 - io.enmasse - agent - - ${project.build.directory}/noderoot/agent - ${project.build.directory}/noderoot/licensereporter - --silent - ${project.basedir}/../scripts/licenses.xsl - - - - - org.apache.maven.plugins - maven-resources-plugin - - - copy-packagejson-agent - generate-resources - - copy-resources - - - ${agent-node-install-directory} - - - ${project.basedir} - - package.json - yarn.lock - - - - - - - - - com.github.eirslett - frontend-maven-plugin - - - install node and yarn - agent - - install-node-and-yarn - - - ${node.version} - ${yarn.version} - ${agent-node-install-directory} - - - - yarn install - agent - - yarn - - process-resources - - ${agent-node-install-directory} - install --production --fetch-retry-mintimeout 60000 --fetch-retries 10 --maxsockets 2 --quiet - - - - yarn install tests - - yarn - - test - - ${agent-node-install-directory} - install --quiet - - - - yarn test - - yarn - - test - - ${agent-node-install-directory} - run test - - - - - - org.apache.maven.plugins - maven-assembly-plugin - - - dist - package - - single - - - false - - src/assembly/unix-dist.xml - - posix - - - - - - org.codehaus.mojo - license-maven-plugin - - true - true - - - - - - - cvecheck - - - - org.owasp - dependency-check-maven - - - true - false - false - false - false - false - false - - - ${project.basedir} - - - - - - - - - - release - - - - com.github.eirslett - frontend-maven-plugin - - - install node and yarn - license-reporter - - install-node-and-yarn - - - ${node.version} - ${yarn.version} - ${licensereporter-node-install-directory} - - - - yarn install - license-reporter - - yarn - - process-resources - - ${licensereporter-node-install-directory} - global add --prefix ${licensereporter-node-install-directory} license-reporter@1.2.1 - - - - - - org.codehaus.mojo - exec-maven-plugin - - - generate-license-metadata - - exec - - process-resources - - ${project.build.directory} - ${project.basedir}/../scripts/create-license-metadata.sh - - ${licensereporter-node-install-directory} - ${project.build.outputDirectory}/licenses - agent - ${agent-node-install-directory} - - - ${license-reporter-opts} - - - - - - - - - - diff --git a/agent/src/assembly/unix-dist.xml b/agent/src/assembly/unix-dist.xml deleted file mode 100644 index 1605b6d87e6..00000000000 --- a/agent/src/assembly/unix-dist.xml +++ /dev/null @@ -1,69 +0,0 @@ - - - - - dist - - - tar.gz - zip - - False - - - ${project.basedir}/target/noderoot/agent - / - - node_modules/** - - 0644 - - - ${project.basedir}/bin - bin - - ** - - 0755 - - - ${project.basedir}/lib - lib - - ** - - 0644 - - - ${project.basedir} - - package.json - - 0644 - - - ${project.build.outputDirectory} - / - - licenses/** - - 0644 - - - diff --git a/agent/test/address_ctrl.js b/agent/test/address_ctrl.js deleted file mode 100644 index ed65bdce66c..00000000000 --- a/agent/test/address_ctrl.js +++ /dev/null @@ -1,237 +0,0 @@ -/* - * Copyright 2017 Red Hat Inc. - * - * 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. - */ -'use strict'; - -var assert = require('assert'); -var http = require('http'); - -var address_ctrl_lib = require('../lib/address_ctrl'); - - -function MockAddressSource () { - this.addresses = { - apiVersion: 'enmasse.io/v1', - kind: 'AddressList', - items : [] - }; - this.schema = { - "apiVersion": "enmasse.io/v1", - "kind": "Schema", - "spec": { - "addressSpaceTypes": [ - { - "name": "standard", - "addressTypes": [ - { - "name": "queue", - "description": "store and forward, competing consumers", - "plans": [ - { - "name": "standard", - "description": "simple" - }, - { - "name": "another", - "description": "perhaps more complex" - } - ] - }, - { - "name": "topic", - "description": "store and forward, non-competing consumers", - "plans": [ - { - "name": "standard", - "description": "only one available" - } - ] - } - ], - "plans": [ - { - "name": "unrestrained", - "description": "no address-space level constraints" - } - ] - } - ] - } - }; -} - -MockAddressSource.prototype.clear = function () { - this.addresses.items = []; -}; - -MockAddressSource.prototype.listen = function (port, callback) { - var self = this; - this.server = http.createServer(function (request, response) { - if (request.method === 'GET' && request.url === '/apis/enmasse.io/v1/schema/') { - self.get_schema(request, response); - } else if (request.method === 'GET' && request.url === '/apis/enmasse.io/v1/addresses/') { - self.get_addresses(request, response); - } else if (request.method === 'POST' && request.url === '/apis/enmasse.io/v1/addresses/') { - self.post_addresses(request, response); - } else if (request.method === 'DELETE' && request.url.indexOf('/apis/enmasse.io/v1/addresses/') === 0) { - self.delete_address(request, response); - } - }); - this.server.listen(port || 0, function () { - self.port = self.server.address().port; - if (callback) callback(); - }); - return this.server; -}; - -MockAddressSource.prototype.close = function (callback) { - this.server.close(callback); -} - -MockAddressSource.prototype.get_schema = function (request, response) { - response.end(JSON.stringify(this.schema)); -}; - -MockAddressSource.prototype.get_addresses = function (request, response) { - response.end(JSON.stringify(this.addresses)); -}; - -MockAddressSource.prototype.add_address = function (address) { - if (this.addresses.items.some(function (a) { return a.metadata.name === address.metadata.name; })) { - throw new Error('Already exists'); - } else { - this.addresses.items.push(address); - } -} - -MockAddressSource.prototype.post_addresses = function (request, response) { - var self = this; - var bodytext = ''; - request.on('data', function (data) { bodytext += data; }); - request.on('end', function () { - var body = JSON.parse(bodytext); - try { - if (body.kind === 'AddressList') { - body.items.forEach(function (a) { self.add_address(a); }); - response.statusCode = 200; - response.end(); - } else if (body.kind === 'Address') { - self.add_address(o); - response.statusCode = 200; - response.end(); - } else { - response.statusCode = 500; - response.end('Unrecognised resource kind %s', body.kind); - } - } catch (error) { - response.statusCode = 500; - response.end(error.message); - } - }); -}; - -MockAddressSource.prototype.delete_address = function (request, response) { - var parts = request.url.split('/'); - var address = parts.pop(); - for (var i = 0; i < this.addresses.items.length; i++) { - if (this.addresses.items[i].spec.address === address) { - this.addresses.items.splice(i, 1); - response.statusCode = 200; - response.end(); - return; - } - } - response.statusCode = 400; - response.end(); -}; - -describe('address controller interaction', function() { - var address_source; - var address_ctrl; - - beforeEach(function(done) { - address_source = new MockAddressSource(); - address_source.listen(0, function () { - address_ctrl = address_ctrl_lib.create({"KUBERNETES_TOKEN": "mytoken", "ADDRESS_CONTROLLER_SERVICE_HOST": "localhost", "ADDRESS_CONTROLLER_SERVICE_PORT_HTTPS": address_source.port}); - done(); - }); - - }); - - afterEach(function(done) { - address_source.close(done); - }); - - it('requests creation of an address', function(done) { - address_ctrl.create_address({address:'myaddress', type:'queue', plan:'standard'}).then(function() { - assert.equal(address_source.addresses.items.length, 1); - assert.equal(address_source.addresses.items[0].spec.address, 'myaddress'); - assert.equal(address_source.addresses.items[0].spec.type, 'queue'); - assert.equal(address_source.addresses.items[0].spec.plan, 'standard'); - done(); - }); - }); - - it('requests deletion of addresses', function (done) { - var addresses = [{address:'myaddress', type:'queue', plan:'standard'}, {address:'foo', type:'bar', plan:'baz'}, {address:'another', type:'topic', plan:'fancy'}]; - var deletions = [addresses[0].address, addresses[2].address]; - Promise.all(addresses.map(function (a) { return address_ctrl.create_address(a); })).then(function() { - Promise.all(deletions.map(function (a) { return address_ctrl.delete_address({address:a}); })).then(function() { - assert.equal(address_source.addresses.items.length, 1); - assert.equal(address_source.addresses.items[0].spec.address, 'foo'); - assert.equal(address_source.addresses.items[0].spec.type, 'bar'); - assert.equal(address_source.addresses.items[0].spec.plan, 'baz'); - done(); - }); - }); - }); - - it('retrieves address types for current address-space', function (done) { - address_ctrl.get_address_types().then(function (address_types) { - assert.equal(address_types.length, 2); - assert.equal(address_types[0].name, 'queue'); - assert.equal(address_types[0].plans.length, 2); - assert.equal(address_types[0].plans[0].name, 'standard'); - assert.equal(address_types[0].plans[1].name, 'another'); - assert.equal(address_types[1].name, 'topic'); - assert.equal(address_types[1].plans.length, 1); - assert.equal(address_types[1].plans[0].name, 'standard'); - done(); - }); - }); - - it('handles error creating an address', function(done) { - address_ctrl.create_address({address:'myaddress', type:'queue', plan:'standard'}).then(function() { - assert.equal(address_source.addresses.items.length, 1); - assert.equal(address_source.addresses.items[0].spec.address, 'myaddress'); - assert.equal(address_source.addresses.items[0].spec.type, 'queue'); - assert.equal(address_source.addresses.items[0].spec.plan, 'standard'); - address_ctrl.create_address({address:'myaddress', type:'foo', plan:'bar'}).then(function () { - assert.fail('second creation request should fail'); - }).catch(function (error) { - done(); - }); - }); - }); - - it('handles invalid kind for schema', function (done) { - address_source.schema.kind = 'foo'; - address_ctrl.get_address_types().then(function () { - assert.fail('bad schema kind should fail'); - }).catch(function (error) { - done(); - }); - }); -}); diff --git a/agent/test/address_list.js b/agent/test/address_list.js deleted file mode 100644 index d227fabda8d..00000000000 --- a/agent/test/address_list.js +++ /dev/null @@ -1,184 +0,0 @@ -/* - * Copyright 2017 Red Hat Inc. - * - * 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. - */ -'use strict'; - -var assert = require('assert'); -var rhea = require('rhea'); - -var AddressList = require('../lib/address_list.js'); -var AddressSource = require('../lib/external_address_source.js'); - -function MockConfigServ () { - this.address_list = []; - this.connections = {}; - this.container = rhea.create_container({id:'mock-config-serv'}); - this.container.on('sender_open', this.on_subscribe.bind(this)); - this.container.on('connection_open', this.register.bind(this)); - this.container.on('connection_close', this.unregister.bind(this)); - this.container.on('disconnected', this.unregister.bind(this)); -} - -MockConfigServ.prototype.listen = function (port) { - this.port = port; - this.server = this.container.listen({port:port}); - var self = this; - this.server.on('listening', function () { - self.port = self.server.address().port; - }); - return this.server; -}; - -MockConfigServ.prototype.close = function (callback) { - if (this.server) this.server.close(callback); - else callback(); -}; - -MockConfigServ.prototype.notify = function (sender) { - var msg = {subject: 'enmasse.io/v1/AddressList'}; - - if (this.address_list.length > 0) { - var body = {}; - body.items = this.address_list.map(function (address) { - return { spec: address }; - }); - msg.body = JSON.stringify(body); - } - sender.send(msg); -}; - -function is_subscriber(link) { - return link.is_sender() && link.source.address === 'v1/addresses'; -} - -MockConfigServ.prototype.notify_all = function (message) { - var self = this; - var f; - if (message === undefined) { - f = function (link) { self.notify(link) }; - } else { - f = function (link) { link.send(message); }; - } - for (var c in this.connections) { - this.connections[c].each_link(f, is_subscriber); - } -} - -MockConfigServ.prototype.on_subscribe = function (context) { - this.notify(context.sender); -}; - -MockConfigServ.prototype.register = function (context) { - this.connections[context.connection.container_id] = context.connection; -}; - -MockConfigServ.prototype.unregister = function (context) { - delete this.connections[context.connection.container_id]; -}; - -MockConfigServ.prototype.add = function (address, type) { - this.address_list.push({'address':address, 'type':type}); -}; - -MockConfigServ.prototype.delete_all = function () { - this.address_list = []; -}; - -describe('address registry', function() { - var address_source; - var connection; - - beforeEach(function(done) { - address_source = new MockConfigServ(); - address_source.listen(0).on('listening', done); - }); - - afterEach(function(done) { - if (connection) connection.close(); - address_source.close(done); - }); - - function create_address_list() { - connection = rhea.connect({port:address_source.port}); - var src = new AddressSource(connection); - var address_list = new AddressList(); - src.on('addresses_defined', address_list.addresses_defined.bind(address_list)); - return address_list; - } - - it('subscribes to configserv', function(done) { - var addresses = create_address_list(); - address_source.add('foo', 'anycast'); - addresses.on('updated', function (addr) { - assert.equal(addr.address, 'foo'); - assert.equal(addr.type, 'anycast'); - assert.equal(addresses.get('foo').type, 'anycast'); - done(); - }); - }); - it('handles empty body', function(done) { - var addresses = create_address_list(); - address_source.add('foo', 'anycast'); - addresses.on('updated', function (addr) { - assert.equal(addr.address, 'foo'); - assert.equal(addr.type, 'anycast'); - assert.equal(addresses.get('foo').type, 'anycast'); - address_source.delete_all(); - address_source.notify_all(); - }); - addresses.on('deleted', function (addr) { - assert.equal(addr.address, 'foo'); - assert.equal(addr.type, 'anycast'); - assert.equal(addresses.get('foo'), undefined); - done(); - }); - }); - it('handles body with no items', function(done) { - var addresses = create_address_list(); - address_source.add('foo', 'anycast'); - addresses.on('updated', function (addr) { - assert.equal(addr.address, 'foo'); - assert.equal(addr.type, 'anycast'); - assert.equal(addresses.get('foo').type, 'anycast'); - address_source.notify_all({subject: 'enmasse.io/v1/AddressList', body:'{}'}); - }); - addresses.on('deleted', function (addr) { - assert.equal(addr.address, 'foo'); - assert.equal(addr.type, 'anycast'); - assert.equal(addresses.get('foo'), undefined); - done(); - }); - }); - it('handles updates', function(done) { - var addresses = create_address_list(); - address_source.add('foo', 'anycast'); - var count = 0; - addresses.on('updated', function (addr) { - if (++count === 1) { - assert.equal(addr.address, 'foo'); - assert.equal(addr.type, 'anycast'); - assert.equal(addresses.get('foo').type, 'anycast'); - address_source.delete_all(); - address_source.add('foo', 'queue'); - address_source.notify_all(); - } else { - assert.equal(addr.address, 'foo'); - assert.equal(addr.type, 'queue'); - assert.equal(addresses.get('foo').type, 'queue'); - done(); - } - }); - }); -}); diff --git a/agent/test/broker_controller.js b/agent/test/broker_controller.js deleted file mode 100644 index 59e70142767..00000000000 --- a/agent/test/broker_controller.js +++ /dev/null @@ -1,199 +0,0 @@ -/* - * Copyright 2017 Red Hat Inc. - * - * 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. - */ -'use strict'; - -var assert = require('assert'); -var util = require('util'); -var MockBroker = require('../testlib/mock_broker.js'); -var broker_controller = require('../lib/broker_controller.js'); - -function random_number(max) { - return Math.floor(Math.random() * Math.floor(max)); -} - -describe('broker controller', function() { - var broker; - var controller; - - beforeEach(function(done) { - broker = new MockBroker('mybroker'); - broker.listen().on('listening', function () { - controller = broker_controller.create_agent(); - controller.connect({port:broker.port}); - controller.on('ready', function () { - done(); - }); - }); - }); - - afterEach(function(done) { - controller.close(); - broker.close(done); - }); - - it('creates a queue', function(done) { - controller.addresses_defined([{address:'foo',type:'queue'}]).then(function () { - var addresses = broker.list_addresses(); - var queues = broker.list_queues(); - broker.verify_queue(addresses, queues, 'foo'); - assert.equal(addresses.length, 0); - assert.equal(queues.length, 0); - done(); - }); - }); - it('creates a topic', function(done) { - controller.addresses_defined([{address:'bar',type:'topic'}]).then(function () { - var addresses = broker.list_addresses(); - broker.verify_topic(addresses, 'bar'); - assert.equal(addresses.length, 0); - done(); - }); - }); - it('deletes a queue', function(done) { - controller.addresses_defined([{address:'foo',type:'queue'}, {address:'bar',type:'topic'}]).then(function () { - var addresses = broker.list_addresses(); - var queues = broker.list_queues(); - broker.verify_queue(addresses, queues, 'foo'); - broker.verify_topic(addresses, 'bar'); - assert.equal(addresses.length, 0); - assert.equal(queues.length, 0); - controller.addresses_defined([{address:'bar',type:'topic'}]).then(function () { - var addresses = broker.list_addresses(); - var queues = broker.list_queues(); - broker.verify_topic(addresses, 'bar'); - assert.equal(addresses.length, 0, util.format('extra addresses: %j', addresses)); - assert.equal(queues.length, 0, util.format('extra queues: %j', queues)); - done(); - }).catch(done); - }).catch(done); - }); - it('deletes a topic', function(done) { - controller.addresses_defined([{address:'foo',type:'queue'}, {address:'bar',type:'topic'}]).then(function () { - var addresses = broker.list_addresses(); - var queues = broker.list_queues(); - broker.verify_queue(addresses, queues, 'foo'); - broker.verify_topic(addresses, 'bar'); - assert.equal(addresses.length, 0); - assert.equal(queues.length, 0); - controller.addresses_defined([{address:'foo',type:'queue'}]).then(function () { - var addresses = broker.list_addresses(); - var queues = broker.list_queues(); - broker.verify_queue(addresses, queues, 'foo'); - assert.equal(addresses.length, 0); - assert.equal(queues.length, 0); - done(); - }).catch(done); - }).catch(done); - }); - it('retrieves topic stats', function(done) { - broker.add_topic_address('foo', { - 's1':{durable:true, messageCount:10, consumerCount:9, messagesAdded:8, deliveringCount:7, messagesAcked:6, messagesExpired:5, messagesKilled: 4}, - 's2':{durable:false, messageCount:11, consumerCount:10, messagesAdded:9, deliveringCount:8, messagesAcked:7, messagesExpired:6, messagesKilled: 5}, - }, 21); - for (var i = 0; i < 5; i++) broker.add_connection({ clientAddress: "example:" + random_number(16384) + 49152}, [{destination:'foo'}]); - Promise.all([ - new Promise(function (resolve, reject) { - controller.on('address_stats_retrieved', function (stats) { - resolve(stats); - }); - }), - controller.retrieve_stats() - ]).then(function (results) { - var stats = results[0]; - assert.equal(stats.foo.propagated, 100); - assert.equal(stats.foo.messages_in, 17); - assert.equal(stats.foo.messages_out, 13); - assert.equal(stats.foo.receivers, 2); - assert.equal(stats.foo.senders, 5); - done(); - }).catch(done); - }); - function generate_address_list(count, allowed_types) { - var types = allowed_types || ['anycast', 'multicast', 'queue', 'topic']; - var list = []; - for (var i = 0; i < count; i++) { - list.push({address:util.format('address-%s', (i+1)), type:types[i % types.length]}); - } - return list; - } - - it('creates lots of queues', function(done) { - this.timeout(15000); - var desired = generate_address_list(2000, ['queue']); - controller._sync_addresses(desired).then(function () { - controller.close(); - broker.verify_addresses(desired); - done(); - }).catch(done); - }); - it('creates lots of topics', function(done) { - this.timeout(15000); - var desired = generate_address_list(2000, ['topic']); - controller._sync_addresses(desired).then(function () { - controller.close(); - broker.verify_addresses(desired); - done(); - }).catch(done); - }); - it('creates lots of queues and topics', function(done) { - this.timeout(15000); - var desired = generate_address_list(2000, ['queue', 'topic']); - controller._sync_addresses(desired).then(function () { - controller.close(); - broker.verify_addresses(desired); - done(); - }).catch(done); - }); - - var config = { BROKER_GLOBAL_MAX_SIZE: "64MB" }; - - it('get address settings async - returns setting maxSizeBytes calculated from infra', function(done) { - config.BROKER_GLOBAL_MAX_SIZE="64MB"; - var brokerAddressSettings = new broker_controller.BrokerController(undefined, config); - brokerAddressSettings.get_address_settings_async({address:'foo',type:'queue', plan: 'small-queue', status: {planStatus: {name: "small-queue", resources: {broker: 0.2}}}}).then(function (result) { - assert.equal(13421773, result.maxSizeBytes); - done(); - }); - }); - - it('get address settings async - returns setting maxSizeBytes calculated from infra with partitions', function(done) { - config.BROKER_GLOBAL_MAX_SIZE="64MB"; - var brokerAddressSettings = new broker_controller.BrokerController(undefined, config); - brokerAddressSettings.get_address_settings_async({address:'foo',type:'queue', plan: 'small-queue', status: {planStatus: {name: "small-queue", partitions: 2, resources: {broker: 0.2}}}}).then(function (result) { - assert.equal(6710886, result.maxSizeBytes); - done(); - }); - }); - - it('get address settings async - returns setting maxSizeBytes calculated from broker', function(done) { - config.BROKER_GLOBAL_MAX_SIZE=undefined; - var brokerAddressSettings = new broker_controller.BrokerController(undefined, config); - brokerAddressSettings.get_address_settings_async({address:'foo',type:'queue', plan: 'small-queue', status: {planStatus: {name: "small-queue", resources: {broker: 0.1}}}}, Promise.resolve(1000000)).then(function (result) { - assert.equal(100000, result.maxSizeBytes); - done(); - }); - }); - - it('get address settings async - returns ttl settings', function(done) { - var brokerAddressSettings = new broker_controller.BrokerController(undefined, config); - brokerAddressSettings.get_address_settings_async({address:'foo',type:'queue', plan: 'small-queue', status: {messageTtl: {minimum: 1000, maximum: 2000}}}, Promise.resolve(undefined)).then(function (result) { - assert.equal(1000, result.minExpiryDelay); - assert.equal(2000, result.maxExpiryDelay); - done(); - }); - }); - -}); diff --git a/agent/test/broker_stats.js b/agent/test/broker_stats.js deleted file mode 100644 index 1d4f196e9c2..00000000000 --- a/agent/test/broker_stats.js +++ /dev/null @@ -1,311 +0,0 @@ -/* - * Copyright 2017 Red Hat Inc. - * - * 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. - */ -'use strict'; - -var assert = require('assert'); -var events = require('events'); -var util = require('util'); -var rhea = require('rhea'); -var BrokerStats = require('../lib/broker_stats.js'); -var MockBroker = require('../testlib/mock_broker.js'); -var ResourceServer = require('../testlib/mock_resource_server.js').ResourceServer; - -var num_brokers = 0; - -function PodServer () { - this.resource_server = new ResourceServer(true, function (b) { return b.get_pod_definition(); }); -} - -PodServer.prototype.add_broker = function (name, port) { - num_brokers += 1; - var broker = new MockBroker(name || 'broker-' + num_brokers); - broker.listen(port); - this.resource_server.add_resource('pods', broker); - return broker; -}; - -PodServer.prototype.remove_broker = function (broker) { - this.resource_server.remove_resource('pods', broker); -}; - -PodServer.prototype.listen = function (port) { - return this.resource_server.listen(port); -}; - -PodServer.prototype.close = function (callback) { - this.resource_server.resources["pods"].forEach(function (b) { b.close(); }); - this.resource_server.close(callback); -}; - -describe('broker stats', function() { - this.timeout(5000); - var discovery; - var broker; - - beforeEach(function(done) { - discovery = new PodServer(); - num_brokers = 0; - broker = discovery.add_broker(); - var s = discovery.listen(0).on('listening', function () { - process.env.CONFIGURATION_SERVICE_HOST = 'localhost'; - process.env.CONFIGURATION_SERVICE_PORT = s.address().port; - done(); - }); - }); - - afterEach(function(done) { - discovery.close(function () { - done(); - }); - }); - - it('retrieves queue stats from a single broker', function(done) { - broker.add_queue_address('myqueue', - { - durable:true, - messageCount:10, - consumerCount:3, - messagesAdded:44, - deliveringCount:5, - messagesAcked:8, - messagesExpired:4, - messagesKilled: 2 - }); - var stats = new BrokerStats({port:discovery.resource_server.port, host:'localhost', namespace:'default', token: 'foo'}); - broker.on('connected', function () { - stats._retrieve().then(function (results) { - assert.equal(results.myqueue.depth, 10); - assert.equal(results.myqueue.shards.length, 1); - assert.equal(results.myqueue.shards[0].durable, true); - assert.equal(results.myqueue.shards[0].messages, 10); - assert.equal(results.myqueue.shards[0].consumers, 3); - assert.equal(results.myqueue.shards[0].enqueued, 44); - assert.equal(results.myqueue.shards[0].delivering, 5); - assert.equal(results.myqueue.shards[0].acknowledged, 8); - assert.equal(results.myqueue.shards[0].expired, 4); - assert.equal(results.myqueue.shards[0].killed, 2); - stats.watcher.close(); - done(); - }).catch(done); - }); - }); - it('aggregates queue stats from multiple brokers', function(done) { - broker.add_queue_address('myqueue', - { - durable:true, - messageCount:10, - consumerCount:3, - messagesAdded:44, - deliveringCount:5, - messagesAcked:8, - messagesExpired:4, - messagesKilled: 2 - }); - var broker2 = discovery.add_broker(); - broker2.add_queue_address('myqueue', - { - durable:true, - messageCount:22, - consumerCount:1, - messagesAdded:88, - deliveringCount:9, - messagesAcked:58, - messagesExpired:7, - messagesKilled: 3 - }); - var stats = new BrokerStats({port:discovery.resource_server.port, host:'localhost', namespace:'default', token: 'foo'}); - broker2.on('connected', function () { - stats._retrieve().then(function (results) { - assert.equal(results.myqueue.depth, 32); - assert.equal(results.myqueue.shards.length, 2); - var b1, b2; - if (results.myqueue.shards[0].name === 'broker-1') { - b1 = 0; - b2 = 1; - } else { - b1 = 1; - b2 = 0; - } - assert.equal(results.myqueue.shards[b1].name, 'broker-1'); - assert.equal(results.myqueue.shards[b1].durable, true); - assert.equal(results.myqueue.shards[b1].messages, 10); - assert.equal(results.myqueue.shards[b1].consumers, 3); - assert.equal(results.myqueue.shards[b1].enqueued, 44); - assert.equal(results.myqueue.shards[b1].delivering, 5); - assert.equal(results.myqueue.shards[b1].acknowledged, 8); - assert.equal(results.myqueue.shards[b1].expired, 4); - assert.equal(results.myqueue.shards[b1].killed, 2); - - assert.equal(results.myqueue.shards[b2].name, 'broker-2'); - assert.equal(results.myqueue.shards[b2].durable, true); - assert.equal(results.myqueue.shards[b2].messages, 22); - assert.equal(results.myqueue.shards[b2].consumers, 1); - assert.equal(results.myqueue.shards[b2].enqueued, 88); - assert.equal(results.myqueue.shards[b2].delivering, 9); - assert.equal(results.myqueue.shards[b2].acknowledged, 58); - assert.equal(results.myqueue.shards[b2].expired, 7); - assert.equal(results.myqueue.shards[b2].killed, 3); - stats.watcher.close(); - done(); - }).catch(done); - }); - }); - - - it('aggregates topic stats from multiple brokers', function(done) { - broker.add_topic_address('mytopic', - { - 'dsub1':{durable:true, messageCount:10, consumerCount:9, messagesAdded:8, deliveringCount:7, messagesAcked:6, messagesExpired:5, messagesKilled: 4}, - 'sub2':{durable:false, messageCount:11, consumerCount:10, messagesAdded:9, deliveringCount:8, messagesAcked:7, messagesExpired:6, messagesKilled: 5}, - }); - var broker2 = discovery.add_broker(); - broker2.add_topic_address('mytopic', - { - 'sub3':{durable:false, messageCount:9, consumerCount:8, messagesAdded:7, deliveringCount:6, messagesAcked:5, messagesExpired:4, messagesKilled: 3} - }); - var broker3 = discovery.add_broker(); - broker3.add_topic_address('mytopic', - { - 'dsub4':{durable:true, messageCount:7, consumerCount:0, messagesAdded:7, deliveringCount:7, messagesAcked:7, messagesExpired:7, messagesKilled: 7}, - 'dsub5':{durable:true, messageCount:1, consumerCount:1, messagesAdded:1, deliveringCount:1, messagesAcked:1, messagesExpired:1, messagesKilled: 1}, - 'sub6':{durable:false, messageCount:2, consumerCount:2, messagesAdded:2, deliveringCount:2, messagesAcked:2, messagesExpired:2, messagesKilled: 2} - }); - var stats = new BrokerStats({port:discovery.resource_server.port, host:'localhost', namespace:'default', token: 'foo'}); - broker3.on('connected', function () { - stats._retrieve().then(function (results) { - assert.equal(results.mytopic.depth, 40); - assert.equal(results.mytopic.shards.length, 3); - - var b1, b2, b3; - for (var i = 0; i < results.mytopic.shards.length; i++) { - if (results.mytopic.shards[i].name === 'broker-1') { - b1 = i; - } else if (results.mytopic.shards[i].name === 'broker-2') { - b2 = i; - } else if (results.mytopic.shards[i].name === 'broker-3') { - b3 = i; - } - } - - assert.equal(results.mytopic.shards[b1].subscription_count, 2); - assert.equal(results.mytopic.shards[b1].durable_subscription_count, 1); - assert.equal(results.mytopic.shards[b1].inactive_durable_subscription_count, 0); - assert.equal(results.mytopic.shards[b1].name, 'broker-1'); - assert.equal(results.mytopic.shards[b1].subscriptions.length, 2); - assert.equal(results.mytopic.shards[b1].subscriptions[0].durable, true); - assert.equal(results.mytopic.shards[b1].subscriptions[0].messages, 10); - assert.equal(results.mytopic.shards[b1].subscriptions[0].consumers, 9); - assert.equal(results.mytopic.shards[b1].subscriptions[0].enqueued, 8); - assert.equal(results.mytopic.shards[b1].subscriptions[0].delivering, 7); - assert.equal(results.mytopic.shards[b1].subscriptions[0].acknowledged, 6); - assert.equal(results.mytopic.shards[b1].subscriptions[0].expired, 5); - assert.equal(results.mytopic.shards[b1].subscriptions[0].killed, 4); - assert.equal(results.mytopic.shards[b1].subscriptions[1].durable, false); - assert.equal(results.mytopic.shards[b1].subscriptions[1].messages, 11); - assert.equal(results.mytopic.shards[b1].subscriptions[1].consumers, 10); - assert.equal(results.mytopic.shards[b1].subscriptions[1].enqueued, 9); - assert.equal(results.mytopic.shards[b1].subscriptions[1].delivering, 8); - assert.equal(results.mytopic.shards[b1].subscriptions[1].acknowledged, 7); - assert.equal(results.mytopic.shards[b1].subscriptions[1].expired, 6); - assert.equal(results.mytopic.shards[b1].subscriptions[1].killed, 5); - - assert.equal(results.mytopic.shards[b2].subscription_count, 1); - assert.equal(results.mytopic.shards[b2].durable_subscription_count, 0); - assert.equal(results.mytopic.shards[b2].inactive_durable_subscription_count, 0); - assert.equal(results.mytopic.shards[b2].name, 'broker-2'); - assert.equal(results.mytopic.shards[b2].subscriptions.length, 1); - assert.equal(results.mytopic.shards[b2].subscriptions[0].durable, false); - assert.equal(results.mytopic.shards[b2].subscriptions[0].messages, 9); - assert.equal(results.mytopic.shards[b2].subscriptions[0].consumers, 8); - assert.equal(results.mytopic.shards[b2].subscriptions[0].enqueued, 7); - assert.equal(results.mytopic.shards[b2].subscriptions[0].delivering, 6); - assert.equal(results.mytopic.shards[b2].subscriptions[0].acknowledged, 5); - assert.equal(results.mytopic.shards[b2].subscriptions[0].expired, 4); - assert.equal(results.mytopic.shards[b2].subscriptions[0].killed, 3); - - assert.equal(results.mytopic.shards[b3].subscription_count, 3); - assert.equal(results.mytopic.shards[b3].durable_subscription_count, 2); - assert.equal(results.mytopic.shards[b3].inactive_durable_subscription_count, 1); - assert.equal(results.mytopic.shards[b3].name, 'broker-3'); - assert.equal(results.mytopic.shards[b3].subscriptions.length, 3); - assert.equal(results.mytopic.shards[b3].subscriptions[0].durable, true); - assert.equal(results.mytopic.shards[b3].subscriptions[0].messages, 7); - assert.equal(results.mytopic.shards[b3].subscriptions[0].consumers, 0); - assert.equal(results.mytopic.shards[b3].subscriptions[0].enqueued, 7); - assert.equal(results.mytopic.shards[b3].subscriptions[0].delivering, 7); - assert.equal(results.mytopic.shards[b3].subscriptions[0].acknowledged, 7); - assert.equal(results.mytopic.shards[b3].subscriptions[0].expired, 7); - assert.equal(results.mytopic.shards[b3].subscriptions[0].killed, 7); - - for (var i = 1; i < 3; i++) { - assert.equal(results.mytopic.shards[b3].subscriptions[i].durable, i == 1); - assert.equal(results.mytopic.shards[b3].subscriptions[i].messages, i); - assert.equal(results.mytopic.shards[b3].subscriptions[i].consumers, i); - assert.equal(results.mytopic.shards[b3].subscriptions[i].enqueued, i); - assert.equal(results.mytopic.shards[b3].subscriptions[i].delivering, i); - assert.equal(results.mytopic.shards[b3].subscriptions[i].acknowledged, i); - assert.equal(results.mytopic.shards[b3].subscriptions[i].expired, i); - assert.equal(results.mytopic.shards[b3].subscriptions[i].killed, i); - } - stats.watcher.close(); - done(); - }).catch(done); - }); - }); - - it('handles removal of broker from list', function(done) { - broker.add_queue_address('myqueue', - { - durable:true, - messageCount:10, - consumerCount:3, - messagesAdded:44, - deliveringCount:5, - messagesAcked:8, - messagesExpired:4, - messagesKilled: 2 - }); - var broker2 = discovery.add_broker(); - broker2.add_queue_address('myqueue', - { - durable:true, - messageCount:22, - consumerCount:1, - messagesAdded:88, - deliveringCount:9, - messagesAcked:58, - messagesExpired:7, - messagesKilled: 3 - }); - var stats = new BrokerStats({port:discovery.resource_server.port, host:'localhost', namespace:'default', token: 'foo'}); - broker2.on('connected', function () { - stats._retrieve().then(function (results) { - assert.equal(results.myqueue.depth, 32); - assert.equal(results.myqueue.shards.length, 2); - discovery.remove_broker(broker2); - broker2.on('disconnected', function () { - stats._retrieve().then(function (new_results) { - assert.equal(new_results.myqueue.depth, 10); - assert.equal(new_results.myqueue.shards.length, 1); - stats.watcher.close(); - done(); - }); - }); - }); - }); - }); - -}); diff --git a/agent/test/ca-cert.pem b/agent/test/ca-cert.pem deleted file mode 100644 index d4fb5b2cb00..00000000000 --- a/agent/test/ca-cert.pem +++ /dev/null @@ -1,19 +0,0 @@ ------BEGIN CERTIFICATE----- -MIIDIjCCAgoCCQCIjdDA1w7hrzANBgkqhkiG9w0BAQUFADBTMQswCQYDVQQGEwJY -WDEVMBMGA1UEBwwMRGVmYXVsdCBDaXR5MRwwGgYDVQQKDBNEZWZhdWx0IENvbXBh -bnkgTHRkMQ8wDQYDVQQDDAZUZXN0Q0EwHhcNMTUxMTA5MjIyMTU3WhcNMjUxMTA2 -MjIyMTU3WjBTMQswCQYDVQQGEwJYWDEVMBMGA1UEBwwMRGVmYXVsdCBDaXR5MRww -GgYDVQQKDBNEZWZhdWx0IENvbXBhbnkgTHRkMQ8wDQYDVQQDDAZUZXN0Q0EwggEi -MA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCz4qxT+qEAjYax4VxR6JNMKGnd -Ryv5Q/Gl6oVQK0cGT03QLWIKVSX3UkZ3toyreKBTh0qrnKGc07b5P/mMtMvgwd0M -2hFMd7CP1+JMEj9JrbtB6kUX7cLXtp1EvK3uziNnm7YwoeF8GUlDH2CDyQASst/D -AshzUE2h2AvWTHBnT6lPRtKUfHKRT7BfGyeBnX5n9sFOoDOglRk4DwOguFvLxH8b -nNIO5LnBU6ksOfQ3nffRHN6QIv5t942wqnoudAmV3MI1QFW/Jyybg9QXwxsIv1SJ -oN7tvZDBjvVYvsNKEeZj6/QcR5y2tb2ylSTMx8qKET/pap/0KiwZSCOi9xB9AgMB -AAEwDQYJKoZIhvcNAQEFBQADggEBACEa5i5YbQpn0APASw2NqOECBqebMrKMOVz3 -Cnrlchl7dNmdH3WpAA36Gqig20MZoceiSGLhdXMD78jCrWnIJYPp2KhRYP2oibD0 -gQkVY1lNCf0n630Q1uwC6UZ3pfobVoE8EbYUZrKbU9HXpkBFVezn7msZG0VDs2Q+ -+URtTrssRiW986QWfWjSnXYd0EpGav+Bqux+Bv0YutG46cSSxnAXOzpkdzjUciJZ -Qw2QMvUEBhjuNxz7OFZHGJ4Lcr8BvCUytSY6ir4KT6VC2iJtbgdBRip4viTzQlGV -P7P+m8Ik9xOWk5e2v9A10U76BCM+DfUcoAKZoG5mUvoNVgTLJ2c= ------END CERTIFICATE----- diff --git a/agent/test/internal_address_source.js b/agent/test/internal_address_source.js deleted file mode 100644 index 1f6cb58c8ed..00000000000 --- a/agent/test/internal_address_source.js +++ /dev/null @@ -1,373 +0,0 @@ -/* - * Copyright 2017 Red Hat Inc. - * - * 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. - */ -'use strict'; - -var assert = require('assert'); -var EventEmitter = require('events'); - -var AddressSource = require('../lib/internal_address_source'); -var AddressServer = require('../testlib/mock_resource_server.js').AddressServer; - -describe('address source', function() { - var address_server; - var address_plans_source; - - beforeEach(function(done) { - address_plans_source = new EventEmitter(); - address_server = new AddressServer(); - address_server.listen(0, done); - }); - - afterEach(function(done) { - address_server.close(done); - }); - - it('retrieves all addresses - event addresses_defined', function(done) { - address_server.add_address_definition({address:'s1.foo', type:'queue'}, undefined, '1234'); - address_server.add_address_definition({address:'s1.bar', type:'topic'}, undefined, '1234'); - address_server.add_address_definition({address:'s2.baz', type:'queue'}, undefined, "4321"); - var source = new AddressSource({port:address_server.port, host:'localhost', token:'foo', namespace:'default', ADDRESS_SPACE_PREFIX: 's1.'}); - source.start(); - source.watcher.close();//prevents watching - source.on('addresses_defined', function (addresses) { - assert.equal(addresses.length, 2); - //relies on sorted order (TODO: avoid relying on any order) - assert.equal(addresses[0].address, 's1.bar'); - assert.equal(addresses[0].type, 'topic'); - assert.equal(addresses[1].address, 's1.foo'); - assert.equal(addresses[1].type, 'queue'); - done(); - }); - }); - it('retrieves all addresses - addresses_ready', function(done) { - address_server.add_address_definition({address:'s1.foo', type:'queue'}, undefined, '1234'); - address_server.add_address_definition({address:'s1.bar', type:'topic'}, undefined, '1234'); - address_server.add_address_definition({address:'s1.pending', type:'anycast'}, undefined, '1234', {}, {phase: 'Pending'}); - address_server.add_address_definition({address:'s1.terminating', type:'anycast'}, undefined, '1234', {}, {phase: 'Terminating'}); - address_server.add_address_definition({address:'s2.baz', type:'queue'}, undefined, "4321"); - var source = new AddressSource({port:address_server.port, host:'localhost', token:'foo', namespace:'default', ADDRESS_SPACE_PREFIX: 's1.'}); - source.start(); - source.watcher.close();//prevents watching - source.on('addresses_ready', function (addresses) { - assert.equal(addresses.length, 2); - //relies on sorted order (TODO: avoid relying on any order) - assert.equal(addresses[0].address, 's1.bar'); - assert.equal(addresses[0].type, 'topic'); - assert.equal(addresses[1].address, 's1.foo'); - assert.equal(addresses[1].type, 'queue'); - done(); - }); - }); - it('indicates allocation to broker', function(done) { - address_server.add_address_definition({address:'foo', type:'queue'}, undefined, '1234', undefined, {brokerStatuses:[{containerId: 'broker-1'}]}); - address_server.add_address_definition({address:'bar', type:'topic'}, undefined, '1234', undefined, {brokerStatuses:[{containerId: 'broker-2'}]}); - address_server.add_address_definition({address:'baz', type:'anycast'}, undefined, '1234'); - var source = new AddressSource({port:address_server.port, host:'localhost', token:'foo', namespace:'default', INFRA_UUID: '1234'}); - source.start(); - source.watcher.close();//prevents watching - source.on('addresses_defined', function (addresses) { - assert.equal(addresses.length, 3); - //relies on sorted order (TODO: avoid relying on any order) - assert.equal(addresses[0].address, 'bar'); - assert.equal(addresses[0].type, 'topic'); - assert.equal(addresses[0].allocated_to[0].containerId, 'broker-2'); - assert.equal(addresses[1].address, 'baz'); - assert.equal(addresses[1].type, 'anycast'); - assert.equal(addresses[1].allocated_to, undefined); - assert.equal(addresses[2].address, 'foo'); - assert.equal(addresses[2].type, 'queue'); - assert.equal(addresses[2].allocated_to[0].containerId, 'broker-1'); - done(); - }); - }); - it('watches for changes', function(done) { - var source = new AddressSource({port:address_server.port, host:'localhost', token:'foo', namespace:'default', INFRA_UUID: '1234'}); - source.start(); - source.once('addresses_defined', function () { - setTimeout(function () { - address_server.add_address_definition({address:'foo', type:'queue'}, undefined, '1234'); - address_server.add_address_definition({address:'bar', type:'topic'}, undefined, '1234'); - var addresses; - source.on('addresses_defined', function (latest) { - addresses = latest; - }); - setTimeout(function () { - assert.equal(addresses.length, 2); - //relies on sorted order (TODO: avoid relying on any order) - assert.equal(addresses[0].address, 'bar'); - assert.equal(addresses[0].type, 'topic'); - assert.equal(addresses[1].address, 'foo'); - assert.equal(addresses[1].type, 'queue'); - source.watcher.close().then(function () { - done(); - }); - }, 200); - }, 200); - }); - }); - it('watches for changes - address updated', function(done) { - address_server.add_address_definition({address:'foo', type:'queue', plan: 'myplan1' }, undefined, '1234'); - var source = new AddressSource({port:address_server.port, host:'localhost', token:'foo', namespace:'default', INFRA_UUID: '1234'}); - source.start(); - source.once('addresses_defined', (addresses) => { - assert.equal(addresses.length, 1); - process.nextTick(() => { - source.on('addresses_defined', (updates) => { - assert.equal(updates.length, 1); - assert.equal(updates[0].address, 'foo'); - assert.equal(updates[0].type, 'queue'); - assert.equal(updates[0].plan, 'myplan2'); - source.watcher.close().then(() => { - done(); - }); - }); - address_server.update_address_definition({address:'foo', type:'queue', plan: 'myplan2' }, undefined, '1234'); - }); - }); - }); - it('watches for changes even on error', function(done) { - var count = 0; - address_server.failure_injector = { - match: function (request) { - return count++ === 0 && request.watch === false && request.type === 'address_server'; - }, - code: function (request) { - return 500; - } - }; - var source = new AddressSource({port:address_server.port, host:'localhost', token:'foo', namespace:'default', INFRA_UUID: '1234'}); - source.start(); - source.once('addresses_defined', function () { - setTimeout(function () { - address_server.add_address_definition({address:'foo', type:'queue'}, undefined, '1234'); - address_server.add_address_definition({address:'bar', type:'topic'}, undefined, '1234'); - var addresses; - source.on('addresses_defined', function (latest) { - addresses = latest; - }); - setTimeout(function () { - assert.equal(addresses.length, 2); - //relies on sorted order (TODO: avoid relying on any order) - assert.equal(addresses[0].address, 'bar'); - assert.equal(addresses[0].type, 'topic'); - assert.equal(addresses[1].address, 'foo'); - assert.equal(addresses[1].type, 'queue'); - source.watcher.close().then(function () { - done(); - }); - }, 200); - }, 200); - }); - }); - it('updates status readiness', function(done) { - address_server.add_address_definition({address:'foo', type:'queue', plan: 'myplan'}, undefined, '1234'); - address_server.add_address_definition({address:'bar', type:'topic'}, undefined, '1234'); - var source = new AddressSource({port:address_server.port, host:'localhost', token:'foo', namespace:'default', INFRA_UUID: '1234'}); - source.start(address_plans_source); - address_plans_source.emit("addressplans_defined", [{ - kind: 'AddressPlan', - metadata: {name: 'myplan'}, - spec: {addressType: 'queue'} - }]); - source.watcher.close(); - source.on('addresses_defined', function (addresses) { - source.check_status({foo:{propagated:100}}).then(function () { - var address = address_server.find_resource('addresses', 'foo'); - assert.equal(address.status.isReady, true); - assert.equal(address.status.phase, 'Active'); - - done(); - }).catch(done); - }); - }); - it('updates readiness after recreation', function(done) { - address_server.add_address_definition({address:'foo', type:'queue', plan: 'myplan'}); - address_server.add_address_definition({address:'bar', type:'topic'}); - var source = new AddressSource({port:address_server.port, host:'localhost', token:'foo', namespace:'default'}); - source.start(address_plans_source); - address_plans_source.emit("addressplans_defined", [{ - kind: 'AddressPlan', - metadata: {name: 'myplan'}, - spec: {addressType: 'queue'} - }]); - source.once('addresses_defined', function (addresses) { - source.check_status({foo:{propagated:100}}).then(function () { - address_server.remove_resource_by_name('addresses', 'foo'); - source.once('addresses_defined', function (addresses) { - address_server.add_address_definition({address:'foo', type:'queue', plan: 'myplan'}); - source.once('addresses_defined', function (addresses) { - source.watcher.close(); - source.check_status({foo:{propagated:100}}).then(function () { - var address = address_server.find_resource('addresses', 'foo'); - assert.equal(address.status.isReady, true); - done(); - }).catch(done); - }); - }); - }); - }); - }); - it('updates status - plan not found', function(done) { - address_server.add_address_definition({address:'foo', type:'queue', plan: 'not found'}); - var source = new AddressSource({port:address_server.port, host:'localhost', token:'foo', namespace:'default'}); - source.start(address_plans_source); - address_plans_source.emit("addressplans_defined", [{ - kind: 'AddressPlan', - metadata: {name: 'myplan'}, - }]); - - source.once('addresses_defined', function (addresses) { - source.check_status({foo:{propagated:100}}).then(function (add) { - var address = address_server.find_resource('addresses', 'foo'); - assert.equal(address.status.isReady, false); - assert.equal(address.status.messages.length, 1); - assert.equal(address.status.messages[0], "Unknown address plan 'not found'"); - done(); - }); - }); - }); - it('updates status - plan status', function(done) { - address_server.add_address_definition({address:'foo', type:'queue', plan: 'myplan'}); - var source = new AddressSource({port:address_server.port, host:'localhost', token:'foo', namespace:'default'}); - source.start(address_plans_source); - address_plans_source.emit("addressplans_defined", [{ - kind: 'AddressPlan', - metadata: {name: 'myplan'}, - spec: { - addressType: 'queue', - resources: { - broker: 0.01 - } - } - }]); - - source.once('addresses_defined', function (addresses) { - source.check_status({foo:{propagated:100}}).then(function (add) { - var address = address_server.find_resource('addresses', 'foo'); - assert.equal(address.status.isReady, true); - assert.equal(address.status.messages.length, 0); - assert.equal(address.status.messages.length, 0); - assert.deepEqual(address.status.planStatus, {name: 'myplan', - partitions: 1, - resources: { - broker: 0.01 - }}); - done(); - }); - }); - }); - it('updates status - ttl from plan', function(done) { - address_server.add_address_definition({address:'foo', type:'queue', plan: 'myplan'}); - var source = new AddressSource({port:address_server.port, host:'localhost', token:'foo', namespace:'default'}); - source.start(address_plans_source); - address_plans_source.emit("addressplans_defined", [{ - kind: 'AddressPlan', - metadata: {name: 'myplan'}, - spec: { - addressType: 'queue', - messageTtl: { - minimum: 1000, - maximum: 2000 - }, - } - }]); - - source.once('addresses_defined', function (addresses) { - source.check_status({foo:{propagated:100}}).then(function (add) { - var address = address_server.find_resource('addresses', 'foo'); - assert.equal(address.status.isReady, true); - assert.equal(address.status.messageTtl.minimum, 1000); - assert.equal(address.status.messageTtl.maximum, 2000); - done(); - }); - }); - }); - it('updates status - ttl from address', function(done) { - address_server.add_address_definition({address:'foo', type:'queue', plan: 'myplan', messageTtl: {minimum: 1000, maximum: 2000}}); - var source = new AddressSource({port:address_server.port, host:'localhost', token:'foo', namespace:'default'}); - source.start(address_plans_source); - address_plans_source.emit("addressplans_defined", [{ - kind: 'AddressPlan', - metadata: {name: 'myplan'}, - spec: { - addressType: 'queue' - } - }]); - - source.once('addresses_defined', function (addresses) { - source.check_status({foo:{propagated:100}}).then(function (add) { - var address = address_server.find_resource('addresses', 'foo'); - assert.equal(address.status.isReady, true); - assert.equal(address.status.messageTtl.minimum, 1000); - assert.equal(address.status.messageTtl.maximum, 2000); - done(); - }); - }); - }); - it('updates status - ttl address overrides plan', function(done) { - address_server.add_address_definition({address:'foo', type:'queue', plan: 'myplan', messageTtl: {minimum: 500, maximum: 750}}); - var source = new AddressSource({port:address_server.port, host:'localhost', token:'foo', namespace:'default'}); - source.start(address_plans_source); - address_plans_source.emit("addressplans_defined", [{ - kind: 'AddressPlan', - metadata: {name: 'myplan'}, - spec: { - addressType: 'queue', - messageTtl: { - minimum: 600, - maximum: 2000 - }, - } - }]); - - source.once('addresses_defined', function (addresses) { - source.check_status({foo:{propagated:100}}).then(function (add) { - var address = address_server.find_resource('addresses', 'foo'); - assert.equal(address.status.isReady, true); - assert.equal(address.status.messageTtl.minimum, 600); - assert.equal(address.status.messageTtl.maximum, 750); - done(); - }); - }); - }); - - function equal_properties (a, b) { - for (let k in a) { - if (a[k] !== b[k]) return false; - } - for (let k in b) { - if (b[k] !== a[k]) return false; - } - return true; - } - - it('compares the name of the address', function(done) { - let source = new AddressSource({}); - source.last = {}; - - // Populate the initial - source.get_changes('foo', [{name:'ab',address:'AB'}], equal_properties) - - // Add the 'aa' address - let c = source.get_changes('foo', [{name:'aa',address:'aa'},{name:'ab',address:'AB'}], equal_properties) - assert(c !== undefined); - assert.equal(c.added.length, 1); - assert.equal(c.added[0].name, 'aa'); - assert.equal(c.removed.length, 0); - assert.equal(c.modified.length, 0); - done(); - }); -}); diff --git a/agent/test/internal_addressplan_source.js b/agent/test/internal_addressplan_source.js deleted file mode 100644 index 41443977c44..00000000000 --- a/agent/test/internal_addressplan_source.js +++ /dev/null @@ -1,147 +0,0 @@ -/* - * Copyright 2020 Red Hat Inc. - * - * 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. - */ -'use strict'; - -var assert = require('assert'); -var EventEmitter = require('events'); - -var AddressPlanSource = require('../lib/internal_addressplan_source'); -var AddressServer = require('../testlib/mock_resource_server.js').AddressServer; - -describe('addressplan source', function() { - var address_server; - var address_space_plan_source; - - beforeEach(function(done) { - address_space_plan_source = new EventEmitter(); - address_server = new AddressServer(); - address_server.listen(0, done); - }); - - afterEach(function(done) { - address_server.close(done); - }); - - it('retrieves address plans', function(done) { - address_server.add_address_plan({plan_name:'small', address_type:'queue'}); - address_server.add_address_plan({plan_name:'medium', address_type:'queue'}); - address_server.add_address_plan({plan_name:'large', address_type:'queue'}); - address_server.add_address_plan({plan_name:'belongs_to_another_space_plan', address_type:'queue'}); - - var source = new AddressPlanSource({port:address_server.port, host:'localhost', token:'foo', namespace:'default'}); - source.start(address_space_plan_source); - address_space_plan_source.emit("addressspaceplan_defined", { - kind: 'AddressPlan', - metadata: {name: 'spaceplan'}, - spec: { - addressPlans: ['small', 'medium', 'large'], - } - }); - - source.on('addressplans_defined', function (addressplans) { - source.watcher.close(); - assert.equal(addressplans.length, 3); - done(); - }); - }); - - it('watches for changes - additional plan', function(done) { - - address_server.add_address_plan({plan_name:'small', address_type:'queue'}); - address_server.add_address_plan({plan_name:'large', address_type:'queue'}); - - var source = new AddressPlanSource({port:address_server.port, host:'localhost', token:'foo', namespace:'default', ADDRESS_SPACE_PLAN: 'space', ADDRESS_SPACE_PREFIX: 's1.'}); - source.start(address_space_plan_source); - address_space_plan_source.emit("addressspaceplan_defined", { - kind: 'AddressPlan', - metadata: {name: 'spaceplan'}, - spec: { - addressPlans: ['small', 'large'], - } - }); - source.once('addressplans_defined', (addressplans) => { - assert.equal(addressplans.length, 2); - process.nextTick(() => { - address_server.add_address_plan({plan_name:'medium', address_type:'queue'}); - address_space_plan_source.emit("addressspaceplan_defined", { - kind: 'AddressPlan', - metadata: {name: 'spaceplan'}, - spec: { - addressPlans: ['small', 'medium', 'large'], - } - }); - }); - - source.on('addressplans_defined', (update) => { - source.watcher.close(); - assert.equal(update.length, 3); - done(); - }); - }); - }); - it('watches for changes - redefined plan - resources', function(done) { - - address_server.add_address_plan({plan_name:'small', address_type:'queue', resources: {broker: 0.4}}); - - var source = new AddressPlanSource({port:address_server.port, host:'localhost', token:'foo', namespace:'default', ADDRESS_SPACE_PLAN: 'space', ADDRESS_SPACE_PREFIX: 's1.'}); - source.start(address_space_plan_source); - address_space_plan_source.emit("addressspaceplan_defined", { - kind: 'AddressPlan', - metadata: {name: 'spaceplan'}, - spec: { - addressPlans: ['small'], - } - }); - source.once('addressplans_defined', (addressplans) => { - assert.equal(addressplans.length, 1); - process.nextTick(() => { - address_server.update_address_plan({plan_name:'small', address_type:'queue', resources: {broker: 0.5}}); - }); - - source.on('addressplans_defined', (update) => { - source.watcher.close(); - assert.equal(update.length, 1); - done(); - }); - }); - }); - it('watches for changes - redefined plan - ttl', function(done) { - - address_server.add_address_plan({plan_name:'small', address_type:'queue', messageTtl: {minimum: 1000, maximum: 2000}}); - - var source = new AddressPlanSource({port:address_server.port, host:'localhost', token:'foo', namespace:'default', ADDRESS_SPACE_PLAN: 'space', ADDRESS_SPACE_PREFIX: 's1.'}); - source.start(address_space_plan_source); - address_space_plan_source.emit("addressspaceplan_defined", { - kind: 'AddressPlan', - metadata: {name: 'spaceplan'}, - spec: { - addressPlans: ['small'], - } - }); - source.once('addressplans_defined', (addressplans) => { - assert.equal(addressplans.length, 1); - process.nextTick(() => { - address_server.update_address_plan({plan_name:'small', address_type:'queue', messageTtl: {minimum: 1001, maximum: 1999}}); - }); - - source.on('addressplans_defined', (update) => { - source.watcher.close(); - assert.equal(update.length, 1); - done(); - }); - }); - }); -}); diff --git a/agent/test/internal_addressspaceplan_source.js b/agent/test/internal_addressspaceplan_source.js deleted file mode 100644 index fbbea5c60a6..00000000000 --- a/agent/test/internal_addressspaceplan_source.js +++ /dev/null @@ -1,68 +0,0 @@ -/* - * Copyright 2020 Red Hat Inc. - * - * 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. - */ -'use strict'; - -var assert = require('assert'); - -var AddressSpacePlanSource = require('../lib/internal_addressspaceplan_source'); -var AddressServer = require('../testlib/mock_resource_server.js').AddressServer; - -describe('addressspaceplan source', function() { - var address_server; - - beforeEach(function(done) { - address_server = new AddressServer(); - address_server.listen(0, done); - }); - - afterEach(function(done) { - address_server.close(done); - }); - - it('retrieves addressspace plan', function(done) { - address_server.add_address_space_plan({plan_name:'space', address_plans:['small', 'medium', 'large']}); - address_server.add_address_space_plan({plan_name:'anotherplan', address_plans:['small', 'medium', 'large']}); - - var source = new AddressSpacePlanSource({port:address_server.port, host:'localhost', token:'foo', namespace:'default', ADDRESS_SPACE_PLAN: 'space'}); - source.start(); - source.watcher.close(); - source.on('addressspaceplan_defined', (addressspaceplan) => { - assert.equal(addressspaceplan.metadata.name, "space"); - assert.deepEqual(addressspaceplan.spec.addressPlans, ['small', 'medium', 'large']); - - done(); - }); - }); - - it('watches for changes', function(done) { - address_server.add_address_space_plan({plan_name:'space', address_plans:['small', 'medium', 'large']}); - var source = new AddressSpacePlanSource({port:address_server.port, host:'localhost', token:'foo', namespace:'default', ADDRESS_SPACE_PLAN: 'space', ADDRESS_SPACE_PREFIX: 's1.'}); - source.start(); - source.once('addressspaceplan_defined', (addressspaceplan) => { - assert.equal(addressspaceplan.metadata.name, "space"); - process.nextTick(() => { - address_server.update_address_space_plan({plan_name:'space', address_plans:['small', 'medium', 'large', 'xlarge']}); - }); - - source.on('addressspaceplan_defined', (update) => { - source.watcher.close(); - assert.equal(update.metadata.name, "space"); - assert.deepEqual(update.spec.addressPlans, ['small', 'medium', 'large', 'xlarge']); - done(); - }); - }); - }); -}); diff --git a/agent/test/kubernetes.js b/agent/test/kubernetes.js deleted file mode 100644 index d3921fcafb6..00000000000 --- a/agent/test/kubernetes.js +++ /dev/null @@ -1,152 +0,0 @@ -/* - * Copyright 2020, EnMasse authors. - * License: Apache License 2.0 (see the file LICENSE or http://apache.org/licenses/LICENSE-2.0.html). - * - */ - -var assert = require('assert'); -var kubernetes = require('../lib/kubernetes.js'); -var AddressServer = require('../testlib/mock_resource_server.js').AddressServer; - - -describe('kubernetes interaction', function () { - - function createAddr() { - address_server.add_address_definition({address: 's1.addr1', type: 'queue'}, "addr1", '1234'); - } - - const addr = { - kind: 'Address', - metadata: {labels: {infraUuid: '1234'}, name: 'addr1'}, - spec: {address: 's1.addr1', type: 'queue'}, - status: {phase: 'Active'} - }; - var address_server; - var watcher; - var options; - - beforeEach(function (done) { - address_server = new AddressServer(); - address_server.listen(0, () => { - options = {namespace: "ns", token: "tok", host: "localhost", port: address_server.port}; - done(); - }); - }); - - afterEach(function (done) { - if (!watcher) { - watcher = { - close: () => { - return Promise.resolve(); - } - } - } - watcher.close().finally(() => address_server.close(done)); - }); - - it('no population', function (done) { - try { - watcher = kubernetes.watch("addresses", options); - watcher.list(); - watcher.on("updated", (objs) => { - assert.deepEqual(objs, []); - done(); - }); - } catch (e) { - done(e); - } - }); - - it('initial population', function (done) { - try { - createAddr(); - - watcher = kubernetes.watch("addresses", options); - watcher.list(); - watcher.on("updated", (objs) => { - assert.deepEqual(objs, [addr]); - done(); - }); - } catch (e) { - done(e); - } - }); - - it('new member added', function (done) { - try { - watcher = kubernetes.watch("addresses", options); - - watcher.once("updated", (objs) => { - assert.deepEqual(objs, []); - - process.nextTick(() => { - watcher.once("updated", objs => { - assert.deepEqual(objs, [addr]); - done(); - }); - createAddr(); - }); - }); - watcher.list(); - } catch (e) { - done(e); - } - }); - - it('watcher recovers from 500', function (done) { - var count = 0; - - address_server.failure_injector = { - match: (request) => { - var match = request.watch === true && count === 0; - if (match) { - count++; - process.nextTick(() => { - createAddr(); - }); - } - return match; - }, - code: () => { - return 500; - } - }; - try { - watcher = kubernetes.watch("addresses", options); - - watcher.on("updated", (objs) => { - if (objs.length === 1) { - done(); - } - }); - - watcher.list(); - } catch (e) { - done(e); - } - }); - - it('watcher recovers from connection lost', function (done) { - try { - watcher = kubernetes.watch("addresses", options); - - watcher.once("updated", () => { - address_server.close(() => { - address_server.listen(options.port, () => { - createAddr(); - }); - }); - - watcher.on("updated", (objs) => { - if (objs.length === 1) { - done(); - } - }); - }); - - watcher.list(); - } catch (e) { - done(e); - } - }); -}); diff --git a/agent/test/log.js b/agent/test/log.js deleted file mode 100644 index aba66a8366e..00000000000 --- a/agent/test/log.js +++ /dev/null @@ -1,38 +0,0 @@ -/* - * Copyright 2017 Red Hat Inc. - * - * 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. - */ -'use strict'; - -var assert = require('assert'); -var log = require('../lib/log.js'); - -describe('logger', function() { - it('sets level from LOGLEVEL env var', function (done) { - process.env.LOGLEVEL = 'warn'; - assert.equal(log.logger().level, 1); - delete process.env.LOGLEVEL; - done(); - }); - it('ignores invalid level in env', function (done) { - process.env.LOGLEVEL = 'foo'; - assert.equal(log.logger().level, 2); - delete process.env.LOGLEVEL; - done(); - }); - it('ignores invalid level in log request', function (done) { - log.logger().log('foo', 'ignore me!'); - done(); - }); -}); diff --git a/agent/test/podgroup.js b/agent/test/podgroup.js deleted file mode 100644 index 7217ef9595c..00000000000 --- a/agent/test/podgroup.js +++ /dev/null @@ -1,55 +0,0 @@ -/* - * Copyright 2018 Red Hat Inc. - * - * 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. - */ -'use strict'; - -var assert = require('assert'); -var podgroup = require('../lib/podgroup.js'); - -function dummy (details) { return details; } - -describe('podgroup update', function() { - it('adds new pods', function(done) { - var group = podgroup(dummy); - assert(group.update([{name:'foo', value:'bar'}])); - assert(group.pods.foo); - assert.equal(group.pods.foo.name, 'foo'); - assert.equal(group.pods.foo.value, 'bar'); - done(); - }); - it('ignores existing pods', function(done) { - var group = podgroup(dummy); - assert(group.update([{name:'foo', value:'bar'}])); - assert(group.pods.foo); - assert.equal(group.pods.foo.name, 'foo'); - assert.equal(group.pods.foo.value, 'bar'); - assert(!group.update([{name:'foo', value:'baz'}])); - assert.equal(group.pods.foo.value, 'bar'); - done(); - }); - it('removes stale pods', function(done) { - var group = podgroup(dummy); - assert(group.update([{name:'foo', value:'x'},{name:'bar',colour:'blue'}])); - assert.equal(group.pods.foo.name, 'foo'); - assert.equal(group.pods.foo.value, 'x'); - assert.equal(group.pods.bar.name, 'bar'); - assert.equal(group.pods.bar.colour, 'blue'); - assert(group.update([{name:'bar',colour:'blue'}])); - assert.equal(group.pods.foo, undefined); - assert.equal(group.pods.bar.name, 'bar'); - assert.equal(group.pods.bar.colour, 'blue'); - done(); - }); -}); diff --git a/agent/test/ragent.js b/agent/test/ragent.js deleted file mode 100644 index 77176c7ca35..00000000000 --- a/agent/test/ragent.js +++ /dev/null @@ -1,1231 +0,0 @@ -/* - * Copyright 2017 Red Hat Inc. - * - * 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. - */ -'use strict'; - -var assert = require('assert'); -var child_process = require('child_process'); -var events = require('events'); -var path = require('path'); -var util = require('util'); -var http = require('http'); - -var rhea = require('rhea'); - -var Ragent = require('../lib/ragent.js'); -var tls_options = require('../lib/tls_options.js'); -var MockBroker = require('../testlib/mock_broker.js'); -var MockRouter = require('../testlib/mock_router.js'); -var match_source_address = require('../lib/utils.js').match_source_address; -var mock_resource_server = require('../testlib/mock_resource_server.js'); -var AddressServer = mock_resource_server.AddressServer; -var ResourceServer = mock_resource_server.ResourceServer; -var myutils = require('../lib/utils.js'); - -function remove(list, predicate) { - var removed = []; - for (var i = 0; i < list.length; ) { - if (predicate(list[i])) { - removed.push(list.splice(i, 1)[0]); - } else { - i++; - } - } - return removed; -} - -function broker_state(id) { - return {clusterId: id, containerId: id + "-0", state: 'Active'}; -} - -function verify_subscription(name, topic, all_linkroutes, allocated_to) { - var prefix = topic + '::' + name; - var linkroutes = remove(all_linkroutes, function (o) { return o.prefix === prefix; }); - assert.equal(linkroutes.length, 1, 'no link route found for subscription ' + name + ' on ' + topic); - assert.equal(linkroutes[0].prefix, prefix); - assert.equal(linkroutes[0].direction, 'out'); - if (allocated_to) { - var containerId = allocated_to[0].containerId; - assert.equal(linkroutes[0].containerId, containerId + '-out'); - } -} - -function verify_topic(name, all_linkroutes, allocated_to) { - var linkroutes = remove(all_linkroutes, function (o) { return o.prefix === name; }); - assert.equal(linkroutes.length, 2, 'no link routes found for topic ' + name); - assert.equal(linkroutes[0].prefix, name); - assert.equal(linkroutes[1].prefix, name); - if (linkroutes[0].direction === 'in') { - assert.equal(linkroutes[1].direction, 'out'); - if (allocated_to) { - var containerId = allocated_to[0].containerId; - assert.equal(linkroutes[0].containerId, containerId + '-in'); - assert.equal(linkroutes[1].containerId, containerId + '-out'); - } - } else { - assert.equal(linkroutes[0].direction, 'out'); - assert.equal(linkroutes[1].direction, 'in'); - if (allocated_to) { - var containerId = allocated_to[0].containerId; - assert.equal(linkroutes[0].containerId, containerId + '-out'); - assert.equal(linkroutes[1].containerId, containerId + '-in'); - } - } -} - -function verify_queue(name, all_addresses, all_autolinks, allocated_to) { - var addresses = remove(all_addresses, function (o) { return o.prefix === name; }); - assert.equal(addresses.length, 1, 'did not find queue ' + name); - assert.equal(addresses[0].prefix, name); - assert.equal(addresses[0].distribution, 'balanced'); - assert.equal(addresses[0].waypoint, true); - - var autolinks = remove(all_autolinks, function (o) { return o.address === name; }); - if (allocated_to !== undefined) { - if (allocated_to[0].state === 'Active') { - assert.equal(autolinks.length, 2, 'did not find required autolinks for queue ' + name); - assert.equal(autolinks[0].address, name); - assert.equal(autolinks[1].address, name); - if (autolinks[0].direction === 'in') { - assert.equal(autolinks[1].direction, 'out'); - assert.equal(autolinks[0].containerId, util.format('%s-in', allocated_to[0].containerId)); - assert.equal(autolinks[1].containerId, util.format('%s-out', allocated_to[0].containerId)); - } else { - assert.equal(autolinks[0].containerId, util.format('%s-out', allocated_to[0].containerId)); - assert.equal(autolinks[1].containerId, util.format('%s-in', allocated_to[0].containerId)); - assert.equal(autolinks[0].direction, 'out'); - assert.equal(autolinks[1].direction, 'in'); - } - } else { - assert.equal(1, autolinks.length); - assert.equal(autolinks[0].containerId, util.format('%s-in', allocated_to[0].containerId || name)); - assert.equal(autolinks[0].direction, 'in'); - } - } else { - assert.equal(autolinks.length, 2, 'did not find required autolinks for queue ' + name); - assert.equal(autolinks[0].address, name); - assert.equal(autolinks[1].address, name); - if (autolinks[0].direction === 'in') { - assert.equal(autolinks[1].direction, 'out'); - assert.equal(autolinks[0].containerId, util.format('%s-in', name)); - assert.equal(autolinks[1].containerId, util.format('%s-out', name)); - } else { - assert.equal(autolinks[0].containerId, util.format('%s-out', name)); - assert.equal(autolinks[1].containerId, util.format('%s-in', name)); - assert.equal(autolinks[0].direction, 'out'); - assert.equal(autolinks[1].direction, 'in'); - } - } -} - -function verify_anycast(name, all_addresses) { - var addresses = remove(all_addresses, function (o) { return o.prefix === name; }); - assert.equal(addresses.length, 1); - assert.equal(addresses[0].prefix, name); - assert.equal(addresses[0].distribution, 'balanced'); - assert.equal(addresses[0].waypoint, false); -} - -function verify_multicast(name, all_addresses) { - var addresses = remove(all_addresses, function (o) { return o.prefix === name; }); - assert.equal(addresses.length, 1); - assert.equal(addresses[0].prefix, name); - assert.equal(addresses[0].distribution, 'multicast'); - assert.equal(addresses[0].waypoint, false); -} - -function verify_addresses(inputs, router, brokers, verify_extra) { - if (util.isArray(router)) { - router.forEach(function (r) { verify_addresses(inputs, r, brokers, verify_extra); }); - } else { - var addresses = router.list_objects('org.apache.qpid.dispatch.router.config.address'); - var linkroutes = router.list_objects('org.apache.qpid.dispatch.router.config.linkRoute'); - var autolinks = router.list_objects('org.apache.qpid.dispatch.router.config.autoLink'); - for (var i = 0; i < inputs.length; i++) { - var a = inputs[i]; - if (a.type === 'queue') { - verify_queue(a.address, addresses, autolinks, a.allocated_to); - } else if (a.type === 'topic') { - verify_topic(a.address, linkroutes, a.allocated_to); - } else if (a.type === 'subscription') { - verify_subscription(a.address, a.topic, linkroutes, a.allocated_to); - } else if (a.type === 'anycast') { - verify_anycast(a.address, addresses); - } else if (a.type === 'multicast') { - verify_multicast(a.address, addresses); - } else { - console.warn('Cannot verify address of type: ' + a.type); - } - } - if (verify_extra) verify_extra({'addresses':addresses, 'linkroutes':linkroutes,'autolinks':autolinks}); - - var expected_addresses = 0; - if (brokers !== undefined && brokers > 0) { - var health_check_addresses = 0; - for (var i = 0; i < addresses.length; i++) { - if (addresses[i].prefix.startsWith("!!HEALTH_CHECK_")) { - health_check_addresses += 1; - } - } - assert.equal(brokers, health_check_addresses); - expected_addresses = health_check_addresses; - } - - if (addresses.length != expected_addresses) console.warn('Unexpected addresses found: ' + JSON.stringify(addresses)); - if (autolinks.length != (expected_addresses * 2)) console.warn('Unexpected auto links found: ' + JSON.stringify(autolinks)); - if (linkroutes.length) console.warn('Unexpected link routes found: ' + JSON.stringify(linkroutes)); - assert.equal(addresses.length, expected_addresses); - assert.equal(linkroutes.length, 0); - assert.equal(autolinks.length, expected_addresses * 2); - } -} - -function get_neighbors(name, connectivity) { - var neighbors = connectivity[name]; - if (neighbors === undefined) { - neighbors = []; - connectivity[name] = neighbors; - } - return neighbors; -} - -function are_connected(router_a, router_b, n) { - var from_a = router_a.list_objects('connector').filter(function (c) { return c.host === router_b.name; }); - var from_b = router_b.list_objects('connector').filter(function (c) { return c.host === router_a.name; }); - if (n) { - if (from_a.length + from_b.length < n) { - console.warn('insufficient connectivity between ' + router_a.name + ' and ' + router_b.name + ' expected ' + n + ' got ' + - (from_a.length + from_b.length) + ': ' + JSON.stringify(from_a.concat(from_b))); - } - return from_a.length + from_b.length === n; - } else { - if (from_a.length + from_b.length > 1) { - console.warn('unexpected connectivity between ' + router_a.name + ' and ' + router_b.name + ': ' + JSON.stringify(from_a.concat(from_b))); - } - } - return from_a.length || from_b.length; -} - -function verify_full_mesh(routers) { - for (var i = 0; i < routers.length; i++) { - for (var j = i+1; j < routers.length; j++) { - assert.ok(are_connected(routers[i], routers[j]), 'routers not connected: ' + routers[i].name + ' and ' + routers[j].name); - } - } -} - -function verify_full_mesh_n(routers, n) { - for (var i = 0; i < routers.length; i++) { - for (var j = i+1; j < routers.length; j++) { - assert.ok(are_connected(routers[i], routers[j], n), 'routers not connected: ' + routers[i].name + ' and ' + routers[j].name); - } - } -} - -function RouterList(port, basename) { - this.counter = 1; - this.routers = []; - this.port = port; - this.basename = basename || 'router'; -} - -RouterList.prototype.new_router = function (name, opts) { - var r = new MockRouter(name || this.new_name(), this.port || 55672, opts); - this.routers.push(r); - return r; -}; - -RouterList.prototype.new_name = function () { - return this.basename + '-' + this.counter++; -}; - -RouterList.prototype.close = function () { - return Promise.all(this.routers.map(function (r) { return r.close(); })); -} - -function HealthChecker(conn) { - this.connection = conn; - this.address = undefined; - this.receiver = conn.open_receiver({source:{dynamic:true}}); - this.sender = conn.open_sender('health-check'); - this.counter = 0; - this.handlers = {}; - this.requests = []; - this.receiver.on('message', this.incoming.bind(this)); - this.receiver.on('receiver_open', this.ready.bind(this)); - this.connection.on('connection_close', this.closed.bind(this)); - this.connection.on('disconnected', this.disconnected.bind(this)); -} - -HealthChecker.prototype.check = function (input) { - var id = this.counter.toString(); - this.counter++; - var request = {correlation_id:id, subject:'health-check', body:JSON.stringify(input)}; - if (this.address) { - this._send_pending_requests(); - this._send_request(request); - } else { - this.requests.push(request); - } - var handlers = this.handlers; - return new Promise(function (resolve, reject) { - handlers[id] = function (response) { - if (response.body !== undefined) { - resolve(response.body); - } else { - reject('failed: ' + response); - } - }; - }); -} - -HealthChecker.prototype.incoming = function (context) { - var message = context.message; - var handler = this.handlers[message.correlation_id]; - if (handler) { - handler(message); - delete this.handlers[message.correlation_id]; - } -}; - -HealthChecker.prototype._abort_requests = function (error) { - for (var h in this.handlers) { - this.handlers[h](error); - delete this.handlers[h]; - } - while (this.requests.length > 0) { this.requests.shift(); }; -}; - -HealthChecker.prototype.disconnected = function (context) { - this.address = undefined; - this._abort_requests('disconnected'); -}; - -HealthChecker.prototype.closed = function (context) { - this.connection = undefined; - this.sender = undefined; - this.address = undefined; - this._abort_requests('closed '+ context.connection.error); -}; - -HealthChecker.prototype.ready = function (context) { - this.address = context.receiver.source.address; - this._send_pending_requests(); -}; - -HealthChecker.prototype._send_pending_requests = function () { - for (var i = 0; i < this.requests.length; i++) { - this._send_request(this.requests[i]); - } - this.requests = []; -}; - -HealthChecker.prototype._send_request = function (request) { - if (this.sender) { - request.reply_to = this.address; - this.sender.send(request); - } -}; - -function generate_address_list(count, allowed_types) { - var types = allowed_types || ['anycast', 'multicast', 'queue', 'topic']; - var list = []; - for (var i = 0; i < count; i++) { - list.push({address:util.format('address-%s', (i+1)), type:types[i % types.length]}); - } - return list; -} - -describe('basic router configuration', function() { - this.timeout(10000); - var ragent; - var routers; - - beforeEach(function(done) { - ragent = new Ragent(); - ragent.listen({port:0}).on('listening', function (){ - routers = new RouterList(ragent.server.address().port); - done(); - }); - }); - - afterEach(function(done) { - routers.close().then(function () { - ragent.server.close(); - done(); - }); - }); - - function new_router(name) { - return routers.new_router(name); - } - - function multi_router_address_test(count, address_list, verification, initial_config, propagation_wait) { - return function(done) { - var routers = []; - for (var i = 0; i < count; i++) { - routers.push(new_router()); - } - if (initial_config) initial_config(routers); - ragent.sync_addresses(address_list); - ragent.wait_for_stable(address_list.length, routers.length).then(function () { - verification ? verification(routers, address_list) : verify_addresses(address_list, routers); - done(); - }).catch(function (error) { - console.error('Failed: %s', error); - }); - }; - } - - function simple_address_test(address_list, verification, initial_config, propagation_wait) { - return multi_router_address_test(1, address_list, function (routers, address_list) { - verification ? verification(routers[0], address_list) : verify_addresses(address_list, routers[0]); - }, function (routers) { - if (initial_config) initial_config(routers[0]); - }, propagation_wait); - } - - it('configures a single anycast address', simple_address_test([{address:'foo',type:'anycast'}])); - it('configures a single multicast address', simple_address_test([{address:'foo',type:'multicast'}])); - it('configures a single queue address', simple_address_test([{address:'foo',type:'queue'}])); - it('configures a single topic address', simple_address_test([{address:'foo',type:'topic'}])); - it('configures a topic and subscription', simple_address_test([{address:'foo',type:'topic'}, {address:'sub',type:'subscription',topic:'foo','allocated_to':[broker_state('broker-0')]}])); - it('configures multiple anycast addresses', simple_address_test([{address:'a',type:'anycast'}, {address:'b',type:'anycast'}, {address:'c',type:'anycast'}])); - it('configures multiple multicast addresses', simple_address_test([{address:'a',type:'multicast'}, {address:'b',type:'multicast'}, {address:'c',type:'multicast'}])); - it('configures multiple topics', simple_address_test([{address:'a',type:'topic'}, {address:'b',type:'topic'}, {address:'c',type:'topic'}])); - it('configures multiple topics and subscriptions', simple_address_test([{address:'a',type:'topic'}, {address:'b',type:'topic'}, {address:'c',type:'topic'}, - {address:'sub-a',type:'subscription',topic:'a','allocated_to':[broker_state('broker-0')]}, - {address:'sub-b',type:'subscription',topic:'b','allocated_to':[broker_state('broker-1')]}, - {address:'sub-b2',type:'subscription',topic:'b','allocated_to':[broker_state('broker-2')]}, - {address:'sub-c',type:'subscription',topic:'c','allocated_to':[broker_state('broker-3')]}])); - it('configures multiple queues', simple_address_test([{address:'a',type:'queue'}, {address:'b',type:'queue'}, {address:'c',type:'queue'}])); - it('configures autolinks based on drain state', simple_address_test([{address:'c',type:'queue', allocated_to:[{clusterId: 'broker-1', containerId: 'broker-1-0', state: 'Draining'}]}])); - it('configures queues based on allocation', simple_address_test([{address:'a',type:'queue'}, {address:'b',type:'queue', allocated_to:[broker_state('broker-1')]}, {address:'c',type:'queue', allocated_to:[broker_state('broker-1')]}])); - it('configures topic based on allocation', simple_address_test([{address:'a',type:'topic'}, {address:'b',type:'topic', allocated_to:[broker_state('broker-1')]}, {address:'c',type:'topic', allocated_to:[broker_state('broker-1')]}])); - it('configures multiple different types of address', simple_address_test([{address:'a',type:'topic'}, {address:'b',type:'queue'}, {address:'c',type:'anycast'}, {address:'d',type:'multicast'}])); - it('removes unwanted address config', simple_address_test([{address:'a',type:'topic'}, {address:'b',type:'queue'}, {address:'c',type:'anycast'}, {address:'d',type:'multicast'}], undefined, - function (router) { - router.create_object('org.apache.qpid.dispatch.router.config.address', 'ragent-foo', {prefix:'foo', distribution:'closest', 'waypoint':false}); - router.create_object('org.apache.qpid.dispatch.router.config.linkRoute', 'ragent-bar', {prefix:'bar', direction:'in'}); - router.create_object('org.apache.qpid.dispatch.router.config.autoLink', 'ragent-baz', {address:'baz', direction:'out', containerId: 'baz'}); - })); - it('removes or updates address config', simple_address_test([{address:'a',type:'topic'}, {address:'b',type:'queue'}, {address:'c',type:'anycast'}, {address:'d',type:'multicast'}], undefined, - function (router) { - router.create_object('org.apache.qpid.dispatch.router.config.address', 'ragent-a', {prefix:'a', distribution:'closest', 'waypoint':false}); - router.create_object('org.apache.qpid.dispatch.router.config.linkRoute', 'ragent-b-in', {prefix:'b', direction:'in'}); - router.create_object('org.apache.qpid.dispatch.router.config.linkRoute', 'ragent-b-out', {prefix:'b', direction:'out'}); - router.create_object('org.apache.qpid.dispatch.router.config.autoLink', 'ragent-baz', {address:'baz', direction:'out', containerId: 'baz'}); - })); - it('configures addresses on multiple routers', multi_router_address_test(3, [{address:'a',type:'topic'}, {address:'b',type:'queue'}, {address:'c',type:'anycast'}, {address:'d',type:'multicast'}])); - it('configures multiple routers into a full mesh', multi_router_address_test(6, [], function (routers) { - verify_full_mesh(routers); - })); - it('configures routers correctly whenever they connect', function(done) { - ragent.sync_addresses([{address:'a',type:'topic'}, {address:'b',type:'queue'}]); - var routers = []; - for (var i = 0; i < 2; i++) { - routers.push(new_router()); - } - ragent.wait_for_stable(2, routers.length).then(function () { - verify_addresses([{address:'a',type:'topic'}, {address:'b',type:'queue'}], routers); - verify_full_mesh(routers); - //update addresses - ragent.sync_addresses([{address:'a',type:'queue'}, {address:'c',type:'topic'}, {address:'d',type:'multicast'}]); - //add another couple of routers - for (var i = 0; i < 2; i++) { - routers.push(new_router()); - } - ragent.wait_for_stable(3, routers.length).then(function () { - verify_addresses([{address:'a',type:'queue'}, {address:'c',type:'topic'}, {address:'d',type:'multicast'}], routers); - verify_full_mesh(routers); - done(); - }).catch(done); - }).catch(done); - }); - it('handles health-check requests', function(done) { - var routers = []; - for (var i = 0; i < 2; i++) { - routers.push(new_router()); - } - routers[1].on('connected', function () { - var client = rhea.connect({port:ragent.server.address().port}); - var checker = new HealthChecker(client); - checker.check([{name:'x'}, {name:'b', store_and_forward:true, multicast:false}]).then(function (result) { - assert.equal(result, false); - ragent.sync_addresses([{address:'a',type:'topic'}]); - ragent.wait_for_stable(1, routers.length).then(function () { - checker.check([{name:'x'}, {name:'b', store_and_forward:true, multicast:false}]).then(function (result) { - assert.equal(result, false); - ragent.sync_addresses([{address:'a',type:'topic'}, {address:'b',type:'queue'}]); - ragent.wait_for_stable(2, routers.length).then(function () { - verify_addresses([{address:'a',type:'topic'}, {address:'b',type:'queue'}], routers); - verify_full_mesh(routers); - checker.check([{name:'b', store_and_forward:true, multicast:false}]).then(function (result) { - assert.equal(result, true); - client.close(); - client.on('connection_close', function () { done(); } ); - }).catch(done); - }).catch(done); - }).catch(done); - }).catch(done); - }).catch(done); - }); - }); - it('handles disconnection of router', function(done) { - var routers = []; - for (var i = 0; i < 6; i++) { - routers.push(new_router()); - } - ragent.sync_addresses([{address:'a',type:'topic'}, {address:'b',type:'queue'}]); - ragent.wait_for_stable(2, routers.length).then(function () { - verify_addresses([{address:'a',type:'topic'}, {address:'b',type:'queue'}], routers); - verify_full_mesh(routers); - //make sure we close a router which some remaining router still has a connector to - var r; - for (var i = 0; i < routers.length; i++) { - if (routers[i].check_connector_from(routers)) { - r = routers[i]; - routers.splice(i, 1); - break; - } - } - r.close_with_error({name:'amqp:internal-error', description:'just a test'}).then(function () { - routers.push(new_router()); - ragent.wait_for_stable(2, routers.length).then(function () { - verify_addresses([{address:'a',type:'topic'}, {address:'b',type:'queue'}], routers); - verify_full_mesh(routers); - done(); - }, 500); - }); - }).catch(done); - }); - it('handles invalid messages', function(done) { - var routers = []; - for (var i = 0; i < 2; i++) { - routers.push(new_router()); - } - var client = rhea.connect({port:ragent.server.address().port}); - var sender = client.open_sender(); - sender.send({subject:'enmasse.io/v1/AddressList'}); - sender.send({subject:'enmasse.io/v1/AddressList', body:'foo'}); - sender.send({subject:'enmasse.io/v1/AddressList', body:'{{{'}); - sender.send({subject:'enmasse.io/v1/AddressList', body:'{"items":101}'}); - sender.send({subject:'health-check'}); - sender.send({subject:'random-nonsense'}); - ragent.sync_addresses([{address:'a',type:'topic'}, {address:'b',type:'queue'}]); - ragent.wait_for_stable(2, routers.length).then(function () { - verify_addresses([{address:'a',type:'topic'}, {address:'b',type:'queue'}], routers); - verify_full_mesh(routers); - done(); - }).catch(done); - }); - it('handles address query error', simple_address_test([{address:'a',type:'topic'}, {address:'b',type:'queue'}, {address:'c',type:'anycast'}, {address:'d',type:'multicast'}], undefined, - function (router) { - router.set_onetime_error_response('QUERY','org.apache.qpid.dispatch.router.config.address'); - })); - it('handles address auto-link error', simple_address_test([{address:'a',type:'topic'}, {address:'b',type:'queue'}, {address:'c',type:'anycast'}, {address:'d',type:'multicast'}], undefined, - function (router) { - router.set_onetime_error_response('QUERY','org.apache.qpid.dispatch.router.config.autoLink'); - })); - it('handles address link-route error', simple_address_test([{address:'a',type:'topic'}, {address:'b',type:'queue'}, {address:'c',type:'anycast'}, {address:'d',type:'multicast'}], undefined, - function (router) { - router.set_onetime_error_response('QUERY','org.apache.qpid.dispatch.router.config.linkRoute'); - })); - it('handles connector query error', simple_address_test([{address:'a',type:'topic'}, {address:'b',type:'queue'}, {address:'c',type:'anycast'}, {address:'d',type:'multicast'}], undefined, - function (router) { - router.set_onetime_error_response('QUERY','connector'); - })); - it('handles listener query error', simple_address_test([{address:'a',type:'topic'}, {address:'b',type:'queue'}, {address:'c',type:'anycast'}, {address:'d',type:'multicast'}], undefined, - function (router) { - router.set_onetime_error_response('QUERY','listener'); - })); - it('handles address create error', simple_address_test([{address:'a',type:'topic'}, {address:'b',type:'queue'}, {address:'c',type:'anycast'}, {address:'d',type:'multicast'}], undefined, - function (router) { - router.set_onetime_error_response('CREATE','org.apache.qpid.dispatch.router.config.address'); - })); - it('handles address create error 2', simple_address_test([{address:'c',type:'anycast'}, {address:'d',type:'multicast'}], undefined, - function (router) { - router.set_onetime_error_response('CREATE','org.apache.qpid.dispatch.router.config.address'); - })); - it('handles address delete error', simple_address_test([{address:'a',type:'topic'}, {address:'b',type:'queue'}, {address:'c',type:'anycast'}, {address:'d',type:'multicast'}], undefined, - function (router) { - router.create_object('org.apache.qpid.dispatch.router.config.address', 'ragent-foo', {prefix:'foo', distribution:'closest', 'waypoint':false}); - router.set_onetime_error_response('DELETE','org.apache.qpid.dispatch.router.config.address'); - })); - it('handles connector create error', multi_router_address_test(2, [{address:'a',type:'topic'}, {address:'b',type:'queue'}, {address:'c',type:'anycast'}, {address:'d',type:'multicast'}], undefined, - function (routers) { - routers[0].set_onetime_error_response('CREATE','connector'); - routers[1].set_onetime_error_response('CREATE','connector'); - })); - it('establishes multiple connectors between routers', function(done) { - process.env.ROUTER_NUM_CONNECTORS=2; - var routers = []; - for (var i = 0; i < 3; i++) { - routers.push(new_router()); - } - var address_list = [{address:'a',type:'topic'}, {address:'b',type:'queue'}]; - ragent.sync_addresses(address_list); - ragent.wait_for_stable(address_list.length, routers.length).then(function () { - verify_full_mesh_n(routers, 2); - delete process.env.ROUTER_NUM_CONNECTORS; - done(); - }).catch(done); - }); - it('ignores link route override', simple_address_test([{address:'a',type:'topic'}, {address:'b',type:'queue'}], - function (routers, address_list) { - verify_addresses(address_list, routers, 0, function (objects) { - var extra_linkroutes = remove(objects.linkroutes, function (o) { return o.prefix === 'foo' && o.direction === 'in'; }); - assert.equal(extra_linkroutes.length, 1); - assert.equal(extra_linkroutes[0].name, 'override-foo'); - }); - }, - function (router) { - router.create_object('org.apache.qpid.dispatch.router.config.linkRoute', 'override-foo', {prefix:'foo', direction:'in'}); - })); - - it('handles message with no correlation', simple_address_test([{address:'a',type:'topic'}, {address:'b',type:'queue'}], undefined, - function (router) { - var f = function (request, response, context) { - var reply_link = context.connection.find_sender(function (s) { return match_source_address(s, request.reply_to); }); - if (reply_link) { - reply_link.send({to:request.reply_to, subject:'fake message with no correlation'}); - } - delete this.special; - return false; - }; - router.special = f.bind(router); - })); - it('handles query response with no body', simple_address_test([{address:'a',type:'topic'}, {address:'b',type:'queue'}], undefined, function (router) { - var f = function (request, response) { - if (request.application_properties.operation === 'QUERY' - && request.application_properties.entityType === 'org.apache.qpid.dispatch.router.config.address') { - - response.application_properties = {}; - response.application_properties.statusCode = 200; - response.correlation_id = request.correlation_id; - delete this.special; - return true; - } else { - return false; - } - }; - router.special = f.bind(router); - })); - it('configures large number of anycast addresses', simple_address_test(generate_address_list(2000, ['anycast']), undefined, undefined, 6000)); - it('configures large number of multicast addresses', simple_address_test(generate_address_list(2000, ['multicast']), undefined, undefined, 6000)); - it('configures large number of queues', simple_address_test(generate_address_list(2000, ['queue']), undefined, undefined, 6000)); - it('configures large number of topics', simple_address_test(generate_address_list(2000, ['topic']), undefined, undefined, 6000)); - it('configures large number of mixed addresses', simple_address_test(generate_address_list(2000), undefined, undefined, 6000)); -}); - -function localpath(name) { - return path.resolve(__dirname, name); -} - -describe('configuration from configmaps', function() { - this.timeout(5000); - var ragent; - var address_source; - var routers; - var watcher; - - beforeEach(function(done) { - address_source = new AddressServer(); - address_source.listen(0).on('listening', function () { - ragent = new Ragent(); - watcher = ragent.subscribe_to_addresses({token:'foo', namespace:'default', host: 'localhost', port:address_source.port}); - ragent.listen({port:0}).on('listening', function (){ - routers = new RouterList(ragent.server.address().port); - done(); - }); - }); - }); - - afterEach(function(done) { - watcher.close().then(function () { - routers.close().then(function () { - ragent.server.close(); - address_source.close(); - done(); - }); - }); - }); - - it('retrieves initial list of addresses', function (done) { - var router = routers.new_router(); - address_source.add_address_definition({address:'a', type:'topic'}, undefined, '1234'); - address_source.add_address_definition({address:'b', type:'queue'}, undefined, '1234'); - ragent.wait_for_stable(2).then(function () { - verify_addresses([{address: 'a', type: 'topic'}, {address: 'b', type: 'queue'}], router); - done(); - }).catch(done); - }); - it('watches for new addresses', function (done) { - var router = routers.new_router(); - address_source.add_address_definition({address:'a', type:'topic'}, undefined, '1234'); - address_source.add_address_definition({address:'b', type:'queue'}, undefined, '1234'); - ragent.wait_for_stable(2).then(function () { - address_source.add_address_definition({address:'c', type:'anycast'}, undefined, '1234'); - address_source.add_address_definition({address:'d', type:'queue'}, undefined, '1234'); - //address_source.remove_resource_by_name('b'); - ragent.wait_for_stable(4).then(function () { - verify_addresses([{address:'a', type:'topic'}, {address:'b', type:'queue'}, {address:'c', type:'anycast'}, {address:'d', type:'queue'}], router); - done(); - }).catch(done); - }).catch(done); - }); - it('watches for deleted addresses', function (done) { - var router = routers.new_router(); - address_source.add_address_definition({address:'a', type:'topic'}, undefined, '1234'); - address_source.add_address_definition({address:'b', type:'queue'}, undefined, '1234'); - ragent.wait_for_stable(2).then(function () { - address_source.add_address_definition({address:'c', type:'anycast'}, undefined, '1234'); - address_source.add_address_definition({address:'d', type:'queue'}, undefined, '1234'); - address_source.remove_resource_by_name('addresses', 'b'); - ragent.wait_for_stable(3).then(function () { - verify_addresses([{address:'a', type:'topic'}, {address:'c', type:'anycast'}, {address:'d', type:'queue'}], router); - done(); - }).catch(done); - }).catch(done); - }); - it('ignores pending and terminating addresses', function (done) { - var router = routers.new_router(); - address_source.add_address_definition({address:'a', type:'queue'}, undefined, '1234'); - address_source.add_address_definition({address:'b', type:'queue'}, undefined, '1234', undefined, {phase:'Pending'}); - address_source.add_address_definition({address:'c', type:'topic'}, undefined, '1234', undefined, {phase:'Terminating'}); - address_source.add_address_definition({address:'d', type:'topic'}, undefined, '1234'); - ragent.wait_for_stable(2).then(function () { - verify_addresses([{address:'a', type:'queue'}, {address:'d', type:'topic'}], router); - done(); - }).catch(done); - }); -}); - -function RouterGroup (name) { - this.name = name; - this.ragent = new Ragent(); - this.routers = undefined; - this.port = undefined; -} - -RouterGroup.prototype.listen = function () { - var self = this; - return new Promise(function(resolve, reject) { - self.ragent.listen({port:0}).on('listening', resolve); - }).then(function () { - self.port = self.ragent.server.address().port; - self.routers = new RouterList(self.port, self.name); - }); -}; - -RouterGroup.prototype.close = function () { - var self = this; - return this.routers.close().then(function () { - if (self.ragent.watcher) self.ragent.watcher.close(); - self.ragent.server.close(); - }); -}; - -RouterGroup.prototype.pod = function () { - return { - ready : (this.port === undefined ? 'False' : 'True'), - phase : 'Running', - name : this.name, - host : 'localhost', - port : this.port - }; -}; - -RouterGroup.prototype.get_pod_definition = function () { - return { - metadata: { - name: this.name, - labels: { - name: 'admin' - } - }, - spec: { - containers: [ - { - name: 'ragent', - ports: [ - { - name: 'amqp', - containerPort: this.port - } - ] - } - ] - }, - status: { - podIP: '127.0.0.1', - phase: 'Running', - conditions : [ - { type: 'Initialized', status: 'True' }, - { type: 'Ready', status: (this.port === undefined ? 'False' : 'True') } - ] - } - }; -}; - -function fill(n, f) { - return Array.apply(null, Array(n)).map(function (v, i) { return f(i); }); -} - -describe('cooperating ragent group', function() { - this.timeout(5000); - var counter; - var groups; - var podserver; - - beforeEach(function(done) { - counter = 0; - groups = fill(4, function (i) { return new RouterGroup('ragent-' + (i+1)); }); - podserver = new ResourceServer(true); - Promise.all(groups.map(function (g) { return g.listen(); })).then(function (){ - podserver.listen(0).on('listening', function () { - done(); - }); - }); - }); - - afterEach(function(done) { - Promise.all(groups.map(function (g) {return g.close()})).then(function () { - podserver.close(); - done(); - }); - }); - - function new_router() { - return groups[counter++ % groups.length].routers.new_router(); - } - - it('establishes full mesh', function (done) { - //connect some routers - var routers = []; - routers = routers.concat(fill(3, new_router)); - //inform ragent instances of each other - groups.forEach(function (g) { - g.ragent.sync_addresses([]); - podserver.add_resource('pods', g.get_pod_definition()); - }); - groups.forEach(function(g) { - g.ragent.watch_pods({port:podserver.port, token:'foo', namespace:'default'}); - }); - //connect some more routers - routers = routers.concat(fill(3, new_router)); - //wait for some time for things to settle, then verify we have a full mesh - setTimeout(function () { - verify_full_mesh(routers); - done(); - }, 1000/*1 second wait for propagation*/);//TODO: add ability to be notified of propagation in some way - }); - it('handles removal of other ragents', function (done) { - //connect some routers - var routers = groups.map(function (g) { - return new Array(g.routers.new_router(), g.routers.new_router()); - }); - //inform ragent instances of each other - groups.forEach(function (g) { - g.ragent.sync_addresses([]); - podserver.add_resource('pods', g.get_pod_definition()); - }); - groups.forEach(function(g) { - g.ragent.watch_pods({port:podserver.port, token:'foo', namespace:'default'}); - }); - //wait for some time for things to settle, then verify we have a full mesh - setTimeout(function () { - verify_full_mesh([].concat.apply([], routers)); - var leaving = groups.pop(); - podserver.remove_resource_by_name('pods', leaving.name); - //shutdown one of the ragents - routers.pop(); - leaving.close().then(function() { - setTimeout(function () { - verify_full_mesh([].concat.apply([], routers)); - done(); - }, 1000/*1 second wait for propagation*/);//TODO: add ability to be notified of propagation in some way - }); - }, 1000/*1 second wait for propagation*/);//TODO: add ability to be notified of propagation in some way - }); -}); - -describe('health support', function() { - var ragent, server, port; - - beforeEach(function(done) { - ragent = new Ragent(); - server = ragent.listen_health({HEALTH_PORT:0}); - server.on('listening', function () { - port = server.address().port; - done(); - }); - }); - - afterEach(function(done) { - server.close(); - done(); - }); - - it('responds to health request', function (done) { - http.get('http://localhost:' + port, function (response) { - assert.equal(response.statusCode, 200); - done(); - }); - }); -}); - -function merge(a, b) { - var result = {}; - for (var ka in a) { - result[ka] = a[ka]; - } - for (var kb in b) { - result[kb] = b[kb]; - } - return result; -} - -function mock_address_source(addresses) { - var server = new AddressServer(); - for (var i = 0; i < addresses.length; i++) { - server.add_address_definition(addresses[i], undefined, '1234'); - } - return server; -} - -describe('forked process', function() { - this.timeout(5000); - var ragent; - var address_source = mock_address_source([{address:'a',type:'topic'}, {address:'b',type:'queue'}]); - var routers; - - beforeEach(function(done) { - address_source.listen(0).on('listening', function () { - var env = merge(process.env, {'LOGLEVEL':'info', 'DEBUG':'ragent*', 'AMQP_PORT':0, 'INFRA_UUID': '1234', token:'foo', host: 'localhost', 'port':address_source.port, namespace:'default'}); - ragent = child_process.fork(path.resolve(__dirname, '../lib/ragent.js'), [], {silent:true, 'env':env}); - ragent.stderr.on('data', function (data) { - if (data.toString().match('Router agent listening on')) { - var matches = data.toString().match(/on (\d+)/); - var port = matches[1]; - routers = new RouterList(port); - done(); - } - }); - }); - }); - - afterEach(function(done) { - ragent.kill(); - address_source.close(); - done(); - }); - - it('subscribes to configuration service', function (done) { - var router = routers.new_router(); - setTimeout(function () { - verify_addresses([{address:'a',type:'topic'}, {address:'b',type:'queue'}], router); - router.close().then(function () { - done(); - }); - }, 1000/*1 second wait for propagation*/);//TODO: add ability to be notified of propagation in some way - }); -}); - - -describe('run method', function() { - this.timeout(5000); - var ragent; - var address_source = mock_address_source([{address:'a',type:'topic'}, {address:'b',type:'queue'}]); - var routers; - var watcher; - - beforeEach(function(done) { - address_source.listen(0).on('listening', function() { - var env = {'AMQP_PORT':0, 'port':address_source.port, token:'foo', namespace:'default'}; - ragent = new Ragent(); - watcher = ragent.run(env, function (port) { - routers = new RouterList(port); - done(); - }); - }); - }); - - afterEach(function(done) { - watcher.close(); - ragent.server.close(); - address_source.close(); - done(); - }); - - it('subscribes to configuration service', function (done) { - var router = routers.new_router(); - ragent.wait_for_stable(2).then(function () { - verify_addresses([{address:'a',type:'topic'}, {address:'b',type:'queue'}], router); - router.close().then(function () { - done(); - }); - }).catch(done); - }); -}); - -function delay(f, time) { - return new Promise(function (resolve, reject) { - setTimeout(function () { - f(); - resolve(); - }, time); - }); -} - - -describe('changing router configuration', function() { - this.timeout(5000); - var ragent; - var routers; - - beforeEach(function(done) { - ragent = new Ragent(); - ragent.listen({port:0}).on('listening', function (){ - routers = new RouterList(ragent.server.address().port); - done(); - }); - }); - - afterEach(function(done) { - routers.close().then(function () { - ragent.server.close(); - done(); - }); - }); - - function new_router(name) { - return routers.new_router(name); - } - - it('deletes and recreates a topic', function (done) { - var router = new_router(); - var client = rhea.connect({port:ragent.server.address().port}); - client.on('connection_close', function () { done(); } ); - - ragent.sync_addresses([{address:'mytopic',type:'topic'}]); - delay(function () { - ragent.sync_addresses([]); - }, 200).then(function () { - delay(function () { - verify_addresses([], router); - ragent.sync_addresses([{address:'mytopic',type:'topic'}]); - }, 200).then(function () { - ragent.wait_for_stable(1).then(function () { - verify_addresses([{address:'mytopic',type:'topic'}], router); - client.close(); - }).catch(done); - }); - }); - }); -}); - -describe('broker configuration', function() { - this.timeout(6000); - var ragent; - var address_source; - var routers; - var port; - var connections; - var watcher; - - beforeEach(function(done) { - address_source = new AddressServer(); - connections = []; - address_source.listen(0).on('listening', function () { - ragent = new Ragent(); - watcher = ragent.subscribe_to_addresses({token:'foo', namespace:'default', host: 'localhost', port:address_source.port}); - ragent.listen({port:0}).on('listening', function (){ - port = ragent.server.address().port; - routers = new RouterList(ragent.server.address().port); - done(); - }); - }); - }); - - function close(connection) { - if (connection.is_open()) { - return new Promise(function (resolve) { - try { - connection.close(); - } catch (error) { - console.error(error); - resolve(); - } - connection.on('connection_close', resolve); - connection.on('connection_error', resolve); - }); - } else { - return Promise.resolve(); - } - } - - afterEach(function(done) { - watcher.close().then(function () { - setTimeout(function () { - Promise.all(connections.map(close)).then(function () { - routers.close().then(function () { - try { - ragent.server.close(); - address_source.close(); - } catch (error) { - console.error(error); - } - done(); - }).catch(done); - }).catch(done); - }, 500); - }).catch(done); - }); - - function connect_broker(broker) { - var conn = broker.connect(port); - connections.push(conn); - return new Promise(function (resolve, reject) { - conn.once('connection_open', function () { - resolve(); - }); - }); - } - - it('creates queues on associated brokers', function (done) { - var router = routers.new_router(); - var broker_a = new MockBroker('broker_a-0'); - var broker_b = new MockBroker('broker_b-0'); - Promise.all([connect_broker(broker_a), connect_broker(broker_b)]).then(function () { - address_source.add_address_definition({address:'a', type:'queue'}, undefined, '1234', undefined, {brokerStatuses:[{clusterId: 'broker_a', containerId:'broker_a-0', state: 'Active'}]}); - address_source.add_address_definition({address:'b', type:'queue'}, undefined, '1234', undefined, {brokerStatuses:[{clusterId: 'broker_b', containerId:'broker_b-0', state: 'Active'}]}); - ragent.wait_for_stable(2, 1, 2).then(function () { - verify_addresses([{address:'a', type:'queue', allocated_to:[broker_state('broker_a')]}, {address:'b', type:'queue', allocated_to:[broker_state('broker_b')]}], router, 2); - //verify queues on respective brokers: - broker_a.verify_addresses([{address:'a', type:'queue'}]); - broker_b.verify_addresses([{address:'b', type:'queue'}]); - done(); - }).catch(done); - }).catch(done); - }); - - it('deletes queues from associated brokers', function (done) { - var router = routers.new_router(); - var broker_a = new MockBroker('broker_a-0'); - var broker_b = new MockBroker('broker_b-0'); - Promise.all([connect_broker(broker_a), connect_broker(broker_b)]).then(function () { - address_source.add_address_definition({address:'a', type:'queue'}, 'address-config-a', '1234', undefined, {brokerStatuses:[{clusterId: 'broker_a', containerId:'broker_a-0', state: 'Active'}]}); - address_source.add_address_definition({address:'b', type:'queue'}, 'address-config-b', '1234', undefined, {brokerStatuses:[{clusterId: 'broker_b', containerId:'broker_b-0', state: 'Active'}]}); - address_source.add_address_definition({address:'c', type:'queue'}, 'address-config-c', '1234', undefined, {brokerStatuses:[{clusterId: 'broker_a', containerId:'broker_a-0', state: 'Active'}]}); - ragent.wait_for_stable(3, 1, 2).then(function () { - verify_addresses([{address:'a', type:'queue', allocated_to:[broker_state('broker_a')]}, {address:'b', type:'queue', allocated_to:[broker_state('broker_b')]}, {address:'c', type:'queue', allocated_to:[broker_state('broker_a')]}], router, 2); - //verify queues on respective brokers: - broker_a.verify_addresses([{address:'a', type:'queue'}, {address:'c', type:'queue'}]); - broker_b.verify_addresses([{address:'b', type:'queue'}]); - //delete configmap - address_source.remove_resource_by_name('addresses', 'address-config-a'); - ragent.wait_for_stable(2, 1, 2).then(function () { - verify_addresses([{address:'b', type:'queue', allocated_to:[broker_state('broker_b')]}, {address:'c', type:'queue', allocated_to:[broker_state('broker_a')]}], router, 2); - broker_a.verify_addresses([{address:'c', type:'queue'}]); - broker_b.verify_addresses([{address:'b', type:'queue'}]); - done(); - }).catch(done); - }).catch(done); - }); - }); - - it('creates subscriptions on associated brokers', function (done) { - var router = routers.new_router(); - var broker_a = new MockBroker('broker_a-0'); - var broker_b = new MockBroker('broker_b-0'); - Promise.all([connect_broker(broker_a), connect_broker(broker_b)]).then(function () { - address_source.add_address_definition({address:'a', type:'topic'}, undefined, '1234', undefined, {brokerStatuses:[{containerId:'broker_a-0', state: 'Active'}]}); - address_source.add_address_definition({address:'b', type:'topic'}, undefined, '1234', undefined, {brokerStatuses:[{containerId:'broker_b-0', state: 'Active'}]}); - address_source.add_address_definition({address:'sub-a', type:'subscription', topic:'a'}, undefined, '1234', undefined, {brokerStatuses:[{containerId:'broker_a-0', state: 'Active'}]}); - address_source.add_address_definition({address:'sub-b', type:'subscription', topic:'b'}, undefined, '1234', undefined, {brokerStatuses:[{containerId:'broker_b-0', state: 'Active'}]}); - ragent.wait_for_stable(4, 1, 2).then(function () { - verify_addresses([{address:'a', type:'topic', allocated_to:[broker_state('broker_a')]}, {address:'b', type:'topic', allocated_to:[broker_state('broker_b')]}, - {address:'sub-a', type:'subscription', topic:'a', allocated_to:[broker_state('broker_a')]}, - {address:'sub-b', type:'subscription', topic:'b', allocated_to:[broker_state('broker_b')]}], router, 2); - //verify queues on respective brokers: - broker_a.verify_addresses([{address:'a', type:'topic'}, {address:'sub-a', type:'subscription', topic:'a'}]); - broker_b.verify_addresses([{address:'b', type:'topic'}, {address:'sub-b', type:'subscription', topic:'b'}]); - done(); - }).catch(done); - }).catch(done); - }); - - it('handles subscriptions and topics in correct order', function (done) { - var router = routers.new_router(); - var broker_a = new MockBroker('broker_a-0'); - var broker_b = new MockBroker('broker_b-0'); - Promise.all([connect_broker(broker_a), connect_broker(broker_b)]).then(function () { - address_source.add_address_definition({address:'sub-a', type:'subscription', topic:'topic-a'}, undefined, '1234', undefined, {brokerStatuses:[{containerId:'broker_a-0', state: 'Active'}]}); - address_source.add_address_definition({address:'sub-b', type:'subscription', topic:'topic-b'}, undefined, '1234', undefined, {brokerStatuses:[{containerId:'broker_b-0', state: 'Active'}]}); - address_source.add_address_definition({address:'topic-a', type:'topic'}, undefined, '1234', undefined, {brokerStatuses:[{containerId:'broker_a-0', state: 'Active'}]}); - address_source.add_address_definition({address:'topic-b', type:'topic'}, undefined, '1234', undefined, {brokerStatuses:[{containerId:'broker_b-0', state: 'Active'}]}); - ragent.wait_for_stable(4, 1, 2).then(function () { - verify_addresses([{address:'topic-a', type:'topic', allocated_to:[broker_state('broker_a')]}, {address:'topic-b', type:'topic', allocated_to:[broker_state('broker_b')]}, - {address:'sub-a', type:'subscription', topic:'topic-a', allocated_to:[broker_state('broker_a')]}, - {address:'sub-b', type:'subscription', topic:'topic-b', allocated_to:[broker_state('broker_b')]}], router, 2); - //verify queues on respective brokers: - broker_a.verify_addresses([{address:'topic-a', type:'topic'}, {address:'sub-a', type:'subscription', topic:'topic-a'}]); - broker_b.verify_addresses([{address:'topic-b', type:'topic'}, {address:'sub-b', type:'subscription', topic:'topic-b'}]); - done(); - }).catch(done); - }).catch(done); - }); - - it('handles broker disconnection', function (done) { - var router = routers.new_router(); - var broker_a = new MockBroker('broker_a'); - var broker_b = new MockBroker('broker_b'); - Promise.all([connect_broker(broker_a), connect_broker(broker_b)]).then(function () { - ragent.wait_for_stable(0, 1, 2).then(function () { - assert(ragent.connected_brokers['broker_a'] !== undefined); - assert(ragent.connected_brokers['broker_b'] !== undefined); - connections[1].close(); - ragent.wait_for_stable(0, 1, 1).then(function () { - assert(ragent.connected_brokers['broker_a'] !== undefined, 'broker-a SHOULD be in connected_broker map'); - assert(ragent.connected_brokers['broker_b'] === undefined, 'broker-b should NOT be in connected_broker map'); - done(); - }).catch(done); - }).catch(done); - }); - }); - - it('creates lots of queues on associated brokers', function (done) { - this.timeout(60000); - var router = routers.new_router(); - var broker_a = new MockBroker('broker_a-0'); - var broker_b = new MockBroker('broker_b-0'); - Promise.all([connect_broker(broker_a), connect_broker(broker_b)]).then(function () { - var desired = generate_address_list(2000, ['queue']); - desired.forEach(function (a, i) { - var allocated_to = i % 2 ? 'broker_a' : 'broker_b'; - a.allocated_to = [broker_state(allocated_to)]; - address_source.add_address_definition(a, undefined, '1234', undefined, {brokerStatuses:[{clusterId: allocated_to, containerId: allocated_to + "-0", state: 'Active'}]}); - }); - ragent.wait_for_stable(2000, 1, 2).then(function () { - verify_addresses(desired, router, 2); - //verify queues on respective brokers: - broker_a.verify_addresses(desired.filter(function (a) { return a.allocated_to[0].containerId === 'broker_a-0'; })); - broker_b.verify_addresses(desired.filter(function (a) { return a.allocated_to[0].containerId === 'broker_b-0'; })); - done(); - }).catch(done); - }).catch(done); - }); -}); diff --git a/agent/test/registry.js b/agent/test/registry.js deleted file mode 100644 index c25777ee306..00000000000 --- a/agent/test/registry.js +++ /dev/null @@ -1,96 +0,0 @@ -/* - * Copyright 2017 Red Hat Inc. - * - * 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. - */ -'use strict'; - -var assert = require('assert'); - -var Registry = require('../lib/registry.js'); - -describe('registry', function() { - var registry; - - beforeEach(function(done) { - registry = new Registry(); - done(); - }); - - afterEach(function(done) { - done(); - }); - - function check_expected(expected, actual, done) { - assert.equal(expected.type, actual.type); - assert.equal(JSON.stringify(expected.value), JSON.stringify(actual.value)); - if (++i >= events.length && done) { - done(); - } - } - - function verifier(type, events, done) { - return function (o) { - var e = events.shift(); - assert.equal(e.type, type); - assert.equal(JSON.stringify(e.value), JSON.stringify(o)); - if (events.length === 0) { - done(); - } - } - } - - function expect(events, done) { - registry.on('updated', verifier('updated', events, done)); - registry.on('deleted', verifier('updated', events, done)); - } - - it('update', function(done) { - expect([{type:'updated', value:{id:'foo', x:100, y:['a','b','c']}}, - {type:'updated', value:{id:'foo', x:100, y:['d','e','f']}}, - {type:'updated', value:{id:'foo', x:200, y:['d','e','f']}}], done); - registry.update('foo', {id:'foo', x:100, y:['a','b','c']});//new item - registry.update('foo', {id:'foo', x:100, y:['a','b','c']});//no change - registry.update('foo', {id:'foo', x:100, y:['d','e','f']}); - registry.update('foo', {id:'foo', x:200, y:['d','e','f']}); - }); - - it('update_if_exists', function(done) { - expect([{type:'updated', value:{id:'foo', x:100, y:['d','e','f']}}, - {type:'updated', value:{id:'foo', x:200, y:['d','e','f']}}], done); - registry.update_if_exists('foo', {id:'foo', x:100, y:['a','b','c']});//new item - registry.update('foo', {id:'foo', x:100, y:['d','e','f']}); - registry.update_if_exists('foo', {id:'foo', x:200, y:['d','e','f']}); - }); - - it('for_each', function(done) { - registry.set({ - 'a': {foo:'bar',baz:10}, - 'b': {colour:'red',age:99}, - 'c': {name:'bob',type:'kangaroo'} - }); - var collected = {}; - registry.for_each(function (o) { - for (var f in o) { - collected[f] = o[f]; - } - }); - var expected = { - foo:'bar',baz:10, - colour:'red',age:99, - name:'bob',type:'kangaroo' - }; - assert.equal(JSON.stringify(expected), JSON.stringify(collected)); - done(); - }); -}); diff --git a/agent/test/router_stats.js b/agent/test/router_stats.js deleted file mode 100644 index b883ffd5ddc..00000000000 --- a/agent/test/router_stats.js +++ /dev/null @@ -1,499 +0,0 @@ -/* - * Copyright 2017 Red Hat Inc. - * - * 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. - */ -'use strict'; - -var assert = require('assert'); -var events = require('events'); -var util = require('util'); -var rhea = require('rhea'); -var uuidv1 = require('uuid/v1'); -var RouterStats = require('../lib/router_stats.js'); -var AddressList = require('../lib/address_list.js'); -var Registry = require('../lib/registry.js'); - -function random_number(max) { - return Math.floor(Math.random() * Math.floor(max)); -} - -function NameGenerator (base, separator) { - this.base = base; - this.separator = separator || '_'; - this.counter = 1; -} - -NameGenerator.prototype.next = function(base) { - return this.base + this.separator + this.counter++; -}; - -function DummyConn (name, builder) { - this.name = name; - this.builder = builder; -} - -DummyConn.prototype.sender = function (address, details, phase) { - this.builder.link(this.name, true, address, details, phase); -}; - -DummyConn.prototype.receiver = function (address, details, phase) { - this.builder.link(this.name, false, address, details, phase); -}; - -function StatsBuilder (router) { - this.conn_names = new NameGenerator(router.name + '-conn'); - this.link_names = new NameGenerator(router.name + '-link'); - this.router = router; -} - -StatsBuilder.prototype.connection = function (details) { - var name = this.conn_names.next(); - var attributes = details || {}; - if (attributes.role === undefined) attributes.role = 'normal'; - if (attributes.identity === undefined) attributes.identity = name; - if (attributes.host === undefined) attributes.host = "example:" + random_number(16384) + 49152; - if (attributes.container === undefined) attributes.container = uuidv1(); - this.router.create_object('org.apache.qpid.dispatch.connection', name, attributes); - return new DummyConn(name, this); -}; - -StatsBuilder.prototype.link = function (connection, is_sender, address, details, phase) { - var attributes = details || {}; - if (attributes.linkType === undefined) attributes.linkType='endpoint'; - attributes.connectionId = connection; - attributes.linkDir = is_sender ? 'in' : 'out'; - attributes.owningAddr = 'M' + (phase || 0) + address; - this.router.create_object('org.apache.qpid.dispatch.router.link', this.link_names.next(), attributes); -}; - -StatsBuilder.prototype.anycast = function (name, messages_in, messages_out, details) { - var attributes = details || {}; - attributes.deliveriesIngress = messages_in; - attributes.deliveriesEgress = messages_out; - this.router.create_object('org.apache.qpid.dispatch.router.config.address', name, {prefix:name, distribution:'balanced', waypoint:false}); - this.router.create_object('org.apache.qpid.dispatch.router.address', 'M0' + name, attributes); -}; - -StatsBuilder.prototype.queue = function (name, messages_in, messages_out, details) { - var attributes_0 = details || {}; - var attributes_1 = details || {}; - attributes_0.deliveriesIngress = messages_in; - attributes_1.deliveriesEgress = messages_out; - this.router.create_object('org.apache.qpid.dispatch.router.config.address', name, {prefix:name, distribution:'balanced', waypoint:true}); - this.router.create_object('org.apache.qpid.dispatch.router.address', 'M0' + name, attributes_0); - this.router.create_object('org.apache.qpid.dispatch.router.address', 'M1' + name, attributes_1); -}; - -StatsBuilder.prototype.topic = function (name) { - for (var dir in {'in':'in', 'out':'out'}) { - this.router.create_object('org.apache.qpid.dispatch.router.config.linkRoute', name+'-'+dir, {prefix:name, dir:dir}); - } -}; - -var router_names = new NameGenerator('router'); - -function MockRouter (name) { - this.name = name || router_names.next(); - events.EventEmitter.call(this); - this.objects = {}; - this.create_object('listener', 'default', {name:'default', address:name, port:55672, role:'inter-router'}); - this.container = rhea.create_container({id:this.name}); - this.container.on('message', this.on_message.bind(this)); - var self = this; - this.container.on('sender_open', function(context) { - if (context.sender.source.dynamic) { - var id = self.container.generate_uuid(); - context.sender.set_source({address:id}); - } - }); - this.nodes = {}; - this.add_node(this); - this.populate = new StatsBuilder(this); -} - -util.inherits(MockRouter, events.EventEmitter); - -function match_source_address(link, address) { - return link && link.local && link.local.attach && link.local.attach.source - && link.local.attach.source.value[0].toString() === address; -} - -MockRouter.prototype.add_node = function (router) { - this.nodes[router.get_mgmt_node_address()] = router; -}; - -MockRouter.prototype.connect = function (port) { - this.connection = this.container.connect({'port':port || 55672, properties:{product:'qpid-dispatch-router'}}); - var self = this; - this.connection.on('connection_open', function () { self.emit('connected', self); }); -}; - -MockRouter.prototype.listen = function (port) { - this.listener = this.container.listen({'port':port, properties:{product:'qpid-dispatch-router'}}); - return this.listener; -}; - -MockRouter.prototype.create_object = function (type, name, attributes) -{ - if (this.objects[type] === undefined) { - this.objects[type] = {}; - } - this.objects[type][name] = (attributes === undefined) ? {} : attributes; - this.objects[type][name].name = name; - this.objects[type][name].id = name; -}; - -MockRouter.prototype.delete_object = function (type, name) -{ - delete this.objects[type][name]; -}; - -MockRouter.prototype.list_objects = function (type) -{ - var results = []; - for (var key in this.objects[type]) { - results.push(this.objects[type][key]); - } - return results; -}; - -MockRouter.prototype.close = function () -{ - if (this.connection) this.connection.close(); - if (this.listener) this.listener.close(); -}; - -function get_attribute_names(objects) { - var names = {}; - for (var i in objects) { - for (var f in objects[i]) { - names[f] = f; - } - } - return Object.keys(names); -} - -function query_result(names, objects) { - var results = []; - for (var j in objects) { - var record = []; - for (var i = 0; i < names.length; i++) { - record.push(objects[j][names[i]]); - } - results.push(record); - } - return {attributeNames:names, 'results':results}; -} - -MockRouter.prototype.on_message = function (context) -{ - var request = context.message; - var node = this; - var target = context.message.to || context.receiver.remote.attach.target.address; - if (target !== '$management' && this.nodes[target]) { - node = this.nodes[target]; - } - var reply_to = request.reply_to; - var response = {to: reply_to}; - if (request.correlation_id) { - response.correlation_id = request.correlation_id; - } - response.application_properties = {}; - response.application_properties.statusCode = 200; - - if (request.application_properties.operation === 'CREATE') { - node.create_object(request.application_properties.type, request.application_properties.name, request.body); - } else if (request.application_properties.operation === 'DELETE') { - node.delete_object(request.application_properties.type, request.application_properties.name); - } else if (request.application_properties.operation === 'QUERY') { - var results = node.list_objects(request.application_properties.entityType); - var attributes = request.body.attributeNames; - if (!attributes || attributes.length === 0) { - attributes = get_attribute_names(results); - } - response.body = query_result(attributes, results); - } else if (request.application_properties.operation === 'GET-MGMT-NODES') { - response.body = Object.keys(node.nodes); - } - - var reply_link = context.connection.find_sender(function (s) { return match_source_address(s, reply_to); }); - if (reply_link) { - reply_link.send(response); - } -}; - -MockRouter.prototype.get_mgmt_node_address = function () { - return "amqp:/_topo/0/" + this.name +"/$management" -}; - -describe('router stats', function() { - var router; - var router_stats; - var extra_nodes; - - beforeEach(function(done) { - router = new MockRouter(); - extra_nodes = []; - var l = router.listen(0); - l.on('listening', function () { - router_stats = new RouterStats(rhea.connect({port:l.address().port})); - done(); - }); - }); - - function add_router_nodes(count) { - for (var i = 0; i < count; i++) { - var r = new MockRouter(); - extra_nodes.push(r); - router.add_node(r); - } - } - - afterEach(function(done) { - router_stats.close(); - router.close(); - done(); - }); - - it('retrieves stats for an anycast address from a single router', function(done) { - //populate router: - router.populate.anycast('foo', 10, 8); - //create some fake connection- and link- stats: - var c = router.populate.connection(); - for (var i = 0; i < 2; i++) { - c.sender('foo', {presettledCount:1, undeliveredCount:2, unsettledCount:3, acceptedCount:4, rejectedCount:5, releasedCount:6, modifiedCount:7}); - c.receiver('foo', {presettledCount:7, undeliveredCount:6, unsettledCount:5, acceptedCount:4, rejectedCount:3, releasedCount:2, modifiedCount:1}); - } - - //retrieve stats: - router_stats._retrieve().then(function(results) { - assert.equal(results.addresses.foo.senders, 2); - assert.equal(results.addresses.foo.receivers, 2); - assert.equal(results.addresses.foo.messages_in, 10); - assert.equal(results.addresses.foo.messages_out, 8); - - assert.equal(results.addresses.foo.outcomes.ingress.presettled, 2); - assert.equal(results.addresses.foo.outcomes.ingress.undelivered, 4); - assert.equal(results.addresses.foo.outcomes.ingress.unsettled, 6); - assert.equal(results.addresses.foo.outcomes.ingress.accepted, 8); - assert.equal(results.addresses.foo.outcomes.ingress.rejected, 10); - assert.equal(results.addresses.foo.outcomes.ingress.released, 12); - assert.equal(results.addresses.foo.outcomes.ingress.modified, 14); - - assert.equal(results.addresses.foo.outcomes.egress.presettled, 14); - assert.equal(results.addresses.foo.outcomes.egress.undelivered, 12); - assert.equal(results.addresses.foo.outcomes.egress.unsettled, 10); - assert.equal(results.addresses.foo.outcomes.egress.accepted, 8); - assert.equal(results.addresses.foo.outcomes.egress.rejected, 6); - assert.equal(results.addresses.foo.outcomes.egress.released, 4); - assert.equal(results.addresses.foo.outcomes.egress.modified, 2); - - assert.equal(results.addresses.foo.outcomes.ingress.links.length, 2); - assert.equal(results.addresses.foo.outcomes.ingress.links[0].backlog, 5); - assert.equal(results.addresses.foo.outcomes.ingress.links[0].backlog, 5); - assert.equal(results.addresses.foo.outcomes.egress.links.length, 2); - assert.equal(results.addresses.foo.outcomes.egress.links[0].backlog, 11); - assert.equal(results.addresses.foo.outcomes.egress.links[0].backlog, 11); - - done(); - }).catch(done); - }); - it('retrieves stats for a queue from a single router', function(done) { - //populate router: - router.populate.queue('foo', 64, 46); - //create some fake connection- and link- stats: - var c = router.populate.connection(); - for (var i = 0; i < 2; i++) { - c.sender('foo'); - c.receiver('foo'); - } - - //retrieve stats: - router_stats._retrieve().then(function(results) { - assert.equal(results.addresses.foo.senders, 2); - assert.equal(results.addresses.foo.receivers, 2); - assert.equal(results.addresses.foo.messages_in, 64); - assert.equal(results.addresses.foo.messages_out, 46); - done(); - }).catch(done); - }); - it('retrieves stats for an anycast address from multiple routers', function(done) { - add_router_nodes(2); - //populate routers: - router.populate.anycast('foo', 10, 8); - extra_nodes[0].populate.anycast('foo', 6, 4); - extra_nodes[1].populate.anycast('foo', 24, 3); - //create some fake connection- and link- stats: - var c = router.populate.connection(); - for (var i = 0; i < 2; i++) { - c.sender('foo', {presettledCount:1, undeliveredCount:2, unsettledCount:3, acceptedCount:4, rejectedCount:5, releasedCount:6, modifiedCount:7}); - c.receiver('foo', {presettledCount:7, undeliveredCount:6, unsettledCount:5, acceptedCount:4, rejectedCount:3, releasedCount:2, modifiedCount:1}); - } - for (var i = 0 ; i < extra_nodes.length; i++) { - var cx = router.populate.connection(); - var delta = i+1; - cx.sender('foo', {presettledCount:delta, undeliveredCount:delta, unsettledCount:delta, acceptedCount:delta, rejectedCount:delta, releasedCount:delta, modifiedCount:delta}); - cx.receiver('foo', {presettledCount:delta, undeliveredCount:delta, unsettledCount:delta, acceptedCount:delta, rejectedCount:delta, releasedCount:delta, modifiedCount:delta}); - } - - //retrieve stats: - router_stats._retrieve().then(function(results) { - assert.equal(results.addresses.foo.senders, 4); - assert.equal(results.addresses.foo.receivers, 4); - assert.equal(results.addresses.foo.messages_in, 40); - assert.equal(results.addresses.foo.messages_out, 15); - - assert.equal(results.addresses.foo.outcomes.ingress.presettled, 5); - assert.equal(results.addresses.foo.outcomes.ingress.undelivered, 7); - assert.equal(results.addresses.foo.outcomes.ingress.unsettled, 9); - assert.equal(results.addresses.foo.outcomes.ingress.accepted, 11); - assert.equal(results.addresses.foo.outcomes.ingress.rejected, 13); - assert.equal(results.addresses.foo.outcomes.ingress.released, 15); - assert.equal(results.addresses.foo.outcomes.ingress.modified, 17); - - assert.equal(results.addresses.foo.outcomes.egress.presettled, 17); - assert.equal(results.addresses.foo.outcomes.egress.undelivered, 15); - assert.equal(results.addresses.foo.outcomes.egress.unsettled, 13); - assert.equal(results.addresses.foo.outcomes.egress.accepted, 11); - assert.equal(results.addresses.foo.outcomes.egress.rejected, 9); - assert.equal(results.addresses.foo.outcomes.egress.released, 7); - assert.equal(results.addresses.foo.outcomes.egress.modified, 5); - - assert.equal(results.addresses.foo.outcomes.ingress.links.length, 4); - assert.equal(results.addresses.foo.outcomes.egress.links.length, 4); - - done(); - }).catch(done); - }); - it('retrieves stats for a queue from multiple routers', function(done) { - add_router_nodes(2); - //populate router: - router.populate.queue('foo', 64, 46); - extra_nodes[0].populate.queue('foo', 6, 4); - extra_nodes[1].populate.queue('foo', 20, 5); - //create some fake connection- and link- stats: - var c = router.populate.connection(); - for (var i = 0; i < 2; i++) { - c.sender('foo'); - c.receiver('foo'); - } - for (var i = 0; i < 3; i++) { - extra_nodes[0].populate.connection().sender('foo'); - } - var c2 = extra_nodes[1].populate.connection(); - for (var i = 0; i < 3; i++) { - c2.receiver('foo'); - } - c2.sender('foo'); - - //retrieve stats: - router_stats._retrieve().then(function(results) { - assert.equal(results.addresses.foo.senders, 6); - assert.equal(results.addresses.foo.receivers, 5); - assert.equal(results.addresses.foo.messages_in, 90); - assert.equal(results.addresses.foo.messages_out, 55); - done(); - }).catch(done); - }); - it('retrieves propagation for a topic from a single router', function(done) { - //populate router: - router.populate.topic('bar'); - //retrieve stats: - router_stats._retrieve().then(function(results) { - assert.equal(results.addresses.bar.propagated, 100); - done(); - }).catch(done); - }); - it('retrieves sender and receiver stats for a topic from a single router', function(done) { - //populate router: - router.populate.topic('bar'); - for (var i = 0; i < 10; i++) { - var c = router.populate.connection(); - c.receiver('bar'); - c.receiver('bar'); - c.sender('bar'); - var c2 = router.populate.connection({role:'route-container'}); - c2.receiver('bar'); - c2.sender('bar'); - } - //retrieve stats: - router_stats._retrieve().then(function(results) { - assert.equal(results.addresses.bar.senders, 10); - assert.equal(results.addresses.bar.receivers, 20); - done(); - }).catch(function (error) { - done(error); - }); - }); - it('updates registries with stats for a queue from a single router', function(done) { - //populate router: - router.populate.queue('foo', 64, 46); - //create some fake connection- and link- stats: - var c = router.populate.connection(); - for (var i = 0; i < 2; i++) { - c.sender('foo'); - c.receiver('foo'); - } - - //retrieve stats: - var connections = new Registry(); - var addresses = new AddressList(); - addresses.set({foo:{address:'foo'}}); - router_stats.retrieve(addresses, connections).then(function() { - assert.equal(addresses.get('foo').senders, 2); - assert.equal(addresses.get('foo').receivers, 2); - assert.equal(addresses.get('foo').messages_in, 64); - assert.equal(addresses.get('foo').messages_out, 46); - done(); - }).catch(done); - }); - it('retrieves stats for a queue from a changing set of routers', function(done) { - //populate router: - router.populate.queue('foo', 64, 46); - //create some fake connection- and link- stats: - var c = router.populate.connection(); - for (var i = 0; i < 2; i++) { - c.sender('foo'); - c.receiver('foo'); - } - - //retrieve stats: - router_stats._retrieve().then(function(results) { - assert.equal(results.addresses.foo.senders, 2); - assert.equal(results.addresses.foo.receivers, 2); - assert.equal(results.addresses.foo.messages_in, 64); - assert.equal(results.addresses.foo.messages_out, 46); - add_router_nodes(2); - extra_nodes[0].populate.queue('foo', 6, 4); - extra_nodes[1].populate.queue('foo', 20, 5); - for (var i = 0; i < 3; i++) { - extra_nodes[0].populate.connection().sender('foo'); - } - var c2 = extra_nodes[1].populate.connection(); - for (var i = 0; i < 3; i++) { - c2.receiver('foo'); - } - c2.sender('foo'); - - //retrieve stats: - router_stats._retrieve().then(function(results) { - assert.equal(results.addresses.foo.senders, 6); - assert.equal(results.addresses.foo.receivers, 5); - assert.equal(results.addresses.foo.messages_in, 90); - assert.equal(results.addresses.foo.messages_out, 55); - done(); - }).catch(done); - }).catch(done); - }); -}); diff --git a/agent/test/sasl.js b/agent/test/sasl.js deleted file mode 100644 index 2fe4b77e3d1..00000000000 --- a/agent/test/sasl.js +++ /dev/null @@ -1,31 +0,0 @@ -/* - * Copyright 2017 Red Hat Inc. - * - * 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. - */ -'use strict'; - -var assert = require('assert'); -var myutils = require('../lib/sasl.js'); - - -describe('xoauth', function () { - it ('parse', function (done) { - var knownGood = "dXNlcj1zb21ldXNlckBleGFtcGxlLmNvbQFhdXRoPUJlYXJlciB5YTI5LnZGOWRmdDRxbVRjMk52YjNSbGNrQmhkSFJoZG1semRHRXVZMjl0Q2cBAQ=="; - var buf = Buffer.from(knownGood, 'base64'); - var res = myutils.parseXOAuth2Reponse(buf); - assert.equal(res.user, "someuser@example.com"); - assert.equal(res.token, "ya29.vF9dft4qmTc2Nvb3RlckBhdHRhdmlzdGEuY29tCg"); - done(); - }); -}); diff --git a/agent/test/server-cert.pem b/agent/test/server-cert.pem deleted file mode 100644 index b67b1bf669c..00000000000 --- a/agent/test/server-cert.pem +++ /dev/null @@ -1,19 +0,0 @@ ------BEGIN CERTIFICATE----- -MIIDJTCCAg0CCQCsMJm3OM+m2DANBgkqhkiG9w0BAQUFADBTMQswCQYDVQQGEwJY -WDEVMBMGA1UEBwwMRGVmYXVsdCBDaXR5MRwwGgYDVQQKDBNEZWZhdWx0IENvbXBh -bnkgTHRkMQ8wDQYDVQQDDAZUZXN0Q0EwHhcNMTUxMTA5MjIyMzA5WhcNMjUxMTA2 -MjIyMzA5WjBWMQswCQYDVQQGEwJYWDEVMBMGA1UEBwwMRGVmYXVsdCBDaXR5MRww -GgYDVQQKDBNEZWZhdWx0IENvbXBhbnkgTHRkMRIwEAYDVQQDDAlsb2NhbGhvc3Qw -ggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDFt+yj/kppJGAWhCUdED03 -WRgHbGZjf94eKbD2k0z2V7q/xf8rKdP/rmX1l4ozR9ty6njzXn62Hx9dNtiPGgB8 -TXC2PFVyRSA+fp7bCtpowCoeumMfWnV+HJbhJhwXk42SX0Gx5CRKhJmDR0RSDjSY -h2ncN1U7G11ycn4+4a9NgI6ywqWqLUwSTjvb/4GtHEWw5bVtR7Ck9uOI+BTMJIxm -SGN2d4ScS2OWDjM7K8+qEeOXrW6Pi5UZUjSyQt2KR2eCsZpxfcaulDsGQnttX/9A -9ADBWs0A4Pq+aNWHfKX/2/BvXgIZsbV3EBkoPmJN0tTXcsi8mGikyNlaVcCO+ffR -AgMBAAEwDQYJKoZIhvcNAQEFBQADggEBAFUtCUE6c+SjW52gxRBw+YrZK8yiS8iz -azKPEOoU0bPdW2zl/xwuHmaDruvQkTVRs7Eg57gpMInEPviEYQClUDB1E+RG1UaM -h47Mb9lXf7RxARKQxnnwRqBGzDJpf+A08n4Qjosz4TI+mL/DdD4SoDGT4hjZuCym -vdGT9iNUyyUMUTdTNjxJUsvXwn66mJsB+bLR1mZwjX7rNrwyME/L1J3h8r+Jfj6A -veJyzwPhXTRVVsH6aVi0rXIJflHP9HSXpJ1T1cbZ3hbYg2H7t6gJoTUsKRD2P9NU -Ht1chIVGoc6PPOXXdVRCcOa7gQ80BljGUknL7HIJJJO4/T5nhSvsZTk= ------END CERTIFICATE----- diff --git a/agent/test/server-key.pem b/agent/test/server-key.pem deleted file mode 100644 index de8d38b5473..00000000000 --- a/agent/test/server-key.pem +++ /dev/null @@ -1,27 +0,0 @@ ------BEGIN RSA PRIVATE KEY----- -MIIEpAIBAAKCAQEAxbfso/5KaSRgFoQlHRA9N1kYB2xmY3/eHimw9pNM9le6v8X/ -KynT/65l9ZeKM0fbcup4815+th8fXTbYjxoAfE1wtjxVckUgPn6e2wraaMAqHrpj -H1p1fhyW4SYcF5ONkl9BseQkSoSZg0dEUg40mIdp3DdVOxtdcnJ+PuGvTYCOssKl -qi1MEk472/+BrRxFsOW1bUewpPbjiPgUzCSMZkhjdneEnEtjlg4zOyvPqhHjl61u -j4uVGVI0skLdikdngrGacX3GrpQ7BkJ7bV//QPQAwVrNAOD6vmjVh3yl/9vwb14C -GbG1dxAZKD5iTdLU13LIvJhopMjZWlXAjvn30QIDAQABAoIBAQC3g9BcIqG0V3Ps -UjkUcccwATzhXwbl4YArpOzA4NW1bJZEe6UszgUcsyWlv0Mh99dDIUikMxgPOHHn -FgoJQMwkk8hDDVsN1gVonCQcEUXJplsx7DQmnNKi/Cw6zCGU7Hqh2lJip+vbUpoe -FF7gfpjo3f8uzBkK5IpmuMhe2yDYIA7ex3rP07LB2u51IIsA//GMz4WeNI6/9EIu -MRwzoCXDMdi/Ga85NfsrG416QYr8aGs1wuCDUc7OFMqciXJ9aJ4zQp4AAIk3zUnN -JvDf/aPa8h5UTzmF3yEu91oeGoPllbhp6v1NShJORWhDIIm6t/a7N3aC5ZkYEJsp -DmSkvF0xAoGBAP8m4y8SkXGqVg//XMM78Gg5NkxNQXL4Zl8GnooPbpSmaPBZX/w1 -EqQoqOHZOMJPz3/JOHfbhw8K8WUXTMkZppkEzEMOIxEhUTH41+gSU6XoJLEuAPz2 -VoYAH58s0mdXpJX23iIXjDpMsJ2w+ScrOvglsM2gyxJweA0ZI53COkqtAoGBAMZg -KnxshzuwG+RVaFd8nmXLjE0m97rpyj7MIMro5hcudjUdLJ27gCgEXoB4+E2pxOkH -1M2aGr5+LMpugaSD1u8buo1gZ/cQfE1mry22OCxHlZ6K3TdIvoT2aJYjXMdOqi27 -QNsSFArvfRQcYCyul0RMhnPZeJ6Jn6GCmjFdbMo1AoGAK2Xm4FvNJ0Msrj3l/kMU -qfWJOcMuLar4ZpNOpfbn/Rqs4Kyi+dfhGtDboMZ8s/g1N+v20dQiDbnzwxkKc24M -exxozcL6zfAXymMj/hIX7jElBInpmx0fLF2bA0zxYKh0kxlRQQU2Ubg3lirCxhnB -RvHKN79wqus8UCYFe9DMb2UCgYAPsphqFCR3Yq4yWVfSZHgcUgkeKdE8pzdr1Ldb -lyZt+y07cr2PFcFEaWQZAnmTm7WLQyVhx6oKHO3qbugvXodpkGYovdI8SL+tWEzQ -bzTuvBNo4lAzCbyZxO3tw4qZ8u2ghMrTCxEu9FiPeNdbcc1iiDOzgyf6U6WgIDeX -ZG8xaQKBgQCDZRhtRgLi3swOaEUbEZAcTTixlr0UvMo3nnZpIexNDkEtmmzJ0DL6 -FyCrHocx9ZZsvnW/XuMg4Zy6R1lZkaUWpli/L41V77BwwmRSpfrgoVvV+rQ9c029 -fPGkKbosElxAafPjAwcQUjxZCaXEclVJ/mjkvXogCL0luhZEZnBk+A== ------END RSA PRIVATE KEY----- diff --git a/agent/test/set.js b/agent/test/set.js deleted file mode 100644 index ecc69c72342..00000000000 --- a/agent/test/set.js +++ /dev/null @@ -1,135 +0,0 @@ -/* - * Copyright 2017 Red Hat Inc. - * - * 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. - */ -'use strict'; - -var assert = require('assert'); -var util = require('util'); -var set = require('../lib/set.js'); -var myutils = require('../lib/utils.js'); - -function numeric_compare(a, b) { - return a - b; -} - -function name_compare(a, b) { - return myutils.string_compare(a.name, b.name); -} - -function numeric_name_compare(a, b) { - return numeric_compare(a.name.split('-')[1], b.name.split('-')[1]); -} - -function random_number(max) { - return Math.floor(Math.random() * Math.floor(max)); -} - -describe('sorted object set', function() { - it('inserts simple strings', function(done) { - var items = set.sorted_object_set(myutils.string_compare); - assert(items.insert('x')); - assert(items.insert('y')); - assert(items.insert('z')); - assert(items.insert('c')); - assert(items.insert('a')); - assert(items.insert('b')); - assert(!items.insert('x')); - assert(!items.insert('c')); - assert.deepEqual(items.to_array(), ['a', 'b', 'c', 'x', 'y', 'z']); - done(); - }); - it('removes simple strings', function(done) { - var items = set.sorted_object_set(myutils.string_compare); - assert(items.insert('x')); - assert(items.insert('y')); - assert(items.insert('z')); - assert(items.insert('c')); - assert(items.insert('a')); - assert(items.insert('b')); - assert(items.remove('x')); - assert.deepEqual(items.to_array(), ['a', 'b', 'c', 'y', 'z']); - assert(items.remove('c')); - assert(!items.remove('foo')); - assert.deepEqual(items.to_array(), ['a', 'b', 'y', 'z']); - done(); - }); - it('inserts named objects', function(done) { - var items = set.sorted_object_set(name_compare); - assert(items.insert({name:'foo',value:'a'})); - assert(items.insert({name:'bar',value:'a'})); - assert(items.insert({name:'you',value:'d'})); - assert(items.insert({name:'me',value:'a'})); - assert(!items.insert({name:'foo',value:'b'})); - assert(!items.insert({name:'you',value:'x'})); - assert.deepEqual(items.to_array(), [{name:'bar',value:'a'}, {name:'foo',value:'a'}, {name:'me',value:'a'}, {name:'you',value:'d'}]); - done(); - }); - it('removes named objects', function(done) { - var items = set.sorted_object_set(name_compare); - assert(items.insert({name:'foo',value:'a'})); - assert(items.insert({name:'bar',value:'a'})); - assert(items.insert({name:'pink',value:'elephant'})); - assert(items.insert({name:'you',value:'d'})); - assert(items.insert({name:'me',value:'a'})); - assert(items.remove({name:'pink',value:'pig'})); - assert(!items.remove({name:'pink',value:'elephant'})); - assert(!items.remove({name:'blue',value:'whale'})); - assert.deepEqual(items.to_array(), [{name:'bar',value:'a'}, {name:'foo',value:'a'}, {name:'me',value:'a'}, {name:'you',value:'d'}]); - done(); - }); - it('replaces named objects', function(done) { - var items = set.sorted_object_set(name_compare); - assert(items.insert({name:'foo',value:'a'})); - assert(items.insert({name:'bar',value:'a'})); - assert(items.insert({name:'pink',value:'elephant'})); - assert(items.insert({name:'you',value:'d'})); - assert(items.insert({name:'me',value:'a'})); - assert(items.replace({name:'pink',value:'pig'})); - assert(!items.replace({name:'blue',value:'whale'})); - assert.deepEqual(items.to_array(), [{name:'bar',value:'a'}, {name:'foo',value:'a'}, {name:'me',value:'a'}, {name:'pink',value:'pig'}, {name:'you',value:'d'}]); - done(); - }); - it('handles lots of objects', function(done) { - var items = set.sorted_object_set(numeric_name_compare); - var size = 10000; - for (var i = 0; i < size; i++) { - assert(items.insert({name:'item-'+i, colour:'red'})); - } - for (var i = 0; i < size; i++) { - assert(!items.insert({name:'item-'+random_number(size), colour:'purple'})); - } - var replaced = set.sorted_object_set(numeric_compare); - for (var i = 0; i < size; i++) { - var x = random_number(size); - replaced.insert(x); - var colour = x % 2 === 0 ? 'green' : 'blue'; - assert(items.replace({name:'item-'+x, colour:colour})); - } - var objects = items.to_array(); - var last; - for (var i = 0; i < objects.length; i++) { - assert.equal(objects[i].name, 'item-'+i); - if (last) assert(numeric_name_compare(last, objects[i]) < 0); - last = objects[i]; - if (replaced.find(i) < 0) { - assert.equal(objects[i].colour, 'red', util.format('expected item-%d to be unaltered', i)); - } else { - var colour = i % 2 === 0 ? 'green' : 'blue'; - assert.equal(objects[i].colour, colour); - } - } - done(); - }); -}); diff --git a/agent/test/tls_options.js b/agent/test/tls_options.js deleted file mode 100644 index 014c9b9afea..00000000000 --- a/agent/test/tls_options.js +++ /dev/null @@ -1,84 +0,0 @@ -/* - * Copyright 2017 Red Hat Inc. - * - * 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. - */ -'use strict'; - -var assert = require('assert'); -var path = require('path'); -var tls_options = require('../lib/tls_options.js'); - -describe('tls options', function() { - it('uses correct default path for ca', function (done) { - assert.equal(tls_options.get_paths().ca, '/etc/enmasse-certs/ca.crt'); - done(); - }); - it('uses correct default path for cert', function (done) { - assert.equal(tls_options.get_paths().cert, '/etc/enmasse-certs/tls.crt'); - done(); - }); - it('uses correct default path for key', function (done) { - assert.equal(tls_options.get_paths().key, '/etc/enmasse-certs/tls.key'); - done(); - }); - it('uses CERT_DIR in determining path for ca', function (done) { - process.env.CERT_DIR = '/tmp/foo'; - assert.equal(tls_options.get_paths().ca, '/tmp/foo/ca.crt'); - done(); - }); - it('uses CERT_DIR in determining path for cert', function (done) { - process.env.CERT_DIR = '/tmp/foo'; - assert.equal(tls_options.get_paths().cert, '/tmp/foo/tls.crt'); - done(); - }); - it('uses CERT_DIR in determining path for key', function (done) { - process.env.CERT_DIR = '/tmp/foo'; - assert.equal(tls_options.get_paths().key, '/tmp/foo/tls.key'); - done(); - }); - it('uses CA_PATH as path for ca', function (done) { - process.env.CA_PATH = '/x/y/ca.pem'; - assert.equal(tls_options.get_paths().ca, '/x/y/ca.pem'); - done(); - }); - it('uses CERT_PATH as path for cert', function (done) { - process.env.CERT_PATH = '/foo/bar/cert.pem'; - assert.equal(tls_options.get_paths().cert, '/foo/bar/cert.pem'); - done(); - }); - it('uses KEY_PATH as path for key', function (done) { - process.env.KEY_PATH = '/blah/blah/key.pem'; - assert.equal(tls_options.get_paths().key, '/blah/blah/key.pem'); - done(); - }); - it('retrieves client options', function (done) { - process.env.CA_PATH = path.resolve(__dirname,'ca-cert.pem'); - process.env.CERT_PATH = path.resolve(__dirname, 'server-cert.pem'); - process.env.KEY_PATH = path.resolve(__dirname, 'server-key.pem'); - var options = tls_options.get_client_options(); - assert.equal(options.transport, 'tls'); - assert.equal(options.rejectUnauthorized, false); - done(); - }); - it('retrieves server options', function (done) { - process.env.CA_PATH = path.resolve(__dirname,'ca-cert.pem'); - process.env.CERT_PATH = path.resolve(__dirname, 'server-cert.pem'); - process.env.KEY_PATH = path.resolve(__dirname, 'server-key.pem'); - var options = tls_options.get_server_options(); - assert.equal(options.transport, 'tls'); - assert.equal(options.rejectUnauthorized, true); - assert.equal(options.requestCert, true); - done(); - }); -}); diff --git a/agent/test/topic_tracker.js b/agent/test/topic_tracker.js deleted file mode 100644 index c21ee0db190..00000000000 --- a/agent/test/topic_tracker.js +++ /dev/null @@ -1,86 +0,0 @@ -/* - * Copyright 2018 Red Hat Inc. - * - * 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. - */ -'use strict'; - -var assert = require('assert'); -var topic_tracker = require('../lib/topic_tracker.js'); -var podgroup = require('../lib/podgroup.js'); - -function dummy (details) { return details; } - -function MockTopic (name) { - this.name = name; - this.pods = podgroup(dummy); -} - -MockTopic.prototype.empty = function () { - return this.pods.empty(); -}; - -MockTopic.prototype.close = function () { - return this.pods.close(); -}; - -function create_topic (name) { - return new MockTopic(name); -} - -describe('topic tracker', function() { - it('adds new pods', function(done) { - var topics = {}; - var tracker = topic_tracker(topics, create_topic); - tracker([{name:'foo', value:'bar', annotations:{address:'a'}, ready:'True', phase:'Running'}]); - assert(topics.a); - assert.equal(topics.a.pods.pods.foo.name, 'foo'); - assert.equal(topics.a.pods.pods.foo.value, 'bar'); - done(); - }); - it('removes deleted pods from the same topic', function(done) { - var topics = {}; - var tracker = topic_tracker(topics, create_topic); - tracker([{name:'foo', value:'bar', annotations:{address:'a'}, ready:'True', phase:'Running'}, - {name:'baz', value:'boo', annotations:{address:'a'}, ready:'True', phase:'Running'}]); - assert(topics.a); - assert.equal(topics.a.pods.pods.foo.name, 'foo'); - assert.equal(topics.a.pods.pods.foo.value, 'bar'); - assert.equal(topics.a.pods.pods.baz.name, 'baz'); - assert.equal(topics.a.pods.pods.baz.value, 'boo'); - tracker([{name:'baz', value:'boo', annotations:{address:'a'}, ready:'True', phase:'Running'}]); - assert(topics.a); - assert.equal(topics.a.pods.pods.foo, undefined); - assert.equal(topics.a.pods.pods.baz.name, 'baz'); - assert.equal(topics.a.pods.pods.baz.value, 'boo'); - done(); - }); - it('removes deleted pods from different topics', function(done) { - var topics = {}; - var tracker = topic_tracker(topics, create_topic); - tracker([{name:'foo', value:'bar', annotations:{address:'a'}, ready:'True', phase:'Running'}, - {name:'baz', value:'boo', annotations:{address:'b'}, ready:'True', phase:'Running'}]); - assert(topics.a); - assert.equal(topics.a.pods.pods.foo.name, 'foo'); - assert.equal(topics.a.pods.pods.foo.value, 'bar'); - assert(topics.b); - assert.equal(topics.b.pods.pods.baz.name, 'baz'); - assert.equal(topics.b.pods.pods.baz.value, 'boo'); - tracker([{name:'baz', value:'boo', annotations:{address:'b'}, ready:'True', phase:'Running'}]); - assert(topics.a === undefined); - assert(topics.b); - assert.equal(topics.b.pods.pods.baz.name, 'baz'); - assert.equal(topics.b.pods.pods.baz.value, 'boo'); - done(); - }); -}); diff --git a/agent/test/utils.js b/agent/test/utils.js deleted file mode 100644 index 9ba1a2f83fb..00000000000 --- a/agent/test/utils.js +++ /dev/null @@ -1,383 +0,0 @@ -/* - * Copyright 2017 Red Hat Inc. - * - * 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. - */ -'use strict'; - -var assert = require('assert'); -var myutils = require('../lib/utils.js'); - -describe('replace', function() { - it('updates matching string', function(done) { - var list = ['a', 'b', 'c']; - assert.equal(myutils.replace(list, 'x', function (o) { return o === 'b'}), true); - assert.deepEqual(list, ['a', 'x', 'c']); - done(); - }); - it('returns false when there is no match', function(done) { - var list = ['a', 'b', 'c']; - assert.equal(myutils.replace(list, 'x', function (o) { return o === 'foo'}), false); - assert.deepEqual(list, ['a', 'b', 'c']); - done(); - }); -}); - -describe('merge', function() { - it('combines fields for multiple objects', function(done) { - var obj = myutils.merge({'foo':10}, {'bar':9}); - assert.deepEqual(obj, {'foo':10, 'bar':9}); - done(); - }); -}); - -describe('kubernetes_name', function () { - it ('removes invalid chars', function (done) { - var invalid = '!"£$%^&*()_?><~#@\':;`/\|ABCDEFGHIJKLMNOPQRSTUVWXYZ'; - var input = 'a!b"c£d$e%f^g&h*i()j_?> 63); - var name = myutils.kubernetes_name(too_long).split(".")[0]; - assert(name.length <= 63); - done(); - }); - it ('ensures names for similar input over 63 chars are unique', function (done) { - var long_a = 'abcdefghiklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789-xxxxxxa'; - var long_b = 'abcdefghiklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789-xxxxxxa'; - assert(long_a.length > 63); - assert(long_b.length > 63); - var name_a = myutils.kubernetes_name(long_a).split("."); - var name_b = myutils.kubernetes_name(long_b).split("."); - assert(name_a[0].length <= 63); - assert(name_b[0].length <= 63); - assert.equal(name_a[0], name_b[0]); - assert(name_a[1].length <= 63); - assert(name_b[1].length <= 63); - assert.notEqual(name_a[1], name_b[1]); - done(); - }); - it ('truncates names with special chars over 64 chars of length', function (done) { - var too_long = 'a!"£$%^&*()_+=-bcdefghiklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789-'; - assert(too_long.length > 63); - var name = myutils.kubernetes_name(too_long).split(".")[0]; - assert(name.length <= 63); - var also_too_long = 'a!"£$%^&*()_+==bcdefghiklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789-'; - var another_name = myutils.kubernetes_name(also_too_long).split(".")[0]; - assert(another_name.length <= 63); - assert.notEqual(name, another_name); - done(); - }); -}); - -describe('serialize', function () { - var SerializeTester = function(initial_failures = 0) { - this.in_use = false; - this.calls = 0; - this.success_count = 0; - this.failure_count = 0; - this.initial_failures = initial_failures; - }; - - SerializeTester.prototype.one_at_a_time = function() { - assert.equal(this.in_use, false, 'function already in use'); - this.in_use = true; - this.calls++; - var self = this; - return new Promise(function (resolve, reject) { - setTimeout(function () { - self.in_use = false; - if (self.initial_failures && self.initial_failures > self.failure_count) { - self.failure_count++; - reject(); - } else { - self.success_count++; - resolve(); - } - }, 10); - }); - }; - - it ('ensures a new invocation is not made until the previous one has finished', function (done) { - var s = new SerializeTester(); - var f = myutils.serialize(s.one_at_a_time.bind(s)); - for (var i = 0; i < 10; i++) { - f(); - } - setTimeout(function () { - assert(s.calls > 1); - done(); - }, 500); - }); - it ('handles invocations at different times', function (done) { - var s = new SerializeTester(); - var f = myutils.serialize(s.one_at_a_time.bind(s)); - setTimeout(f, 5); - setTimeout(f, 15); - setTimeout(f, 16); - setTimeout(f, 20); - setTimeout(f, 28); - - setTimeout(function () { - assert(s.calls > 1); - done(); - }, 100); - }); - it ('failures_rerun', function (done) { - var s = new SerializeTester(1); - var f = myutils.serialize(s.one_at_a_time.bind(s), 10); - setTimeout(f, 5); - - setTimeout(function () { - assert(s.success_count === 1); - assert(s.failure_count === 1); - done(); - }, 100); - }); - it ('rerun_cancelled', function (done) { - var s = new SerializeTester(1); - var f = myutils.serialize(s.one_at_a_time.bind(s), 50); - setTimeout(f, 5); - setTimeout(f, 10); - - setTimeout(function () { - assert(s.success_count === 2); - assert(s.failure_count === 1); - done(); - }, 100); - }); -}); - -describe('changes', function () { - function compare_by_name (a, b) { - return myutils.string_compare(a.name, b.name); - } - function equal_properties (a, b) { - for (let k in a) { - if (a[k] !== b[k]) return false; - } - for (let k in b) { - if (b[k] !== a[k]) return false; - } - return true; - } - - it('returns undefined for identical lists', function(done) { - let c = myutils.changes([{name:'a',type:'foo'},{name:'c',type:'foo'}], [{name:'a',type:'foo'},{name:'c',type:'foo'}], compare_by_name, equal_properties); - assert(c === undefined); - done(); - }); - it('detected when single item added in middle', function(done) { - let c = myutils.changes([{name:'a',type:'foo'},{name:'c',type:'foo'}], [{name:'a',type:'foo'},{name:'b',type:'foo'},{name:'c',type:'foo'}], compare_by_name, equal_properties); - assert(c !== undefined); - assert.equal(c.added.length, 1); - assert.equal(c.added[0].name, 'b'); - assert.equal(c.removed.length, 0); - assert.equal(c.modified.length, 0); - done(); - }); - it('detected when multiple items added in middle', function(done) { - let c = myutils.changes([{name:'a',type:'foo'},{name:'c',type:'foo'}], [{name:'a',type:'foo'},{name:'b',type:'foo'},{name:'bar',type:'foo'},{name:'c',type:'foo'}], compare_by_name, equal_properties); - assert(c !== undefined); - assert.equal(c.added.length, 2); - assert.equal(c.added[0].name, 'b'); - assert.equal(c.added[1].name, 'bar'); - assert.equal(c.removed.length, 0); - assert.equal(c.modified.length, 0); - done(); - }); - it('detected when multiple items added in middle and at end', function(done) { - let c = myutils.changes([{name:'a',type:'foo'},{name:'c',type:'foo'}], [{name:'a',type:'foo'},{name:'b',type:'foo'},{name:'bar',type:'foo'},{name:'c',type:'foo'},{name:'d',type:'foo'},{name:'e',type:'foo'}], compare_by_name, equal_properties); - assert(c !== undefined); - assert.equal(c.added.length, 4); - assert.equal(c.added[0].name, 'b'); - assert.equal(c.added[1].name, 'bar'); - assert.equal(c.added[2].name, 'd'); - assert.equal(c.added[3].name, 'e'); - assert.equal(c.removed.length, 0); - assert.equal(c.modified.length, 0); - done(); - }); - it('detected when single item deleted from middle', function(done) { - let c = myutils.changes([{name:'a',type:'foo'},{name:'b',type:'foo'},{name:'c',type:'foo'}], [{name:'a',type:'foo'},{name:'c',type:'foo'}], compare_by_name, equal_properties); - assert(c !== undefined); - assert.equal(c.added.length, 0); - assert.equal(c.removed.length, 1); - assert.equal(c.removed[0].name, 'b'); - assert.equal(c.modified.length, 0); - done(); - }); - it('detected when single item deleted from start', function(done) { - let c = myutils.changes([{name:'a',type:'foo'},{name:'b',type:'foo'},{name:'c',type:'foo'}], [{name:'b',type:'foo'},{name:'c',type:'foo'}], compare_by_name, equal_properties); - assert(c !== undefined); - assert.equal(c.added.length, 0); - assert.equal(c.removed.length, 1); - assert.equal(c.removed[0].name, 'a'); - assert.equal(c.modified.length, 0); - done(); - }); - it('detected when single item deleted from end', function(done) { - let c = myutils.changes([{name:'a',type:'foo'},{name:'b',type:'foo'},{name:'c',type:'foo'}], [{name:'a',type:'foo'},{name:'b',type:'foo'}], compare_by_name, equal_properties); - assert(c !== undefined); - assert.equal(c.added.length, 0); - assert.equal(c.removed.length, 1); - assert.equal(c.removed[0].name, 'c'); - assert.equal(c.modified.length, 0); - done(); - }); - it('detected when multiple items deleted', function(done) { - let c = myutils.changes([{name:'a',type:'foo'},{name:'b',type:'foo'},{name:'c',type:'foo'},{name:'d',type:'foo'},{name:'e',type:'foo'},{name:'f',type:'foo'}], [{name:'b',type:'foo'},{name:'d',type:'foo'}], compare_by_name, equal_properties); - assert(c !== undefined); - assert.equal(c.added.length, 0); - assert.equal(c.removed.length, 4); - assert.equal(c.removed[0].name, 'a'); - assert.equal(c.removed[1].name, 'c'); - assert.equal(c.removed[2].name, 'e'); - assert.equal(c.removed[3].name, 'f'); - assert.equal(c.modified.length, 0); - done(); - }); - it('detected when multiple items deleted and added', function(done) { - let c = myutils.changes([{name:'a',type:'foo'},{name:'b',type:'foo'},{name:'c',type:'foo'},{name:'d',type:'foo'},{name:'e',type:'foo'},{name:'f',type:'foo'}], [{name:'b',type:'foo'},{name:'bar',type:'foo'},{name:'baz',type:'foo'},{name:'d',type:'foo'},{name:'egg',type:'foo'},{name:'h',type:'foo'}], compare_by_name, equal_properties); - assert(c !== undefined); - assert.equal(c.added.length, 4); - assert.equal(c.added[0].name, 'bar'); - assert.equal(c.added[1].name, 'baz'); - assert.equal(c.added[2].name, 'egg'); - assert.equal(c.added[3].name, 'h'); - assert.equal(c.removed.length, 4); - assert.equal(c.removed[0].name, 'a'); - assert.equal(c.removed[1].name, 'c'); - assert.equal(c.removed[2].name, 'e'); - assert.equal(c.removed[3].name, 'f'); - assert.equal(c.modified.length, 0); - done(); - }); - it('detected when single item modified', function(done) { - let c = myutils.changes([{name:'a',type:'foo'},{name:'b',type:'foo'},{name:'c',type:'foo'}], [{name:'a',type:'foo'},{name:'b',type:'bar'},{name:'c',type:'foo'}], compare_by_name, equal_properties); - assert(c !== undefined); - assert.equal(c.modified.length, 1); - assert.equal(c.modified[0].name, 'b'); - assert.equal(c.modified[0].type, 'bar'); - done(); - }); - it('detects additions, deletions and modifications', function(done) { - let c = myutils.changes([{name:'a',type:'foo'},{name:'b',type:'foo'},{name:'c',type:'foo'},{name:'d',type:'foo'},{name:'e',type:'foo'},{name:'f',type:'foo'}], [{name:'b',type:'bar'},{name:'bar',type:'foo'},{name:'baz',type:'foo'},{name:'d',type:'foo'},{name:'egg',type:'foo'},{name:'h',type:'foo'}], compare_by_name, equal_properties); - assert(c !== undefined); - assert.equal(c.added.length, 4); - assert.equal(c.added[0].name, 'bar'); - assert.equal(c.added[1].name, 'baz'); - assert.equal(c.added[2].name, 'egg'); - assert.equal(c.added[3].name, 'h'); - assert.equal(c.removed.length, 4); - assert.equal(c.removed[0].name, 'a'); - assert.equal(c.removed[1].name, 'c'); - assert.equal(c.removed[2].name, 'e'); - assert.equal(c.removed[3].name, 'f'); - assert.equal(c.modified.length, 1); - assert.equal(c.modified[0].name, 'b'); - assert.equal(c.modified[0].type, 'bar'); - done(); - }); -}); - -describe('coalesce', function () { - it ('merges frequent calls', function (done) { - var count = 0; - function test() { - assert.equal(count, 5); - done(); - } - var f = myutils.coalesce(test, 10, 1000); - for (var i = 0; i < 5; i++) { - count++; - f(); - } - }); - it ('does not exceed max delay', function (done) { - var triggered = 0; - var fired = 0; - function fire() { - fired++; - if (triggered === 10) { - assert.equal(fired, 2); - done(); - } - } - var f = myutils.coalesce(fire, 200, 500); - var interval = setInterval(function () { - f(); - if (++triggered === 10) { - clearInterval(interval); - } - }, 100); - }); -}); - -describe('parseToBytes', function () { - it ('parses size to bytes', function (done) { - assert.strictEqual( myutils.parseToBytes("1024B"), 1024); - assert.strictEqual( myutils.parseToBytes("256.5 B"), 256.5); - assert.strictEqual( myutils.parseToBytes("400 KB"), 409600); - assert.strictEqual( myutils.parseToBytes("10.5Mib"), 11010048); - assert.strictEqual( myutils.parseToBytes("10.5Mb"), 11010048); - assert.strictEqual( myutils.parseToBytes("4Gb"), 4294967296); - assert.strictEqual( myutils.parseToBytes("1024" ), 1024); - - assert.strictEqual( myutils.parseToBytes("35 XYZ"), 35); - assert.strictEqual( myutils.parseToBytes("OnlyUnits"), 0); - assert.strictEqual( myutils.parseToBytes("MB"), 0); - done(); - }); -}); diff --git a/agent/testlib/mock_authservice.js b/agent/testlib/mock_authservice.js deleted file mode 100644 index 015c74cbcb3..00000000000 --- a/agent/testlib/mock_authservice.js +++ /dev/null @@ -1,54 +0,0 @@ -/* - * Copyright 2017 Red Hat Inc. - * - * 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. - */ -'use strict'; - -var rhea = require('rhea'); - -function is_valid_user(username, password) { - return username === password.split().reverse().join(''); -} - -function MockAuthService(f, groups) { - this.groups = groups; - var self = this; - this.container = rhea.create_container({id:'mock-auth-service'}); - this.container.sasl_server_mechanisms.enable_plain(f || is_valid_user); - this.container.sasl_server_mechanisms.enable_anonymous(); - this.container.on('connection_open', function (context) { - if (self.groups) { - var properties = context.connection.local.open.properties || {}; - properties.groups = self.groups; - context.connection.local.open.properties = properties; - } - context.connection.close(); - }); - this.container.on('disconnected', function (context) {}); -} - -MockAuthService.prototype.listen = function (options) { - this.server = this.container.listen(options || {port:0}); - var self = this; - this.server.on('listening', function () { - self.port = self.server.address().port; - }); - return this.server; -}; - -MockAuthService.prototype.close = function (callback) { - if (this.server) this.server.close(callback); -}; - -module.exports = MockAuthService; diff --git a/agent/testlib/mock_broker.js b/agent/testlib/mock_broker.js deleted file mode 100644 index 0c0e6c9b761..00000000000 --- a/agent/testlib/mock_broker.js +++ /dev/null @@ -1,515 +0,0 @@ -/* - * Copyright 2017 Red Hat Inc. - * - * 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. - */ -'use strict'; - -var assert = require('assert'); -var events = require('events'); -var util = require('util'); -var rhea = require('rhea'); -var myutils = require('../lib/utils.js'); -var log = require('../lib/log.js').logger(); - -var counters = {}; - -function next(name) { - if (counters[name] === undefined) { - counters[name] = 1; - } else { - ++counters[name]; - } - return counters[name]; -} - -function generate_id(base) { - return base + '-' + next(base); -} - -function find(array, predicate) { - var results = array.filter(predicate); - if (results.length > 0) return results[0]; - else return undefined; -} - -function match_source_address(link, address) { - return link && link.local && link.local.attach && link.local.attach.source - && link.local.attach.source.value[0].toString() === address; -} - -var address_setting_names = [ - "DLA", - "expiryAddress", - "expiryDelay", - "lastValueQueue", - "deliveryAttempts", - "maxSizeBytes", - "pageSizeBytes", - "pageMaxCacheSize", - "redeliveryDelay", - "redeliveryMultiplier", - "maxRedeliveryDelay", - "redistributionDelay", - "sendToDLAOnNoRoute", - "addressFullMessagePolicy", - "slowConsumerThreshold", - "slowConsumerCheckPeriod", - "slowConsumerPolicy", - "autoCreateJmsQueues", - "autoDeleteJmsQueues", - "autoCreateJmsTopics", - "autoDeleteJmsTopics", - "autoCreateQueues", - "autoDeleteQueues", - "autoCreateAddresses", - "autoDeleteAddresses" -]; - -function get_address_settings (args) { - var settings = {}; - for (var i = 0; i < args.length; i++) { - settings[address_setting_names[i]] = args[i]; - } - return settings; -} - -function MockBroker (name) { - this.name = name; - this.objects = []; - this.container = rhea.create_container({id:this.name}); - this.container.on('message', this.on_message.bind(this)); - var self = this; - this.container.on('connection_open', function (context) { - self.emit('connected', context); - }); - this.container.on('connection_close', function (context) { - self.emit('disconnected', context); - }); - this.container.on('sender_open', function(context) { - if (context.sender.source.dynamic) { - var id = self.container.generate_uuid(); - context.sender.set_source({address:id}); - } else { - context.sender.set_source({address:context.sender.remote.attach.source.address}); - } - }); - - var self = this; - this.objects.push({ - resource_id: 'broker', - getGlobalMaxSize : function () { - return self.global_max_size; - }, - getAddressNames : function () { - return self.list_addresses().map(function (a) { return a.name; }); - }, - getQueueNames : function () { - return self.list_queues().map(function (a) { return a.name; }); - }, - createQueue : function (address, routingType, name, filter, durable, maxConsumers, purgeOnNoConsumers, autoCreateAddress) { - if (self.objects.some(function (o) { return o.type === 'queue' && o.name === name})) { - throw new Error('queue ' + name + ' already exists!'); - } else { - if (autoCreateAddress) { - if (!self.objects.some(function (o) { return o.type === 'address' && o.name === address; })) { - self.add_address(address, false, 0, [name]); - } - } else { - if (!self.objects.some(function (o) { return o.type === 'address' && o.name === address; })) { - throw new Error('No such address ' + address + ' for queue ' + name); - } - } - self.add_queue(name, {'durable':durable, 'routingType':routingType, 'maxConsumers':maxConsumers, 'address':address, 'filter':filter, 'purgeOnNoConsumers':purgeOnNoConsumers}); - } - }, - createAddress : function (name, routingTypes) { - if (self.objects.some(function (o) { return o.type === 'address' && o.name === name; })) { - throw new Error('address ' + name + ' already exists!'); - } else { - self.add_address(name, true); - } - }, - destroyQueue : function (name) { - if (myutils.remove(self.objects, function (o) { return o.type === 'queue' && o.name === name; }) !== 1) { - throw new Error('error deleting queue ' + name); - } else { - function is_queue_address(o) { - return o.type === 'address' && o.name === name - && o.routingTypesAsJSON[0] === 'ANYCAST' - && o.queueNames.length === 1 - && o.queueNames[0] === name; - } - myutils.remove(self.objects, is_queue_address); - } - }, - deleteAddress : function (name) { - if (myutils.remove(self.objects, function (o) { return o.type === 'address' && o.name === name; }) !== 1) { - throw new Error('error deleting address ' + name); - } - }, - addAddressSettings : function () { - if (self.objects.some(function (o) { return o.type === 'address_settings' && o.name === arguments[0]; })) { - throw new Error('address settings for ' + o.match + ' already exists!'); - } else { - self.add_address_settings(arguments[0], get_address_settings(Array.prototype.slice.call(arguments, 1))); - } - }, - removeAddressSettings : function (match) { - if (myutils.remove(self.objects, function (o) { return o.type === 'address_settings' && o.name === match; }) !== 1) { - throw new Error('error deleting address settings ' + match); - } - }, - getAddressSettingsAsJSON : function (match) { - var items = self.get('address_settings'); - return JSON.stringify({data:items, count:items.length}); - }, - listAddresses : function () { - var items = self.get('address'); - return JSON.stringify({data:items, count:items.length}); - }, - listQueues : function () { - var items = self.get('queue'); - return JSON.stringify({data:items, count:items.length}); - }, - listConnectionsAsJSON : function () { - return JSON.stringify(self.get('connection')); - }, - listSessionsAsJSON : function (connectionID) { - return JSON.stringify(self.get('session').filter(function (s) { return s.connectionID === connectionID; })); - }, - listProducersInfoAsJSON : function () { - return JSON.stringify(self.get('producer')); - }, - listAllConsumersAsJSON : function () { - return JSON.stringify(self.get('consumer')); - }, - createConnectorService : function (name, ignore, params) { - if (self.objects.some(function (o) { return o.type === 'connector' && o.name === name; })) { - throw new Error('connector for ' + name + ' already exists!'); - } else { - self.add_connector(name, {name: name, source: params.sourceAddress, target: params.targetAddress}); - } - }, - destroyConnectorService : function (name) { - if (myutils.remove(self.objects, function (o) { return o.type === 'connector' && o.name === name; }) !== 1) { - throw new Error('error deleting connector ' + name); - } - }, - getConnectorServices : function () { - return self.get('connector').map(function (c) { return c.name; }); - } - }); -} - -util.inherits(MockBroker, events.EventEmitter); - -MockBroker.prototype.listen = function (port) { - this.server = this.container.listen({port:port || 0}); - var self = this; - this.server.on('listening', function () { - self.port = self.server.address().port; - }); - return this.server; -}; - -MockBroker.prototype.connect = function (port) { - return this.container.connect({port:port, properties:{product:'apache-activemq-artemis'}}); -}; - -MockBroker.prototype.close = function (callback) { - if (this.server) this.server.close(callback); -}; - -MockBroker.prototype.on_message = function (context) { - var request = context.message; - var resource = context.message.application_properties._AMQ_ResourceName; - var operation = context.message.application_properties._AMQ_OperationName; - var params = request.body ? JSON.parse(request.body) : []; - var target = find(this.objects, function (o) { return o.resource_id === resource; }); - var reply_link = context.connection.find_sender(function (s) { return match_source_address(s, request.reply_to); }); - try { - if (target) { - if (target[operation]) { - var result = target[operation].apply(target, params); - //console.log('invocation of ' + operation + ' on ' + resource + ' returned ' + JSON.stringify(result)); - if (reply_link) { - reply_link.send({application_properties:{_AMQ_OperationSucceeded:true}, body:JSON.stringify([result])}); - } - } else { - throw new Error('no such operation: ' + operation + ' on' + resource); - } - } else { - throw new Error('no such resource: ' + resource); - } - } catch (e) { - console.log('invocation of ' + operation + ' on ' + resource + ' failed: ' + e); - if (reply_link) { - reply_link.send({application_properties:{_AMQ_OperationSucceeded:false}, body:util.format('%s', e)}); - } - } -}; - -var prefixes = { - 'is': false, - 'get': 0 -}; - -function Resource (name, type, accessors, properties) { - this.name = name; - this.type = type; - this.resource_id = type + '.' + name; - var initial_values = properties || {}; - if (accessors === undefined) { - for (var key in properties) { - this[key] = properties[key]; - } - } else { - var self = this; - accessors.forEach(function (accessor) { - for (var prefix in prefixes) { - if (accessor.indexOf(prefix) === 0) { - var property = accessor.charAt(prefix.length).toLowerCase() + accessor.substr(prefix.length + 1); - self[property] = initial_values[property] || prefixes[prefix]; - self[accessor] = function () { return self[property]; }; - } - } - }); - } -} - -function add_id(object, type, id_name) { - var field = id_name || type + 'ID'; - if (object[field] === undefined) { - object[field] = generate_id(type); - } - return object[field]; -} - -MockBroker.prototype.add_resource_with_id = function (type, properties) { - var id = add_id(properties, type); - this.objects.push(new Resource(id, type, undefined, properties)); - return id; -}; - -MockBroker.prototype.add_connection_child_resource = function (type, connectionID, objects) { - if (objects !== undefined) { - var self = this; - objects.forEach(function (o) { - o.connectionID = connectionID; - self.add_resource_with_id(type, o) - }); - } -}; - -MockBroker.prototype.add_connection = function (properties, senders, receivers, sessions) { - var connectionID = this.add_resource_with_id('connection', properties); - this.add_connection_child_resource('producer', connectionID, senders); - this.add_connection_child_resource('consumer', connectionID, receivers); - this.add_connection_child_resource('session', connectionID, sessions); -}; - -var queue_accessors = ['isTemporary', 'isDurable', 'getMessageCount', 'getConsumerCount', 'getMessagesAdded', - 'getDeliveringCount', 'getMessagesAcked', 'getMessagesExpired', 'getMessagesKilled', - 'getAddress', 'getRoutingType']; -var address_accessors = ['getRoutingTypesAsJSON','getRoutingTypes','getNumberOfMessages','getQueueNames', 'getMessageCount']; - -MockBroker.prototype.add_queue = function (name, properties) { - var queue = new Resource(name, 'queue', queue_accessors, properties); - this.objects.push(queue); - return queue; -}; - -MockBroker.prototype.add_address = function (name, is_multicast, messages, queue_names) { - var address = new Resource(name, 'address', address_accessors, { - queueNames: queue_names || [], - numberOfMessages: messages || 0, - routingTypesAsJSON: [is_multicast ? 'MULTICAST' : 'ANYCAST'], - routingTypes: [is_multicast ? 'MULTICAST' : 'ANYCAST'], - messageCount: messages || 0 - }); - this.objects.push(address); - return address; -}; - -MockBroker.prototype.add_address_settings = function (name, settings) { - var o = new Resource(name, 'address_settings', undefined, settings); - this.objects.push(o); - return o; -}; - -MockBroker.prototype.add_queue_address = function (name, properties) { - this.add_queue(name, myutils.merge({address: name, routingType:'ANYCAST'}, properties)); - var addr = this.add_address(name, false, 0, [name]); - return addr; -}; - -MockBroker.prototype.add_topic_address = function (name, subscribers, messages) { - for (var s in subscribers) { - this.add_queue(s, myutils.merge({address: name, routingType:'MULTICAST'}, subscribers[s])); - } - return this.add_address(name, true, messages, Object.keys(subscribers)); -}; - -MockBroker.prototype.add_connector = function (name, connector) { - var o = new Resource(name, 'connector', undefined, connector); - this.objects.push(o); - return o; -}; - -MockBroker.prototype.get = function (type) { - return this.objects.filter(function (o) { return o.type === type; }); -}; - -MockBroker.prototype.list_queues = function () { - return this.get('queue'); -}; - -MockBroker.prototype.list_addresses = function () { - return this.get('address'); -}; - -MockBroker.prototype.get_pod_descriptor = function () { - return { - ready : (this.port === undefined ? 'False' : 'True'), - phase : 'Running', - name : this.name, - host : 'localhost', - ports : { - broker : { - amqp: this.port - } - } - }; -}; - - -MockBroker.prototype.get_pod_definition = function () { - return { - metadata: { - name: this.name, - labels: { - role: 'broker' - } - }, - spec: { - containers: [ - { - name: 'broker', - ports: [ - { - name: 'amqp', - containerPort: this.port - } - ] - } - ] - }, - status: { - podIP: '127.0.0.1', - phase: 'Running', - conditions : [ - { type: 'Initialized', status: 'True' }, - { type: 'Ready', status: (this.port === undefined ? 'False' : 'True') } - ] - } - }; -}; - -function remove(list, predicate) { - var removed = []; - for (var i = 0; i < list.length; ) { - if (predicate(list[i])) { - removed.push(list.splice(i, 1)[0]); - } else { - i++; - } - } - return removed; -} - -MockBroker.prototype.verify_queue = function (addresses, queues, name) { - var results = remove(queues, function (o) { return o.name === name; }); - assert.equal(results.length, 1, util.format('queue %s not found', name)); - assert.equal(results[0].name, name); - results = remove(addresses, function (o) { return o.name === name; }); - assert.equal(results.length, 1, util.format('address %s not found', name)); - assert.equal(results[0].name, name); - assert.equal(results[0].routingTypesAsJSON[0], 'ANYCAST'); - assert.equal(results[0].queueNames[0], name); - return results[0]; -}; - -MockBroker.prototype.verify_topic = function (addresses, name) { - var results = remove(addresses, function (o) { return o.name === name; }); - assert.equal(results.length, 1, util.format('address %s not found', name)); - assert.equal(results[0].name, name); - assert.equal(results[0].routingTypesAsJSON[0], 'MULTICAST'); - return results[0]; -}; - -MockBroker.prototype.verify_subscription = function (queues, name, topic) { - var results = remove(queues, function (o) { return o.name === name; }); - assert.equal(results.length, 1, util.format('subscription queue %s not found', name)); - assert.equal(results[0].name, name); - assert.equal(results[0].address, topic); - return results[0]; -}; - -function is_mgmt_temp(entity) { - return entity.name.indexOf("activemq.management.tmpreply") > -1; -} - -MockBroker.prototype.verify_addresses = function (expected) { - var addresses = this.list_addresses().filter(a => !is_mgmt_temp(a)); - var queues = this.list_queues().filter(q => !is_mgmt_temp(q)); - for (var i = 0; i < expected.length; i++) { - if (expected[i].type === 'queue') { - this.verify_queue(addresses, queues, expected[i].address); - } else if (expected[i].type === 'topic') { - this.verify_topic(addresses, expected[i].address); - } else if (expected[i].type === 'subscription') { - this.verify_subscription(queues, expected[i].address, expected[i].topic); - } - } - assert.equal(addresses.length, 0, util.format("Found addresses: %s", JSON.stringify(addresses))); - assert.equal(queues.length, 0, util.format("Found queues: %s", JSON.stringify(queues))); -}; - -MockBroker.prototype.verify_address_settings = function (expected) { - var settings = this.list_address_settings(); - for (var i = 0; i < expected.length; i++) { - var s = remove(settings, function (o) { return o.name === expected[i].match; })[0]; - assert(s, util.format('no settings object found for %s', expected[i].match)); - for (var f in expected[i]) { - if (f !== 'match') { - assert.equal(s[f], expected[i][f], util.format('address setting %s does not match got %s expected %s', f, s[f], expected[i][f])); - } - } - } - assert.equal(settings.length, 0, util.format('extra settings found: %j', settings)); -}; - -MockBroker.prototype.verify_connectors = function (expected) { - var connectors = this.get('connector'); - for (var i = 0; i < expected.length; i++) { - var s = remove(connectors, function (o) { return o.name === expected[i].name; })[0]; - assert(s, util.format('no connector object found for %s', expected[i].name)); - for (var f in expected[i]) { - assert.equal(s[f], expected[i][f], util.format('connector %s does not match got %s expected %s', f, s[f], expected[i][f])); - } - } - assert.equal(connectors.length, 0, util.format('extra connectors found: %j', connectors)); -}; - -module.exports = MockBroker; diff --git a/agent/testlib/mock_resource_server.js b/agent/testlib/mock_resource_server.js deleted file mode 100644 index a02d3ff2d13..00000000000 --- a/agent/testlib/mock_resource_server.js +++ /dev/null @@ -1,466 +0,0 @@ -/* - * Copyright 2017 Red Hat Inc. - * - * 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. - */ -'use strict'; - -var events = require('events'); -var fs = require('fs'); -var http = require('http'); -var https = require('https'); -var path = require('path'); -var url = require('url'); -var util = require('util'); - -function shift_if_matches(path, match) { - if (path[0] === match) { - path.shift(); - return true; - } else { - return false; - } -} - -function parse(request_url) { - var result = {}; - var path = url.parse(request_url).pathname.split('/'); - shift_if_matches(path, ''); - var api = path.shift(); - if (api === 'api' && path.shift() === 'v1') { - result.watch = shift_if_matches(path, 'watch'); - if (shift_if_matches(path, 'namespaces')) { - result.namespace = path.shift(); - result.type = path.shift(); - result.name = path.shift(); - return result; - } - } else if (api === 'apis') { - result.api_group = path.shift(); - result.api_version = path.shift(); - result.watch = shift_if_matches(path, 'watch'); - if (shift_if_matches(path, 'namespaces')) { - result.namespace = path.shift(); - result.type = path.shift(); - result.name = path.shift(); - return result; - } - } - - return undefined; -} - -function ResourceServer (read_only, externalize, internalize) { - events.EventEmitter.call(this); - this.read_only = read_only; - this.externalize = externalize || function (o) { return o; }; - this.internalize = internalize; - this.resources = {}; - this.watch_timeout = 250; - this.debug = false; - if (this.debug) { - this.on('added', function (o) { - console.log('ADDED: %j', o); - }); - this.on('deleted', function (o) { - console.log('DELETED: %j', o); - }); - } -} - - -util.inherits(ResourceServer, events.EventEmitter); - -ResourceServer.prototype.update = function (type, index, object) { - this.resources[type][index] = this.internalize ? this.codec.internalize(object) : object; -}; - -ResourceServer.prototype.push = function (type, object) { - this.resources[type].push(this.internalize ? this.codec.internalize(object) : object); - this.emit('added', this.externalize(object)); -}; - -function relativepath(name) { - return path.resolve(__dirname, name); -} - -function error(request, response, status_code, message) { - response.statusCode = status_code; - var text = message || http.STATUS_CODES[status_code]; - response.end(util.format('%s: %s %s', text, request.method, request.url)); -} - -ResourceServer.prototype.listen = function (port, callback) { - var self = this; - var options = { - key: fs.readFileSync(relativepath('../test/server-key.pem')), - cert: fs.readFileSync(relativepath('../test/server-cert.pem')) - }; - this.server = https.createServer(options, function (request, response) { - var path = parse(request.url); - if (self.debug) console.log('%s %s => %j', request.method, request.url, path); - if (path === undefined) { - error(request, response, 404); - } else if (self.failure_injector && self.failure_injector.match(path)) { - error(request, response, self.failure_injector.code(path)); - } else { - if (path.watch && request.method === 'GET') { - self.watch_resources(request, response, path.type); - } else if (request.method === 'GET') { - if (path.name) { - self.get_resource(request, response, path.type, path.name); - } else { - self.get_resources(request, response, path.type); - } - } else if (request.method === 'PUT' && path.name) { - self.put_resource(request, response, path.type, path.name); - } else if (request.method === 'DELETE' && path.name) { - self.delete_resource(request, response, path.type, path.name); - } else if (request.method === 'POST') { - self.post_resource(request, response, path.type); - } else { - error(request, response, 405); - } - } - }); - this.server.listen(port || 0, function () { - self.port = self.server.address().port; - if (callback) callback(); - }); - return this.server; -}; - -ResourceServer.prototype.close = function (callback) { - this.clear(); - this.server.close(callback); -}; - -ResourceServer.prototype.clear = function () { - this.resources = {}; -}; - -ResourceServer.prototype.add_resource = function (type, resource) { - if (this.resource_initialiser) { - this.resource_initialiser(resource); - } - if (this.resources[type] === undefined) { - this.resources[type] = [] - } - this.resources[type].push(resource); - this.emit('added', this.externalize(resource)); -}; - -ResourceServer.prototype.resource_modified = function (type, resource) { - this.emit('modified', this.externalize(resource)); -}; - -ResourceServer.prototype.remove_resource = function (type, resource) { - var i = this.resources[type].indexOf(resource); - if (i >= 0) { - this.resources[type].splice(i, 1); - this.emit('deleted', this.externalize(resource)); - } -}; - -ResourceServer.prototype.remove_resource_by_name = function (type, name) { - for (var i = 0; i < this.get_resources_type(type).length; i++) { - if (this.resources[type][i].metadata.name === name) { - var removed = this.resources[type][i]; - this.resources[type].splice(i, 1); - this.emit('deleted', this.externalize(removed)); - return true; - } - } - return false; -}; - -function getLabelSelectorFn(request) { - var parsed = url.parse(request.url, true); - var selector = parsed.query.labelSelector; - if (selector) { - var labelentries = selector.split(','); - var labels = {}; - for (var i = 0; i < labelentries.length; i++) { - var labelentry = labelentries[i]; - var parts = labelentry.split('='); - labels[parts[0]] = parts[1]; - } - return function (object) { - var equal = false; - if (object.metadata.labels) { - for (var label in labels) { - if (object.metadata.labels[label] == undefined || object.metadata.labels[label] !== labels[label]) { - return false; - } - } - return true; - } - return false; - } - } else { - return function () { - return true; - } - } -} - -function Watcher (parent, response, selector) { - this.parent = parent; - this.response = response; - this.selector = selector; - this.callbacks = {}; - this.add_callback('added'); - this.add_callback('deleted'); - this.add_callback('modified'); - for (var k in this.callbacks) { - this.parent.on(k, this.callbacks[k]); - } -} - -Watcher.prototype.add_callback = function (type) { - var response = this.response; - var filter = this.selector; - this.callbacks[type] = function (resource) { - if (filter(resource)) { - response.write(JSON.stringify({type:type.toUpperCase(), object:resource}) + '\n'); - } - }; -}; - -Watcher.prototype.close = function () { - for (var k in this.callbacks) { - this.parent.removeListener(k, this.callbacks[k]); - } - this.response.end(); -} - -ResourceServer.prototype.get_resources_type = function (type) { - return this.resources[type] || []; -}; - -ResourceServer.prototype.get_resources = function (request, response, type) { - var filter = getLabelSelectorFn(request); - response.end(JSON.stringify({kind: 'List', items: this.get_resources_type(type).map(this.externalize).filter(filter)})); -}; - -ResourceServer.prototype.watch_resources = function (request, response, type) { - var filter = getLabelSelectorFn(request); - for (var i = 0; i < this.get_resources_type(type).length; i++) { - var externalized = this.resources[type].map(this.externalize)[i]; - if (filter(externalized)) { - response.write(JSON.stringify({type:'ADDED', object:externalized }) + '\n'); - } - } - var watcher = new Watcher(this, response, filter); - setTimeout(function () { watcher.close(); }, this.watch_timeout); -}; - -ResourceServer.prototype.find_resource = function (type, name) { - for (var i = 0; i < this.get_resources_type(type).length; i++) { - var externalized = this.resources[type].map(this.externalize)[i]; - if (externalized.metadata.name === name) { - return externalized; - } - } -}; - -ResourceServer.prototype.get_resource = function (request, response, type, name) { - var result = this.find_resource(type, name); - if (result) { - response.end(JSON.stringify(result)); - } else { - error(request, response, 404); - } -}; - -ResourceServer.prototype.update_resource = function (type, name, updated) { - for (var i = 0; i < this.get_resources_type(type).length; i++) { - var externalized = this.resources[type].map(this.externalize)[i]; - if (externalized.metadata.name === name) { - this.update(type, i, updated); - this.resource_modified(type, updated); - return true; - } - } - return false; -}; - -ResourceServer.prototype.add_resource_if_not_exists = function (type, resource) { - for (var i = 0; i < this.get_resources_type(type).length; i++) { - var externalized = this.resources[type].map(this.externalize)[i]; - if (externalized.metadata.name === resource.metadata.name) { - return false; - } - } - this.add_resource(type, resource); - return true; -}; - -ResourceServer.prototype.delete_resource = function (request, response, type, name) { - if (this.remove_resource_by_name(type, name)) { - response.statusCode = 200; - response.end(); - } else { - error(request, response, 404); - } -}; - -ResourceServer.prototype.put_resource = function (request, response, type, name) { - var self = this; - var bodytext = ''; - request.on('data', function (data) { bodytext += data; }); - request.on('end', function () { - var body = JSON.parse(bodytext); - if (self.update_resource(type, name, body)) { - response.statusCode = 200; - response.end(); - } else { - error(request, response, 404); - } - }); -}; - -ResourceServer.prototype.post_resource = function (request, response, type) { - var self = this; - var bodytext = ''; - request.on('data', function (data) { bodytext += data; }); - request.on('end', function () { - var body = JSON.parse(bodytext); - if (self.add_resource_if_not_exists(type, body)) { - response.statusCode = 200; - response.end(); - } else { - error(request, response, 409); - } - }); -}; - -function AddressServer() { - ResourceServer.call(this); -} - -util.inherits(AddressServer, ResourceServer); - -AddressServer.prototype.add_address_definition = function (def, name, infra_uuid, annotations, status) { - var address = {kind: 'Address', metadata: {name: name || def.address}, spec:def}; - if (annotations) { - address.metadata.annotations = annotations; - } - address.metadata.labels = {}; - if (infra_uuid) { - address.metadata.labels.infraUuid = infra_uuid; - } - address.status = status || { phase: 'Active' }; - this.add_resource('addresses', address); -}; - -AddressServer.prototype.add_address_definitions = function (defs) { - for (var i in defs) { - this.add_address_definition(defs[i]); - } -}; - -AddressServer.prototype.update_address_definition = function (def, name, infra_uuid, annotations, status) { - var address = {kind: 'Address', metadata: {name: name || def.address}, spec:def}; - if (annotations) { - address.metadata.annotations = annotations; - } - address.metadata.labels = {}; - if (infra_uuid) { - address.metadata.labels.infraUuid = infra_uuid; - } - address.status = status || { phase: 'Active' }; - this.update_resource('addresses', address.metadata.name, address); -}; - -AddressServer.prototype.add_address_plan = function (params) { - var plan = { - kind: 'AddressPlan', - metadata: {name: params.plan_name}, - spec: { - displayName: params.display_name, - shortDescription: params.shortDescription, - longDescription: params.longDescription, - displayOrder: params.displayOrder, - addressType: params.address_type, - messageTtl: params.messageTtl, - } - }; - if (params.resources) { - plan.resources = params.resources; - } - this.add_resource('addressplans', plan); -}; - -AddressServer.prototype.update_address_plan = function (params) { - var plan = { - kind: 'AddressPlan', - metadata: {name: params.plan_name}, - spec: { - displayName: params.display_name, - shortDescription: params.shortDescription, - longDescription: params.longDescription, - displayOrder: params.displayOrder, - addressType: params.address_type, - messageTtl: params.messageTtl, - } - }; - if (params.required_resources) { - plan.requiredResources = params.required_resources; - } - this.update_resource('addressplans', params.plan_name, plan); -}; - -AddressServer.prototype.add_address_space_plan = function (params) { - var plan = { - kind: 'AddressSpacePlan', - metadata: {name: params.plan_name}, - spec: { - displayName: params.display_name, - shortDescription: params.shortDescription, - longDescription: params.longDescription, - displayOrder: params.displayOrder, - addressSpaceType: params.address_space_type, - addressPlans: params.address_plans - } - }; - if (params.required_resources) { - plan.requiredResources = params.required_resources; - } - this.add_resource('addressspaceplans', plan); -}; - -AddressServer.prototype.update_address_space_plan = function(params) { - var plan = { - kind: 'AddressSpacePlan', - metadata: {name: params.plan_name}, - spec: { - displayName: params.display_name, - shortDescription: params.shortDescription, - longDescription: params.longDescription, - displayOrder: params.displayOrder, - addressSpaceType: params.address_space_type, - addressPlans: params.address_plans - } - }; - this.update_resource('addressspaceplans', params.plan_name, plan); -}; - -AddressServer.prototype.resource_initialiser = function (resource) { - if (resource.status === undefined) { - resource.status = {'phase': 'Active'}; - } -}; - -module.exports.ResourceServer = ResourceServer; -module.exports.AddressServer = AddressServer; diff --git a/agent/testlib/mock_router.js b/agent/testlib/mock_router.js deleted file mode 100644 index e5de53a0822..00000000000 --- a/agent/testlib/mock_router.js +++ /dev/null @@ -1,175 +0,0 @@ -/* - * Copyright 2017 Red Hat Inc. - * - * 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. - */ -'use strict'; - -var events = require('events'); -var util = require('util'); -var rhea = require('rhea'); -var tls_options = require('../lib/tls_options.js'); -var match_source_address = require('../lib/utils.js').match_source_address; - -function MockRouter (name, port, opts) { - this.name = name; - events.EventEmitter.call(this); - var options = {'port':port, container_id:name, properties:{product:'qpid-dispatch-router'}}; - for (var o in opts) { - options[o] = opts[o]; - } - this.connection = rhea.connect(options); - this.connection.on('message', this.on_message.bind(this)); - var self = this; - this.connection.on('connection_open', function () { self.emit('connected', self); }); - this.connection.on('sender_open', function (context) { - if (context.sender.source.dynamic) { - var id = rhea.generate_uuid(); - context.sender.set_source({address:id}); - } - }); - this.objects = {}; - this.create_object('listener', 'default', {name:'default', host:name, 'port':port, role:'inter-router'}); -} - -util.inherits(MockRouter, events.EventEmitter); - -MockRouter.prototype.create_object = function (type, name, attributes) -{ - if (this.objects[type] === undefined) { - this.objects[type] = {}; - } - this.objects[type][name] = (attributes === undefined) ? {} : attributes; - this.objects[type][name].name = name; - this.objects[type][name].id = name; -}; - -MockRouter.prototype.delete_object = function (type, name) -{ - delete this.objects[type][name]; -}; - -MockRouter.prototype.list_objects = function (type) -{ - var results = []; - for (var key in this.objects[type]) { - results.push(this.objects[type][key]); - } - return results; -}; - -MockRouter.prototype.close = function () -{ - if (this.connection && !this.connection.is_closed()) { - this.connection.close(); - var conn = this.connection; - return new Promise(function(resolve, reject) { - conn.on('connection_close', resolve); - }); - } else { - return Promise.resolve(); - } -}; - -MockRouter.prototype.close_with_error = function (error) -{ - if (this.connection && !this.connection.is_closed()) { - this.connection.local.close.error = error; - } - return this.close(); -}; - -MockRouter.prototype.has_connector_to = function (other) { - return this.list_objects('connector').some(function (c) { return c.host === other.name; }); -}; - -MockRouter.prototype.check_connector_from = function (others) { - var self = this; - return others.some(function (router) { return router !== self && router.has_connector_to(self); }); -}; - -function get_attribute_names(objects) { - var names = {}; - for (var i in objects) { - for (var f in objects[i]) { - names[f] = f; - } - } - return Object.keys(names); -} - -function query_result(names, objects) { - var results = []; - for (var j in objects) { - var record = []; - for (var i = 0; i < names.length; i++) { - record.push(objects[j][names[i]]); - } - results.push(record); - } - return {attributeNames:names, 'results':results}; -} - -MockRouter.prototype.on_message = function (context) -{ - var request = context.message; - var reply_to = request.reply_to; - var response = {to: reply_to}; - if (!(this.special && this.special(request, response, context))) {// the 'special' method when defined lets errors be injected - if (request.correlation_id) { - response.correlation_id = request.correlation_id; - } - response.application_properties = {}; - - if (request.application_properties.operation === 'CREATE') { - response.application_properties.statusCode = 201; - this.create_object(request.application_properties.type, request.application_properties.name, request.body); - } else if (request.application_properties.operation === 'DELETE') { - response.application_properties.statusCode = 204; - this.delete_object(request.application_properties.type, request.application_properties.name); - } else if (request.application_properties.operation === 'QUERY') { - response.application_properties.statusCode = 200; - var results = this.list_objects(request.application_properties.entityType); - var attributes = request.body.attributeNames; - if (!attributes || attributes.length === 0) { - attributes = get_attribute_names(results); - } - response.body = query_result(attributes, results); - } - } - - var reply_link = context.connection.find_sender(function (s) { return match_source_address(s, reply_to); }); - if (reply_link) { - reply_link.send(response); - } -}; - -MockRouter.prototype.set_onetime_error_response = function (operation, type, name) { - var f = function (request, response) { - if ((operation === undefined || request.application_properties.operation === operation) - && (type === undefined || (operation === 'QUERY' && request.application_properties.entityType === type) || request.application_properties.type === type) - && (name === undefined || request.application_properties.name === name)) { - response.application_properties = {}; - response.application_properties.statusCode = 500; - response.application_properties.statusDescription = 'error simulation'; - response.correlation_id = request.correlation_id; - delete this.special; - return true; - } else { - return false; - } - }; - this.special = f.bind(this); -}; - -module.exports = MockRouter; diff --git a/agent/yarn.lock b/agent/yarn.lock deleted file mode 100644 index 42477c37431..00000000000 --- a/agent/yarn.lock +++ /dev/null @@ -1,1207 +0,0 @@ -# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY. -# yarn lockfile v1 - - -"@babel/code-frame@^7.0.0": - version "7.8.3" - resolved "https://registry.yarnpkg.com/@babel/code-frame/-/code-frame-7.8.3.tgz#33e25903d7481181534e12ec0a25f16b6fcf419e" - integrity sha512-a9gxpmdXtZEInkCSHUJDLHZVBgb1QS0jhss4cPP93EW7s+uC5bikET2twEF3KV+7rDblJcmNvTR7VJejqd2C2g== - dependencies: - "@babel/highlight" "^7.8.3" - -"@babel/highlight@^7.8.3": - version "7.8.3" - resolved "https://registry.yarnpkg.com/@babel/highlight/-/highlight-7.8.3.tgz#28f173d04223eaaa59bc1d439a3836e6d1265797" - integrity sha512-PX4y5xQUvy0fnEVHrYOarRPXVWafSjTW9T0Hab8gVIawpl2Sj0ORyrygANq+KjcNlSSTw0YCLSNA8OyZ1I4yEg== - dependencies: - chalk "^2.0.0" - esutils "^2.0.2" - js-tokens "^4.0.0" - -acorn-jsx@^5.0.0: - version "5.1.0" - resolved "https://registry.yarnpkg.com/acorn-jsx/-/acorn-jsx-5.1.0.tgz#294adb71b57398b0680015f0a38c563ee1db5384" - integrity sha512-tMUqwBWfLFbJbizRmEcWSLw6HnFzfdJs2sOJEOwwtVPMoH/0Ay+E703oZz78VSXZiiDcZrQ5XKjPIUQixhmgVw== - -acorn@^6.0.7: - version "6.4.0" - resolved "https://registry.yarnpkg.com/acorn/-/acorn-6.4.0.tgz#b659d2ffbafa24baf5db1cdbb2c94a983ecd2784" - integrity sha512-gac8OEcQ2Li1dxIEWGZzsp2BitJxwkwcOm0zHAJLcPJaVvm58FRnk6RkuLRpU1EujipU2ZFODv2P9DLMfnV8mw== - -ajv@^6.10.2, ajv@^6.9.1: - version "6.11.0" - resolved "https://registry.yarnpkg.com/ajv/-/ajv-6.11.0.tgz#c3607cbc8ae392d8a5a536f25b21f8e5f3f87fe9" - integrity sha512-nCprB/0syFYy9fVYU1ox1l2KN8S9I+tziH8D4zdZuLT3N6RMlGSGt5FSTpAiHB/Whv8Qs1cWHma1aMKZyaHRKA== - dependencies: - fast-deep-equal "^3.1.1" - fast-json-stable-stringify "^2.0.0" - json-schema-traverse "^0.4.1" - uri-js "^4.2.2" - -ansi-colors@3.2.3: - version "3.2.3" - resolved "https://registry.yarnpkg.com/ansi-colors/-/ansi-colors-3.2.3.tgz#57d35b8686e851e2cc04c403f1c00203976a1813" - integrity sha512-LEHHyuhlPY3TmuUYMh2oz89lTShfvgbmzaBcxve9t/9Wuy7Dwf4yoAKcND7KFT1HAQfqZ12qtc+DUrBMeKF9nw== - -ansi-escapes@^3.2.0: - version "3.2.0" - resolved "https://registry.yarnpkg.com/ansi-escapes/-/ansi-escapes-3.2.0.tgz#8780b98ff9dbf5638152d1f1fe5c1d7b4442976b" - integrity sha512-cBhpre4ma+U0T1oM5fXg7Dy1Jw7zzwv7lt/GoCpr+hDQJoYnKVPLL4dCvSEFMmQurOQvSrwT7SL/DAlhBI97RQ== - -ansi-regex@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-3.0.0.tgz#ed0317c322064f79466c02966bddb605ab37d998" - integrity sha1-7QMXwyIGT3lGbAKWa922Bas32Zg= - -ansi-regex@^4.1.0: - version "4.1.0" - resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-4.1.0.tgz#8b9f8f08cf1acb843756a839ca8c7e3168c51997" - integrity sha512-1apePfXM1UOSqw0o9IiFAovVz9M5S1Dg+4TrDwfMewQ6p/rmMueb7tWZjQ1rx4Loy1ArBggoqGpfqqdI4rondg== - -ansi-styles@^3.2.0, ansi-styles@^3.2.1: - version "3.2.1" - resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-3.2.1.tgz#41fbb20243e50b12be0f04b8dedbf07520ce841d" - integrity sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA== - dependencies: - color-convert "^1.9.0" - -argparse@^1.0.7: - version "1.0.10" - resolved "https://registry.yarnpkg.com/argparse/-/argparse-1.0.10.tgz#bcd6791ea5ae09725e17e5ad988134cd40b3d911" - integrity sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg== - dependencies: - sprintf-js "~1.0.2" - -astral-regex@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/astral-regex/-/astral-regex-1.0.0.tgz#6c8c3fb827dd43ee3918f27b82782ab7658a6fd9" - integrity sha512-+Ryf6g3BKoRc7jfp7ad8tM4TtMiaWvbF/1/sQcZPkkS7ag3D5nMBCe2UfOTONtAkaG0tO0ij3C5Lwmf1EiyjHg== - -balanced-match@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/balanced-match/-/balanced-match-1.0.0.tgz#89b4d199ab2bee49de164ea02b89ce462d71b767" - integrity sha1-ibTRmasr7kneFk6gK4nORi1xt2c= - -brace-expansion@^1.1.7: - version "1.1.11" - resolved "https://registry.yarnpkg.com/brace-expansion/-/brace-expansion-1.1.11.tgz#3c7fcbf529d87226f3d2f52b966ff5271eb441dd" - integrity sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA== - dependencies: - balanced-match "^1.0.0" - concat-map "0.0.1" - -browser-stdout@1.3.1: - version "1.3.1" - resolved "https://registry.yarnpkg.com/browser-stdout/-/browser-stdout-1.3.1.tgz#baa559ee14ced73452229bad7326467c61fabd60" - integrity sha512-qhAVI1+Av2X7qelOfAIYwXONood6XlZE/fXaBSmW/T5SzLAmCgzi+eiWE7fUvbHaeNBQH13UftjpXxsfLkMpgw== - -callsites@^3.0.0: - version "3.1.0" - resolved "https://registry.yarnpkg.com/callsites/-/callsites-3.1.0.tgz#b3630abd8943432f54b3f0519238e33cd7df2f73" - integrity sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ== - -camelcase@^5.0.0: - version "5.3.1" - resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-5.3.1.tgz#e3c9b31569e106811df242f715725a1f4c494320" - integrity sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg== - -chalk@^2.0.0, chalk@^2.0.1, chalk@^2.1.0, chalk@^2.4.2: - version "2.4.2" - resolved "https://registry.yarnpkg.com/chalk/-/chalk-2.4.2.tgz#cd42541677a54333cf541a49108c1432b44c9424" - integrity sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ== - dependencies: - ansi-styles "^3.2.1" - escape-string-regexp "^1.0.5" - supports-color "^5.3.0" - -chardet@^0.7.0: - version "0.7.0" - resolved "https://registry.yarnpkg.com/chardet/-/chardet-0.7.0.tgz#90094849f0937f2eedc2425d0d28a9e5f0cbad9e" - integrity sha512-mT8iDcrh03qDGRRmoA2hmBJnxpllMR+0/0qlzjqZES6NdiWDcZkCNAk4rPFZ9Q85r27unkiNNg8ZOiwZXBHwcA== - -charenc@~0.0.1: - version "0.0.2" - resolved "https://registry.yarnpkg.com/charenc/-/charenc-0.0.2.tgz#c0a1d2f3a7092e03774bfa83f14c0fc5790a8667" - integrity sha1-wKHS86cJLgN3S/qD8UwPxXkKhmc= - -cli-cursor@^2.1.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/cli-cursor/-/cli-cursor-2.1.0.tgz#b35dac376479facc3e94747d41d0d0f5238ffcb5" - integrity sha1-s12sN2R5+sw+lHR9QdDQ9SOP/LU= - dependencies: - restore-cursor "^2.0.0" - -cli-width@^2.0.0: - version "2.2.0" - resolved "https://registry.yarnpkg.com/cli-width/-/cli-width-2.2.0.tgz#ff19ede8a9a5e579324147b0c11f0fbcbabed639" - integrity sha1-/xnt6Kml5XkyQUewwR8PvLq+1jk= - -cliui@^5.0.0: - version "5.0.0" - resolved "https://registry.yarnpkg.com/cliui/-/cliui-5.0.0.tgz#deefcfdb2e800784aa34f46fa08e06851c7bbbc5" - integrity sha512-PYeGSEmmHM6zvoef2w8TPzlrnNpXIjTipYK780YswmIP9vjxmd6Y2a3CB2Ks6/AU8NHjZugXvo8w3oWM2qnwXA== - dependencies: - string-width "^3.1.0" - strip-ansi "^5.2.0" - wrap-ansi "^5.1.0" - -clone@^2.1.2: - version "2.1.2" - resolved "https://registry.yarnpkg.com/clone/-/clone-2.1.2.tgz#1b7f4b9f591f1e8f83670401600345a02887435f" - integrity sha1-G39Ln1kfHo+DZwQBYANFoCiHQ18= - -color-convert@^1.9.0: - version "1.9.3" - resolved "https://registry.yarnpkg.com/color-convert/-/color-convert-1.9.3.tgz#bb71850690e1f136567de629d2d5471deda4c1e8" - integrity sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg== - dependencies: - color-name "1.1.3" - -color-name@1.1.3: - version "1.1.3" - resolved "https://registry.yarnpkg.com/color-name/-/color-name-1.1.3.tgz#a7d0558bd89c42f795dd42328f740831ca53bc25" - integrity sha1-p9BVi9icQveV3UIyj3QIMcpTvCU= - -concat-map@0.0.1: - version "0.0.1" - resolved "https://registry.yarnpkg.com/concat-map/-/concat-map-0.0.1.tgz#d8a96bd77fd68df7793a73036a3ba0d5405d477b" - integrity sha1-2Klr13/Wjfd5OnMDajug1UBdR3s= - -cross-spawn@^6.0.5: - version "6.0.5" - resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-6.0.5.tgz#4a5ec7c64dfae22c3a14124dbacdee846d80cbc4" - integrity sha512-eTVLrBSt7fjbDygz805pMnstIs2VTBNkRm0qxZd+M7A5XDdxVRWO5MxGBXZhjY4cqLYLdtrGqRf8mBPmzwSpWQ== - dependencies: - nice-try "^1.0.4" - path-key "^2.0.1" - semver "^5.5.0" - shebang-command "^1.2.0" - which "^1.2.9" - -crypt@~0.0.1: - version "0.0.2" - resolved "https://registry.yarnpkg.com/crypt/-/crypt-0.0.2.tgz#88d7ff7ec0dfb86f713dc87bbb42d044d3e6c41b" - integrity sha1-iNf/fsDfuG9xPch7u0LQRNPmxBs= - -"debug@0.8.0 - 3.5.0", debug@3.2.6, debug@^3.1.*: - version "3.2.6" - resolved "https://registry.yarnpkg.com/debug/-/debug-3.2.6.tgz#e83d17de16d8a7efb7717edbe5fb10135eee629b" - integrity sha512-mel+jf7nrtEl5Pn1Qx46zARXKDpBbvzezse7p7LqINmdoIk8PYP5SySaxEmYv6TZ0JyEKA1hsCId6DIhgITtWQ== - dependencies: - ms "^2.1.1" - -debug@^2.2.0: - version "2.6.9" - resolved "https://registry.yarnpkg.com/debug/-/debug-2.6.9.tgz#5d128515df134ff327e90a4c93f4e077a536341f" - integrity sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA== - dependencies: - ms "2.0.0" - -debug@^4.0.1: - version "4.1.1" - resolved "https://registry.yarnpkg.com/debug/-/debug-4.1.1.tgz#3b72260255109c6b589cee050f1d516139664791" - integrity sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw== - dependencies: - ms "^2.1.1" - -decamelize@^1.2.0: - version "1.2.0" - resolved "https://registry.yarnpkg.com/decamelize/-/decamelize-1.2.0.tgz#f6534d15148269b20352e7bee26f501f9a191290" - integrity sha1-9lNNFRSCabIDUue+4m9QH5oZEpA= - -deep-is@~0.1.3: - version "0.1.3" - resolved "https://registry.yarnpkg.com/deep-is/-/deep-is-0.1.3.tgz#b369d6fb5dbc13eecf524f91b070feedc357cf34" - integrity sha1-s2nW+128E+7PUk+RsHD+7cNXzzQ= - -define-properties@^1.1.2, define-properties@^1.1.3: - version "1.1.3" - resolved "https://registry.yarnpkg.com/define-properties/-/define-properties-1.1.3.tgz#cf88da6cbee26fe6db7094f61d870cbd84cee9f1" - integrity sha512-3MqfYKj2lLzdMSf8ZIZE/V+Zuy+BgD6f164e8K2w7dgnpKArBDerGYpM46IYYcjnkdPNMjPk9A6VFB8+3SKlXQ== - dependencies: - object-keys "^1.0.12" - -diff@3.5.0: - version "3.5.0" - resolved "https://registry.yarnpkg.com/diff/-/diff-3.5.0.tgz#800c0dd1e0a8bfbc95835c202ad220fe317e5a12" - integrity sha512-A46qtFgd+g7pDZinpnwiRJtxbC1hpgf0uzP3iG89scHk0AUC7A1TGxf5OiiOUv/JMZR8GOt8hL900hV0bOy5xA== - -doctrine@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/doctrine/-/doctrine-3.0.0.tgz#addebead72a6574db783639dc87a121773973961" - integrity sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w== - dependencies: - esutils "^2.0.2" - -emoji-regex@^7.0.1: - version "7.0.3" - resolved "https://registry.yarnpkg.com/emoji-regex/-/emoji-regex-7.0.3.tgz#933a04052860c85e83c122479c4748a8e4c72156" - integrity sha512-CwBLREIQ7LvYFB0WyRvwhq5N5qPhc6PMjD6bYggFlI5YyDgl+0vxq5VHbMOFqLg7hfWzmu8T5Z1QofhmTIhItA== - -es-abstract@^1.17.0-next.1: - version "1.17.4" - resolved "https://registry.yarnpkg.com/es-abstract/-/es-abstract-1.17.4.tgz#e3aedf19706b20e7c2594c35fc0d57605a79e184" - integrity sha512-Ae3um/gb8F0mui/jPL+QiqmglkUsaQf7FwBEHYIFkztkneosu9imhqHpBzQ3h1vit8t5iQ74t6PEVvphBZiuiQ== - dependencies: - es-to-primitive "^1.2.1" - function-bind "^1.1.1" - has "^1.0.3" - has-symbols "^1.0.1" - is-callable "^1.1.5" - is-regex "^1.0.5" - object-inspect "^1.7.0" - object-keys "^1.1.1" - object.assign "^4.1.0" - string.prototype.trimleft "^2.1.1" - string.prototype.trimright "^2.1.1" - -es-to-primitive@^1.2.1: - version "1.2.1" - resolved "https://registry.yarnpkg.com/es-to-primitive/-/es-to-primitive-1.2.1.tgz#e55cd4c9cdc188bcefb03b366c736323fc5c898a" - integrity sha512-QCOllgZJtaUo9miYBcLChTUaHNjJF3PYs1VidD7AwiEj1kYxKeQTctLAezAOH5ZKRH0g2IgPn6KwB4IT8iRpvA== - dependencies: - is-callable "^1.1.4" - is-date-object "^1.0.1" - is-symbol "^1.0.2" - -escape-string-regexp@1.0.5, escape-string-regexp@^1.0.5: - version "1.0.5" - resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz#1b61c0562190a8dff6ae3bb2cf0200ca130b86d4" - integrity sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ= - -eslint-scope@^4.0.3: - version "4.0.3" - resolved "https://registry.yarnpkg.com/eslint-scope/-/eslint-scope-4.0.3.tgz#ca03833310f6889a3264781aa82e63eb9cfe7848" - integrity sha512-p7VutNr1O/QrxysMo3E45FjYDTeXBy0iTltPFNSqKAIfjDSXC+4dj+qfyuD8bfAXrW/y6lW3O76VaYNPKfpKrg== - dependencies: - esrecurse "^4.1.0" - estraverse "^4.1.1" - -eslint-utils@^1.3.1: - version "1.4.3" - resolved "https://registry.yarnpkg.com/eslint-utils/-/eslint-utils-1.4.3.tgz#74fec7c54d0776b6f67e0251040b5806564e981f" - integrity sha512-fbBN5W2xdY45KulGXmLHZ3c3FHfVYmKg0IrAKGOkT/464PQsx2UeIzfz1RmEci+KLm1bBaAzZAh8+/E+XAeZ8Q== - dependencies: - eslint-visitor-keys "^1.1.0" - -eslint-visitor-keys@^1.0.0, eslint-visitor-keys@^1.1.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/eslint-visitor-keys/-/eslint-visitor-keys-1.1.0.tgz#e2a82cea84ff246ad6fb57f9bde5b46621459ec2" - integrity sha512-8y9YjtM1JBJU/A9Kc+SbaOV4y29sSWckBwMHa+FGtVj5gN/sbnKDf6xJUl+8g7FAij9LVaP8C24DUiH/f/2Z9A== - -eslint@5.16.0: - version "5.16.0" - resolved "https://registry.yarnpkg.com/eslint/-/eslint-5.16.0.tgz#a1e3ac1aae4a3fbd8296fcf8f7ab7314cbb6abea" - integrity sha512-S3Rz11i7c8AA5JPv7xAH+dOyq/Cu/VXHiHXBPOU1k/JAM5dXqQPt3qcrhpHSorXmrpu2g0gkIBVXAqCpzfoZIg== - dependencies: - "@babel/code-frame" "^7.0.0" - ajv "^6.9.1" - chalk "^2.1.0" - cross-spawn "^6.0.5" - debug "^4.0.1" - doctrine "^3.0.0" - eslint-scope "^4.0.3" - eslint-utils "^1.3.1" - eslint-visitor-keys "^1.0.0" - espree "^5.0.1" - esquery "^1.0.1" - esutils "^2.0.2" - file-entry-cache "^5.0.1" - functional-red-black-tree "^1.0.1" - glob "^7.1.2" - globals "^11.7.0" - ignore "^4.0.6" - import-fresh "^3.0.0" - imurmurhash "^0.1.4" - inquirer "^6.2.2" - js-yaml "^3.13.0" - json-stable-stringify-without-jsonify "^1.0.1" - levn "^0.3.0" - lodash "^4.17.11" - minimatch "^3.0.4" - mkdirp "^0.5.1" - natural-compare "^1.4.0" - optionator "^0.8.2" - path-is-inside "^1.0.2" - progress "^2.0.0" - regexpp "^2.0.1" - semver "^5.5.1" - strip-ansi "^4.0.0" - strip-json-comments "^2.0.1" - table "^5.2.3" - text-table "^0.2.0" - -espree@^5.0.1: - version "5.0.1" - resolved "https://registry.yarnpkg.com/espree/-/espree-5.0.1.tgz#5d6526fa4fc7f0788a5cf75b15f30323e2f81f7a" - integrity sha512-qWAZcWh4XE/RwzLJejfcofscgMc9CamR6Tn1+XRXNzrvUSSbiAjGOI/fggztjIi7y9VLPqnICMIPiGyr8JaZ0A== - dependencies: - acorn "^6.0.7" - acorn-jsx "^5.0.0" - eslint-visitor-keys "^1.0.0" - -esprima@^4.0.0: - version "4.0.1" - resolved "https://registry.yarnpkg.com/esprima/-/esprima-4.0.1.tgz#13b04cdb3e6c5d19df91ab6987a8695619b0aa71" - integrity sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A== - -esquery@^1.0.1: - version "1.1.0" - resolved "https://registry.yarnpkg.com/esquery/-/esquery-1.1.0.tgz#c5c0b66f383e7656404f86b31334d72524eddb48" - integrity sha512-MxYW9xKmROWF672KqjO75sszsA8Mxhw06YFeS5VHlB98KDHbOSurm3ArsjO60Eaf3QmGMCP1yn+0JQkNLo/97Q== - dependencies: - estraverse "^4.0.0" - -esrecurse@^4.1.0: - version "4.2.1" - resolved "https://registry.yarnpkg.com/esrecurse/-/esrecurse-4.2.1.tgz#007a3b9fdbc2b3bb87e4879ea19c92fdbd3942cf" - integrity sha512-64RBB++fIOAXPw3P9cy89qfMlvZEXZkqqJkjqqXIvzP5ezRZjW+lPWjw35UX/3EhUPFYbg5ER4JYgDw4007/DQ== - dependencies: - estraverse "^4.1.0" - -estraverse@^4.0.0, estraverse@^4.1.0, estraverse@^4.1.1: - version "4.3.0" - resolved "https://registry.yarnpkg.com/estraverse/-/estraverse-4.3.0.tgz#398ad3f3c5a24948be7725e83d11a7de28cdbd1d" - integrity sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw== - -esutils@^2.0.2: - version "2.0.3" - resolved "https://registry.yarnpkg.com/esutils/-/esutils-2.0.3.tgz#74d2eb4de0b8da1293711910d50775b9b710ef64" - integrity sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g== - -external-editor@^3.0.3: - version "3.1.0" - resolved "https://registry.yarnpkg.com/external-editor/-/external-editor-3.1.0.tgz#cb03f740befae03ea4d283caed2741a83f335495" - integrity sha512-hMQ4CX1p1izmuLYyZqLMO/qGNw10wSv9QDCPfzXfyFrOaCSSoRfqE1Kf1s5an66J5JZC62NewG+mK49jOCtQew== - dependencies: - chardet "^0.7.0" - iconv-lite "^0.4.24" - tmp "^0.0.33" - -fast-deep-equal@^3.1.1: - version "3.1.1" - resolved "https://registry.yarnpkg.com/fast-deep-equal/-/fast-deep-equal-3.1.1.tgz#545145077c501491e33b15ec408c294376e94ae4" - integrity sha512-8UEa58QDLauDNfpbrX55Q9jrGHThw2ZMdOky5Gl1CDtVeJDPVrG4Jxx1N8jw2gkWaff5UUuX1KJd+9zGe2B+ZA== - -fast-json-stable-stringify@^2.0.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz#874bf69c6f404c2b5d99c481341399fd55892633" - integrity sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw== - -fast-levenshtein@~2.0.6: - version "2.0.6" - resolved "https://registry.yarnpkg.com/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz#3d8a5c66883a16a30ca8643e851f19baa7797917" - integrity sha1-PYpcZog6FqMMqGQ+hR8Zuqd5eRc= - -figures@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/figures/-/figures-2.0.0.tgz#3ab1a2d2a62c8bfb431a0c94cb797a2fce27c962" - integrity sha1-OrGi0qYsi/tDGgyUy3l6L84nyWI= - dependencies: - escape-string-regexp "^1.0.5" - -file-entry-cache@^5.0.1: - version "5.0.1" - resolved "https://registry.yarnpkg.com/file-entry-cache/-/file-entry-cache-5.0.1.tgz#ca0f6efa6dd3d561333fb14515065c2fafdf439c" - integrity sha512-bCg29ictuBaKUwwArK4ouCaqDgLZcysCFLmM/Yn/FDoqndh/9vNuQfXRDvTuXKLxfD/JtZQGKFT8MGcJBK644g== - dependencies: - flat-cache "^2.0.1" - -find-up@3.0.0, find-up@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/find-up/-/find-up-3.0.0.tgz#49169f1d7993430646da61ecc5ae355c21c97b73" - integrity sha512-1yD6RmLI1XBfxugvORwlck6f75tYL+iR0jqwsOrOxMZyGYqUuDhJ0l4AXdO1iX/FTs9cBAMEk1gWSEx1kSbylg== - dependencies: - locate-path "^3.0.0" - -flat-cache@^2.0.1: - version "2.0.1" - resolved "https://registry.yarnpkg.com/flat-cache/-/flat-cache-2.0.1.tgz#5d296d6f04bda44a4630a301413bdbc2ec085ec0" - integrity sha512-LoQe6yDuUMDzQAEH8sgmh4Md6oZnc/7PjtwjNFSzveXqSHt6ka9fPBuso7IGf9Rz4uqnSnWiFH2B/zj24a5ReA== - dependencies: - flatted "^2.0.0" - rimraf "2.6.3" - write "1.0.3" - -flat@^4.1.0: - version "4.1.0" - resolved "https://registry.yarnpkg.com/flat/-/flat-4.1.0.tgz#090bec8b05e39cba309747f1d588f04dbaf98db2" - integrity sha512-Px/TiLIznH7gEDlPXcUD4KnBusa6kR6ayRUVcnEAbreRIuhkqow/mun59BuRXwoYk7ZQOLW1ZM05ilIvK38hFw== - dependencies: - is-buffer "~2.0.3" - -flatted@^2.0.0: - version "2.0.1" - resolved "https://registry.yarnpkg.com/flatted/-/flatted-2.0.1.tgz#69e57caa8f0eacbc281d2e2cb458d46fdb449e08" - integrity sha512-a1hQMktqW9Nmqr5aktAux3JMNqaucxGcjtjWnZLHX7yyPCmlSV3M54nGYbqT8K+0GhF3NBgmJCc3ma+WOgX8Jg== - -fs.realpath@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/fs.realpath/-/fs.realpath-1.0.0.tgz#1504ad2523158caa40db4a2787cb01411994ea4f" - integrity sha1-FQStJSMVjKpA20onh8sBQRmU6k8= - -function-bind@^1.1.1: - version "1.1.1" - resolved "https://registry.yarnpkg.com/function-bind/-/function-bind-1.1.1.tgz#a56899d3ea3c9bab874bb9773b7c5ede92f4895d" - integrity sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A== - -functional-red-black-tree@^1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/functional-red-black-tree/-/functional-red-black-tree-1.0.1.tgz#1b0ab3bd553b2a0d6399d29c0e3ea0b252078327" - integrity sha1-GwqzvVU7Kg1jmdKcDj6gslIHgyc= - -get-caller-file@^2.0.1: - version "2.0.5" - resolved "https://registry.yarnpkg.com/get-caller-file/-/get-caller-file-2.0.5.tgz#4f94412a82db32f36e3b0b9741f8a97feb031f7e" - integrity sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg== - -glob@7.1.3: - version "7.1.3" - resolved "https://registry.yarnpkg.com/glob/-/glob-7.1.3.tgz#3960832d3f1574108342dafd3a67b332c0969df1" - integrity sha512-vcfuiIxogLV4DlGBHIUOwI0IbrJ8HWPc4MU7HzviGeNho/UJDfi6B5p3sHeWIQ0KGIU0Jpxi5ZHxemQfLkkAwQ== - dependencies: - fs.realpath "^1.0.0" - inflight "^1.0.4" - inherits "2" - minimatch "^3.0.4" - once "^1.3.0" - path-is-absolute "^1.0.0" - -glob@^7.1.2, glob@^7.1.3: - version "7.1.6" - resolved "https://registry.yarnpkg.com/glob/-/glob-7.1.6.tgz#141f33b81a7c2492e125594307480c46679278a6" - integrity sha512-LwaxwyZ72Lk7vZINtNNrywX0ZuLyStrdDtabefZKAY5ZGJhVtgdznluResxNmPitE0SAO+O26sWTHeKSI2wMBA== - dependencies: - fs.realpath "^1.0.0" - inflight "^1.0.4" - inherits "2" - minimatch "^3.0.4" - once "^1.3.0" - path-is-absolute "^1.0.0" - -globals@^11.7.0: - version "11.12.0" - resolved "https://registry.yarnpkg.com/globals/-/globals-11.12.0.tgz#ab8795338868a0babd8525758018c2a7eb95c42e" - integrity sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA== - -growl@1.10.5: - version "1.10.5" - resolved "https://registry.yarnpkg.com/growl/-/growl-1.10.5.tgz#f2735dc2283674fa67478b10181059355c369e5e" - integrity sha512-qBr4OuELkhPenW6goKVXiv47US3clb3/IbuWF9KNKEijAy9oeHxU9IgzjvJhHkUzhaj7rOUD7+YGWqUjLp5oSA== - -has-flag@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/has-flag/-/has-flag-3.0.0.tgz#b5d454dc2199ae225699f3467e5a07f3b955bafd" - integrity sha1-tdRU3CGZriJWmfNGfloH87lVuv0= - -has-symbols@^1.0.0, has-symbols@^1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/has-symbols/-/has-symbols-1.0.1.tgz#9f5214758a44196c406d9bd76cebf81ec2dd31e8" - integrity sha512-PLcsoqu++dmEIZB+6totNFKq/7Do+Z0u4oT0zKOJNl3lYK6vGwwu2hjHs+68OEZbTjiUE9bgOABXbP/GvrS0Kg== - -has@^1.0.3: - version "1.0.3" - resolved "https://registry.yarnpkg.com/has/-/has-1.0.3.tgz#722d7cbfc1f6aa8241f16dd814e011e1f41e8796" - integrity sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw== - dependencies: - function-bind "^1.1.1" - -he@1.2.0: - version "1.2.0" - resolved "https://registry.yarnpkg.com/he/-/he-1.2.0.tgz#84ae65fa7eafb165fddb61566ae14baf05664f0f" - integrity sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw== - -iconv-lite@^0.4.24: - version "0.4.24" - resolved "https://registry.yarnpkg.com/iconv-lite/-/iconv-lite-0.4.24.tgz#2022b4b25fbddc21d2f524974a474aafe733908b" - integrity sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA== - dependencies: - safer-buffer ">= 2.1.2 < 3" - -ignore@^4.0.6: - version "4.0.6" - resolved "https://registry.yarnpkg.com/ignore/-/ignore-4.0.6.tgz#750e3db5862087b4737ebac8207ffd1ef27b25fc" - integrity sha512-cyFDKrqc/YdcWFniJhzI42+AzS+gNwmUzOSFcRCQYwySuBBBy/KjuxWLZ/FHEH6Moq1NizMOBWyTcv8O4OZIMg== - -import-fresh@^3.0.0: - version "3.2.1" - resolved "https://registry.yarnpkg.com/import-fresh/-/import-fresh-3.2.1.tgz#633ff618506e793af5ac91bf48b72677e15cbe66" - integrity sha512-6e1q1cnWP2RXD9/keSkxHScg508CdXqXWgWBaETNhyuBFz+kUZlKboh+ISK+bU++DmbHimVBrOz/zzPe0sZ3sQ== - dependencies: - parent-module "^1.0.0" - resolve-from "^4.0.0" - -imurmurhash@^0.1.4: - version "0.1.4" - resolved "https://registry.yarnpkg.com/imurmurhash/-/imurmurhash-0.1.4.tgz#9218b9b2b928a238b13dc4fb6b6d576f231453ea" - integrity sha1-khi5srkoojixPcT7a21XbyMUU+o= - -inflight@^1.0.4: - version "1.0.6" - resolved "https://registry.yarnpkg.com/inflight/-/inflight-1.0.6.tgz#49bd6331d7d02d0c09bc910a1075ba8165b56df9" - integrity sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk= - dependencies: - once "^1.3.0" - wrappy "1" - -inherits@2: - version "2.0.4" - resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.4.tgz#0fa2c64f932917c3433a0ded55363aae37416b7c" - integrity sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ== - -inquirer@^6.2.2: - version "6.5.2" - resolved "https://registry.yarnpkg.com/inquirer/-/inquirer-6.5.2.tgz#ad50942375d036d327ff528c08bd5fab089928ca" - integrity sha512-cntlB5ghuB0iuO65Ovoi8ogLHiWGs/5yNrtUcKjFhSSiVeAIVpD7koaSU9RM8mpXw5YDi9RdYXGQMaOURB7ycQ== - dependencies: - ansi-escapes "^3.2.0" - chalk "^2.4.2" - cli-cursor "^2.1.0" - cli-width "^2.0.0" - external-editor "^3.0.3" - figures "^2.0.0" - lodash "^4.17.12" - mute-stream "0.0.7" - run-async "^2.2.0" - rxjs "^6.4.0" - string-width "^2.1.0" - strip-ansi "^5.1.0" - through "^2.3.6" - -is-buffer@~1.1.1: - version "1.1.6" - resolved "https://registry.yarnpkg.com/is-buffer/-/is-buffer-1.1.6.tgz#efaa2ea9daa0d7ab2ea13a97b2b8ad51fefbe8be" - integrity sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w== - -is-buffer@~2.0.3: - version "2.0.4" - resolved "https://registry.yarnpkg.com/is-buffer/-/is-buffer-2.0.4.tgz#3e572f23c8411a5cfd9557c849e3665e0b290623" - integrity sha512-Kq1rokWXOPXWuaMAqZiJW4XxsmD9zGx9q4aePabbn3qCRGedtH7Cm+zV8WETitMfu1wdh+Rvd6w5egwSngUX2A== - -is-callable@^1.1.4, is-callable@^1.1.5: - version "1.1.5" - resolved "https://registry.yarnpkg.com/is-callable/-/is-callable-1.1.5.tgz#f7e46b596890456db74e7f6e976cb3273d06faab" - integrity sha512-ESKv5sMCJB2jnHTWZ3O5itG+O128Hsus4K4Qh1h2/cgn2vbgnLSVqfV46AeJA9D5EeeLa9w81KUXMtn34zhX+Q== - -is-date-object@^1.0.1: - version "1.0.2" - resolved "https://registry.yarnpkg.com/is-date-object/-/is-date-object-1.0.2.tgz#bda736f2cd8fd06d32844e7743bfa7494c3bfd7e" - integrity sha512-USlDT524woQ08aoZFzh3/Z6ch9Y/EWXEHQ/AaRN0SkKq4t2Jw2R2339tSXmwuVoY7LLlBCbOIlx2myP/L5zk0g== - -is-fullwidth-code-point@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz#a3b30a5c4f199183167aaab93beefae3ddfb654f" - integrity sha1-o7MKXE8ZkYMWeqq5O+764937ZU8= - -is-promise@^2.1.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/is-promise/-/is-promise-2.1.0.tgz#79a2a9ece7f096e80f36d2b2f3bc16c1ff4bf3fa" - integrity sha1-eaKp7OfwlugPNtKy87wWwf9L8/o= - -is-regex@^1.0.5: - version "1.0.5" - resolved "https://registry.yarnpkg.com/is-regex/-/is-regex-1.0.5.tgz#39d589a358bf18967f726967120b8fc1aed74eae" - integrity sha512-vlKW17SNq44owv5AQR3Cq0bQPEb8+kF3UKZ2fiZNOWtztYE5i0CzCZxFDwO58qAOWtxdBRVO/V5Qin1wjCqFYQ== - dependencies: - has "^1.0.3" - -is-symbol@^1.0.2: - version "1.0.3" - resolved "https://registry.yarnpkg.com/is-symbol/-/is-symbol-1.0.3.tgz#38e1014b9e6329be0de9d24a414fd7441ec61937" - integrity sha512-OwijhaRSgqvhm/0ZdAcXNZt9lYdKFpcRDT5ULUuYXPoT794UNOdU+gpT6Rzo7b4V2HUl/op6GqY894AZwv9faQ== - dependencies: - has-symbols "^1.0.1" - -isexe@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/isexe/-/isexe-2.0.0.tgz#e8fbf374dc556ff8947a10dcb0572d633f2cfa10" - integrity sha1-6PvzdNxVb/iUehDcsFctYz8s+hA= - -js-tokens@^4.0.0: - version "4.0.0" - resolved "https://registry.yarnpkg.com/js-tokens/-/js-tokens-4.0.0.tgz#19203fb59991df98e3a287050d4647cdeaf32499" - integrity sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ== - -js-yaml@3.13.1, js-yaml@^3.13.0: - version "3.13.1" - resolved "https://registry.yarnpkg.com/js-yaml/-/js-yaml-3.13.1.tgz#aff151b30bfdfa8e49e05da22e7415e9dfa37847" - integrity sha512-YfbcO7jXDdyj0DGxYVSlSeQNHbD7XPWvrVWeVUujrQEoZzWJIRrCPoyk6kL6IAjAG2IolMK4T0hNUe0HOUs5Jw== - dependencies: - argparse "^1.0.7" - esprima "^4.0.0" - -json-schema-traverse@^0.4.1: - version "0.4.1" - resolved "https://registry.yarnpkg.com/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz#69f6a87d9513ab8bb8fe63bdb0979c448e684660" - integrity sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg== - -json-stable-stringify-without-jsonify@^1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz#9db7b59496ad3f3cfef30a75142d2d930ad72651" - integrity sha1-nbe1lJatPzz+8wp1FC0tkwrXJlE= - -levn@^0.3.0, levn@~0.3.0: - version "0.3.0" - resolved "https://registry.yarnpkg.com/levn/-/levn-0.3.0.tgz#3b09924edf9f083c0490fdd4c0bc4421e04764ee" - integrity sha1-OwmSTt+fCDwEkP3UwLxEIeBHZO4= - dependencies: - prelude-ls "~1.1.2" - type-check "~0.3.2" - -locate-path@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/locate-path/-/locate-path-3.0.0.tgz#dbec3b3ab759758071b58fe59fc41871af21400e" - integrity sha512-7AO748wWnIhNqAuaty2ZWHkQHRSNfPVIsPIfwEOWO22AmaoVrWavlOcMR5nzTLNYvp36X220/maaRsrec1G65A== - dependencies: - p-locate "^3.0.0" - path-exists "^3.0.0" - -lodash@^4.17.11, lodash@^4.17.12, lodash@^4.17.14, lodash@^4.17.15: - version "4.17.15" - resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.15.tgz#b447f6670a0455bbfeedd11392eff330ea097548" - integrity sha512-8xOcRHvCjnocdS5cpwXQXVzmmh5e5+saE2QGoeQmbKmRS6J3VQppPOIt0MnmE+4xlZoumy0GPG0D0MVIQbNA1A== - -log-symbols@2.2.0: - version "2.2.0" - resolved "https://registry.yarnpkg.com/log-symbols/-/log-symbols-2.2.0.tgz#5740e1c5d6f0dfda4ad9323b5332107ef6b4c40a" - integrity sha512-VeIAFslyIerEJLXHziedo2basKbMKtTw3vfn5IzG0XTjhAVEJyNHnL2p7vc+wBDSdQuUpNw3M2u6xb9QsAY5Eg== - dependencies: - chalk "^2.0.1" - -md5@^2.1.0: - version "2.2.1" - resolved "https://registry.yarnpkg.com/md5/-/md5-2.2.1.tgz#53ab38d5fe3c8891ba465329ea23fac0540126f9" - integrity sha1-U6s41f48iJG6RlMp6iP6wFQBJvk= - dependencies: - charenc "~0.0.1" - crypt "~0.0.1" - is-buffer "~1.1.1" - -mimic-fn@^1.0.0: - version "1.2.0" - resolved "https://registry.yarnpkg.com/mimic-fn/-/mimic-fn-1.2.0.tgz#820c86a39334640e99516928bd03fca88057d022" - integrity sha512-jf84uxzwiuiIVKiOLpfYk7N46TSy8ubTonmneY9vrpHNAnp0QBt2BxWV9dO3/j+BoVAb+a5G6YDPW3M5HOdMWQ== - -minimatch@3.0.4, minimatch@^3.0.4: - version "3.0.4" - resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-3.0.4.tgz#5166e286457f03306064be5497e8dbb0c3d32083" - integrity sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA== - dependencies: - brace-expansion "^1.1.7" - -minimist@0.0.8: - version "0.0.8" - resolved "https://registry.yarnpkg.com/minimist/-/minimist-0.0.8.tgz#857fcabfc3397d2625b8228262e86aa7a011b05d" - integrity sha1-hX/Kv8M5fSYluCKCYuhqp6ARsF0= - -mkdirp@0.5.1, mkdirp@^0.5.1, mkdirp@~0.5.1: - version "0.5.1" - resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-0.5.1.tgz#30057438eac6cf7f8c4767f38648d6697d75c903" - integrity sha1-MAV0OOrGz3+MR2fzhkjWaX11yQM= - dependencies: - minimist "0.0.8" - -mocha-junit-reporter@^1.22.0: - version "1.23.3" - resolved "https://registry.yarnpkg.com/mocha-junit-reporter/-/mocha-junit-reporter-1.23.3.tgz#941e219dd759ed732f8641e165918aa8b167c981" - integrity sha512-ed8LqbRj1RxZfjt/oC9t12sfrWsjZ3gNnbhV1nuj9R/Jb5/P3Xb4duv2eCfCDMYH+fEu0mqca7m4wsiVjsxsvA== - dependencies: - debug "^2.2.0" - md5 "^2.1.0" - mkdirp "~0.5.1" - strip-ansi "^4.0.0" - xml "^1.0.0" - -mocha@^6.1.4: - version "6.2.2" - resolved "https://registry.yarnpkg.com/mocha/-/mocha-6.2.2.tgz#5d8987e28940caf8957a7d7664b910dc5b2fea20" - integrity sha512-FgDS9Re79yU1xz5d+C4rv1G7QagNGHZ+iXF81hO8zY35YZZcLEsJVfFolfsqKFWunATEvNzMK0r/CwWd/szO9A== - dependencies: - ansi-colors "3.2.3" - browser-stdout "1.3.1" - debug "3.2.6" - diff "3.5.0" - escape-string-regexp "1.0.5" - find-up "3.0.0" - glob "7.1.3" - growl "1.10.5" - he "1.2.0" - js-yaml "3.13.1" - log-symbols "2.2.0" - minimatch "3.0.4" - mkdirp "0.5.1" - ms "2.1.1" - node-environment-flags "1.0.5" - object.assign "4.1.0" - strip-json-comments "2.0.1" - supports-color "6.0.0" - which "1.3.1" - wide-align "1.1.3" - yargs "13.3.0" - yargs-parser "13.1.1" - yargs-unparser "1.6.0" - -ms@2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/ms/-/ms-2.0.0.tgz#5608aeadfc00be6c2901df5f9861788de0d597c8" - integrity sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g= - -ms@2.1.1: - version "2.1.1" - resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.1.tgz#30a5864eb3ebb0a66f2ebe6d727af06a09d86e0a" - integrity sha512-tgp+dl5cGk28utYktBsrFqA7HKgrhgPsg6Z/EfhWI4gl1Hwq8B/GmY/0oXZ6nF8hDVesS/FpnYaD/kOWhYQvyg== - -ms@^2.1.1: - version "2.1.2" - resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.2.tgz#d09d1f357b443f493382a8eb3ccd183872ae6009" - integrity sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w== - -mute-stream@0.0.7: - version "0.0.7" - resolved "https://registry.yarnpkg.com/mute-stream/-/mute-stream-0.0.7.tgz#3075ce93bc21b8fab43e1bc4da7e8115ed1e7bab" - integrity sha1-MHXOk7whuPq0PhvE2n6BFe0ee6s= - -natural-compare@^1.4.0: - version "1.4.0" - resolved "https://registry.yarnpkg.com/natural-compare/-/natural-compare-1.4.0.tgz#4abebfeed7541f2c27acfb29bdbbd15c8d5ba4f7" - integrity sha1-Sr6/7tdUHywnrPspvbvRXI1bpPc= - -nice-try@^1.0.4: - version "1.0.5" - resolved "https://registry.yarnpkg.com/nice-try/-/nice-try-1.0.5.tgz#a3378a7696ce7d223e88fc9b764bd7ef1089e366" - integrity sha512-1nh45deeb5olNY7eX82BkPO7SSxR5SSYJiPTrTdFUVYwAl8CKMA5N9PjTYkHiRjisVcxcQ1HXdLhx2qxxJzLNQ== - -node-environment-flags@1.0.5: - version "1.0.5" - resolved "https://registry.yarnpkg.com/node-environment-flags/-/node-environment-flags-1.0.5.tgz#fa930275f5bf5dae188d6192b24b4c8bbac3d76a" - integrity sha512-VNYPRfGfmZLx0Ye20jWzHUjyTW/c+6Wq+iLhDzUI4XmhrDd9l/FozXV3F2xOaXjvp0co0+v1YSR3CMP6g+VvLQ== - dependencies: - object.getownpropertydescriptors "^2.0.3" - semver "^5.7.0" - -object-inspect@^1.7.0: - version "1.7.0" - resolved "https://registry.yarnpkg.com/object-inspect/-/object-inspect-1.7.0.tgz#f4f6bd181ad77f006b5ece60bd0b6f398ff74a67" - integrity sha512-a7pEHdh1xKIAgTySUGgLMx/xwDZskN1Ud6egYYN3EdRW4ZMPNEDUTF+hwy2LUC+Bl+SyLXANnwz/jyh/qutKUw== - -object-keys@^1.0.11, object-keys@^1.0.12, object-keys@^1.1.1: - version "1.1.1" - resolved "https://registry.yarnpkg.com/object-keys/-/object-keys-1.1.1.tgz#1c47f272df277f3b1daf061677d9c82e2322c60e" - integrity sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA== - -object.assign@4.1.0, object.assign@^4.1.0: - version "4.1.0" - resolved "https://registry.yarnpkg.com/object.assign/-/object.assign-4.1.0.tgz#968bf1100d7956bb3ca086f006f846b3bc4008da" - integrity sha512-exHJeq6kBKj58mqGyTQ9DFvrZC/eR6OwxzoM9YRoGBqrXYonaFyGiFMuc9VZrXf7DarreEwMpurG3dd+CNyW5w== - dependencies: - define-properties "^1.1.2" - function-bind "^1.1.1" - has-symbols "^1.0.0" - object-keys "^1.0.11" - -object.getownpropertydescriptors@^2.0.3: - version "2.1.0" - resolved "https://registry.yarnpkg.com/object.getownpropertydescriptors/-/object.getownpropertydescriptors-2.1.0.tgz#369bf1f9592d8ab89d712dced5cb81c7c5352649" - integrity sha512-Z53Oah9A3TdLoblT7VKJaTDdXdT+lQO+cNpKVnya5JDe9uLvzu1YyY1yFDFrcxrlRgWrEFH0jJtD/IbuwjcEVg== - dependencies: - define-properties "^1.1.3" - es-abstract "^1.17.0-next.1" - -once@^1.3.0: - version "1.4.0" - resolved "https://registry.yarnpkg.com/once/-/once-1.4.0.tgz#583b1aa775961d4b113ac17d9c50baef9dd76bd1" - integrity sha1-WDsap3WWHUsROsF9nFC6753Xa9E= - dependencies: - wrappy "1" - -onetime@^2.0.0: - version "2.0.1" - resolved "https://registry.yarnpkg.com/onetime/-/onetime-2.0.1.tgz#067428230fd67443b2794b22bba528b6867962d4" - integrity sha1-BnQoIw/WdEOyeUsiu6UotoZ5YtQ= - dependencies: - mimic-fn "^1.0.0" - -optionator@^0.8.2: - version "0.8.3" - resolved "https://registry.yarnpkg.com/optionator/-/optionator-0.8.3.tgz#84fa1d036fe9d3c7e21d99884b601167ec8fb495" - integrity sha512-+IW9pACdk3XWmmTXG8m3upGUJst5XRGzxMRjXzAuJ1XnIFNvfhjjIuYkDvysnPQ7qzqVzLt78BCruntqRhWQbA== - dependencies: - deep-is "~0.1.3" - fast-levenshtein "~2.0.6" - levn "~0.3.0" - prelude-ls "~1.1.2" - type-check "~0.3.2" - word-wrap "~1.2.3" - -os-tmpdir@~1.0.2: - version "1.0.2" - resolved "https://registry.yarnpkg.com/os-tmpdir/-/os-tmpdir-1.0.2.tgz#bbe67406c79aa85c5cfec766fe5734555dfa1274" - integrity sha1-u+Z0BseaqFxc/sdm/lc0VV36EnQ= - -p-limit@^2.0.0, p-limit@^2.2.0: - version "2.2.2" - resolved "https://registry.yarnpkg.com/p-limit/-/p-limit-2.2.2.tgz#61279b67721f5287aa1c13a9a7fbbc48c9291b1e" - integrity sha512-WGR+xHecKTr7EbUEhyLSh5Dube9JtdiG78ufaeLxTgpudf/20KqyMioIUZJAezlTIi6evxuoUs9YXc11cU+yzQ== - dependencies: - p-try "^2.0.0" - -p-locate@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/p-locate/-/p-locate-3.0.0.tgz#322d69a05c0264b25997d9f40cd8a891ab0064a4" - integrity sha512-x+12w/To+4GFfgJhBEpiDcLozRJGegY+Ei7/z0tSLkMmxGZNybVMSfWj9aJn8Z5Fc7dBUNJOOVgPv2H7IwulSQ== - dependencies: - p-limit "^2.0.0" - -p-try@^2.0.0: - version "2.2.0" - resolved "https://registry.yarnpkg.com/p-try/-/p-try-2.2.0.tgz#cb2868540e313d61de58fafbe35ce9004d5540e6" - integrity sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ== - -parent-module@^1.0.0: - version "1.0.1" - resolved "https://registry.yarnpkg.com/parent-module/-/parent-module-1.0.1.tgz#691d2709e78c79fae3a156622452d00762caaaa2" - integrity sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g== - dependencies: - callsites "^3.0.0" - -path-exists@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/path-exists/-/path-exists-3.0.0.tgz#ce0ebeaa5f78cb18925ea7d810d7b59b010fd515" - integrity sha1-zg6+ql94yxiSXqfYENe1mwEP1RU= - -path-is-absolute@^1.0.0: - version "1.0.1" - resolved "https://registry.yarnpkg.com/path-is-absolute/-/path-is-absolute-1.0.1.tgz#174b9268735534ffbc7ace6bf53a5a9e1b5c5f5f" - integrity sha1-F0uSaHNVNP+8es5r9TpanhtcX18= - -path-is-inside@^1.0.2: - version "1.0.2" - resolved "https://registry.yarnpkg.com/path-is-inside/-/path-is-inside-1.0.2.tgz#365417dede44430d1c11af61027facf074bdfc53" - integrity sha1-NlQX3t5EQw0cEa9hAn+s8HS9/FM= - -path-key@^2.0.1: - version "2.0.1" - resolved "https://registry.yarnpkg.com/path-key/-/path-key-2.0.1.tgz#411cadb574c5a140d3a4b1910d40d80cc9f40b40" - integrity sha1-QRyttXTFoUDTpLGRDUDYDMn0C0A= - -prelude-ls@~1.1.2: - version "1.1.2" - resolved "https://registry.yarnpkg.com/prelude-ls/-/prelude-ls-1.1.2.tgz#21932a549f5e52ffd9a827f570e04be62a97da54" - integrity sha1-IZMqVJ9eUv/ZqCf1cOBL5iqX2lQ= - -progress@^2.0.0: - version "2.0.3" - resolved "https://registry.yarnpkg.com/progress/-/progress-2.0.3.tgz#7e8cf8d8f5b8f239c1bc68beb4eb78567d572ef8" - integrity sha512-7PiHtLll5LdnKIMw100I+8xJXR5gW2QwWYkT6iJva0bXitZKa/XMrSbdmg3r2Xnaidz9Qumd0VPaMrZlF9V9sA== - -punycode@^2.1.0: - version "2.1.1" - resolved "https://registry.yarnpkg.com/punycode/-/punycode-2.1.1.tgz#b58b010ac40c22c5657616c8d2c2c02c7bf479ec" - integrity sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A== - -regexpp@^2.0.1: - version "2.0.1" - resolved "https://registry.yarnpkg.com/regexpp/-/regexpp-2.0.1.tgz#8d19d31cf632482b589049f8281f93dbcba4d07f" - integrity sha512-lv0M6+TkDVniA3aD1Eg0DVpfU/booSu7Eev3TDO/mZKHBfVjgCGTV4t4buppESEYDtkArYFOxTJWv6S5C+iaNw== - -require-directory@^2.1.1: - version "2.1.1" - resolved "https://registry.yarnpkg.com/require-directory/-/require-directory-2.1.1.tgz#8c64ad5fd30dab1c976e2344ffe7f792a6a6df42" - integrity sha1-jGStX9MNqxyXbiNE/+f3kqam30I= - -require-main-filename@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/require-main-filename/-/require-main-filename-2.0.0.tgz#d0b329ecc7cc0f61649f62215be69af54aa8989b" - integrity sha512-NKN5kMDylKuldxYLSUfrbo5Tuzh4hd+2E8NPPX02mZtn1VuREQToYe/ZdlJy+J3uCpfaiGF05e7B8W0iXbQHmg== - -resolve-from@^4.0.0: - version "4.0.0" - resolved "https://registry.yarnpkg.com/resolve-from/-/resolve-from-4.0.0.tgz#4abcd852ad32dd7baabfe9b40e00a36db5f392e6" - integrity sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g== - -restore-cursor@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/restore-cursor/-/restore-cursor-2.0.0.tgz#9f7ee287f82fd326d4fd162923d62129eee0dfaf" - integrity sha1-n37ih/gv0ybU/RYpI9YhKe7g368= - dependencies: - onetime "^2.0.0" - signal-exit "^3.0.2" - -rhea@^1.0.13: - version "1.0.19" - resolved "https://registry.yarnpkg.com/rhea/-/rhea-1.0.19.tgz#ea3a4e1bb22f81c8e8b066a6d5978e73f121bb63" - integrity sha512-52fctFB6zZ+n2nMo1HvreC4HzV8FwSH7zZyTdMXxNaDMDWeVbZo42cjGeJh8+BZMC1+r7YUZrd3p4cNVAfJf1Q== - dependencies: - debug "0.8.0 - 3.5.0" - -rimraf@2.6.3: - version "2.6.3" - resolved "https://registry.yarnpkg.com/rimraf/-/rimraf-2.6.3.tgz#b2d104fe0d8fb27cf9e0a1cda8262dd3833c6cab" - integrity sha512-mwqeW5XsA2qAejG46gYdENaxXjx9onRNCfn7L0duuP4hCuTIi/QO7PDK07KJfp1d+izWPrzEJDcSqBa0OZQriA== - dependencies: - glob "^7.1.3" - -run-async@^2.2.0: - version "2.3.0" - resolved "https://registry.yarnpkg.com/run-async/-/run-async-2.3.0.tgz#0371ab4ae0bdd720d4166d7dfda64ff7a445a6c0" - integrity sha1-A3GrSuC91yDUFm19/aZP96RFpsA= - dependencies: - is-promise "^2.1.0" - -rxjs@^6.4.0: - version "6.5.4" - resolved "https://registry.yarnpkg.com/rxjs/-/rxjs-6.5.4.tgz#e0777fe0d184cec7872df147f303572d414e211c" - integrity sha512-naMQXcgEo3csAEGvw/NydRA0fuS2nDZJiw1YUWFKU7aPPAPGZEsD4Iimit96qwCieH6y614MCLYwdkrWx7z/7Q== - dependencies: - tslib "^1.9.0" - -"safer-buffer@>= 2.1.2 < 3": - version "2.1.2" - resolved "https://registry.yarnpkg.com/safer-buffer/-/safer-buffer-2.1.2.tgz#44fa161b0187b9549dd84bb91802f9bd8385cd6a" - integrity sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg== - -semver@^5.5.0, semver@^5.5.1, semver@^5.7.0: - version "5.7.1" - resolved "https://registry.yarnpkg.com/semver/-/semver-5.7.1.tgz#a954f931aeba508d307bbf069eff0c01c96116f7" - integrity sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ== - -set-blocking@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/set-blocking/-/set-blocking-2.0.0.tgz#045f9782d011ae9a6803ddd382b24392b3d890f7" - integrity sha1-BF+XgtARrppoA93TgrJDkrPYkPc= - -shebang-command@^1.2.0: - version "1.2.0" - resolved "https://registry.yarnpkg.com/shebang-command/-/shebang-command-1.2.0.tgz#44aac65b695b03398968c39f363fee5deafdf1ea" - integrity sha1-RKrGW2lbAzmJaMOfNj/uXer98eo= - dependencies: - shebang-regex "^1.0.0" - -shebang-regex@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/shebang-regex/-/shebang-regex-1.0.0.tgz#da42f49740c0b42db2ca9728571cb190c98efea3" - integrity sha1-2kL0l0DAtC2yypcoVxyxkMmO/qM= - -signal-exit@^3.0.2: - version "3.0.2" - resolved "https://registry.yarnpkg.com/signal-exit/-/signal-exit-3.0.2.tgz#b5fdc08f1287ea1178628e415e25132b73646c6d" - integrity sha1-tf3AjxKH6hF4Yo5BXiUTK3NkbG0= - -slice-ansi@^2.1.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/slice-ansi/-/slice-ansi-2.1.0.tgz#cacd7693461a637a5788d92a7dd4fba068e81636" - integrity sha512-Qu+VC3EwYLldKa1fCxuuvULvSJOKEgk9pi8dZeCVK7TqBfUNTH4sFkk4joj8afVSfAYgJoSOetjx9QWOJ5mYoQ== - dependencies: - ansi-styles "^3.2.0" - astral-regex "^1.0.0" - is-fullwidth-code-point "^2.0.0" - -sprintf-js@~1.0.2: - version "1.0.3" - resolved "https://registry.yarnpkg.com/sprintf-js/-/sprintf-js-1.0.3.tgz#04e6926f662895354f3dd015203633b857297e2c" - integrity sha1-BOaSb2YolTVPPdAVIDYzuFcpfiw= - -"string-width@^1.0.2 || 2", string-width@^2.1.0: - version "2.1.1" - resolved "https://registry.yarnpkg.com/string-width/-/string-width-2.1.1.tgz#ab93f27a8dc13d28cac815c462143a6d9012ae9e" - integrity sha512-nOqH59deCq9SRHlxq1Aw85Jnt4w6KvLKqWVik6oA9ZklXLNIOlqg4F2yrT1MVaTjAqvVwdfeZ7w7aCvJD7ugkw== - dependencies: - is-fullwidth-code-point "^2.0.0" - strip-ansi "^4.0.0" - -string-width@^3.0.0, string-width@^3.1.0: - version "3.1.0" - resolved "https://registry.yarnpkg.com/string-width/-/string-width-3.1.0.tgz#22767be21b62af1081574306f69ac51b62203961" - integrity sha512-vafcv6KjVZKSgz06oM/H6GDBrAtz8vdhQakGjFIvNrHA6y3HCF1CInLy+QLq8dTJPQ1b+KDUqDFctkdRW44e1w== - dependencies: - emoji-regex "^7.0.1" - is-fullwidth-code-point "^2.0.0" - strip-ansi "^5.1.0" - -string.prototype.trimleft@^2.1.1: - version "2.1.1" - resolved "https://registry.yarnpkg.com/string.prototype.trimleft/-/string.prototype.trimleft-2.1.1.tgz#9bdb8ac6abd6d602b17a4ed321870d2f8dcefc74" - integrity sha512-iu2AGd3PuP5Rp7x2kEZCrB2Nf41ehzh+goo8TV7z8/XDBbsvc6HQIlUl9RjkZ4oyrW1XM5UwlGl1oVEaDjg6Ag== - dependencies: - define-properties "^1.1.3" - function-bind "^1.1.1" - -string.prototype.trimright@^2.1.1: - version "2.1.1" - resolved "https://registry.yarnpkg.com/string.prototype.trimright/-/string.prototype.trimright-2.1.1.tgz#440314b15996c866ce8a0341894d45186200c5d9" - integrity sha512-qFvWL3/+QIgZXVmJBfpHmxLB7xsUXz6HsUmP8+5dRaC3Q7oKUv9Vo6aMCRZC1smrtyECFsIT30PqBJ1gTjAs+g== - dependencies: - define-properties "^1.1.3" - function-bind "^1.1.1" - -strip-ansi@^4.0.0: - version "4.0.0" - resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-4.0.0.tgz#a8479022eb1ac368a871389b635262c505ee368f" - integrity sha1-qEeQIusaw2iocTibY1JixQXuNo8= - dependencies: - ansi-regex "^3.0.0" - -strip-ansi@^5.0.0, strip-ansi@^5.1.0, strip-ansi@^5.2.0: - version "5.2.0" - resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-5.2.0.tgz#8c9a536feb6afc962bdfa5b104a5091c1ad9c0ae" - integrity sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA== - dependencies: - ansi-regex "^4.1.0" - -strip-json-comments@2.0.1, strip-json-comments@^2.0.1: - version "2.0.1" - resolved "https://registry.yarnpkg.com/strip-json-comments/-/strip-json-comments-2.0.1.tgz#3c531942e908c2697c0ec344858c286c7ca0a60a" - integrity sha1-PFMZQukIwml8DsNEhYwobHygpgo= - -supports-color@6.0.0: - version "6.0.0" - resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-6.0.0.tgz#76cfe742cf1f41bb9b1c29ad03068c05b4c0e40a" - integrity sha512-on9Kwidc1IUQo+bQdhi8+Tijpo0e1SS6RoGo2guUwn5vdaxw8RXOF9Vb2ws+ihWOmh4JnCJOvaziZWP1VABaLg== - dependencies: - has-flag "^3.0.0" - -supports-color@^5.3.0: - version "5.5.0" - resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-5.5.0.tgz#e2e69a44ac8772f78a1ec0b35b689df6530efc8f" - integrity sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow== - dependencies: - has-flag "^3.0.0" - -table@^5.2.3: - version "5.4.6" - resolved "https://registry.yarnpkg.com/table/-/table-5.4.6.tgz#1292d19500ce3f86053b05f0e8e7e4a3bb21079e" - integrity sha512-wmEc8m4fjnob4gt5riFRtTu/6+4rSe12TpAELNSqHMfF3IqnA+CH37USM6/YR3qRZv7e56kAEAtd6nKZaxe0Ug== - dependencies: - ajv "^6.10.2" - lodash "^4.17.14" - slice-ansi "^2.1.0" - string-width "^3.0.0" - -text-table@^0.2.0: - version "0.2.0" - resolved "https://registry.yarnpkg.com/text-table/-/text-table-0.2.0.tgz#7f5ee823ae805207c00af2df4a84ec3fcfa570b4" - integrity sha1-f17oI66AUgfACvLfSoTsP8+lcLQ= - -through@^2.3.6: - version "2.3.8" - resolved "https://registry.yarnpkg.com/through/-/through-2.3.8.tgz#0dd4c9ffaabc357960b1b724115d7e0e86a2e1f5" - integrity sha1-DdTJ/6q8NXlgsbckEV1+Doai4fU= - -tmp@^0.0.33: - version "0.0.33" - resolved "https://registry.yarnpkg.com/tmp/-/tmp-0.0.33.tgz#6d34335889768d21b2bcda0aa277ced3b1bfadf9" - integrity sha512-jRCJlojKnZ3addtTOjdIqoRuPEKBvNXcGYqzO6zWZX8KfKEpnGY5jfggJQ3EjKuu8D4bJRr0y+cYJFmYbImXGw== - dependencies: - os-tmpdir "~1.0.2" - -tslib@^1.9.0: - version "1.10.0" - resolved "https://registry.yarnpkg.com/tslib/-/tslib-1.10.0.tgz#c3c19f95973fb0a62973fb09d90d961ee43e5c8a" - integrity sha512-qOebF53frne81cf0S9B41ByenJ3/IuH8yJKngAX35CmiZySA0khhkovshKK+jGCaMnVomla7gVlIcc3EvKPbTQ== - -type-check@~0.3.2: - version "0.3.2" - resolved "https://registry.yarnpkg.com/type-check/-/type-check-0.3.2.tgz#5884cab512cf1d355e3fb784f30804b2b520db72" - integrity sha1-WITKtRLPHTVeP7eE8wgEsrUg23I= - dependencies: - prelude-ls "~1.1.2" - -uri-js@^4.2.2: - version "4.2.2" - resolved "https://registry.yarnpkg.com/uri-js/-/uri-js-4.2.2.tgz#94c540e1ff772956e2299507c010aea6c8838eb0" - integrity sha512-KY9Frmirql91X2Qgjry0Wd4Y+YTdrdZheS8TFwvkbLWf/G5KNJDCh6pKL5OZctEW4+0Baa5idK2ZQuELRwPznQ== - dependencies: - punycode "^2.1.0" - -uuid@^3.3.3: - version "3.4.0" - resolved "https://registry.yarnpkg.com/uuid/-/uuid-3.4.0.tgz#b23e4358afa8a202fe7a100af1f5f883f02007ee" - integrity sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A== - -which-module@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/which-module/-/which-module-2.0.0.tgz#d9ef07dce77b9902b8a3a8fa4b31c3e3f7e6e87a" - integrity sha1-2e8H3Od7mQK4o6j6SzHD4/fm6Ho= - -which@1.3.1, which@^1.2.9: - version "1.3.1" - resolved "https://registry.yarnpkg.com/which/-/which-1.3.1.tgz#a45043d54f5805316da8d62f9f50918d3da70b0a" - integrity sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ== - dependencies: - isexe "^2.0.0" - -wide-align@1.1.3: - version "1.1.3" - resolved "https://registry.yarnpkg.com/wide-align/-/wide-align-1.1.3.tgz#ae074e6bdc0c14a431e804e624549c633b000457" - integrity sha512-QGkOQc8XL6Bt5PwnsExKBPuMKBxnGxWWW3fU55Xt4feHozMUhdUMaBCk290qpm/wG5u/RSKzwdAC4i51YigihA== - dependencies: - string-width "^1.0.2 || 2" - -word-wrap@~1.2.3: - version "1.2.3" - resolved "https://registry.yarnpkg.com/word-wrap/-/word-wrap-1.2.3.tgz#610636f6b1f703891bd34771ccb17fb93b47079c" - integrity sha512-Hz/mrNwitNRh/HUAtM/VT/5VH+ygD6DV7mYKZAtHOrbs8U7lvPS6xf7EJKMF0uW1KJCl0H701g3ZGus+muE5vQ== - -wrap-ansi@^5.1.0: - version "5.1.0" - resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-5.1.0.tgz#1fd1f67235d5b6d0fee781056001bfb694c03b09" - integrity sha512-QC1/iN/2/RPVJ5jYK8BGttj5z83LmSKmvbvrXPNCLZSEb32KKVDJDl/MOt2N01qU2H/FkzEa9PKto1BqDjtd7Q== - dependencies: - ansi-styles "^3.2.0" - string-width "^3.0.0" - strip-ansi "^5.0.0" - -wrappy@1: - version "1.0.2" - resolved "https://registry.yarnpkg.com/wrappy/-/wrappy-1.0.2.tgz#b5243d8f3ec1aa35f1364605bc0d1036e30ab69f" - integrity sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8= - -write@1.0.3: - version "1.0.3" - resolved "https://registry.yarnpkg.com/write/-/write-1.0.3.tgz#0800e14523b923a387e415123c865616aae0f5c3" - integrity sha512-/lg70HAjtkUgWPVZhZcm+T4hkL8Zbtp1nFNOn3lRrxnlv50SRBv7cR7RqR+GMsd3hUXy9hWBo4CHTbFTcOYwig== - dependencies: - mkdirp "^0.5.1" - -xml@^1.0.0: - version "1.0.1" - resolved "https://registry.yarnpkg.com/xml/-/xml-1.0.1.tgz#78ba72020029c5bc87b8a81a3cfcd74b4a2fc1e5" - integrity sha1-eLpyAgApxbyHuKgaPPzXS0ovweU= - -y18n@^4.0.0: - version "4.0.0" - resolved "https://registry.yarnpkg.com/y18n/-/y18n-4.0.0.tgz#95ef94f85ecc81d007c264e190a120f0a3c8566b" - integrity sha512-r9S/ZyXu/Xu9q1tYlpsLIsa3EeLXXk0VwlxqTcFRfg9EhMW+17kbt9G0NrgCmhGb5vT2hyhJZLfDGx+7+5Uj/w== - -yargs-parser@13.1.1, yargs-parser@^13.1.1: - version "13.1.1" - resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-13.1.1.tgz#d26058532aa06d365fe091f6a1fc06b2f7e5eca0" - integrity sha512-oVAVsHz6uFrg3XQheFII8ESO2ssAf9luWuAd6Wexsu4F3OtIW0o8IribPXYrD4WC24LWtPrJlGy87y5udK+dxQ== - dependencies: - camelcase "^5.0.0" - decamelize "^1.2.0" - -yargs-unparser@1.6.0: - version "1.6.0" - resolved "https://registry.yarnpkg.com/yargs-unparser/-/yargs-unparser-1.6.0.tgz#ef25c2c769ff6bd09e4b0f9d7c605fb27846ea9f" - integrity sha512-W9tKgmSn0DpSatfri0nx52Joq5hVXgeLiqR/5G0sZNDoLZFOr/xjBUDcShCOGNsBnEMNo1KAMBkTej1Hm62HTw== - dependencies: - flat "^4.1.0" - lodash "^4.17.15" - yargs "^13.3.0" - -yargs@13.3.0, yargs@^13.3.0: - version "13.3.0" - resolved "https://registry.yarnpkg.com/yargs/-/yargs-13.3.0.tgz#4c657a55e07e5f2cf947f8a366567c04a0dedc83" - integrity sha512-2eehun/8ALW8TLoIl7MVaRUrg+yCnenu8B4kBlRxj3GJGDKU1Og7sMXPNm1BYyM1DOJmTZ4YeN/Nwxv+8XJsUA== - dependencies: - cliui "^5.0.0" - find-up "^3.0.0" - get-caller-file "^2.0.1" - require-directory "^2.1.1" - require-main-filename "^2.0.0" - set-blocking "^2.0.0" - string-width "^3.0.0" - which-module "^2.0.0" - y18n "^4.0.0" - yargs-parser "^13.1.1" diff --git a/amqp-utils/pom.xml b/amqp-utils/pom.xml index 0a365109638..185a8096e5a 100644 --- a/amqp-utils/pom.xml +++ b/amqp-utils/pom.xml @@ -4,7 +4,7 @@ io.enmasse enmasse - 0.32-SNAPSHOT + 1.0-SNAPSHOT 4.0.0 amqp-utils diff --git a/api-common/pom.xml b/api-common/pom.xml index b1e7677e781..69e9241907a 100644 --- a/api-common/pom.xml +++ b/api-common/pom.xml @@ -4,7 +4,7 @@ io.enmasse enmasse - 0.32-SNAPSHOT + 1.0-SNAPSHOT 4.0.0 api-common @@ -13,21 +13,11 @@ io.enmasse api-model - - io.enmasse - keycloak-user-api - compile - io.enmasse amqp-utils compile - - io.enmasse - k8s-api - compile - io.fabric8 openshift-client @@ -84,11 +74,6 @@ mockito-core test - - io.enmasse - k8s-api-testutil - test - io.fabric8 kubernetes-server-mock diff --git a/api-common/src/main/java/io/enmasse/api/common/DefaultExceptionMapper.java b/api-common/src/main/java/io/enmasse/api/common/DefaultExceptionMapper.java index cceadc8066f..d908f01c3f9 100644 --- a/api-common/src/main/java/io/enmasse/api/common/DefaultExceptionMapper.java +++ b/api-common/src/main/java/io/enmasse/api/common/DefaultExceptionMapper.java @@ -13,8 +13,6 @@ import com.fasterxml.jackson.core.JsonProcessingException; import io.enmasse.address.model.UnresolvedAddressException; import io.enmasse.address.model.UnresolvedAddressSpaceException; -import io.enmasse.user.keycloak.KeycloakUnavailableException; -import io.enmasse.user.model.v1.UserValidationFailedException; import io.fabric8.kubernetes.client.KubernetesClientException; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -40,10 +38,8 @@ public Response toResponse(Exception exception) { statusCode = Response.Status.INTERNAL_SERVER_ERROR.getStatusCode(); } - } else if (exception instanceof UnresolvedAddressException || exception instanceof JsonProcessingException || exception instanceof UnresolvedAddressSpaceException || exception instanceof ValidationException || exception instanceof UserValidationFailedException) { + } else if (exception instanceof UnresolvedAddressException || exception instanceof JsonProcessingException || exception instanceof UnresolvedAddressSpaceException || exception instanceof ValidationException) { statusCode = Response.Status.BAD_REQUEST.getStatusCode(); - } else if (exception instanceof KeycloakUnavailableException) { - statusCode = Response.Status.SERVICE_UNAVAILABLE.getStatusCode(); } else { statusCode = Response.Status.INTERNAL_SERVER_ERROR.getStatusCode(); } diff --git a/api-model-annotator/pom.xml b/api-model-annotator/pom.xml index 157e5f543e2..7f1fc1aa6d2 100644 --- a/api-model-annotator/pom.xml +++ b/api-model-annotator/pom.xml @@ -4,7 +4,7 @@ io.enmasse enmasse - 0.32-SNAPSHOT + 1.0-SNAPSHOT 4.0.0 api-model-annotator diff --git a/api-model/generator/generate.go b/api-model/generator/generate.go index dbf2bd5f06d..40451d02748 100644 --- a/api-model/generator/generate.go +++ b/api-model/generator/generate.go @@ -39,6 +39,10 @@ type Schema struct { MessagingAddressList enmasseapi.MessagingAddressList MessagingEndpoint enmasseapi.MessagingEndpoint MessagingEndpointList enmasseapi.MessagingEndpointList + MessagingPlan enmasseapi.MessagingPlan + MessagingPlanList enmasseapi.MessagingPlanList + MessagingAddressPlan enmasseapi.MessagingAddressPlan + MessagingAddressPlanList enmasseapi.MessagingAddressPlanList } func main() { diff --git a/api-model/pom.xml b/api-model/pom.xml index 42adb616d47..e816ee7d461 100644 --- a/api-model/pom.xml +++ b/api-model/pom.xml @@ -4,7 +4,7 @@ io.enmasse enmasse - 0.32-SNAPSHOT + 1.0-SNAPSHOT 4.0.0 api-model @@ -50,6 +50,10 @@ org.glassfish javax.el + + io.quarkus + quarkus-core + org.slf4j slf4j-api @@ -166,6 +170,19 @@ + + + org.jboss.jandex + jandex-maven-plugin + + + make-index + + jandex + + + + diff --git a/api-model/src/main/java/io/enmasse/address/model/CoreCrd.java b/api-model/src/main/java/io/enmasse/address/model/CoreCrd.java index b533cdf161f..5179ec1292c 100644 --- a/api-model/src/main/java/io/enmasse/address/model/CoreCrd.java +++ b/api-model/src/main/java/io/enmasse/address/model/CoreCrd.java @@ -24,6 +24,8 @@ public class CoreCrd { private static final CustomResourceDefinition MESSAGING_TENANT_CRD; private static final CustomResourceDefinition MESSAGING_ADDRESS_CRD; private static final CustomResourceDefinition MESSAGING_ENDPOINT_CRD; + private static final CustomResourceDefinition MESSAGING_PLAN_CRD; + private static final CustomResourceDefinition MESSAGING_ADDRESS_PLAN_CRD; static { ADDRESS_CRD = CustomResources.createCustomResource(GROUP, VERSION, Address.KIND); @@ -33,6 +35,8 @@ public class CoreCrd { MESSAGING_TENANT_CRD = CustomResources.createCustomResource(GROUP, VERSION_BETA2, "MessagingTenant"); MESSAGING_ADDRESS_CRD= CustomResources.createCustomResource(GROUP, VERSION_BETA2, "MessagingAddress"); MESSAGING_ENDPOINT_CRD= CustomResources.createCustomResource(GROUP, VERSION_BETA2, "MessagingEndpoint"); + MESSAGING_PLAN_CRD= CustomResources.createCustomResource(GROUP, VERSION_BETA2, "MessagingPlan"); + MESSAGING_ADDRESS_PLAN_CRD= CustomResources.createCustomResource(GROUP, VERSION_BETA2, "MessagingAddressPlan"); } public static void registerCustomCrds() { @@ -56,6 +60,12 @@ public static void registerCustomCrds() { KubernetesDeserializer.registerCustomKind(API_VERSION_BETA2, "MessagingEndpoint", MessagingEndpoint.class); KubernetesDeserializer.registerCustomKind(API_VERSION_BETA2, "MessagingEndpointList", MessagingEndpointList.class); + + KubernetesDeserializer.registerCustomKind(API_VERSION_BETA2, "MessagingPlan", MessagingPlan.class); + KubernetesDeserializer.registerCustomKind(API_VERSION_BETA2, "MessagingPlanList", MessagingPlanList.class); + + KubernetesDeserializer.registerCustomKind(API_VERSION_BETA2, "MessagingAddressPlan", MessagingAddressPlan.class); + KubernetesDeserializer.registerCustomKind(API_VERSION_BETA2, "MessagingAddressPlanList", MessagingAddressPlanList.class); } public static CustomResourceDefinition addresses() { return ADDRESS_CRD; @@ -84,4 +94,12 @@ public static CustomResourceDefinition messagingAddresses() { public static CustomResourceDefinition messagingEndpoints() { return MESSAGING_ENDPOINT_CRD; } + + public static CustomResourceDefinition messagingPlans() { + return MESSAGING_PLAN_CRD; + } + + public static CustomResourceDefinition messagingAddressPlans() { + return MESSAGING_ADDRESS_PLAN_CRD; + } } diff --git a/api-model/src/main/java/io/enmasse/common/model/AbstractHasMetadata.java b/api-model/src/main/java/io/enmasse/common/model/AbstractHasMetadata.java index b70f7a5b867..7ab488957f0 100644 --- a/api-model/src/main/java/io/enmasse/common/model/AbstractHasMetadata.java +++ b/api-model/src/main/java/io/enmasse/common/model/AbstractHasMetadata.java @@ -16,6 +16,7 @@ import io.fabric8.kubernetes.api.model.Doneable; import io.fabric8.kubernetes.api.model.HasMetadata; import io.fabric8.kubernetes.api.model.ObjectMeta; +import io.quarkus.runtime.annotations.RegisterForReflection; import io.sundr.builder.annotations.Buildable; import io.sundr.builder.annotations.Inline; @@ -28,6 +29,7 @@ prefix = "Doneable", value = "done")) @SuppressWarnings("serial") +@RegisterForReflection public abstract class AbstractHasMetadata extends AbstractResource implements HasMetadata { @FunctionalInterface diff --git a/api-model/src/main/java/io/enmasse/common/model/AbstractList.java b/api-model/src/main/java/io/enmasse/common/model/AbstractList.java index acaf42926fc..9cfa0346e5d 100644 --- a/api-model/src/main/java/io/enmasse/common/model/AbstractList.java +++ b/api-model/src/main/java/io/enmasse/common/model/AbstractList.java @@ -15,8 +15,10 @@ import io.fabric8.kubernetes.api.model.KubernetesResource; import io.fabric8.kubernetes.api.model.KubernetesResourceList; import io.fabric8.kubernetes.api.model.ListMeta; +import io.quarkus.runtime.annotations.RegisterForReflection; @SuppressWarnings("serial") +@RegisterForReflection public abstract class AbstractList extends AbstractResource implements KubernetesResource, KubernetesResourceList { diff --git a/api-model/src/main/java/io/enmasse/common/model/AbstractResource.java b/api-model/src/main/java/io/enmasse/common/model/AbstractResource.java index 382cf14f549..adfe3789627 100644 --- a/api-model/src/main/java/io/enmasse/common/model/AbstractResource.java +++ b/api-model/src/main/java/io/enmasse/common/model/AbstractResource.java @@ -6,6 +6,7 @@ package io.enmasse.common.model; import io.fabric8.kubernetes.api.model.Doneable; +import io.quarkus.runtime.annotations.RegisterForReflection; import io.sundr.builder.annotations.Buildable; import io.sundr.builder.annotations.Inline; @@ -17,6 +18,7 @@ type = Doneable.class, prefix = "Doneable", value = "done")) +@RegisterForReflection public abstract class AbstractResource { private String kind; diff --git a/api-model/src/main/java/io/enmasse/common/model/DefaultCustomResource.java b/api-model/src/main/java/io/enmasse/common/model/DefaultCustomResource.java index 1a1c0ae7d1f..0a66c47c71d 100644 --- a/api-model/src/main/java/io/enmasse/common/model/DefaultCustomResource.java +++ b/api-model/src/main/java/io/enmasse/common/model/DefaultCustomResource.java @@ -15,6 +15,7 @@ import com.fasterxml.jackson.annotation.JsonInclude; import com.fasterxml.jackson.annotation.JsonPropertyOrder; import com.fasterxml.jackson.databind.annotation.JsonDeserialize; +import io.quarkus.runtime.annotations.RegisterForReflection; /** * A default set of Jackson annotations for custom resources. @@ -25,6 +26,7 @@ @JsonInclude(JsonInclude.Include.NON_NULL) @Retention(RUNTIME) @Target(TYPE) +@RegisterForReflection public @interface DefaultCustomResource { } diff --git a/api-model/src/main/java/io/enmasse/iot/model/v1/AcceptedStatus.java b/api-model/src/main/java/io/enmasse/iot/model/v1/AcceptedStatus.java index 23d55501710..894fe83bd41 100644 --- a/api-model/src/main/java/io/enmasse/iot/model/v1/AcceptedStatus.java +++ b/api-model/src/main/java/io/enmasse/iot/model/v1/AcceptedStatus.java @@ -8,6 +8,7 @@ import com.fasterxml.jackson.annotation.JsonInclude; import io.fabric8.kubernetes.api.model.Doneable; +import io.quarkus.runtime.annotations.RegisterForReflection; import io.sundr.builder.annotations.Buildable; import io.sundr.builder.annotations.Inline; @@ -22,6 +23,7 @@ ) ) @JsonInclude(JsonInclude.Include.NON_NULL) +@RegisterForReflection public class AcceptedStatus { /* diff --git a/api-model/src/main/java/io/enmasse/iot/model/v1/AdapterConfig.java b/api-model/src/main/java/io/enmasse/iot/model/v1/AdapterConfig.java index 88d4b70ca49..26c3f05cb58 100644 --- a/api-model/src/main/java/io/enmasse/iot/model/v1/AdapterConfig.java +++ b/api-model/src/main/java/io/enmasse/iot/model/v1/AdapterConfig.java @@ -8,6 +8,7 @@ import com.fasterxml.jackson.annotation.JsonInclude; import io.fabric8.kubernetes.api.model.Doneable; +import io.quarkus.runtime.annotations.RegisterForReflection; import io.sundr.builder.annotations.Buildable; import io.sundr.builder.annotations.Inline; @@ -22,6 +23,7 @@ ) ) @JsonInclude(JsonInclude.Include.NON_NULL) +@RegisterForReflection public class AdapterConfig extends ServiceConfig { private Boolean enabled; diff --git a/api-model/src/main/java/io/enmasse/iot/model/v1/AdapterConfiguration.java b/api-model/src/main/java/io/enmasse/iot/model/v1/AdapterConfiguration.java index 4ded55b6223..73eb1dc111d 100644 --- a/api-model/src/main/java/io/enmasse/iot/model/v1/AdapterConfiguration.java +++ b/api-model/src/main/java/io/enmasse/iot/model/v1/AdapterConfiguration.java @@ -8,6 +8,7 @@ import com.fasterxml.jackson.databind.node.ObjectNode; import io.fabric8.kubernetes.api.model.Doneable; +import io.quarkus.runtime.annotations.RegisterForReflection; import io.sundr.builder.annotations.Buildable; import io.sundr.builder.annotations.Inline; @@ -20,6 +21,7 @@ prefix = "Doneable", value = "done")) @JsonInclude(JsonInclude.Include.NON_NULL) +@RegisterForReflection public class AdapterConfiguration { private Boolean enabled; diff --git a/api-model/src/main/java/io/enmasse/iot/model/v1/AdapterOptions.java b/api-model/src/main/java/io/enmasse/iot/model/v1/AdapterOptions.java index 4684a6a6828..ae474298531 100644 --- a/api-model/src/main/java/io/enmasse/iot/model/v1/AdapterOptions.java +++ b/api-model/src/main/java/io/enmasse/iot/model/v1/AdapterOptions.java @@ -8,6 +8,7 @@ import com.fasterxml.jackson.annotation.JsonInclude; import io.fabric8.kubernetes.api.model.Doneable; +import io.quarkus.runtime.annotations.RegisterForReflection; import io.sundr.builder.annotations.Buildable; import io.sundr.builder.annotations.Inline; @@ -20,6 +21,7 @@ prefix = "Doneable", value = "done")) @JsonInclude(JsonInclude.Include.NON_NULL) +@RegisterForReflection public class AdapterOptions { private long maxPayloadSize; diff --git a/api-model/src/main/java/io/enmasse/iot/model/v1/AdapterStatus.java b/api-model/src/main/java/io/enmasse/iot/model/v1/AdapterStatus.java index f00c72a1be3..a9696019156 100644 --- a/api-model/src/main/java/io/enmasse/iot/model/v1/AdapterStatus.java +++ b/api-model/src/main/java/io/enmasse/iot/model/v1/AdapterStatus.java @@ -7,6 +7,7 @@ import com.fasterxml.jackson.annotation.JsonInclude; import io.fabric8.kubernetes.api.model.Doneable; +import io.quarkus.runtime.annotations.RegisterForReflection; import io.sundr.builder.annotations.Buildable; import io.sundr.builder.annotations.Inline; @@ -21,6 +22,7 @@ ) ) @JsonInclude(JsonInclude.Include.NON_NULL) +@RegisterForReflection public class AdapterStatus extends CommonStatus { private boolean enabled; diff --git a/api-model/src/main/java/io/enmasse/iot/model/v1/AdaptersConfig.java b/api-model/src/main/java/io/enmasse/iot/model/v1/AdaptersConfig.java index 2764bca788e..067937e3c60 100644 --- a/api-model/src/main/java/io/enmasse/iot/model/v1/AdaptersConfig.java +++ b/api-model/src/main/java/io/enmasse/iot/model/v1/AdaptersConfig.java @@ -9,6 +9,7 @@ import com.fasterxml.jackson.annotation.JsonProperty; import io.fabric8.kubernetes.api.model.Doneable; +import io.quarkus.runtime.annotations.RegisterForReflection; import io.sundr.builder.annotations.Buildable; import io.sundr.builder.annotations.Inline; @@ -23,6 +24,7 @@ ) ) @JsonInclude(JsonInclude.Include.NON_NULL) +@RegisterForReflection public class AdaptersConfig { private AdapterOptions defaults; private AdapterConfig http; diff --git a/api-model/src/main/java/io/enmasse/iot/model/v1/AddressConfig.java b/api-model/src/main/java/io/enmasse/iot/model/v1/AddressConfig.java index 3af696f66ee..6534da2cb98 100644 --- a/api-model/src/main/java/io/enmasse/iot/model/v1/AddressConfig.java +++ b/api-model/src/main/java/io/enmasse/iot/model/v1/AddressConfig.java @@ -8,6 +8,7 @@ import com.fasterxml.jackson.annotation.JsonInclude; import io.fabric8.kubernetes.api.model.Doneable; +import io.quarkus.runtime.annotations.RegisterForReflection; import io.sundr.builder.annotations.Buildable; import io.sundr.builder.annotations.Inline; @@ -20,6 +21,7 @@ prefix = "Doneable", value = "done")) @JsonInclude(JsonInclude.Include.NON_NULL) +@RegisterForReflection public class AddressConfig { private String plan; diff --git a/api-model/src/main/java/io/enmasse/iot/model/v1/AddressSpaceConfig.java b/api-model/src/main/java/io/enmasse/iot/model/v1/AddressSpaceConfig.java index c8c87f656ae..8d21fcc6f17 100644 --- a/api-model/src/main/java/io/enmasse/iot/model/v1/AddressSpaceConfig.java +++ b/api-model/src/main/java/io/enmasse/iot/model/v1/AddressSpaceConfig.java @@ -7,6 +7,7 @@ import com.fasterxml.jackson.annotation.JsonInclude; import io.fabric8.kubernetes.api.model.Doneable; +import io.quarkus.runtime.annotations.RegisterForReflection; import io.sundr.builder.annotations.Buildable; import io.sundr.builder.annotations.Inline; @@ -19,6 +20,7 @@ prefix = "Doneable", value = "done")) @JsonInclude(JsonInclude.Include.NON_NULL) +@RegisterForReflection public class AddressSpaceConfig { private String name; private String plan; diff --git a/api-model/src/main/java/io/enmasse/iot/model/v1/AddressesConfig.java b/api-model/src/main/java/io/enmasse/iot/model/v1/AddressesConfig.java index 90ad1a2a979..92fb0163e63 100644 --- a/api-model/src/main/java/io/enmasse/iot/model/v1/AddressesConfig.java +++ b/api-model/src/main/java/io/enmasse/iot/model/v1/AddressesConfig.java @@ -8,6 +8,7 @@ import com.fasterxml.jackson.annotation.JsonInclude; import io.fabric8.kubernetes.api.model.Doneable; +import io.quarkus.runtime.annotations.RegisterForReflection; import io.sundr.builder.annotations.Buildable; import io.sundr.builder.annotations.Inline; @@ -20,6 +21,7 @@ prefix = "Doneable", value = "done")) @JsonInclude(JsonInclude.Include.NON_NULL) +@RegisterForReflection public class AddressesConfig { private AddressConfig command; diff --git a/api-model/src/main/java/io/enmasse/iot/model/v1/AuthenticationServiceConfig.java b/api-model/src/main/java/io/enmasse/iot/model/v1/AuthenticationServiceConfig.java index 0158599f538..ef3bb47f9ec 100644 --- a/api-model/src/main/java/io/enmasse/iot/model/v1/AuthenticationServiceConfig.java +++ b/api-model/src/main/java/io/enmasse/iot/model/v1/AuthenticationServiceConfig.java @@ -8,6 +8,7 @@ import com.fasterxml.jackson.annotation.JsonInclude; import io.fabric8.kubernetes.api.model.Doneable; +import io.quarkus.runtime.annotations.RegisterForReflection; import io.sundr.builder.annotations.Buildable; import io.sundr.builder.annotations.Inline; @@ -20,5 +21,6 @@ prefix = "Doneable", value = "done")) @JsonInclude(JsonInclude.Include.NON_NULL) +@RegisterForReflection public class AuthenticationServiceConfig extends CommonServiceConfig { } diff --git a/api-model/src/main/java/io/enmasse/iot/model/v1/CollectorConfig.java b/api-model/src/main/java/io/enmasse/iot/model/v1/CollectorConfig.java index 361a11d1e7c..da8c3e87959 100644 --- a/api-model/src/main/java/io/enmasse/iot/model/v1/CollectorConfig.java +++ b/api-model/src/main/java/io/enmasse/iot/model/v1/CollectorConfig.java @@ -8,6 +8,7 @@ import com.fasterxml.jackson.annotation.JsonInclude; import io.fabric8.kubernetes.api.model.Doneable; +import io.quarkus.runtime.annotations.RegisterForReflection; import io.sundr.builder.annotations.Buildable; import io.sundr.builder.annotations.Inline; @@ -20,6 +21,7 @@ prefix = "Doneable", value = "done")) @JsonInclude(JsonInclude.Include.NON_NULL) +@RegisterForReflection public class CollectorConfig { private ContainerConfig container; diff --git a/api-model/src/main/java/io/enmasse/iot/model/v1/CommonAdapterContainers.java b/api-model/src/main/java/io/enmasse/iot/model/v1/CommonAdapterContainers.java index 13b1486cc75..b326a3decdb 100644 --- a/api-model/src/main/java/io/enmasse/iot/model/v1/CommonAdapterContainers.java +++ b/api-model/src/main/java/io/enmasse/iot/model/v1/CommonAdapterContainers.java @@ -8,6 +8,7 @@ import com.fasterxml.jackson.annotation.JsonInclude; import io.fabric8.kubernetes.api.model.Doneable; +import io.quarkus.runtime.annotations.RegisterForReflection; import io.sundr.builder.annotations.Buildable; import io.sundr.builder.annotations.Inline; @@ -22,6 +23,7 @@ ) ) @JsonInclude(JsonInclude.Include.NON_NULL) +@RegisterForReflection public class CommonAdapterContainers { private JavaContainerConfig adapter; diff --git a/api-model/src/main/java/io/enmasse/iot/model/v1/CommonCondition.java b/api-model/src/main/java/io/enmasse/iot/model/v1/CommonCondition.java index 8b1021bd875..142e78623f6 100644 --- a/api-model/src/main/java/io/enmasse/iot/model/v1/CommonCondition.java +++ b/api-model/src/main/java/io/enmasse/iot/model/v1/CommonCondition.java @@ -10,6 +10,7 @@ import com.google.common.base.MoreObjects.ToStringHelper; import io.fabric8.kubernetes.api.model.Doneable; +import io.quarkus.runtime.annotations.RegisterForReflection; import io.sundr.builder.annotations.Buildable; import io.sundr.builder.annotations.Inline; @@ -22,6 +23,7 @@ prefix = "Doneable", value = "done")) @JsonInclude(JsonInclude.Include.NON_NULL) +@RegisterForReflection public class CommonCondition> { private T type; diff --git a/api-model/src/main/java/io/enmasse/iot/model/v1/CommonDeviceRegistry.java b/api-model/src/main/java/io/enmasse/iot/model/v1/CommonDeviceRegistry.java index 3d053fd6c5c..78bcee177f9 100644 --- a/api-model/src/main/java/io/enmasse/iot/model/v1/CommonDeviceRegistry.java +++ b/api-model/src/main/java/io/enmasse/iot/model/v1/CommonDeviceRegistry.java @@ -10,6 +10,7 @@ import com.fasterxml.jackson.annotation.JsonInclude; import io.fabric8.kubernetes.api.model.Doneable; +import io.quarkus.runtime.annotations.RegisterForReflection; import io.sundr.builder.annotations.Buildable; import io.sundr.builder.annotations.Inline; @@ -22,6 +23,7 @@ prefix = "Doneable", value = "done")) @JsonInclude(NON_DEFAULT) +@RegisterForReflection public class CommonDeviceRegistry { private boolean disabled; diff --git a/api-model/src/main/java/io/enmasse/iot/model/v1/CommonLoggingConfig.java b/api-model/src/main/java/io/enmasse/iot/model/v1/CommonLoggingConfig.java index 2dc01bb2afe..995051d0adc 100644 --- a/api-model/src/main/java/io/enmasse/iot/model/v1/CommonLoggingConfig.java +++ b/api-model/src/main/java/io/enmasse/iot/model/v1/CommonLoggingConfig.java @@ -13,6 +13,7 @@ import io.fabric8.kubernetes.api.model.Doneable; import io.fabric8.kubernetes.api.model.ResourceRequirements; +import io.quarkus.runtime.annotations.RegisterForReflection; import io.sundr.builder.annotations.Buildable; import io.sundr.builder.annotations.BuildableReference; import io.sundr.builder.annotations.Inline; @@ -27,6 +28,7 @@ prefix = "Doneable", value = "done")) @JsonInclude(JsonInclude.Include.NON_EMPTY) +@RegisterForReflection public class CommonLoggingConfig { private String level; diff --git a/api-model/src/main/java/io/enmasse/iot/model/v1/CommonServiceConfig.java b/api-model/src/main/java/io/enmasse/iot/model/v1/CommonServiceConfig.java index 05422cc82f9..b5e5509e179 100644 --- a/api-model/src/main/java/io/enmasse/iot/model/v1/CommonServiceConfig.java +++ b/api-model/src/main/java/io/enmasse/iot/model/v1/CommonServiceConfig.java @@ -10,6 +10,7 @@ import com.fasterxml.jackson.annotation.JsonInclude; import io.fabric8.kubernetes.api.model.Doneable; +import io.quarkus.runtime.annotations.RegisterForReflection; import io.sundr.builder.annotations.Buildable; import io.sundr.builder.annotations.Inline; @@ -22,6 +23,7 @@ prefix = "Doneable", value = "done")) @JsonInclude(NON_NULL) +@RegisterForReflection public class CommonServiceConfig extends ServiceConfig { private JavaContainerConfig container; diff --git a/api-model/src/main/java/io/enmasse/iot/model/v1/CommonStatus.java b/api-model/src/main/java/io/enmasse/iot/model/v1/CommonStatus.java index dfbce76a8c9..e8ab41e85cf 100644 --- a/api-model/src/main/java/io/enmasse/iot/model/v1/CommonStatus.java +++ b/api-model/src/main/java/io/enmasse/iot/model/v1/CommonStatus.java @@ -8,6 +8,7 @@ import com.fasterxml.jackson.annotation.JsonInclude; import io.fabric8.kubernetes.api.model.Doneable; +import io.quarkus.runtime.annotations.RegisterForReflection; import io.sundr.builder.annotations.Buildable; import io.sundr.builder.annotations.Inline; @@ -20,6 +21,7 @@ prefix = "Doneable", value = "done")) @JsonInclude(JsonInclude.Include.NON_NULL) +@RegisterForReflection public class CommonStatus { private EndpointStatus endpoint; diff --git a/api-model/src/main/java/io/enmasse/iot/model/v1/ConfigCondition.java b/api-model/src/main/java/io/enmasse/iot/model/v1/ConfigCondition.java index d25e860a291..3b70c28dd49 100644 --- a/api-model/src/main/java/io/enmasse/iot/model/v1/ConfigCondition.java +++ b/api-model/src/main/java/io/enmasse/iot/model/v1/ConfigCondition.java @@ -8,6 +8,7 @@ import com.fasterxml.jackson.annotation.JsonInclude; import io.fabric8.kubernetes.api.model.Doneable; +import io.quarkus.runtime.annotations.RegisterForReflection; import io.sundr.builder.annotations.Buildable; import io.sundr.builder.annotations.Inline; @@ -20,6 +21,7 @@ prefix = "Doneable", value = "done")) @JsonInclude(JsonInclude.Include.NON_NULL) +@RegisterForReflection public class ConfigCondition extends CommonCondition { } diff --git a/api-model/src/main/java/io/enmasse/iot/model/v1/ConfigConditionType.java b/api-model/src/main/java/io/enmasse/iot/model/v1/ConfigConditionType.java index 32ef19a58d4..156ec6eb5a9 100644 --- a/api-model/src/main/java/io/enmasse/iot/model/v1/ConfigConditionType.java +++ b/api-model/src/main/java/io/enmasse/iot/model/v1/ConfigConditionType.java @@ -6,7 +6,9 @@ package io.enmasse.iot.model.v1; import com.fasterxml.jackson.annotation.JsonValue; +import io.quarkus.runtime.annotations.RegisterForReflection; +@RegisterForReflection public enum ConfigConditionType { READY("Ready"), diff --git a/api-model/src/main/java/io/enmasse/iot/model/v1/ContainerConfig.java b/api-model/src/main/java/io/enmasse/iot/model/v1/ContainerConfig.java index 9f72c0f3766..c6a12952e84 100644 --- a/api-model/src/main/java/io/enmasse/iot/model/v1/ContainerConfig.java +++ b/api-model/src/main/java/io/enmasse/iot/model/v1/ContainerConfig.java @@ -9,6 +9,7 @@ import io.fabric8.kubernetes.api.model.Doneable; import io.fabric8.kubernetes.api.model.ResourceRequirements; +import io.quarkus.runtime.annotations.RegisterForReflection; import io.sundr.builder.annotations.Buildable; import io.sundr.builder.annotations.BuildableReference; import io.sundr.builder.annotations.Inline; @@ -25,6 +26,7 @@ ) ) @JsonInclude(JsonInclude.Include.NON_NULL) +@RegisterForReflection public class ContainerConfig { private ResourceRequirements resources; diff --git a/api-model/src/main/java/io/enmasse/iot/model/v1/DeviceConnectionServiceConfig.java b/api-model/src/main/java/io/enmasse/iot/model/v1/DeviceConnectionServiceConfig.java index 923a7661fb0..28a080613d1 100644 --- a/api-model/src/main/java/io/enmasse/iot/model/v1/DeviceConnectionServiceConfig.java +++ b/api-model/src/main/java/io/enmasse/iot/model/v1/DeviceConnectionServiceConfig.java @@ -8,6 +8,7 @@ import com.fasterxml.jackson.annotation.JsonInclude; import io.fabric8.kubernetes.api.model.Doneable; +import io.quarkus.runtime.annotations.RegisterForReflection; import io.sundr.builder.annotations.Buildable; import io.sundr.builder.annotations.Inline; @@ -20,6 +21,7 @@ prefix = "Doneable", value = "done")) @JsonInclude(JsonInclude.Include.NON_NULL) +@RegisterForReflection public class DeviceConnectionServiceConfig { private InfinispanDeviceConnection infinispan; diff --git a/api-model/src/main/java/io/enmasse/iot/model/v1/DeviceRegistryServiceConfig.java b/api-model/src/main/java/io/enmasse/iot/model/v1/DeviceRegistryServiceConfig.java index b5040c8124f..4f65f930c56 100644 --- a/api-model/src/main/java/io/enmasse/iot/model/v1/DeviceRegistryServiceConfig.java +++ b/api-model/src/main/java/io/enmasse/iot/model/v1/DeviceRegistryServiceConfig.java @@ -8,6 +8,7 @@ import com.fasterxml.jackson.annotation.JsonInclude; import io.fabric8.kubernetes.api.model.Doneable; +import io.quarkus.runtime.annotations.RegisterForReflection; import io.sundr.builder.annotations.Buildable; import io.sundr.builder.annotations.Inline; @@ -20,6 +21,7 @@ prefix = "Doneable", value = "done")) @JsonInclude(JsonInclude.Include.NON_NULL) +@RegisterForReflection public class DeviceRegistryServiceConfig { private InfinispanDeviceRegistry infinispan; private JdbcDeviceRegistry jdbc; diff --git a/api-model/src/main/java/io/enmasse/iot/model/v1/DownstreamStrategy.java b/api-model/src/main/java/io/enmasse/iot/model/v1/DownstreamStrategy.java index 0bd88425239..3a0f10e2ae9 100644 --- a/api-model/src/main/java/io/enmasse/iot/model/v1/DownstreamStrategy.java +++ b/api-model/src/main/java/io/enmasse/iot/model/v1/DownstreamStrategy.java @@ -8,6 +8,7 @@ import com.fasterxml.jackson.annotation.JsonInclude; import io.fabric8.kubernetes.api.model.Doneable; +import io.quarkus.runtime.annotations.RegisterForReflection; import io.sundr.builder.annotations.Buildable; import io.sundr.builder.annotations.Inline; @@ -22,6 +23,7 @@ ) ) @JsonInclude(JsonInclude.Include.NON_NULL) +@RegisterForReflection public class DownstreamStrategy { private ExternalDownstreamStrategy externalStrategy; diff --git a/api-model/src/main/java/io/enmasse/iot/model/v1/EndpointConfig.java b/api-model/src/main/java/io/enmasse/iot/model/v1/EndpointConfig.java index 08927741714..40bc3bb7ab1 100644 --- a/api-model/src/main/java/io/enmasse/iot/model/v1/EndpointConfig.java +++ b/api-model/src/main/java/io/enmasse/iot/model/v1/EndpointConfig.java @@ -8,6 +8,7 @@ import com.fasterxml.jackson.annotation.JsonInclude; import io.fabric8.kubernetes.api.model.Doneable; +import io.quarkus.runtime.annotations.RegisterForReflection; import io.sundr.builder.annotations.Buildable; import io.sundr.builder.annotations.Inline; @@ -22,6 +23,7 @@ ) ) @JsonInclude(JsonInclude.Include.NON_NULL) +@RegisterForReflection public class EndpointConfig { private Boolean enableDefaultRoute; diff --git a/api-model/src/main/java/io/enmasse/iot/model/v1/EndpointStatus.java b/api-model/src/main/java/io/enmasse/iot/model/v1/EndpointStatus.java index e969c1d4dce..33bf8af142a 100644 --- a/api-model/src/main/java/io/enmasse/iot/model/v1/EndpointStatus.java +++ b/api-model/src/main/java/io/enmasse/iot/model/v1/EndpointStatus.java @@ -9,6 +9,7 @@ import io.fabric8.kubernetes.api.model.Doneable; import io.fabric8.kubernetes.api.model.ResourceRequirements; +import io.quarkus.runtime.annotations.RegisterForReflection; import io.sundr.builder.annotations.Buildable; import io.sundr.builder.annotations.BuildableReference; import io.sundr.builder.annotations.Inline; @@ -25,6 +26,7 @@ ) ) @JsonInclude(JsonInclude.Include.NON_NULL) +@RegisterForReflection public class EndpointStatus { private String uri; diff --git a/api-model/src/main/java/io/enmasse/iot/model/v1/ExtensionImage.java b/api-model/src/main/java/io/enmasse/iot/model/v1/ExtensionImage.java index 0c78789636e..38f1507961c 100644 --- a/api-model/src/main/java/io/enmasse/iot/model/v1/ExtensionImage.java +++ b/api-model/src/main/java/io/enmasse/iot/model/v1/ExtensionImage.java @@ -11,6 +11,7 @@ import io.fabric8.kubernetes.api.model.Container; import io.fabric8.kubernetes.api.model.Doneable; +import io.quarkus.runtime.annotations.RegisterForReflection; import io.sundr.builder.annotations.Buildable; import io.sundr.builder.annotations.BuildableReference; import io.sundr.builder.annotations.Inline; @@ -27,6 +28,7 @@ prefix = "Doneable", value = "done")) @JsonInclude(NON_NULL) +@RegisterForReflection public class ExtensionImage { private Container container; diff --git a/api-model/src/main/java/io/enmasse/iot/model/v1/ExternalConnectionCacheNames.java b/api-model/src/main/java/io/enmasse/iot/model/v1/ExternalConnectionCacheNames.java index 2e3ebfa1baa..e9af63a503c 100644 --- a/api-model/src/main/java/io/enmasse/iot/model/v1/ExternalConnectionCacheNames.java +++ b/api-model/src/main/java/io/enmasse/iot/model/v1/ExternalConnectionCacheNames.java @@ -10,6 +10,7 @@ import com.fasterxml.jackson.annotation.JsonInclude; import io.fabric8.kubernetes.api.model.Doneable; +import io.quarkus.runtime.annotations.RegisterForReflection; import io.sundr.builder.annotations.Buildable; import io.sundr.builder.annotations.Inline; @@ -22,6 +23,7 @@ prefix = "Doneable", value = "done")) @JsonInclude(NON_NULL) +@RegisterForReflection public class ExternalConnectionCacheNames { private String deviceStatesCacheName; diff --git a/api-model/src/main/java/io/enmasse/iot/model/v1/ExternalDownstreamStrategy.java b/api-model/src/main/java/io/enmasse/iot/model/v1/ExternalDownstreamStrategy.java index f5261bcbe30..a70cdb904b5 100644 --- a/api-model/src/main/java/io/enmasse/iot/model/v1/ExternalDownstreamStrategy.java +++ b/api-model/src/main/java/io/enmasse/iot/model/v1/ExternalDownstreamStrategy.java @@ -13,6 +13,7 @@ import com.fasterxml.jackson.annotation.JsonInclude; import io.fabric8.kubernetes.api.model.Doneable; +import io.quarkus.runtime.annotations.RegisterForReflection; import io.sundr.builder.annotations.Buildable; import io.sundr.builder.annotations.Inline; @@ -28,6 +29,7 @@ ) @JsonIgnoreProperties(ignoreUnknown = true) @JsonInclude(NON_NULL) +@RegisterForReflection public class ExternalDownstreamStrategy { private String host; diff --git a/api-model/src/main/java/io/enmasse/iot/model/v1/ExternalInfinispanDeviceConnectionServer.java b/api-model/src/main/java/io/enmasse/iot/model/v1/ExternalInfinispanDeviceConnectionServer.java index 651571642ce..bd6a5781d6b 100644 --- a/api-model/src/main/java/io/enmasse/iot/model/v1/ExternalInfinispanDeviceConnectionServer.java +++ b/api-model/src/main/java/io/enmasse/iot/model/v1/ExternalInfinispanDeviceConnectionServer.java @@ -11,6 +11,7 @@ import com.fasterxml.jackson.annotation.JsonUnwrapped; import io.fabric8.kubernetes.api.model.Doneable; +import io.quarkus.runtime.annotations.RegisterForReflection; import io.sundr.builder.annotations.Buildable; import io.sundr.builder.annotations.Inline; @@ -23,6 +24,7 @@ prefix = "Doneable", value = "done")) @JsonInclude(NON_NULL) +@RegisterForReflection public class ExternalInfinispanDeviceConnectionServer { @JsonUnwrapped diff --git a/api-model/src/main/java/io/enmasse/iot/model/v1/ExternalInfinispanDeviceRegistryServer.java b/api-model/src/main/java/io/enmasse/iot/model/v1/ExternalInfinispanDeviceRegistryServer.java index d04aca99e09..9e9d418dd39 100644 --- a/api-model/src/main/java/io/enmasse/iot/model/v1/ExternalInfinispanDeviceRegistryServer.java +++ b/api-model/src/main/java/io/enmasse/iot/model/v1/ExternalInfinispanDeviceRegistryServer.java @@ -11,6 +11,7 @@ import com.fasterxml.jackson.annotation.JsonUnwrapped; import io.fabric8.kubernetes.api.model.Doneable; +import io.quarkus.runtime.annotations.RegisterForReflection; import io.sundr.builder.annotations.Buildable; import io.sundr.builder.annotations.Inline; @@ -23,6 +24,7 @@ prefix = "Doneable", value = "done")) @JsonInclude(NON_NULL) +@RegisterForReflection public class ExternalInfinispanDeviceRegistryServer { @JsonUnwrapped diff --git a/api-model/src/main/java/io/enmasse/iot/model/v1/ExternalInfinispanServer.java b/api-model/src/main/java/io/enmasse/iot/model/v1/ExternalInfinispanServer.java index 1297caa99b8..636b16f4481 100644 --- a/api-model/src/main/java/io/enmasse/iot/model/v1/ExternalInfinispanServer.java +++ b/api-model/src/main/java/io/enmasse/iot/model/v1/ExternalInfinispanServer.java @@ -10,6 +10,7 @@ import com.fasterxml.jackson.annotation.JsonInclude; import io.fabric8.kubernetes.api.model.Doneable; +import io.quarkus.runtime.annotations.RegisterForReflection; import io.sundr.builder.annotations.Buildable; import io.sundr.builder.annotations.Inline; @@ -22,6 +23,7 @@ prefix = "Doneable", value = "done")) @JsonInclude(NON_NULL) +@RegisterForReflection public class ExternalInfinispanServer { private String host; diff --git a/api-model/src/main/java/io/enmasse/iot/model/v1/ExternalJdbcDeviceConnectionServer.java b/api-model/src/main/java/io/enmasse/iot/model/v1/ExternalJdbcDeviceConnectionServer.java index c4f92d78ea0..ecac767c138 100644 --- a/api-model/src/main/java/io/enmasse/iot/model/v1/ExternalJdbcDeviceConnectionServer.java +++ b/api-model/src/main/java/io/enmasse/iot/model/v1/ExternalJdbcDeviceConnectionServer.java @@ -16,6 +16,7 @@ import com.fasterxml.jackson.annotation.Nulls; import io.fabric8.kubernetes.api.model.Doneable; +import io.quarkus.runtime.annotations.RegisterForReflection; import io.sundr.builder.annotations.Buildable; import io.sundr.builder.annotations.Inline; @@ -28,6 +29,7 @@ prefix = "Doneable", value = "done")) @JsonInclude(NON_NULL) +@RegisterForReflection public class ExternalJdbcDeviceConnectionServer extends JdbcConnectionInformation { @JsonSetter(nulls = Nulls.AS_EMPTY) diff --git a/api-model/src/main/java/io/enmasse/iot/model/v1/ExternalJdbcDevicesService.java b/api-model/src/main/java/io/enmasse/iot/model/v1/ExternalJdbcDevicesService.java index d46709714b0..a825943362f 100644 --- a/api-model/src/main/java/io/enmasse/iot/model/v1/ExternalJdbcDevicesService.java +++ b/api-model/src/main/java/io/enmasse/iot/model/v1/ExternalJdbcDevicesService.java @@ -13,6 +13,7 @@ import com.fasterxml.jackson.annotation.Nulls; import io.fabric8.kubernetes.api.model.Doneable; +import io.quarkus.runtime.annotations.RegisterForReflection; import io.sundr.builder.annotations.Buildable; import io.sundr.builder.annotations.Inline; @@ -25,6 +26,7 @@ prefix = "Doneable", value = "done")) @JsonInclude(NON_EMPTY) +@RegisterForReflection public class ExternalJdbcDevicesService { @JsonSetter(nulls = Nulls.AS_EMPTY) diff --git a/api-model/src/main/java/io/enmasse/iot/model/v1/ExternalJdbcRegistryServer.java b/api-model/src/main/java/io/enmasse/iot/model/v1/ExternalJdbcRegistryServer.java index 612b0219cba..fb2690aa5f1 100644 --- a/api-model/src/main/java/io/enmasse/iot/model/v1/ExternalJdbcRegistryServer.java +++ b/api-model/src/main/java/io/enmasse/iot/model/v1/ExternalJdbcRegistryServer.java @@ -16,6 +16,7 @@ import com.fasterxml.jackson.annotation.Nulls; import io.fabric8.kubernetes.api.model.Doneable; +import io.quarkus.runtime.annotations.RegisterForReflection; import io.sundr.builder.annotations.Buildable; import io.sundr.builder.annotations.Inline; @@ -28,6 +29,7 @@ prefix = "Doneable", value = "done")) @JsonInclude(NON_NULL) +@RegisterForReflection public class ExternalJdbcRegistryServer { private ExternalJdbcDevicesService adapter; diff --git a/api-model/src/main/java/io/enmasse/iot/model/v1/ExternalRegistryCacheNames.java b/api-model/src/main/java/io/enmasse/iot/model/v1/ExternalRegistryCacheNames.java index 3b00152e0b7..52dec5655cb 100644 --- a/api-model/src/main/java/io/enmasse/iot/model/v1/ExternalRegistryCacheNames.java +++ b/api-model/src/main/java/io/enmasse/iot/model/v1/ExternalRegistryCacheNames.java @@ -10,6 +10,7 @@ import com.fasterxml.jackson.annotation.JsonInclude; import io.fabric8.kubernetes.api.model.Doneable; +import io.quarkus.runtime.annotations.RegisterForReflection; import io.sundr.builder.annotations.Buildable; import io.sundr.builder.annotations.Inline; @@ -22,6 +23,7 @@ prefix = "Doneable", value = "done")) @JsonInclude(NON_NULL) +@RegisterForReflection public class ExternalRegistryCacheNames { private String adapterCredentialsCacheName; diff --git a/api-model/src/main/java/io/enmasse/iot/model/v1/ImageOverride.java b/api-model/src/main/java/io/enmasse/iot/model/v1/ImageOverride.java index 3320e292e5f..707d09bd2f5 100644 --- a/api-model/src/main/java/io/enmasse/iot/model/v1/ImageOverride.java +++ b/api-model/src/main/java/io/enmasse/iot/model/v1/ImageOverride.java @@ -7,6 +7,7 @@ import com.fasterxml.jackson.annotation.JsonInclude; import io.fabric8.kubernetes.api.model.Doneable; +import io.quarkus.runtime.annotations.RegisterForReflection; import io.sundr.builder.annotations.Buildable; import io.sundr.builder.annotations.Inline; @@ -21,6 +22,7 @@ ) ) @JsonInclude(JsonInclude.Include.NON_NULL) +@RegisterForReflection public class ImageOverride { private String name; diff --git a/api-model/src/main/java/io/enmasse/iot/model/v1/InfinispanDeviceConnection.java b/api-model/src/main/java/io/enmasse/iot/model/v1/InfinispanDeviceConnection.java index 6a0b3c26697..eafed9cf65b 100644 --- a/api-model/src/main/java/io/enmasse/iot/model/v1/InfinispanDeviceConnection.java +++ b/api-model/src/main/java/io/enmasse/iot/model/v1/InfinispanDeviceConnection.java @@ -11,6 +11,7 @@ import com.fasterxml.jackson.annotation.JsonUnwrapped; import io.fabric8.kubernetes.api.model.Doneable; +import io.quarkus.runtime.annotations.RegisterForReflection; import io.sundr.builder.annotations.Buildable; import io.sundr.builder.annotations.Inline; @@ -25,6 +26,7 @@ ) ) @JsonInclude(NON_NULL) +@RegisterForReflection public class InfinispanDeviceConnection { @JsonUnwrapped diff --git a/api-model/src/main/java/io/enmasse/iot/model/v1/InfinispanDeviceConnectionServer.java b/api-model/src/main/java/io/enmasse/iot/model/v1/InfinispanDeviceConnectionServer.java index e68ee5e9357..775e7d651f3 100644 --- a/api-model/src/main/java/io/enmasse/iot/model/v1/InfinispanDeviceConnectionServer.java +++ b/api-model/src/main/java/io/enmasse/iot/model/v1/InfinispanDeviceConnectionServer.java @@ -8,6 +8,7 @@ import com.fasterxml.jackson.annotation.JsonInclude; import io.fabric8.kubernetes.api.model.Doneable; +import io.quarkus.runtime.annotations.RegisterForReflection; import io.sundr.builder.annotations.Buildable; import io.sundr.builder.annotations.Inline; @@ -22,6 +23,7 @@ ) ) @JsonInclude(JsonInclude.Include.NON_NULL) +@RegisterForReflection public class InfinispanDeviceConnectionServer { private ExternalInfinispanDeviceConnectionServer external; diff --git a/api-model/src/main/java/io/enmasse/iot/model/v1/InfinispanDeviceRegistry.java b/api-model/src/main/java/io/enmasse/iot/model/v1/InfinispanDeviceRegistry.java index 15b4d7dfcc9..daa21084aaa 100644 --- a/api-model/src/main/java/io/enmasse/iot/model/v1/InfinispanDeviceRegistry.java +++ b/api-model/src/main/java/io/enmasse/iot/model/v1/InfinispanDeviceRegistry.java @@ -11,6 +11,7 @@ import com.fasterxml.jackson.annotation.JsonUnwrapped; import io.fabric8.kubernetes.api.model.Doneable; +import io.quarkus.runtime.annotations.RegisterForReflection; import io.sundr.builder.annotations.Buildable; import io.sundr.builder.annotations.Inline; @@ -23,6 +24,7 @@ prefix = "Doneable", value = "done")) @JsonInclude(NON_NULL) +@RegisterForReflection public class InfinispanDeviceRegistry { @JsonUnwrapped diff --git a/api-model/src/main/java/io/enmasse/iot/model/v1/InfinispanDeviceRegistryServer.java b/api-model/src/main/java/io/enmasse/iot/model/v1/InfinispanDeviceRegistryServer.java index 34cb3c3fcac..06be1094eb5 100644 --- a/api-model/src/main/java/io/enmasse/iot/model/v1/InfinispanDeviceRegistryServer.java +++ b/api-model/src/main/java/io/enmasse/iot/model/v1/InfinispanDeviceRegistryServer.java @@ -8,6 +8,7 @@ import com.fasterxml.jackson.annotation.JsonInclude; import io.fabric8.kubernetes.api.model.Doneable; +import io.quarkus.runtime.annotations.RegisterForReflection; import io.sundr.builder.annotations.Buildable; import io.sundr.builder.annotations.Inline; @@ -22,6 +23,7 @@ ) ) @JsonInclude(JsonInclude.Include.NON_NULL) +@RegisterForReflection public class InfinispanDeviceRegistryServer { private ExternalInfinispanDeviceRegistryServer external; diff --git a/api-model/src/main/java/io/enmasse/iot/model/v1/InterServiceCertificates.java b/api-model/src/main/java/io/enmasse/iot/model/v1/InterServiceCertificates.java index 718b7bb8047..ca7b8819018 100644 --- a/api-model/src/main/java/io/enmasse/iot/model/v1/InterServiceCertificates.java +++ b/api-model/src/main/java/io/enmasse/iot/model/v1/InterServiceCertificates.java @@ -7,6 +7,7 @@ import com.fasterxml.jackson.annotation.JsonInclude; import io.fabric8.kubernetes.api.model.Doneable; +import io.quarkus.runtime.annotations.RegisterForReflection; import io.sundr.builder.annotations.Buildable; import io.sundr.builder.annotations.Inline; @@ -21,6 +22,7 @@ ) ) @JsonInclude(JsonInclude.Include.NON_NULL) +@RegisterForReflection public class InterServiceCertificates { private SecretCertificatesStrategy secretCertificatesStrategy; diff --git a/api-model/src/main/java/io/enmasse/iot/model/v1/IoTConfig.java b/api-model/src/main/java/io/enmasse/iot/model/v1/IoTConfig.java index 1ee5d2d0672..8a19cabb3f2 100644 --- a/api-model/src/main/java/io/enmasse/iot/model/v1/IoTConfig.java +++ b/api-model/src/main/java/io/enmasse/iot/model/v1/IoTConfig.java @@ -7,6 +7,7 @@ import io.enmasse.common.model.AbstractHasMetadata; import io.enmasse.common.model.DefaultCustomResource; import io.fabric8.kubernetes.api.model.Doneable; +import io.quarkus.runtime.annotations.RegisterForReflection; import io.sundr.builder.annotations.Buildable; import io.sundr.builder.annotations.BuildableReference; import io.sundr.builder.annotations.Inline; @@ -22,6 +23,7 @@ value = "done")) @DefaultCustomResource @SuppressWarnings("serial") +@RegisterForReflection public class IoTConfig extends AbstractHasMetadata { public static final String KIND = "IoTConfig"; diff --git a/api-model/src/main/java/io/enmasse/iot/model/v1/IoTConfigList.java b/api-model/src/main/java/io/enmasse/iot/model/v1/IoTConfigList.java index 818fd6eb16c..afa3fc7a6f1 100644 --- a/api-model/src/main/java/io/enmasse/iot/model/v1/IoTConfigList.java +++ b/api-model/src/main/java/io/enmasse/iot/model/v1/IoTConfigList.java @@ -6,9 +6,11 @@ import io.enmasse.common.model.AbstractList; import io.enmasse.common.model.DefaultCustomResource; +import io.quarkus.runtime.annotations.RegisterForReflection; @DefaultCustomResource @SuppressWarnings("serial") +@RegisterForReflection public class IoTConfigList extends AbstractList { public static final String KIND = "IoTConfigList"; diff --git a/api-model/src/main/java/io/enmasse/iot/model/v1/IoTConfigSpec.java b/api-model/src/main/java/io/enmasse/iot/model/v1/IoTConfigSpec.java index 9d7b94adcd4..1bc1398dda4 100644 --- a/api-model/src/main/java/io/enmasse/iot/model/v1/IoTConfigSpec.java +++ b/api-model/src/main/java/io/enmasse/iot/model/v1/IoTConfigSpec.java @@ -9,6 +9,7 @@ import com.fasterxml.jackson.annotation.JsonInclude; import io.fabric8.kubernetes.api.model.Doneable; +import io.quarkus.runtime.annotations.RegisterForReflection; import io.sundr.builder.annotations.Buildable; import io.sundr.builder.annotations.Inline; @@ -23,6 +24,7 @@ ) ) @JsonInclude(JsonInclude.Include.NON_NULL) +@RegisterForReflection public class IoTConfigSpec { private Boolean enableDefaultRoutes; diff --git a/api-model/src/main/java/io/enmasse/iot/model/v1/IoTConfigStatus.java b/api-model/src/main/java/io/enmasse/iot/model/v1/IoTConfigStatus.java index aa5bcb6bebd..15f29273751 100644 --- a/api-model/src/main/java/io/enmasse/iot/model/v1/IoTConfigStatus.java +++ b/api-model/src/main/java/io/enmasse/iot/model/v1/IoTConfigStatus.java @@ -10,6 +10,7 @@ import com.fasterxml.jackson.annotation.JsonInclude; import io.fabric8.kubernetes.api.model.Doneable; +import io.quarkus.runtime.annotations.RegisterForReflection; import io.sundr.builder.annotations.Buildable; import io.sundr.builder.annotations.Inline; @@ -24,6 +25,7 @@ ) ) @JsonInclude(JsonInclude.Include.NON_NULL) +@RegisterForReflection public class IoTConfigStatus { private String phase; diff --git a/api-model/src/main/java/io/enmasse/iot/model/v1/IoTProject.java b/api-model/src/main/java/io/enmasse/iot/model/v1/IoTProject.java index a6cbc9489bc..b7ef893f91f 100644 --- a/api-model/src/main/java/io/enmasse/iot/model/v1/IoTProject.java +++ b/api-model/src/main/java/io/enmasse/iot/model/v1/IoTProject.java @@ -10,6 +10,7 @@ import io.enmasse.common.model.DefaultCustomResource; import io.fabric8.kubernetes.api.model.Doneable; import io.fabric8.kubernetes.api.model.ObjectMeta; +import io.quarkus.runtime.annotations.RegisterForReflection; import io.sundr.builder.annotations.Buildable; import io.sundr.builder.annotations.BuildableReference; import io.sundr.builder.annotations.Inline; @@ -26,6 +27,7 @@ @DefaultCustomResource @SuppressWarnings("serial") @JsonIgnoreProperties(ignoreUnknown = true) +@RegisterForReflection public class IoTProject extends AbstractHasMetadata { public static final String KIND = "IoTProject"; diff --git a/api-model/src/main/java/io/enmasse/iot/model/v1/IoTProjectList.java b/api-model/src/main/java/io/enmasse/iot/model/v1/IoTProjectList.java index fc3deef5192..1bf89957f78 100644 --- a/api-model/src/main/java/io/enmasse/iot/model/v1/IoTProjectList.java +++ b/api-model/src/main/java/io/enmasse/iot/model/v1/IoTProjectList.java @@ -6,9 +6,11 @@ import io.enmasse.common.model.AbstractList; import io.enmasse.common.model.DefaultCustomResource; +import io.quarkus.runtime.annotations.RegisterForReflection; @DefaultCustomResource @SuppressWarnings("serial") +@RegisterForReflection public class IoTProjectList extends AbstractList { public static final String KIND = "IoTProjectList"; diff --git a/api-model/src/main/java/io/enmasse/iot/model/v1/IoTProjectSpec.java b/api-model/src/main/java/io/enmasse/iot/model/v1/IoTProjectSpec.java index 3854a6f0c45..497325e8887 100644 --- a/api-model/src/main/java/io/enmasse/iot/model/v1/IoTProjectSpec.java +++ b/api-model/src/main/java/io/enmasse/iot/model/v1/IoTProjectSpec.java @@ -7,6 +7,7 @@ import com.fasterxml.jackson.annotation.JsonInclude; import io.fabric8.kubernetes.api.model.Doneable; +import io.quarkus.runtime.annotations.RegisterForReflection; import io.sundr.builder.annotations.Buildable; import io.sundr.builder.annotations.Inline; @@ -21,6 +22,7 @@ ) ) @JsonInclude(JsonInclude.Include.NON_NULL) +@RegisterForReflection public class IoTProjectSpec { private DownstreamStrategy downstreamStrategy; diff --git a/api-model/src/main/java/io/enmasse/iot/model/v1/IoTProjectStatus.java b/api-model/src/main/java/io/enmasse/iot/model/v1/IoTProjectStatus.java index 1e337a60f2a..3a07e36c9ad 100644 --- a/api-model/src/main/java/io/enmasse/iot/model/v1/IoTProjectStatus.java +++ b/api-model/src/main/java/io/enmasse/iot/model/v1/IoTProjectStatus.java @@ -10,6 +10,7 @@ import com.fasterxml.jackson.annotation.JsonInclude; import io.fabric8.kubernetes.api.model.Doneable; +import io.quarkus.runtime.annotations.RegisterForReflection; import io.sundr.builder.annotations.Buildable; import io.sundr.builder.annotations.Inline; @@ -24,6 +25,7 @@ ) ) @JsonInclude(JsonInclude.Include.NON_NULL) +@RegisterForReflection public class IoTProjectStatus { private String tenantName; diff --git a/api-model/src/main/java/io/enmasse/iot/model/v1/JavaContainerConfig.java b/api-model/src/main/java/io/enmasse/iot/model/v1/JavaContainerConfig.java index e81598d9c07..18f3a1f4d8c 100644 --- a/api-model/src/main/java/io/enmasse/iot/model/v1/JavaContainerConfig.java +++ b/api-model/src/main/java/io/enmasse/iot/model/v1/JavaContainerConfig.java @@ -10,6 +10,7 @@ import io.fabric8.kubernetes.api.model.Doneable; import io.fabric8.kubernetes.api.model.ResourceRequirements; +import io.quarkus.runtime.annotations.RegisterForReflection; import io.sundr.builder.annotations.Buildable; import io.sundr.builder.annotations.BuildableReference; import io.sundr.builder.annotations.Inline; @@ -24,6 +25,7 @@ prefix = "Doneable", value = "done")) @JsonInclude(JsonInclude.Include.NON_NULL) +@RegisterForReflection public class JavaContainerConfig { private Boolean requireNativeTls; diff --git a/api-model/src/main/java/io/enmasse/iot/model/v1/JavaContainerDefaults.java b/api-model/src/main/java/io/enmasse/iot/model/v1/JavaContainerDefaults.java index 7c5061f1d61..4346d84db81 100644 --- a/api-model/src/main/java/io/enmasse/iot/model/v1/JavaContainerDefaults.java +++ b/api-model/src/main/java/io/enmasse/iot/model/v1/JavaContainerDefaults.java @@ -7,6 +7,7 @@ import com.fasterxml.jackson.annotation.JsonInclude; import io.fabric8.kubernetes.api.model.Doneable; +import io.quarkus.runtime.annotations.RegisterForReflection; import io.sundr.builder.annotations.Buildable; import io.sundr.builder.annotations.Inline; @@ -21,6 +22,7 @@ ) ) @JsonInclude(JsonInclude.Include.NON_NULL) +@RegisterForReflection public class JavaContainerDefaults { private Boolean requireNativeTls; diff --git a/api-model/src/main/java/io/enmasse/iot/model/v1/JdbcConnectionInformation.java b/api-model/src/main/java/io/enmasse/iot/model/v1/JdbcConnectionInformation.java index 25f937a4c76..6e5e60ec16f 100644 --- a/api-model/src/main/java/io/enmasse/iot/model/v1/JdbcConnectionInformation.java +++ b/api-model/src/main/java/io/enmasse/iot/model/v1/JdbcConnectionInformation.java @@ -11,6 +11,7 @@ import com.fasterxml.jackson.annotation.JsonInclude; import io.fabric8.kubernetes.api.model.Doneable; +import io.quarkus.runtime.annotations.RegisterForReflection; import io.sundr.builder.annotations.Buildable; import io.sundr.builder.annotations.Inline; @@ -23,6 +24,7 @@ prefix = "Doneable", value = "done")) @JsonInclude(NON_EMPTY) +@RegisterForReflection public class JdbcConnectionInformation { private String url; diff --git a/api-model/src/main/java/io/enmasse/iot/model/v1/JdbcDeviceConnection.java b/api-model/src/main/java/io/enmasse/iot/model/v1/JdbcDeviceConnection.java index db7bfa2cdb6..5b4102dc311 100644 --- a/api-model/src/main/java/io/enmasse/iot/model/v1/JdbcDeviceConnection.java +++ b/api-model/src/main/java/io/enmasse/iot/model/v1/JdbcDeviceConnection.java @@ -11,6 +11,7 @@ import com.fasterxml.jackson.annotation.JsonUnwrapped; import io.fabric8.kubernetes.api.model.Doneable; +import io.quarkus.runtime.annotations.RegisterForReflection; import io.sundr.builder.annotations.Buildable; import io.sundr.builder.annotations.Inline; @@ -23,6 +24,7 @@ prefix = "Doneable", value = "done")) @JsonInclude(NON_NULL) +@RegisterForReflection public class JdbcDeviceConnection { @JsonUnwrapped diff --git a/api-model/src/main/java/io/enmasse/iot/model/v1/JdbcDeviceConnectionServer.java b/api-model/src/main/java/io/enmasse/iot/model/v1/JdbcDeviceConnectionServer.java index a78dfe9da33..9fe68241752 100644 --- a/api-model/src/main/java/io/enmasse/iot/model/v1/JdbcDeviceConnectionServer.java +++ b/api-model/src/main/java/io/enmasse/iot/model/v1/JdbcDeviceConnectionServer.java @@ -8,6 +8,7 @@ import com.fasterxml.jackson.annotation.JsonInclude; import io.fabric8.kubernetes.api.model.Doneable; +import io.quarkus.runtime.annotations.RegisterForReflection; import io.sundr.builder.annotations.Buildable; import io.sundr.builder.annotations.Inline; @@ -20,6 +21,7 @@ prefix = "Doneable", value = "done")) @JsonInclude(JsonInclude.Include.NON_NULL) +@RegisterForReflection public class JdbcDeviceConnectionServer { private ExternalJdbcDeviceConnectionServer external; diff --git a/api-model/src/main/java/io/enmasse/iot/model/v1/JdbcDeviceRegistry.java b/api-model/src/main/java/io/enmasse/iot/model/v1/JdbcDeviceRegistry.java index 9d01bf2619e..05a19f09b5c 100644 --- a/api-model/src/main/java/io/enmasse/iot/model/v1/JdbcDeviceRegistry.java +++ b/api-model/src/main/java/io/enmasse/iot/model/v1/JdbcDeviceRegistry.java @@ -12,6 +12,7 @@ import com.fasterxml.jackson.annotation.JsonUnwrapped; import io.fabric8.kubernetes.api.model.Doneable; +import io.quarkus.runtime.annotations.RegisterForReflection; import io.sundr.builder.annotations.Buildable; import io.sundr.builder.annotations.Inline; @@ -24,6 +25,7 @@ prefix = "Doneable", value = "done")) @JsonInclude(NON_NULL) +@RegisterForReflection public class JdbcDeviceRegistry { @JsonUnwrapped diff --git a/api-model/src/main/java/io/enmasse/iot/model/v1/JdbcRegistryServer.java b/api-model/src/main/java/io/enmasse/iot/model/v1/JdbcRegistryServer.java index 510263af37f..69bac9877cf 100644 --- a/api-model/src/main/java/io/enmasse/iot/model/v1/JdbcRegistryServer.java +++ b/api-model/src/main/java/io/enmasse/iot/model/v1/JdbcRegistryServer.java @@ -8,6 +8,7 @@ import com.fasterxml.jackson.annotation.JsonInclude; import io.fabric8.kubernetes.api.model.Doneable; +import io.quarkus.runtime.annotations.RegisterForReflection; import io.sundr.builder.annotations.Buildable; import io.sundr.builder.annotations.Inline; @@ -20,6 +21,7 @@ prefix = "Doneable", value = "done")) @JsonInclude(JsonInclude.Include.NON_NULL) +@RegisterForReflection public class JdbcRegistryServer { private ExternalJdbcRegistryServer external; diff --git a/api-model/src/main/java/io/enmasse/iot/model/v1/LogbackConfig.java b/api-model/src/main/java/io/enmasse/iot/model/v1/LogbackConfig.java index 0949b3c0f66..56ee0de5675 100644 --- a/api-model/src/main/java/io/enmasse/iot/model/v1/LogbackConfig.java +++ b/api-model/src/main/java/io/enmasse/iot/model/v1/LogbackConfig.java @@ -9,6 +9,7 @@ import io.fabric8.kubernetes.api.model.Doneable; import io.fabric8.kubernetes.api.model.ResourceRequirements; +import io.quarkus.runtime.annotations.RegisterForReflection; import io.sundr.builder.annotations.Buildable; import io.sundr.builder.annotations.BuildableReference; import io.sundr.builder.annotations.Inline; @@ -23,6 +24,7 @@ prefix = "Doneable", value = "done")) @JsonInclude(JsonInclude.Include.NON_EMPTY) +@RegisterForReflection public class LogbackConfig extends CommonLoggingConfig { private String logback; diff --git a/api-model/src/main/java/io/enmasse/iot/model/v1/LoggingConfig.java b/api-model/src/main/java/io/enmasse/iot/model/v1/LoggingConfig.java index 5cd5a997920..2bbf7772568 100644 --- a/api-model/src/main/java/io/enmasse/iot/model/v1/LoggingConfig.java +++ b/api-model/src/main/java/io/enmasse/iot/model/v1/LoggingConfig.java @@ -13,6 +13,7 @@ import io.fabric8.kubernetes.api.model.Doneable; import io.fabric8.kubernetes.api.model.ResourceRequirements; +import io.quarkus.runtime.annotations.RegisterForReflection; import io.sundr.builder.annotations.Buildable; import io.sundr.builder.annotations.BuildableReference; import io.sundr.builder.annotations.Inline; @@ -27,6 +28,7 @@ prefix = "Doneable", value = "done")) @JsonInclude(JsonInclude.Include.NON_EMPTY) +@RegisterForReflection public class LoggingConfig { private String level; diff --git a/api-model/src/main/java/io/enmasse/iot/model/v1/LoggingDefaults.java b/api-model/src/main/java/io/enmasse/iot/model/v1/LoggingDefaults.java index e385ecd2981..5130d5562ed 100644 --- a/api-model/src/main/java/io/enmasse/iot/model/v1/LoggingDefaults.java +++ b/api-model/src/main/java/io/enmasse/iot/model/v1/LoggingDefaults.java @@ -9,6 +9,7 @@ import io.fabric8.kubernetes.api.model.Doneable; import io.fabric8.kubernetes.api.model.ResourceRequirements; +import io.quarkus.runtime.annotations.RegisterForReflection; import io.sundr.builder.annotations.Buildable; import io.sundr.builder.annotations.BuildableReference; import io.sundr.builder.annotations.Inline; @@ -23,6 +24,7 @@ prefix = "Doneable", value = "done")) @JsonInclude(JsonInclude.Include.NON_EMPTY) +@RegisterForReflection public class LoggingDefaults { private String logback; diff --git a/api-model/src/main/java/io/enmasse/iot/model/v1/ManagedDownstreamStrategy.java b/api-model/src/main/java/io/enmasse/iot/model/v1/ManagedDownstreamStrategy.java index 393314619ff..3b1d9a63316 100644 --- a/api-model/src/main/java/io/enmasse/iot/model/v1/ManagedDownstreamStrategy.java +++ b/api-model/src/main/java/io/enmasse/iot/model/v1/ManagedDownstreamStrategy.java @@ -9,6 +9,7 @@ import com.fasterxml.jackson.annotation.JsonInclude; import io.fabric8.kubernetes.api.model.Doneable; +import io.quarkus.runtime.annotations.RegisterForReflection; import io.sundr.builder.annotations.Buildable; import io.sundr.builder.annotations.Inline; @@ -21,6 +22,7 @@ prefix = "Doneable", value = "done")) @JsonInclude(NON_NULL) +@RegisterForReflection public class ManagedDownstreamStrategy { private AddressSpaceConfig addressSpace; diff --git a/api-model/src/main/java/io/enmasse/iot/model/v1/ManagedStatus.java b/api-model/src/main/java/io/enmasse/iot/model/v1/ManagedStatus.java index e9c500a05a0..9718e3e2fcd 100644 --- a/api-model/src/main/java/io/enmasse/iot/model/v1/ManagedStatus.java +++ b/api-model/src/main/java/io/enmasse/iot/model/v1/ManagedStatus.java @@ -8,6 +8,7 @@ import com.fasterxml.jackson.annotation.JsonInclude; import io.fabric8.kubernetes.api.model.Doneable; +import io.quarkus.runtime.annotations.RegisterForReflection; import io.sundr.builder.annotations.Buildable; import io.sundr.builder.annotations.Inline; @@ -20,6 +21,7 @@ prefix = "Doneable", value = "done")) @JsonInclude(JsonInclude.Include.NON_NULL) +@RegisterForReflection public class ManagedStatus { private String passwordTime; diff --git a/api-model/src/main/java/io/enmasse/iot/model/v1/ManagementConfig.java b/api-model/src/main/java/io/enmasse/iot/model/v1/ManagementConfig.java index 77a1c929aa0..236edde96b9 100644 --- a/api-model/src/main/java/io/enmasse/iot/model/v1/ManagementConfig.java +++ b/api-model/src/main/java/io/enmasse/iot/model/v1/ManagementConfig.java @@ -8,6 +8,7 @@ import com.fasterxml.jackson.annotation.JsonInclude; import io.fabric8.kubernetes.api.model.Doneable; +import io.quarkus.runtime.annotations.RegisterForReflection; import io.sundr.builder.annotations.Buildable; import io.sundr.builder.annotations.Inline; @@ -20,6 +21,7 @@ prefix = "Doneable", value = "done")) @JsonInclude(JsonInclude.Include.NON_NULL) +@RegisterForReflection public class ManagementConfig { private EndpointConfig endpoint; diff --git a/api-model/src/main/java/io/enmasse/iot/model/v1/MeshConfig.java b/api-model/src/main/java/io/enmasse/iot/model/v1/MeshConfig.java index 72ac66b2c5a..0b9c16c713d 100644 --- a/api-model/src/main/java/io/enmasse/iot/model/v1/MeshConfig.java +++ b/api-model/src/main/java/io/enmasse/iot/model/v1/MeshConfig.java @@ -9,6 +9,7 @@ import com.fasterxml.jackson.annotation.JsonUnwrapped; import io.fabric8.kubernetes.api.model.Doneable; +import io.quarkus.runtime.annotations.RegisterForReflection; import io.sundr.builder.annotations.Buildable; import io.sundr.builder.annotations.Inline; @@ -21,6 +22,7 @@ prefix = "Doneable", value = "done")) @JsonInclude(JsonInclude.Include.NON_NULL) +@RegisterForReflection public class MeshConfig { @JsonUnwrapped diff --git a/api-model/src/main/java/io/enmasse/iot/model/v1/OperatorConfig.java b/api-model/src/main/java/io/enmasse/iot/model/v1/OperatorConfig.java index bf443723376..3dd0cc9fcb2 100644 --- a/api-model/src/main/java/io/enmasse/iot/model/v1/OperatorConfig.java +++ b/api-model/src/main/java/io/enmasse/iot/model/v1/OperatorConfig.java @@ -8,6 +8,7 @@ import com.fasterxml.jackson.annotation.JsonInclude; import io.fabric8.kubernetes.api.model.Doneable; +import io.quarkus.runtime.annotations.RegisterForReflection; import io.sundr.builder.annotations.Buildable; import io.sundr.builder.annotations.Inline; @@ -20,6 +21,7 @@ prefix = "Doneable", value = "done")) @JsonInclude(JsonInclude.Include.NON_NULL) +@RegisterForReflection public class OperatorConfig extends ServiceConfig { private ContainerConfig container; diff --git a/api-model/src/main/java/io/enmasse/iot/model/v1/ProjectCondition.java b/api-model/src/main/java/io/enmasse/iot/model/v1/ProjectCondition.java index 2385d49b8b9..1c390eb2b11 100644 --- a/api-model/src/main/java/io/enmasse/iot/model/v1/ProjectCondition.java +++ b/api-model/src/main/java/io/enmasse/iot/model/v1/ProjectCondition.java @@ -8,6 +8,7 @@ import com.fasterxml.jackson.annotation.JsonInclude; import io.fabric8.kubernetes.api.model.Doneable; +import io.quarkus.runtime.annotations.RegisterForReflection; import io.sundr.builder.annotations.Buildable; import io.sundr.builder.annotations.Inline; @@ -20,6 +21,7 @@ prefix = "Doneable", value = "done")) @JsonInclude(JsonInclude.Include.NON_NULL) +@RegisterForReflection public class ProjectCondition extends CommonCondition { } diff --git a/api-model/src/main/java/io/enmasse/iot/model/v1/ProjectConditionType.java b/api-model/src/main/java/io/enmasse/iot/model/v1/ProjectConditionType.java index c0f81da1745..efa68e3f51d 100644 --- a/api-model/src/main/java/io/enmasse/iot/model/v1/ProjectConditionType.java +++ b/api-model/src/main/java/io/enmasse/iot/model/v1/ProjectConditionType.java @@ -6,7 +6,9 @@ package io.enmasse.iot.model.v1; import com.fasterxml.jackson.annotation.JsonValue; +import io.quarkus.runtime.annotations.RegisterForReflection; +@RegisterForReflection public enum ProjectConditionType { READY("Ready"), RESOURCES_CREATED("ResourcesCreated"), diff --git a/api-model/src/main/java/io/enmasse/iot/model/v1/ResourceLimits.java b/api-model/src/main/java/io/enmasse/iot/model/v1/ResourceLimits.java index 46c307394ab..4b85e1857e8 100644 --- a/api-model/src/main/java/io/enmasse/iot/model/v1/ResourceLimits.java +++ b/api-model/src/main/java/io/enmasse/iot/model/v1/ResourceLimits.java @@ -7,6 +7,7 @@ import com.fasterxml.jackson.annotation.JsonInclude; import io.fabric8.kubernetes.api.model.Doneable; +import io.quarkus.runtime.annotations.RegisterForReflection; import io.sundr.builder.annotations.Buildable; import io.sundr.builder.annotations.Inline; @@ -19,6 +20,7 @@ prefix = "Doneable", value = "done")) @JsonInclude(JsonInclude.Include.NON_NULL) +@RegisterForReflection public class ResourceLimits { private Long maximumConnections; diff --git a/api-model/src/main/java/io/enmasse/iot/model/v1/SecretCertificatesStrategy.java b/api-model/src/main/java/io/enmasse/iot/model/v1/SecretCertificatesStrategy.java index 5accbd46c83..540b8d37436 100644 --- a/api-model/src/main/java/io/enmasse/iot/model/v1/SecretCertificatesStrategy.java +++ b/api-model/src/main/java/io/enmasse/iot/model/v1/SecretCertificatesStrategy.java @@ -9,6 +9,7 @@ import com.fasterxml.jackson.annotation.JsonInclude; import io.fabric8.kubernetes.api.model.Doneable; +import io.quarkus.runtime.annotations.RegisterForReflection; import io.sundr.builder.annotations.Buildable; import io.sundr.builder.annotations.Inline; @@ -23,6 +24,7 @@ ) ) @JsonInclude(JsonInclude.Include.NON_NULL) +@RegisterForReflection public class SecretCertificatesStrategy { private String caSecretName; diff --git a/api-model/src/main/java/io/enmasse/iot/model/v1/SecretNameStrategy.java b/api-model/src/main/java/io/enmasse/iot/model/v1/SecretNameStrategy.java index f50130c9dd2..9d494e174e3 100644 --- a/api-model/src/main/java/io/enmasse/iot/model/v1/SecretNameStrategy.java +++ b/api-model/src/main/java/io/enmasse/iot/model/v1/SecretNameStrategy.java @@ -8,6 +8,7 @@ import com.fasterxml.jackson.annotation.JsonInclude; import io.fabric8.kubernetes.api.model.Doneable; +import io.quarkus.runtime.annotations.RegisterForReflection; import io.sundr.builder.annotations.Buildable; import io.sundr.builder.annotations.Inline; @@ -22,6 +23,7 @@ ) ) @JsonInclude(JsonInclude.Include.NON_NULL) +@RegisterForReflection public class SecretNameStrategy { private String secretName; diff --git a/api-model/src/main/java/io/enmasse/iot/model/v1/ServiceCAStrategy.java b/api-model/src/main/java/io/enmasse/iot/model/v1/ServiceCAStrategy.java index ed052e54e01..33e20c57ccc 100644 --- a/api-model/src/main/java/io/enmasse/iot/model/v1/ServiceCAStrategy.java +++ b/api-model/src/main/java/io/enmasse/iot/model/v1/ServiceCAStrategy.java @@ -7,6 +7,7 @@ import com.fasterxml.jackson.annotation.JsonInclude; import io.fabric8.kubernetes.api.model.Doneable; +import io.quarkus.runtime.annotations.RegisterForReflection; import io.sundr.builder.annotations.Buildable; import io.sundr.builder.annotations.Inline; @@ -21,5 +22,6 @@ ) ) @JsonInclude(JsonInclude.Include.NON_NULL) +@RegisterForReflection public class ServiceCAStrategy { } diff --git a/api-model/src/main/java/io/enmasse/iot/model/v1/ServiceConfig.java b/api-model/src/main/java/io/enmasse/iot/model/v1/ServiceConfig.java index c147bc7c1bf..7492c18bba3 100644 --- a/api-model/src/main/java/io/enmasse/iot/model/v1/ServiceConfig.java +++ b/api-model/src/main/java/io/enmasse/iot/model/v1/ServiceConfig.java @@ -9,6 +9,7 @@ import io.fabric8.kubernetes.api.model.Affinity; import io.fabric8.kubernetes.api.model.Doneable; +import io.quarkus.runtime.annotations.RegisterForReflection; import io.sundr.builder.annotations.Buildable; import io.sundr.builder.annotations.Inline; @@ -21,6 +22,7 @@ prefix = "Doneable", value = "done")) @JsonInclude(JsonInclude.Include.NON_NULL) +@RegisterForReflection public class ServiceConfig { private Integer replicas; diff --git a/api-model/src/main/java/io/enmasse/iot/model/v1/ServiceStatus.java b/api-model/src/main/java/io/enmasse/iot/model/v1/ServiceStatus.java index 017a875e05b..9e8c415be04 100644 --- a/api-model/src/main/java/io/enmasse/iot/model/v1/ServiceStatus.java +++ b/api-model/src/main/java/io/enmasse/iot/model/v1/ServiceStatus.java @@ -5,5 +5,8 @@ package io.enmasse.iot.model.v1; +import io.quarkus.runtime.annotations.RegisterForReflection; + +@RegisterForReflection public class ServiceStatus extends CommonStatus { } diff --git a/api-model/src/main/java/io/enmasse/iot/model/v1/ServicesConfig.java b/api-model/src/main/java/io/enmasse/iot/model/v1/ServicesConfig.java index 2730018446d..566295eacef 100644 --- a/api-model/src/main/java/io/enmasse/iot/model/v1/ServicesConfig.java +++ b/api-model/src/main/java/io/enmasse/iot/model/v1/ServicesConfig.java @@ -8,6 +8,7 @@ import com.fasterxml.jackson.annotation.JsonInclude; import io.fabric8.kubernetes.api.model.Doneable; +import io.quarkus.runtime.annotations.RegisterForReflection; import io.sundr.builder.annotations.Buildable; import io.sundr.builder.annotations.Inline; @@ -22,6 +23,7 @@ ) ) @JsonInclude(JsonInclude.Include.NON_NULL) +@RegisterForReflection public class ServicesConfig { private DeviceConnectionServiceConfig deviceConnection; private DeviceRegistryServiceConfig deviceRegistry; diff --git a/api-model/src/main/java/io/enmasse/iot/model/v1/TenantConfiguration.java b/api-model/src/main/java/io/enmasse/iot/model/v1/TenantConfiguration.java index d79f945989e..1d369a94168 100644 --- a/api-model/src/main/java/io/enmasse/iot/model/v1/TenantConfiguration.java +++ b/api-model/src/main/java/io/enmasse/iot/model/v1/TenantConfiguration.java @@ -13,6 +13,7 @@ import com.fasterxml.jackson.databind.node.ObjectNode; import io.fabric8.kubernetes.api.model.Doneable; +import io.quarkus.runtime.annotations.RegisterForReflection; import io.sundr.builder.annotations.Buildable; import io.sundr.builder.annotations.Inline; @@ -25,6 +26,7 @@ prefix = "Doneable", value = "done")) @JsonInclude(JsonInclude.Include.NON_NULL) +@RegisterForReflection public class TenantConfiguration { private Boolean enabled; @@ -34,8 +36,8 @@ public class TenantConfiguration { @JsonSetter(nulls = Nulls.AS_EMPTY) private Map adapters; - private ObjectNode defaults; - private ObjectNode extensions; + private Map defaults; + private Map extensions; @JsonSetter(nulls = Nulls.AS_EMPTY) private List trustAnchors; @@ -66,19 +68,19 @@ public void setAdapters(Map adapters) { this.adapters = adapters; } - public ObjectNode getDefaults() { + public Map getDefaults() { return defaults; } - public void setDefaults(ObjectNode defaults) { + public void setDefaults(Map defaults) { this.defaults = defaults; } - public ObjectNode getExtensions() { + public Map getExtensions() { return extensions; } - public void setExtensions(ObjectNode extensions) { + public void setExtensions(Map extensions) { this.extensions = extensions; } diff --git a/api-model/src/main/java/io/enmasse/iot/model/v1/TenantServiceConfig.java b/api-model/src/main/java/io/enmasse/iot/model/v1/TenantServiceConfig.java index f90d0d59538..051e5535789 100644 --- a/api-model/src/main/java/io/enmasse/iot/model/v1/TenantServiceConfig.java +++ b/api-model/src/main/java/io/enmasse/iot/model/v1/TenantServiceConfig.java @@ -8,6 +8,7 @@ import com.fasterxml.jackson.annotation.JsonInclude; import io.fabric8.kubernetes.api.model.Doneable; +import io.quarkus.runtime.annotations.RegisterForReflection; import io.sundr.builder.annotations.Buildable; import io.sundr.builder.annotations.Inline; @@ -20,5 +21,6 @@ prefix = "Doneable", value = "done")) @JsonInclude(JsonInclude.Include.NON_NULL) +@RegisterForReflection public class TenantServiceConfig extends CommonServiceConfig { } diff --git a/api-model/src/main/java/io/enmasse/iot/model/v1/TlsOptions.java b/api-model/src/main/java/io/enmasse/iot/model/v1/TlsOptions.java index 397df162021..bcae5882bbe 100644 --- a/api-model/src/main/java/io/enmasse/iot/model/v1/TlsOptions.java +++ b/api-model/src/main/java/io/enmasse/iot/model/v1/TlsOptions.java @@ -11,6 +11,7 @@ import com.fasterxml.jackson.annotation.Nulls; import io.fabric8.kubernetes.api.model.Doneable; +import io.quarkus.runtime.annotations.RegisterForReflection; import io.sundr.builder.annotations.Buildable; import io.sundr.builder.annotations.Inline; @@ -25,6 +26,7 @@ ) ) @JsonInclude(JsonInclude.Include.NON_EMPTY) +@RegisterForReflection public class TlsOptions { private List versions; diff --git a/api-model/src/main/java/io/enmasse/iot/model/v1/TrustAnchor.java b/api-model/src/main/java/io/enmasse/iot/model/v1/TrustAnchor.java index 9d472d6acb0..47db6850d9d 100644 --- a/api-model/src/main/java/io/enmasse/iot/model/v1/TrustAnchor.java +++ b/api-model/src/main/java/io/enmasse/iot/model/v1/TrustAnchor.java @@ -7,6 +7,7 @@ import com.fasterxml.jackson.annotation.JsonInclude; import io.fabric8.kubernetes.api.model.Doneable; +import io.quarkus.runtime.annotations.RegisterForReflection; import io.sundr.builder.annotations.Buildable; import io.sundr.builder.annotations.Inline; @@ -19,6 +20,7 @@ prefix = "Doneable", value = "done")) @JsonInclude(JsonInclude.Include.NON_EMPTY) +@RegisterForReflection public class TrustAnchor { private Boolean enabled; diff --git a/api-model/src/main/resources/schema/kube-schema.json b/api-model/src/main/resources/schema/kube-schema.json index 24c8858680f..51c5902d3c0 100644 --- a/api-model/src/main/resources/schema/kube-schema.json +++ b/api-model/src/main/resources/schema/kube-schema.json @@ -161,6 +161,145 @@ "io.fabric8.kubernetes.api.model.KubernetesResourceList\u003cMessagingAddress\u003e" ] }, + "github_com_enmasseproject_enmasse_pkg_apis_enmasse_v1beta2_MessagingAddressPlan": { + "type": "object", + "description": "", + "properties": { + "apiVersion": { + "type": "string", + "description": "", + "default": "enmasse.io/v1beta2", + "required": true + }, + "kind": { + "type": "string", + "description": "", + "default": "MessagingAddressPlan", + "required": true + }, + "metadata": { + "$ref": "#/definitions/kubernetes_apimachinery_ObjectMeta", + "existingJavaType": "io.fabric8.kubernetes.api.model.ObjectMeta" + }, + "spec": { + "$ref": "#/definitions/github_com_enmasseproject_enmasse_pkg_apis_enmasse_v1beta2_MessagingAddressPlanSpec", + "javaType": "MessagingAddressPlanSpec" + }, + "status": { + "$ref": "#/definitions/github_com_enmasseproject_enmasse_pkg_apis_enmasse_v1beta2_MessagingAddressPlanStatus", + "javaType": "MessagingAddressPlanStatus" + } + }, + "additionalProperties": true, + "javaType": "MessagingAddressPlan", + "javaInterfaces": [ + "io.fabric8.kubernetes.api.model.HasMetadata" + ] + }, + "github_com_enmasseproject_enmasse_pkg_apis_enmasse_v1beta2_MessagingAddressPlanCondition": { + "type": "object", + "description": "", + "properties": { + "lastTransitionTime": { + "$ref": "#/definitions/kubernetes_apimachinery_Time", + "existingJavaType": "java.lang.String" + }, + "message": { + "type": "string", + "description": "" + }, + "reason": { + "type": "string", + "description": "" + }, + "status": { + "type": "string", + "description": "" + }, + "type": { + "type": "string", + "description": "" + } + }, + "additionalProperties": true, + "javaType": "MessagingAddressPlanCondition", + "javaInterfaces": [ + "io.fabric8.kubernetes.api.model.KubernetesResource" + ] + }, + "github_com_enmasseproject_enmasse_pkg_apis_enmasse_v1beta2_MessagingAddressPlanList": { + "type": "object", + "description": "", + "properties": { + "apiVersion": { + "type": "string", + "description": "", + "default": "enmasse.io/v1beta2", + "required": true + }, + "items": { + "type": "array", + "description": "", + "items": { + "$ref": "#/definitions/github_com_enmasseproject_enmasse_pkg_apis_enmasse_v1beta2_MessagingAddressPlan", + "javaType": "MessagingAddressPlan" + } + }, + "kind": { + "type": "string", + "description": "", + "default": "MessagingAddressPlanList", + "required": true + }, + "metadata": { + "$ref": "#/definitions/kubernetes_apimachinery_ListMeta", + "existingJavaType": "io.fabric8.kubernetes.api.model.ListMeta" + } + }, + "additionalProperties": true, + "javaType": "MessagingAddressPlanList", + "javaInterfaces": [ + "io.fabric8.kubernetes.api.model.KubernetesResource", + "io.fabric8.kubernetes.api.model.KubernetesResourceList\u003cMessagingAddressPlan\u003e" + ] + }, + "github_com_enmasseproject_enmasse_pkg_apis_enmasse_v1beta2_MessagingAddressPlanSpec": { + "type": "object", + "description": "", + "additionalProperties": true, + "javaType": "MessagingAddressPlanSpec", + "javaInterfaces": [ + "io.fabric8.kubernetes.api.model.KubernetesResource" + ] + }, + "github_com_enmasseproject_enmasse_pkg_apis_enmasse_v1beta2_MessagingAddressPlanStatus": { + "type": "object", + "description": "", + "properties": { + "conditions": { + "type": "array", + "description": "", + "javaOmitEmpty": true, + "items": { + "$ref": "#/definitions/github_com_enmasseproject_enmasse_pkg_apis_enmasse_v1beta2_MessagingAddressPlanCondition", + "javaType": "MessagingAddressPlanCondition" + } + }, + "message": { + "type": "string", + "description": "" + }, + "phase": { + "type": "string", + "description": "" + } + }, + "additionalProperties": true, + "javaType": "MessagingAddressPlanStatus", + "javaInterfaces": [ + "io.fabric8.kubernetes.api.model.KubernetesResource" + ] + }, "github_com_enmasseproject_enmasse_pkg_apis_enmasse_v1beta2_MessagingAddressSpec": { "type": "object", "description": "", @@ -1029,6 +1168,145 @@ "io.fabric8.kubernetes.api.model.KubernetesResource" ] }, + "github_com_enmasseproject_enmasse_pkg_apis_enmasse_v1beta2_MessagingPlan": { + "type": "object", + "description": "", + "properties": { + "apiVersion": { + "type": "string", + "description": "", + "default": "enmasse.io/v1beta2", + "required": true + }, + "kind": { + "type": "string", + "description": "", + "default": "MessagingPlan", + "required": true + }, + "metadata": { + "$ref": "#/definitions/kubernetes_apimachinery_ObjectMeta", + "existingJavaType": "io.fabric8.kubernetes.api.model.ObjectMeta" + }, + "spec": { + "$ref": "#/definitions/github_com_enmasseproject_enmasse_pkg_apis_enmasse_v1beta2_MessagingPlanSpec", + "javaType": "MessagingPlanSpec" + }, + "status": { + "$ref": "#/definitions/github_com_enmasseproject_enmasse_pkg_apis_enmasse_v1beta2_MessagingPlanStatus", + "javaType": "MessagingPlanStatus" + } + }, + "additionalProperties": true, + "javaType": "MessagingPlan", + "javaInterfaces": [ + "io.fabric8.kubernetes.api.model.HasMetadata" + ] + }, + "github_com_enmasseproject_enmasse_pkg_apis_enmasse_v1beta2_MessagingPlanCondition": { + "type": "object", + "description": "", + "properties": { + "lastTransitionTime": { + "$ref": "#/definitions/kubernetes_apimachinery_Time", + "existingJavaType": "java.lang.String" + }, + "message": { + "type": "string", + "description": "" + }, + "reason": { + "type": "string", + "description": "" + }, + "status": { + "type": "string", + "description": "" + }, + "type": { + "type": "string", + "description": "" + } + }, + "additionalProperties": true, + "javaType": "MessagingPlanCondition", + "javaInterfaces": [ + "io.fabric8.kubernetes.api.model.KubernetesResource" + ] + }, + "github_com_enmasseproject_enmasse_pkg_apis_enmasse_v1beta2_MessagingPlanList": { + "type": "object", + "description": "", + "properties": { + "apiVersion": { + "type": "string", + "description": "", + "default": "enmasse.io/v1beta2", + "required": true + }, + "items": { + "type": "array", + "description": "", + "items": { + "$ref": "#/definitions/github_com_enmasseproject_enmasse_pkg_apis_enmasse_v1beta2_MessagingPlan", + "javaType": "MessagingPlan" + } + }, + "kind": { + "type": "string", + "description": "", + "default": "MessagingPlanList", + "required": true + }, + "metadata": { + "$ref": "#/definitions/kubernetes_apimachinery_ListMeta", + "existingJavaType": "io.fabric8.kubernetes.api.model.ListMeta" + } + }, + "additionalProperties": true, + "javaType": "MessagingPlanList", + "javaInterfaces": [ + "io.fabric8.kubernetes.api.model.KubernetesResource", + "io.fabric8.kubernetes.api.model.KubernetesResourceList\u003cMessagingPlan\u003e" + ] + }, + "github_com_enmasseproject_enmasse_pkg_apis_enmasse_v1beta2_MessagingPlanSpec": { + "type": "object", + "description": "", + "additionalProperties": true, + "javaType": "MessagingPlanSpec", + "javaInterfaces": [ + "io.fabric8.kubernetes.api.model.KubernetesResource" + ] + }, + "github_com_enmasseproject_enmasse_pkg_apis_enmasse_v1beta2_MessagingPlanStatus": { + "type": "object", + "description": "", + "properties": { + "conditions": { + "type": "array", + "description": "", + "javaOmitEmpty": true, + "items": { + "$ref": "#/definitions/github_com_enmasseproject_enmasse_pkg_apis_enmasse_v1beta2_MessagingPlanCondition", + "javaType": "MessagingPlanCondition" + } + }, + "message": { + "type": "string", + "description": "" + }, + "phase": { + "type": "string", + "description": "" + } + }, + "additionalProperties": true, + "javaType": "MessagingPlanStatus", + "javaInterfaces": [ + "io.fabric8.kubernetes.api.model.KubernetesResource" + ] + }, "github_com_enmasseproject_enmasse_pkg_apis_enmasse_v1beta2_MessagingTenant": { "type": "object", "description": "", @@ -1135,6 +1413,15 @@ "type": "object", "description": "", "properties": { + "capabilities": { + "type": "array", + "description": "", + "javaOmitEmpty": true, + "items": { + "type": "string", + "description": "" + } + }, "messagingInfrastructureRef": { "$ref": "#/definitions/github_com_enmasseproject_enmasse_pkg_apis_enmasse_v1beta2_MessagingInfrastructureReference", "javaType": "MessagingInfrastructureReference" @@ -1150,6 +1437,19 @@ "type": "object", "description": "", "properties": { + "broker": { + "$ref": "#/definitions/github_com_enmasseproject_enmasse_pkg_apis_enmasse_v1beta2_MessagingAddressBroker", + "javaType": "MessagingAddressBroker" + }, + "capabilities": { + "type": "array", + "description": "", + "javaOmitEmpty": true, + "items": { + "type": "string", + "description": "" + } + }, "conditions": { "type": "array", "description": "", @@ -1538,6 +1838,14 @@ "$ref": "#/definitions/github_com_enmasseproject_enmasse_pkg_apis_enmasse_v1beta2_MessagingAddressList", "javaType": "MessagingAddressList" }, + "MessagingAddressPlan": { + "$ref": "#/definitions/github_com_enmasseproject_enmasse_pkg_apis_enmasse_v1beta2_MessagingAddressPlan", + "javaType": "MessagingAddressPlan" + }, + "MessagingAddressPlanList": { + "$ref": "#/definitions/github_com_enmasseproject_enmasse_pkg_apis_enmasse_v1beta2_MessagingAddressPlanList", + "javaType": "MessagingAddressPlanList" + }, "MessagingEndpoint": { "$ref": "#/definitions/github_com_enmasseproject_enmasse_pkg_apis_enmasse_v1beta2_MessagingEndpoint", "javaType": "MessagingEndpoint" @@ -1554,6 +1862,14 @@ "$ref": "#/definitions/github_com_enmasseproject_enmasse_pkg_apis_enmasse_v1beta2_MessagingInfrastructureList", "javaType": "MessagingInfrastructureList" }, + "MessagingPlan": { + "$ref": "#/definitions/github_com_enmasseproject_enmasse_pkg_apis_enmasse_v1beta2_MessagingPlan", + "javaType": "MessagingPlan" + }, + "MessagingPlanList": { + "$ref": "#/definitions/github_com_enmasseproject_enmasse_pkg_apis_enmasse_v1beta2_MessagingPlanList", + "javaType": "MessagingPlanList" + }, "MessagingTenant": { "$ref": "#/definitions/github_com_enmasseproject_enmasse_pkg_apis_enmasse_v1beta2_MessagingTenant", "javaType": "MessagingTenant" diff --git a/broker-plugin/amqp-connector/pom.xml b/broker-plugin/amqp-connector/pom.xml deleted file mode 100644 index 0337027327c..00000000000 --- a/broker-plugin/amqp-connector/pom.xml +++ /dev/null @@ -1,86 +0,0 @@ - - - - io.enmasse - broker-plugin - 0.32-SNAPSHOT - - 4.0.0 - amqp-connector - - - org.apache.activemq - artemis-amqp-protocol - provided - - - org.apache.activemq - artemis-jdbc-store - - - org.apache.activemq - artemis-native - - - org.apache.activemq - artemis-jms-client - - - org.apache.activemq - artemis-journal - - - org.apache.activemq - artemis-selector - - - org.jgroups - jgroups - - - commons-beanutils - commons-beanutils - - - commons-io - commons-io - - - org.apache.johnzon - johnzon-core - - - org.apache.geronimo.specs - geronimo-json_1.0_spec - - - io.netty - netty-codec-http - - - io.netty - netty-transport-native-epoll - - - io.netty - netty-handler - - - io.netty - netty-codec - - - io.netty - netty-transport - - - - - org.jboss.logging - jboss-logging-processor - provided - true - - - diff --git a/broker-plugin/amqp-connector/src/main/java/org/apache/activemq/artemis/integration/amqp/AMQPClientConnectionFactory.java b/broker-plugin/amqp-connector/src/main/java/org/apache/activemq/artemis/integration/amqp/AMQPClientConnectionFactory.java deleted file mode 100644 index 7e871cb6276..00000000000 --- a/broker-plugin/amqp-connector/src/main/java/org/apache/activemq/artemis/integration/amqp/AMQPClientConnectionFactory.java +++ /dev/null @@ -1,88 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You 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 org.apache.activemq.artemis.integration.amqp; - -import org.apache.activemq.artemis.core.server.ActiveMQServer; -import org.apache.activemq.artemis.protocol.amqp.broker.AMQPConnectionCallback; -import org.apache.activemq.artemis.protocol.amqp.broker.ActiveMQProtonRemotingConnection; -import org.apache.activemq.artemis.protocol.amqp.broker.ProtonProtocolManager; -import org.apache.activemq.artemis.protocol.amqp.proton.AMQPConnectionContext; -import org.apache.activemq.artemis.protocol.amqp.proton.AMQPConstants; -import org.apache.activemq.artemis.protocol.amqp.sasl.ClientSASLFactory; -import org.apache.activemq.artemis.spi.core.remoting.Connection; -import org.apache.qpid.proton.amqp.Symbol; -import org.apache.qpid.proton.engine.Link; -import org.jboss.logging.Logger; - -import java.util.Map; -import java.util.Optional; -import java.util.concurrent.Executor; - -/** - * Connection factory for outgoing AMQP connections. - */ -public class AMQPClientConnectionFactory { - - private static final String PRESERVE_INITIATED_LINKS_SOURCE_TARGET_INFO_ENV_VAR = "PRESERVE_INITIATED_LINKS_SOURCE_TARGET_INFO"; - private final ActiveMQServer server; - private final String containerId; - private final Map connectionProperties; - private final int idleTimeout; - private final boolean useCoreSubscriptionNaming; - private final boolean preserveInitiatedLinksSourceTargetInfo; - - public AMQPClientConnectionFactory(ActiveMQServer server, String containerId, Map connectionProperties, int idleTimeout) { - this.server = server; - this.containerId = containerId; - this.connectionProperties = connectionProperties; - this.idleTimeout = idleTimeout; - this.useCoreSubscriptionNaming = false; - - String preserve = System.getenv(PRESERVE_INITIATED_LINKS_SOURCE_TARGET_INFO_ENV_VAR) == null ? "true" : System.getenv(PRESERVE_INITIATED_LINKS_SOURCE_TARGET_INFO_ENV_VAR); - preserveInitiatedLinksSourceTargetInfo = Boolean.parseBoolean(preserve); - - if (preserveInitiatedLinksSourceTargetInfo) { - ActiveMQAMQPLogger.LOGGER.info("Preserving the source/target info of initiated links"); - } - - } - - public ActiveMQProtonRemotingConnection createConnection(ProtonProtocolManager protocolManager, Connection connection, Optional linkInitiator, ClientSASLFactory clientSASLFactory) { - AMQPConnectionCallback connectionCallback = new AMQPConnectionCallback(protocolManager, connection, server.getExecutorFactory().getExecutor(), server); - - Executor executor = server.getExecutorFactory().getExecutor(); - - AMQPConnectionContext amqpConnection = new AMQPConnectionContext(protocolManager, connectionCallback, containerId, idleTimeout, protocolManager.getMaxFrameSize(), AMQPConstants.Connection.DEFAULT_CHANNEL_MAX, useCoreSubscriptionNaming, server.getScheduledPool(), false, clientSASLFactory, connectionProperties) { - @Override - public void onRemoteOpen(Link link) throws Exception { - if (preserveInitiatedLinksSourceTargetInfo) { - linkInitiator.ifPresent(initiator -> initiator.preserveInitiatedSourceTargetInfo(link)); - } - super.onRemoteOpen(link); - } - }; - linkInitiator.ifPresent(amqpConnection::addEventHandler); - - ActiveMQProtonRemotingConnection delegate = new ActiveMQProtonRemotingConnection(protocolManager, amqpConnection, connection, executor); - delegate.addFailureListener(connectionCallback); - delegate.addCloseListener(connectionCallback); - - connectionCallback.setProtonConnectionDelegate(delegate); - - return delegate; - } -} diff --git a/broker-plugin/amqp-connector/src/main/java/org/apache/activemq/artemis/integration/amqp/AMQPConnectorService.java b/broker-plugin/amqp-connector/src/main/java/org/apache/activemq/artemis/integration/amqp/AMQPConnectorService.java deleted file mode 100644 index c5c88b81574..00000000000 --- a/broker-plugin/amqp-connector/src/main/java/org/apache/activemq/artemis/integration/amqp/AMQPConnectorService.java +++ /dev/null @@ -1,199 +0,0 @@ -/* - * Copyright 2018, EnMasse authors. - * License: Apache License 2.0 (see the file LICENSE or http://apache.org/licenses/LICENSE-2.0.html). - */ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You 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 org.apache.activemq.artemis.integration.amqp; - -import java.util.Arrays; -import java.util.Collections; -import java.util.Map; -import java.util.Optional; -import java.util.concurrent.*; - -import io.netty.util.concurrent.DefaultThreadFactory; -import org.apache.activemq.artemis.api.core.ActiveMQException; -import org.apache.activemq.artemis.core.remoting.impl.netty.NettyConnector; -import org.apache.activemq.artemis.core.remoting.impl.netty.TransportConstants; -import org.apache.activemq.artemis.core.server.ActiveMQComponent; -import org.apache.activemq.artemis.core.server.ActiveMQServer; -import org.apache.activemq.artemis.core.server.ConnectorService; -import org.apache.activemq.artemis.protocol.amqp.broker.ProtonProtocolManager; -import org.apache.activemq.artemis.protocol.amqp.broker.ProtonProtocolManagerFactory; -import org.apache.activemq.artemis.protocol.amqp.client.ProtonClientProtocolManager; -import org.apache.activemq.artemis.protocol.amqp.sasl.ClientSASL; -import org.apache.activemq.artemis.protocol.amqp.sasl.ClientSASLFactory; -import org.apache.activemq.artemis.spi.core.protocol.RemotingConnection; -import org.apache.activemq.artemis.spi.core.remoting.BaseConnectionLifeCycleListener; -import org.apache.activemq.artemis.spi.core.remoting.Connection; - -import org.apache.activemq.artemis.utils.ConfigurationHelper; -import org.apache.qpid.proton.amqp.Symbol; - -/** - * Connector service for outgoing AMQP connections. - */ -public class AMQPConnectorService implements ConnectorService, BaseConnectionLifeCycleListener { - private static final Symbol groupSymbol = Symbol.getSymbol("qd.route-container-group"); - private final String name; - private final ActiveMQServer server; - private volatile RemotingConnection connection; - private final ExecutorService closeExecutor = ThreadPoolExecutor.newSingleThreadExecutor(new DefaultThreadFactory(AMQPConnectorService.class)); - private final ExecutorService nettyThreadPool; - private final ScheduledExecutorService scheduledExecutorService; - private final ProtonClientConnectionManager lifecycleHandler; - private volatile boolean started = false; - private final Map connectorConfig; - - public AMQPConnectorService(String connectorName, Map connectorConfig, String containerId, String groupId, Optional linkInfo, ActiveMQServer server, ScheduledExecutorService scheduledExecutorService, ExecutorService nettyThreadPool, int idleTimeout) { - this.name = connectorName; - this.connectorConfig = connectorConfig; - this.server = server; - this.scheduledExecutorService = scheduledExecutorService; - this.nettyThreadPool = nettyThreadPool; - AMQPClientConnectionFactory factory = new AMQPClientConnectionFactory(server, containerId, Collections.singletonMap(groupSymbol, groupId), idleTimeout); - boolean sslEnabled = ConfigurationHelper.getBooleanProperty(TransportConstants.SSL_ENABLED_PROP_NAME, TransportConstants.DEFAULT_SSL_ENABLED, connectorConfig); - - ClientSASLFactory saslClientFactory = null; - if (sslEnabled) { - ActiveMQAMQPLogger.LOGGER.infov("Enabling SSL for AMQP Connector {0}", name); - saslClientFactory = availableMechanims -> { - if (Arrays.asList(availableMechanims).contains("EXTERNAL")) { - return new ClientSASL() { - @Override - public String getName() { - return "EXTERNAL"; - } - - @Override - public byte[] getInitialResponse() { - return new byte[0]; - } - - @Override - public byte[] getResponse(final byte[] challenge) { - return new byte[0]; - } - }; - } else { - return null; - } - }; - } else { - ActiveMQAMQPLogger.LOGGER.infov("Disabling SSL for AMQP Connector {0}", name); - } - this.lifecycleHandler = new ProtonClientConnectionManager(factory, linkInfo.map(LinkInitiator::new), saslClientFactory); - } - - @Override - public void start() throws Exception { - - scheduledExecutorService.submit(() -> { - Connection connection; - try { - ActiveMQAMQPLogger.LOGGER.infov("Starting connector {0}", name); - ProtonClientProtocolManager protocolManager = new ProtonClientProtocolManager(new ProtonProtocolManagerFactory(), server); - - // These settings have the desired semantics when working in a cloud environment and for the built-in message - // forwarding. - protocolManager.setAmqpTreatRejectAsUnmodifiedDeliveryFailed(true); - protocolManager.setAmqpUseModifiedForTransientDeliveryErrors(true); - protocolManager.setAmqpMinLargeMessageSize(-1); - - NettyConnector connector = new NettyConnector(connectorConfig, lifecycleHandler, AMQPConnectorService.this, closeExecutor, nettyThreadPool, server.getScheduledPool(), protocolManager); - connector.start(); - connection = connector.createConnection(); - } catch (Throwable t) { - // Note - the scheduledExecutorService supplied by Artemis ignores exceptions. Ensure the cause of the failure is logged.. - ActiveMQAMQPLogger.LOGGER.errorv(t, "Error starting connector {0}", name); - throw t; - } - - if (connection != null) { - started = true; - } else { - ActiveMQAMQPLogger.LOGGER.infov("Error starting connector {0}, retrying in 5 seconds", name); - scheduledExecutorService.schedule(() -> { - start(); - return true; - }, 5, TimeUnit.SECONDS); - } - }); - } - - @Override - public void stop() throws Exception { - scheduledExecutorService.submit(() -> { - try { - started = false; - ActiveMQAMQPLogger.LOGGER.infov("Stopping connector {0}", name); - if (connection != null) { - lifecycleHandler.stop(); - } - closeExecutor.shutdown(); - nettyThreadPool.shutdown(); - ActiveMQAMQPLogger.LOGGER.infov("Stopped connector {0}", name); - } catch (Throwable t) { - // Note - the scheduledExecutorService supplied by Artemis ignores exceptions. Ensure the cause of the failure is logged.. - ActiveMQAMQPLogger.LOGGER.errorv(t, "Error stopping connector {0}", name); - throw t; - } - }); - } - - @Override - public boolean isStarted() { - return started; - } - - @Override - public String getName() { - return name; - } - - @Override - public void connectionCreated(ActiveMQComponent component, Connection connection, ProtonProtocolManager protocol) { - ActiveMQAMQPLogger.LOGGER.infov("connectionCreated for connector {0}", name); - lifecycleHandler.connectionCreated(component, connection, protocol); - this.connection = connection.getProtocolConnection(); - } - - @Override - public void connectionDestroyed(Object connectionID) { - lifecycleHandler.connectionDestroyed(connectionID); - ActiveMQAMQPLogger.LOGGER.infov("connectionDestroyed for connector {0}", name); - if (started) { - scheduledExecutorService.schedule(() -> { - start(); - return true; - }, 5, TimeUnit.SECONDS); - } - } - - @Override - public void connectionException(Object connectionID, ActiveMQException me) { - ActiveMQAMQPLogger.LOGGER.infov("connectionException for connector {0}: {1}", name, me.getMessage()); - lifecycleHandler.connectionException(connectionID, me); - } - - @Override - public void connectionReadyForWrites(Object connectionID, boolean ready) { - ActiveMQAMQPLogger.LOGGER.debugv("connectionReadyForWrites for connector {0}", name); - lifecycleHandler.connectionReadyForWrites(connectionID, ready); - } -} diff --git a/broker-plugin/amqp-connector/src/main/java/org/apache/activemq/artemis/integration/amqp/AMQPConnectorServiceFactory.java b/broker-plugin/amqp-connector/src/main/java/org/apache/activemq/artemis/integration/amqp/AMQPConnectorServiceFactory.java deleted file mode 100644 index 55018d57370..00000000000 --- a/broker-plugin/amqp-connector/src/main/java/org/apache/activemq/artemis/integration/amqp/AMQPConnectorServiceFactory.java +++ /dev/null @@ -1,160 +0,0 @@ -/* - * Copyright 2018, EnMasse authors. - * License: Apache License 2.0 (see the file LICENSE or http://apache.org/licenses/LICENSE-2.0.html). - */ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You 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 org.apache.activemq.artemis.integration.amqp; - -import io.netty.util.concurrent.DefaultThreadFactory; -import org.apache.activemq.artemis.core.persistence.StorageManager; -import org.apache.activemq.artemis.core.postoffice.PostOffice; -import org.apache.activemq.artemis.core.postoffice.impl.PostOfficeImpl; -import org.apache.activemq.artemis.core.remoting.impl.netty.TransportConstants; -import org.apache.activemq.artemis.core.server.ConnectorService; -import org.apache.activemq.artemis.core.server.ConnectorServiceFactory; -import org.apache.qpid.proton.amqp.Symbol; - -import java.util.*; -import java.util.concurrent.ExecutorService; -import java.util.concurrent.ScheduledExecutorService; -import java.util.stream.Collectors; - -/** - * Connector service factory for AMQP Connector Services that can be used to establish outgoing AMQP connections. - */ -public class AMQPConnectorServiceFactory implements ConnectorServiceFactory { - private static final String CONTAINER_ID = "containerId"; - private static final String CLUSTER = "clusterId"; - private static final String SOURCE_ADDRESS = "sourceAddress"; - private static final String TARGET_ADDRESS = "targetAddress"; - private static final String LINK_NAME = "linkName"; - private static final String DIRECTION = "direction"; - private static final String NETTY_THREADS = "nettyThreads"; - private static final String IDLE_TIMEOUT = "idleTimeoutMs"; - private static final String LINK_CAPABILITIES = "linkCapabilities"; - - private static final Set requiredProperties = initializeRequiredProperties(); - private static final Set allowedProperties = initializeAllowedProperties(); - - private static Set initializeAllowedProperties() { - Set properties = initializeRequiredProperties(); - properties.add(TARGET_ADDRESS); - properties.add(SOURCE_ADDRESS); - properties.add(DIRECTION); - properties.add(LINK_NAME); - properties.add(CONTAINER_ID); - properties.add(LINK_CAPABILITIES); - properties.add(TransportConstants.SSL_ENABLED_PROP_NAME); - properties.add(TransportConstants.SSL_PROVIDER); - properties.add(TransportConstants.VERIFY_HOST_PROP_NAME); - properties.add(TransportConstants.NEED_CLIENT_AUTH_PROP_NAME); - properties.add(TransportConstants.KEYSTORE_PATH_PROP_NAME); - properties.add(TransportConstants.KEYSTORE_PASSWORD_PROP_NAME); - properties.add(TransportConstants.TRUSTSTORE_PATH_PROP_NAME); - properties.add(TransportConstants.TRUSTSTORE_PASSWORD_PROP_NAME); - properties.add(TransportConstants.USE_GLOBAL_WORKER_POOL_PROP_NAME); - properties.add(TransportConstants.REMOTING_THREADS_PROPNAME); - properties.add(TransportConstants.NETTY_CONNECT_TIMEOUT); - properties.add(IDLE_TIMEOUT); - properties.add(NETTY_THREADS); - return properties; - } - - private static Set initializeRequiredProperties() { - Set properties = new LinkedHashSet<>(); - properties.add(TransportConstants.HOST_PROP_NAME); - properties.add(TransportConstants.PORT_PROP_NAME); - properties.add(CLUSTER); - return properties; - } - - private static void setOrDefault(Map srcMap, Map dstMap, String key, Object defaultValue) { - dstMap.put(key, srcMap.getOrDefault(key, defaultValue)); - } - - @Override - public ConnectorService createConnectorService(String connectorName, Map configuration, StorageManager storageManager, PostOffice postOffice, ScheduledExecutorService scheduledExecutorService) { - String clusterId = (String)configuration.get(CLUSTER); - - Map connectorConfig = new HashMap<>(); - connectorConfig.put(TransportConstants.HOST_PROP_NAME, configuration.get(TransportConstants.HOST_PROP_NAME)); - connectorConfig.put(TransportConstants.PORT_PROP_NAME, configuration.get(TransportConstants.PORT_PROP_NAME)); - - setOrDefault(configuration, connectorConfig, TransportConstants.SSL_ENABLED_PROP_NAME, true); - setOrDefault(configuration, connectorConfig, TransportConstants.VERIFY_HOST_PROP_NAME, false); - setOrDefault(configuration, connectorConfig, TransportConstants.NEED_CLIENT_AUTH_PROP_NAME, true); - setOrDefault(configuration, connectorConfig, TransportConstants.KEYSTORE_PATH_PROP_NAME, System.getenv("KEYSTORE_PATH")); - setOrDefault(configuration, connectorConfig, TransportConstants.KEYSTORE_PASSWORD_PROP_NAME, "enmasse"); - setOrDefault(configuration, connectorConfig, TransportConstants.TRUSTSTORE_PATH_PROP_NAME, System.getenv("TRUSTSTORE_PATH")); - setOrDefault(configuration, connectorConfig, TransportConstants.TRUSTSTORE_PASSWORD_PROP_NAME, "enmasse"); - setOrDefault(configuration, connectorConfig, TransportConstants.NETTY_CONNECT_TIMEOUT, "10000"); - setOrDefault(configuration, connectorConfig, TransportConstants.SSL_PROVIDER, "OPENSSL"); - - Optional sourceAddress = Optional.ofNullable((String)configuration.get(SOURCE_ADDRESS)); - Optional targetAddress = Optional.ofNullable((String)configuration.get(TARGET_ADDRESS)); - Optional direction = Optional.ofNullable((String)configuration.get(DIRECTION)).map(Direction::valueOf); - String linkName = Optional.ofNullable((String)configuration.get(LINK_NAME)).orElse(null); - List capabilities = Optional.ofNullable((String)configuration.get(LINK_CAPABILITIES)) - .map(str -> { - String[] parts = str.split(","); - return Arrays.stream(parts) - .map(Symbol::getSymbol) - .collect(Collectors.toList()); - - }) - .orElse(null); - - String containerId = Optional.ofNullable((String)configuration.get(CONTAINER_ID)).orElse(clusterId); - - int nettyThreads = Optional.ofNullable((String)configuration.get(NETTY_THREADS)).map(Integer::parseInt).orElse(4); - int idleTimeout = Optional.ofNullable((String)configuration.get(IDLE_TIMEOUT)).map(Integer::parseInt).orElse(16_000); - - Optional linkInfo = sourceAddress.flatMap(s -> - targetAddress.flatMap(t -> - direction.map(d -> new LinkInfo(linkName, s, t, d, capabilities)))); - - if (linkInfo.isPresent()) { - ActiveMQAMQPLogger.LOGGER.infof("Creating connector host %s port %s with link %s", configuration.get(TransportConstants.HOST_PROP_NAME), configuration.get(TransportConstants.PORT_PROP_NAME), linkInfo.get()); - } else { - ActiveMQAMQPLogger.LOGGER.infof("Creating connector host %s port %s", configuration.get(TransportConstants.HOST_PROP_NAME), configuration.get(TransportConstants.PORT_PROP_NAME)); - } - - ExecutorService nettyThreadPool = ThreadPoolExecutor.newFixedThreadPool(nettyThreads, new DefaultThreadFactory("connector-" + connectorName)); - return new AMQPConnectorService(connectorName, - connectorConfig, - containerId, - clusterId, - linkInfo, - ((PostOfficeImpl)postOffice).getServer(), - scheduledExecutorService, - nettyThreadPool, - idleTimeout); - } - - @Override - public Set getAllowableProperties() { - return allowedProperties; - } - - @Override - public Set getRequiredProperties() { - return requiredProperties; - } - - -} diff --git a/broker-plugin/amqp-connector/src/main/java/org/apache/activemq/artemis/integration/amqp/ActiveMQAMQPLogger.java b/broker-plugin/amqp-connector/src/main/java/org/apache/activemq/artemis/integration/amqp/ActiveMQAMQPLogger.java deleted file mode 100644 index 14aabf97ef6..00000000000 --- a/broker-plugin/amqp-connector/src/main/java/org/apache/activemq/artemis/integration/amqp/ActiveMQAMQPLogger.java +++ /dev/null @@ -1,45 +0,0 @@ -/* - * Copyright 2018, EnMasse authors. - * License: Apache License 2.0 (see the file LICENSE or http://apache.org/licenses/LICENSE-2.0.html). - */ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You 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 org.apache.activemq.artemis.integration.amqp; - -import org.jboss.logging.BasicLogger; -import org.jboss.logging.Logger; -import org.jboss.logging.annotations.MessageLogger; - -/** - * Logger Code 19 - * - * each message id must be 6 digits long starting with 19, the 3rd digit donates the level so - * - * INF0 1 - * WARN 2 - * DEBUG 3 - * ERROR 4 - * TRACE 5 - * FATAL 6 - * - * so an INFO message would be 191000 to 191999 - */ -@MessageLogger(projectCode = "AMQ") -interface ActiveMQAMQPLogger extends BasicLogger { - - ActiveMQAMQPLogger LOGGER = Logger.getMessageLogger(ActiveMQAMQPLogger.class, ActiveMQAMQPLogger.class.getPackage().getName()); -} diff --git a/broker-plugin/amqp-connector/src/main/java/org/apache/activemq/artemis/integration/amqp/Direction.java b/broker-plugin/amqp-connector/src/main/java/org/apache/activemq/artemis/integration/amqp/Direction.java deleted file mode 100644 index 70f184148fc..00000000000 --- a/broker-plugin/amqp-connector/src/main/java/org/apache/activemq/artemis/integration/amqp/Direction.java +++ /dev/null @@ -1,10 +0,0 @@ -/* - * Copyright 2019, EnMasse authors. - * License: Apache License 2.0 (see the file LICENSE or http://apache.org/licenses/LICENSE-2.0.html). - */ -package org.apache.activemq.artemis.integration.amqp; - -public enum Direction { - in, - out -} diff --git a/broker-plugin/amqp-connector/src/main/java/org/apache/activemq/artemis/integration/amqp/LinkInfo.java b/broker-plugin/amqp-connector/src/main/java/org/apache/activemq/artemis/integration/amqp/LinkInfo.java deleted file mode 100644 index 7d029262bf9..00000000000 --- a/broker-plugin/amqp-connector/src/main/java/org/apache/activemq/artemis/integration/amqp/LinkInfo.java +++ /dev/null @@ -1,77 +0,0 @@ -/* - * Copyright 2018, EnMasse authors. - * License: Apache License 2.0 (see the file LICENSE or http://apache.org/licenses/LICENSE-2.0.html). - */ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You 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 org.apache.activemq.artemis.integration.amqp; - -import org.apache.qpid.proton.amqp.Symbol; - -import java.util.ArrayList; -import java.util.Collections; -import java.util.List; - -public class LinkInfo { - private final String linkName; - private final String sourceAddress; - private final String targetAddress; - private final Direction direction; - private final List capabilities = new ArrayList<>(); - - public LinkInfo(String linkName, String sourceAddress, String targetAddress, Direction direction, List capabilities) { - this.linkName = linkName; - this.sourceAddress = sourceAddress; - this.targetAddress = targetAddress; - this.direction = direction; - if (capabilities != null) { - this.capabilities.addAll(capabilities); - } - } - - public String getTargetAddress() { - return targetAddress; - } - - public String getLinkName() { - return linkName; - } - - public String getSourceAddress() { - return sourceAddress; - } - - public Direction getDirection() { - return direction; - } - - public List getCapabilities() { - return Collections.unmodifiableList(capabilities); - } - - @Override - public String toString() { - return "LinkInfo{" + - "linkName='" + linkName + '\'' + - ", sourceAddress='" + sourceAddress + '\'' + - ", targetAddress='" + targetAddress + '\'' + - ", direction=" + direction + - ", capabilities=" + capabilities + - '}'; - } - -} diff --git a/broker-plugin/amqp-connector/src/main/java/org/apache/activemq/artemis/integration/amqp/LinkInitiator.java b/broker-plugin/amqp-connector/src/main/java/org/apache/activemq/artemis/integration/amqp/LinkInitiator.java deleted file mode 100644 index 0661917a679..00000000000 --- a/broker-plugin/amqp-connector/src/main/java/org/apache/activemq/artemis/integration/amqp/LinkInitiator.java +++ /dev/null @@ -1,292 +0,0 @@ -/* - * Copyright 2018, EnMasse authors. - * License: Apache License 2.0 (see the file LICENSE or http://apache.org/licenses/LICENSE-2.0.html). - */ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You 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 org.apache.activemq.artemis.integration.amqp; - -import io.netty.buffer.ByteBuf; -import org.apache.activemq.artemis.protocol.amqp.proton.handler.EventHandler; -import org.apache.activemq.artemis.protocol.amqp.proton.handler.ProtonHandler; -import org.apache.activemq.artemis.spi.core.remoting.ReadyListener; -import org.apache.qpid.proton.amqp.Symbol; -import org.apache.qpid.proton.amqp.messaging.Accepted; -import org.apache.qpid.proton.amqp.messaging.Modified; -import org.apache.qpid.proton.amqp.messaging.Rejected; -import org.apache.qpid.proton.amqp.messaging.Released; -import org.apache.qpid.proton.amqp.messaging.Source; -import org.apache.qpid.proton.amqp.messaging.Target; -import org.apache.qpid.proton.amqp.messaging.TerminusDurability; -import org.apache.qpid.proton.engine.Connection; -import org.apache.qpid.proton.engine.Delivery; -import org.apache.qpid.proton.engine.Link; -import org.apache.qpid.proton.engine.Receiver; -import org.apache.qpid.proton.engine.Sender; -import org.apache.qpid.proton.engine.Session; -import org.apache.qpid.proton.engine.Transport; -import org.apache.qpid.proton.engine.impl.LinkMutator; - -import java.util.Map; -import java.util.concurrent.ConcurrentHashMap; - -/** - * Event handler for establishing outgoing links - */ -public class LinkInitiator implements EventHandler { - private final LinkInfo linkInfo; - private final Map initiatedReceivingLinks = new ConcurrentHashMap<>(); - private final Map initiatedSendingLinks = new ConcurrentHashMap<>(); - - public LinkInitiator(LinkInfo linkInfo) { - this.linkInfo = linkInfo; - } - - @Override - public void onAuthInit(ProtonHandler handler, Connection connection, boolean sasl) { - - } - - @Override - public void onSaslRemoteMechanismChosen(ProtonHandler handler, String mech) { - - } - - @Override - public void onAuthFailed(final ProtonHandler protonHandler, final Connection connection) { - - } - - @Override - public void onAuthSuccess(final ProtonHandler protonHandler, final Connection connection) { - - } - - @Override - public void onSaslMechanismsOffered(final ProtonHandler handler, final String[] mechanisms) { - - } - - @Override - public void onInit(Connection connection) throws Exception { - - } - - @Override - public void onLocalOpen(Connection connection) throws Exception { - - } - - @Override - public void onRemoteOpen(org.apache.qpid.proton.engine.Connection connection) throws Exception { - connection.session().open(); - } - - @Override - public void onLocalClose(Connection connection) throws Exception { - - } - - @Override - public void onRemoteClose(Connection connection) throws Exception { - - } - - @Override - public void onFinal(Connection connection) throws Exception { - - } - - @Override - public void onInit(Session session) throws Exception { - - } - - @Override - public void onLocalOpen(Session session) throws Exception { - - } - - @Override - public void onRemoteOpen(Session session) throws Exception { - createLink(session); - } - - @Override - public void onLocalClose(Session session) throws Exception { - - } - - @Override - public void onRemoteClose(Session session) throws Exception { - - } - - @Override - public void onFinal(Session session) throws Exception { - - } - - @Override - public void onInit(Link link) throws Exception { - - } - - @Override - public void onLocalOpen(Link link) throws Exception { - - } - - @Override - public void onRemoteOpen(Link link) throws Exception { - ActiveMQAMQPLogger.LOGGER.infov("{0} onRemoteOpen", link.getName()); - } - - @Override - public void onLocalClose(Link link) throws Exception { - ActiveMQAMQPLogger.LOGGER.infov("{0} onLocalClose", link.getName()); - } - - @Override - public void onRemoteClose(Link link) throws Exception { - ActiveMQAMQPLogger.LOGGER.infov("Link {0} closed. Closing connection", link.getName()); - initiatedSendingLinks.remove(link); - initiatedReceivingLinks.remove(link); - link.getSession().getConnection().close(); - } - - @Override - public void onFlow(Link link) throws Exception { - - } - - @Override - public void onFinal(Link link) throws Exception { - - } - - @Override - public void onRemoteDetach(Link link) throws Exception { - ActiveMQAMQPLogger.LOGGER.infov("Link {0} detached. Closing connection", link.getName()); - initiatedSendingLinks.remove(link); - initiatedReceivingLinks.remove(link); - link.getSession().getConnection().close(); - } - - @Override - public void onLocalDetach(Link link) throws Exception { - ActiveMQAMQPLogger.LOGGER.infov("{0} onLocalDetach", link.getName()); - } - - @Override - public void onDelivery(Delivery delivery) throws Exception { - - } - - @Override - public boolean flowControl(ReadyListener readyListener) { - if (this.linkInfo.getDirection().equals(Direction.in)) { - readyListener.readyForWriting(); - } - return true; - } - - @Override - public void onTransport(Transport transport) throws Exception { - - } - - @Override - public void pushBytes(ByteBuf bytes) { - - } - - private void createLink(org.apache.qpid.proton.engine.Session session) throws Exception { - Link initiatedLink = null; - switch (linkInfo.getDirection()) { - case out: - initiatedLink = createSender(session); - initiatedSendingLinks.put(initiatedLink, initiatedLink.getSource().copy()); - break; - case in: - initiatedLink = createReceiver(session); - initiatedReceivingLinks.put(initiatedLink, initiatedLink.getTarget().copy()); - break; - } - } - - private Link createReceiver(Session session) { - Receiver receiver; - if (linkInfo.getLinkName() != null) { - receiver = session.receiver(linkInfo.getLinkName()); - } else { - receiver = session.receiver(linkInfo.getSourceAddress()); - } - Target target = new Target(); - target.setAddress(linkInfo.getTargetAddress()); - if (!linkInfo.getCapabilities().isEmpty()) { - target.setCapabilities(Symbol.getSymbol("qd.waypoint")); - } - receiver.setTarget(target); - - Source source = new Source(); - source.setAddress(linkInfo.getSourceAddress()); - source.setDurable(TerminusDurability.UNSETTLED_STATE); - if (!linkInfo.getCapabilities().isEmpty()) { - source.setCapabilities(linkInfo.getCapabilities().toArray(new Symbol[0])); - source.setCapabilities(Symbol.getSymbol("qd.waypoint")); - } - receiver.setSource(source); - receiver.open(); - return receiver; - } - - private Link createSender(Session session) { - Sender sender; - if (linkInfo.getLinkName() != null) { - sender = session.sender(linkInfo.getLinkName()); - } else { - sender = session.sender(linkInfo.getTargetAddress()); - } - Target target = new Target(); - target.setAddress(linkInfo.getTargetAddress()); - if (!linkInfo.getCapabilities().isEmpty()) { - target.setCapabilities(Symbol.getSymbol("qd.waypoint")); - } - sender.setTarget(target); - - Source source = new Source(); - source.setAddress(linkInfo.getSourceAddress()); - source.setDurable(TerminusDurability.UNSETTLED_STATE); - source.setOutcomes(Accepted.DESCRIPTOR_SYMBOL, Rejected.DESCRIPTOR_SYMBOL, Released.DESCRIPTOR_SYMBOL, Modified.DESCRIPTOR_SYMBOL); - if (!linkInfo.getCapabilities().isEmpty()) { - source.setCapabilities(Symbol.getSymbol("qd.waypoint")); - } - sender.setSource(source); - - sender.open(); - return sender; - } - - public void preserveInitiatedSourceTargetInfo(Link link) { - if (initiatedReceivingLinks.containsKey(link)) { - LinkMutator.setRemoteTarget(link, initiatedReceivingLinks.get(link)); - } else if (initiatedSendingLinks.containsKey(link)) { - LinkMutator.setRemoteSource(link, initiatedSendingLinks.get(link)); - } - } -} diff --git a/broker-plugin/amqp-connector/src/main/java/org/apache/activemq/artemis/integration/amqp/ProtonClientConnectionManager.java b/broker-plugin/amqp-connector/src/main/java/org/apache/activemq/artemis/integration/amqp/ProtonClientConnectionManager.java deleted file mode 100644 index f769711828e..00000000000 --- a/broker-plugin/amqp-connector/src/main/java/org/apache/activemq/artemis/integration/amqp/ProtonClientConnectionManager.java +++ /dev/null @@ -1,116 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You 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 org.apache.activemq.artemis.integration.amqp; - -import org.apache.activemq.artemis.api.core.ActiveMQBuffer; -import org.apache.activemq.artemis.api.core.ActiveMQException; -import org.apache.activemq.artemis.api.core.ActiveMQRemoteDisconnectException; -import org.apache.activemq.artemis.core.server.ActiveMQComponent; -import org.apache.activemq.artemis.protocol.amqp.broker.ActiveMQProtonRemotingConnection; -import org.apache.activemq.artemis.protocol.amqp.broker.ProtonProtocolManager; -import org.apache.activemq.artemis.protocol.amqp.proton.handler.EventHandler; -import org.apache.activemq.artemis.protocol.amqp.sasl.ClientSASLFactory; -import org.apache.activemq.artemis.spi.core.protocol.RemotingConnection; -import org.apache.activemq.artemis.spi.core.remoting.BaseConnectionLifeCycleListener; -import org.apache.activemq.artemis.spi.core.remoting.BufferHandler; -import org.apache.activemq.artemis.spi.core.remoting.Connection; -import org.jboss.logging.Logger; - -import java.util.Map; -import java.util.Optional; -import java.util.concurrent.ConcurrentHashMap; - -/** - * Manages the lifecycle of a proton client connection. - */ -public class ProtonClientConnectionManager implements BaseConnectionLifeCycleListener, BufferHandler { - private final Map connectionMap = new ConcurrentHashMap<>(); - private static final Logger log = Logger.getLogger(ProtonClientConnectionManager.class); - private final AMQPClientConnectionFactory connectionFactory; - private final Optional linkInitiator; - private final ClientSASLFactory clientSASLFactory; - - public ProtonClientConnectionManager(AMQPClientConnectionFactory connectionFactory, Optional linkInitiator, ClientSASLFactory clientSASLFactory) { - this.connectionFactory = connectionFactory; - this.linkInitiator = linkInitiator; - this.clientSASLFactory = clientSASLFactory; - } - - @Override - public void connectionCreated(ActiveMQComponent component, Connection connection, ProtonProtocolManager protocolManager) { - ActiveMQProtonRemotingConnection amqpConnection = connectionFactory.createConnection(protocolManager, connection, linkInitiator, clientSASLFactory); - connectionMap.put(connection.getID(), amqpConnection); - amqpConnection.open(); - - log.info("Connection " + amqpConnection.getRemoteAddress() + " created"); - } - - @Override - public void connectionDestroyed(Object connectionID) { - RemotingConnection connection = connectionMap.remove(connectionID); - if (connection != null) { - log.info("Connection " + connection.getRemoteAddress() + " destroyed"); - connection.fail(new ActiveMQRemoteDisconnectException()); - } else { - log.error("Connection with id " + connectionID + " not found in connectionDestroyed"); - } - } - - @Override - public void connectionException(Object connectionID, ActiveMQException me) { - RemotingConnection connection = connectionMap.get(connectionID); - if (connection != null) { - log.info("Connection " + connection.getRemoteAddress() + " exception: " + me.getMessage()); - connection.fail(me); - } else { - log.error("Connection with id " + connectionID + " not found in connectionException"); - } - } - - @Override - public void connectionReadyForWrites(Object connectionID, boolean ready) { - RemotingConnection connection = connectionMap.get(connectionID); - if (connection != null) { - if (log.isDebugEnabled()) { - log.debug("Connection " + connection.getRemoteAddress() + " ready"); - } - connection.getTransportConnection().fireReady(true); - } else { - log.error("Connection with id " + connectionID + " not found in connectionReadyForWrites()!"); - } - } - - public void stop() { - for (RemotingConnection connection : connectionMap.values()) { - connection.destroy(); - } - } - - @Override - public void bufferReceived(Object connectionID, ActiveMQBuffer buffer) { - RemotingConnection connection = connectionMap.get(connectionID); - if (connection != null) { - connection.bufferReceived(connectionID, buffer); - } else { - log.error("Connection with id " + connectionID + " not found in bufferReceived()!"); - } - } - - public RemotingConnection getConnection(Object connectionId) { - return connectionMap.get(connectionId); - } -} diff --git a/broker-plugin/amqp-connector/src/main/java/org/apache/activemq/artemis/integration/amqp/ThreadPoolExecutor.java b/broker-plugin/amqp-connector/src/main/java/org/apache/activemq/artemis/integration/amqp/ThreadPoolExecutor.java deleted file mode 100644 index 1461b11813d..00000000000 --- a/broker-plugin/amqp-connector/src/main/java/org/apache/activemq/artemis/integration/amqp/ThreadPoolExecutor.java +++ /dev/null @@ -1,45 +0,0 @@ -/* - * Copyright 2020, EnMasse authors. - * License: Apache License 2.0 (see the file LICENSE or http://apache.org/licenses/LICENSE-2.0.html). - */ - -package org.apache.activemq.artemis.integration.amqp; - -import java.util.concurrent.*; - -public class ThreadPoolExecutor { - public static ExecutorService newSingleThreadExecutor(ThreadFactory threadFactory) { - return newFixedThreadPool(1, threadFactory); - } - - public static ExecutorService newFixedThreadPool(int nThreads, ThreadFactory threadFactory) { - return new java.util.concurrent.ThreadPoolExecutor(nThreads, nThreads, 0L, TimeUnit.MILLISECONDS, new LinkedBlockingQueue<>(), threadFactory) { - @Override - protected void afterExecute(Runnable r, Throwable t) { - super.afterExecute(r, t); - if (t == null && r instanceof Future) { - try { - Future future = (Future) r; - if (future.isDone()) { - future.get(); - } - } catch (CancellationException ce) { - t = ce; - } catch (ExecutionException ee) { - t = ee.getCause(); - } catch (InterruptedException ie) { - Thread.currentThread().interrupt(); - } - } - if (t != null) { - if (t instanceof Error) { - ActiveMQAMQPLogger.LOGGER.fatalf(t, "Connector service failed"); - Runtime.getRuntime().halt(1); - } else { - ActiveMQAMQPLogger.LOGGER.warnf(t, "Connector service failed"); - } - } - } - }; - } -} diff --git a/broker-plugin/amqp-connector/src/main/java/org/apache/qpid/proton/engine/impl/LinkMutator.java b/broker-plugin/amqp-connector/src/main/java/org/apache/qpid/proton/engine/impl/LinkMutator.java deleted file mode 100644 index ab6eb1a0dda..00000000000 --- a/broker-plugin/amqp-connector/src/main/java/org/apache/qpid/proton/engine/impl/LinkMutator.java +++ /dev/null @@ -1,26 +0,0 @@ -/* - * Copyright 2020, EnMasse authors. - * License: Apache License 2.0 (see the file LICENSE or http://apache.org/licenses/LICENSE-2.0.html). - */ - -package org.apache.qpid.proton.engine.impl; - -import org.apache.activemq.artemis.integration.amqp.LinkInitiator; -import org.apache.qpid.proton.amqp.transport.Source; -import org.apache.qpid.proton.amqp.transport.Target; -import org.apache.qpid.proton.engine.Link; -import org.jboss.logging.Logger; - -public class LinkMutator { - private static final Logger log = Logger.getLogger(LinkInitiator.class); - - public static void setRemoteTarget(Link link, Target target) { - log.debugv("Changing target from {} => {}}", link.getRemoteTarget(), target); - ((LinkImpl) link).setRemoteTarget(target); - } - - public static void setRemoteSource(Link link, Source source) { - log.debugv("Changing source from {} => {}}", link.getRemoteSource(), source); - ((LinkImpl) link).setRemoteSource(source); - } -} diff --git a/broker-plugin/broker-cli/pom.xml b/broker-plugin/broker-cli/pom.xml index bbfaa4139e6..eb7c2e225db 100644 --- a/broker-plugin/broker-cli/pom.xml +++ b/broker-plugin/broker-cli/pom.xml @@ -5,7 +5,7 @@ io.enmasse broker-plugin - 0.32-SNAPSHOT + 1.0-SNAPSHOT 4.0.0 broker-cli diff --git a/broker-plugin/plugin/pom.xml b/broker-plugin/plugin/pom.xml index 52091f824ba..921ac36dcf8 100644 --- a/broker-plugin/plugin/pom.xml +++ b/broker-plugin/plugin/pom.xml @@ -4,7 +4,7 @@ io.enmasse broker-plugin - 0.32-SNAPSHOT + 1.0-SNAPSHOT pom 4.0.0 @@ -13,14 +13,6 @@ /opt/broker-plugin - - io.enmasse - amqp-connector - - - io.enmasse - sasl-delegation - io.enmasse broker-cli diff --git a/broker-plugin/plugin/src/assembly/unix-dist.xml b/broker-plugin/plugin/src/assembly/unix-dist.xml index 2b9abf6b113..2632b11cb83 100644 --- a/broker-plugin/plugin/src/assembly/unix-dist.xml +++ b/broker-plugin/plugin/src/assembly/unix-dist.xml @@ -44,18 +44,6 @@ - - ${project.basedir}/../amqp-connector/target/amqp-connector-${project.version}.jar - ${plugin.home}/lib - amqp-connector.jar - 0644 - - - ${project.basedir}/../sasl-delegation/target/sasl-delegation-${project.version}.jar - ${plugin.home}/lib - sasl-delegation.jar - 0644 - ${project.basedir}/../broker-cli/target/broker-cli-${project.version}.jar ${plugin.home}/lib diff --git a/broker-plugin/plugin/src/main/resources/brokered/broker.xml b/broker-plugin/plugin/src/main/resources/brokered/broker.xml deleted file mode 100644 index 9f9d6bc14a3..00000000000 --- a/broker-plugin/plugin/src/main/resources/brokered/broker.xml +++ /dev/null @@ -1,115 +0,0 @@ - - - - - - - - $CONTAINER_ID - - true - - - ASYNCIO - - ./data/paging - - ./data/bindings - - ./data/journal - - ./data/large-messages - - true - - 2 - - -1 - - 10M - - 2212000 - - - 5000 - - - 90 - - - true - - 120000 - - 60000 - - HALT - - ${GLOBAL_MAX_SIZE} - - - - true - - ${MESSAGE_EXPIRY_SCAN_PERIOD} - - - tcp://127.0.0.1:61616?tcpSendBufferSize=1048576;tcpReceiveBufferSize=1048576;protocols=CORE - tcp://0.0.0.0:5672?protocols=AMQP,CORE,OPENWIRE,STOMP,MQTT;saslMechanisms=PLAIN;amqpMinLargeMessageSize=-1 - tcp://0.0.0.0:5671?protocols=AMQP,CORE,OPENWIRE,STOMP,MQTT;sslEnabled=true;saslMechanisms=PLAIN;keyStorePath=${EXTERNAL_KEYSTORE_PATH};keyStorePassword=enmasse;enabledProtocols=TLSv1.2,TLSv1.3;amqpMinLargeMessageSize=-1 - - tcp://0.0.0.0:55671?protocols=AMQP;saslMechanisms=ANONYMOUS;sslEnabled=true;keyStorePath=${KEYSTORE_PATH};keyStorePassword=enmasse;trustStorePath=${TRUSTSTORE_PATH};trustStorePassword=enmasse;verifyHost=false;needClientAuth=true - - - - - - - - - - - true - / - # - + - - - - - - -1 - 1 - 1.5 - 10000 - - -1 - 10 - ${ADDRESS_FULL_POLICY} - - - - - diff --git a/broker-plugin/plugin/src/main/resources/brokered/login.config b/broker-plugin/plugin/src/main/resources/brokered/login.config deleted file mode 100644 index f5249ed7aee..00000000000 --- a/broker-plugin/plugin/src/main/resources/brokered/login.config +++ /dev/null @@ -1,30 +0,0 @@ -/* - * Copyright 2017 Red Hat Inc. - * - * 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. - */ -activemq { - io.enmasse.artemis.sasl_delegation.SaslDelegatingLogin required - hostname="${AUTHENTICATION_SERVICE_HOST}" - port="${AUTHENTICATION_SERVICE_PORT}" - sasl_hostname="${AUTHENTICATION_SERVICE_SASL_INIT_HOST}" - use_tls="true" - truststore_path="${AUTH_TRUSTSTORE_PATH}" - truststore_password="enmasse" - valid_cert_users="agent.${INFRA_UUID}:admin" - default_roles_authenticated="all" - default_roles_unauthenticated="admin" - security_settings="enmasse" - support_user_path="/opt/apache-artemis/support/username" - support_password_path="/opt/apache-artemis/support/password"; -}; diff --git a/broker-plugin/plugin/src/main/resources/shared/broker.xml b/broker-plugin/plugin/src/main/resources/shared/broker.xml index cfb1e04b333..bf11f6849ff 100644 --- a/broker-plugin/plugin/src/main/resources/shared/broker.xml +++ b/broker-plugin/plugin/src/main/resources/shared/broker.xml @@ -85,7 +85,7 @@ under the License. - + diff --git a/broker-plugin/plugin/src/main/resources/standard/colocated/broker.xml b/broker-plugin/plugin/src/main/resources/standard/colocated/broker.xml deleted file mode 100644 index 7e87f734ecc..00000000000 --- a/broker-plugin/plugin/src/main/resources/standard/colocated/broker.xml +++ /dev/null @@ -1,166 +0,0 @@ - - - - - - - - $CONTAINER_ID - - true - - - ASYNCIO - - ./data/paging - - ./data/bindings - - ./data/journal - - ./data/large-messages - - true - - 2 - - -1 - - 2212000 - - - 5000 - - - 90 - - - true - - 120000 - - 60000 - - HALT - - ${GLOBAL_MAX_SIZE} - - - - true - - ${MESSAGE_EXPIRY_SCAN_PERIOD} - - - tcp://127.0.0.1:61616?tcpSendBufferSize=1048576;tcpReceiveBufferSize=1048576;protocols=CORE - tcp://0.0.0.0:5673?protocols=AMQP;sslEnabled=true;keyStorePath=${KEYSTORE_PATH};keyStorePassword=enmasse;trustStorePath=${TRUSTSTORE_PATH};trustStorePassword=enmasse;verifyHost=false;needClientAuth=true - - - - - - - - - - - - - - -1 - 1 - 1.5 - 10000 - !!GLOBAL_DLQ - - -1 - 10 - ${ADDRESS_FULL_POLICY} - ANYCAST - - - probe - - - - - true - / - # - + - - - -
- - - -
-
- - - -
-
- - - - org.apache.activemq.artemis.integration.amqp.AMQPConnectorServiceFactory - - - - - - - org.apache.activemq.artemis.integration.amqp.AMQPConnectorServiceFactory - - - - - - - - - org.apache.activemq.artemis.integration.amqp.AMQPConnectorServiceFactory - - - - - - - - - org.apache.activemq.artemis.integration.amqp.AMQPConnectorServiceFactory - - - - - - - - -
-
- diff --git a/broker-plugin/plugin/src/main/resources/standard/login.config b/broker-plugin/plugin/src/main/resources/standard/login.config deleted file mode 100644 index 30975799b8e..00000000000 --- a/broker-plugin/plugin/src/main/resources/standard/login.config +++ /dev/null @@ -1,30 +0,0 @@ -/* - * Copyright 2017 Red Hat Inc. - * - * 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. - */ -activemq { - io.enmasse.artemis.sasl_delegation.SaslDelegatingLogin required - hostname="${AUTHENTICATION_SERVICE_HOST}" - port="${AUTHENTICATION_SERVICE_PORT}" - sasl_hostname="${AUTHENTICATION_SERVICE_SASL_INIT_HOST}" - use_tls="true" - truststore_path="${AUTH_TRUSTSTORE_PATH}" - truststore_password="enmasse" - valid_cert_users="admin.${INFRA_UUID}:admin;router.${INFRA_UUID}:admin;broker.${INFRA_UUID}:admin;subserv.${INFRA_UUID}:admin" - default_roles_authenticated="all" - default_roles_unauthenticated="admin" - security_settings="enmasse" - support_user_path="/opt/apache-artemis/support/username" - support_password_path="/opt/apache-artemis/support/password"; -}; diff --git a/broker-plugin/plugin/src/main/resources/standard/sharded-queue/README.md b/broker-plugin/plugin/src/main/resources/standard/sharded-queue/README.md deleted file mode 100644 index b11055caaf8..00000000000 --- a/broker-plugin/plugin/src/main/resources/standard/sharded-queue/README.md +++ /dev/null @@ -1,3 +0,0 @@ -*NOTE*: The `broker.xml` file in this folder is needed to support upgrading -from 0.26.X where sharded queues were deployed using this broker.xml rather -than as a colocated broker which is the case after 0.26.x. diff --git a/broker-plugin/plugin/src/main/resources/standard/sharded-queue/broker.xml b/broker-plugin/plugin/src/main/resources/standard/sharded-queue/broker.xml deleted file mode 100644 index 8a1c0707158..00000000000 --- a/broker-plugin/plugin/src/main/resources/standard/sharded-queue/broker.xml +++ /dev/null @@ -1,163 +0,0 @@ - - - - - - - - - - $CONTAINER_ID - - true - - - ASYNCIO - - ./data/paging - - ./data/bindings - - ./data/journal - - ./data/large-messages - - true - - 2 - - -1 - - 10M - - 2212000 - - true - - - 5000 - - - 90 - - - true - - 120000 - - 60000 - - HALT - - ${GLOBAL_MAX_SIZE} - - - - ${MESSAGE_EXPIRY_SCAN_PERIOD} - - - tcp://127.0.0.1:61616?tcpSendBufferSize=1048576;tcpReceiveBufferSize=1048576;protocols=CORE - tcp://0.0.0.0:5673?protocols=AMQP;sslEnabled=true;keyStorePath=${KEYSTORE_PATH};keyStorePassword=enmasse;trustStorePath=${TRUSTSTORE_PATH};trustStorePassword=enmasse;verifyHost=false;needClientAuth=true - - - - - - - - - - - - - - -1 - 1 - 1.5 - 10000 - !!GLOBAL_DLQ - - -1 - 10 - ${ADDRESS_FULL_POLICY} - - - probe - - - -
- - - -
-
- - - -
-
- - true - / - # - + - - - - org.apache.activemq.artemis.integration.amqp.AMQPConnectorServiceFactory - - - - - - - - - org.apache.activemq.artemis.integration.amqp.AMQPConnectorServiceFactory - - - - - - - - - org.apache.activemq.artemis.integration.amqp.AMQPConnectorServiceFactory - - - - - - - - -
-
- diff --git a/broker-plugin/plugin/src/main/resources/standard/sharded-topic/broker.xml b/broker-plugin/plugin/src/main/resources/standard/sharded-topic/broker.xml deleted file mode 100644 index 507f49e9b04..00000000000 --- a/broker-plugin/plugin/src/main/resources/standard/sharded-topic/broker.xml +++ /dev/null @@ -1,200 +0,0 @@ - - - - - - - - $CONTAINER_ID - - true - - - ASYNCIO - - ./data/paging - - ./data/bindings - - ./data/journal - - ./data/large-messages - - true - - 2 - - -1 - - 10M - - 2212000 - - - - 5000 - - - 90 - - - true - - 120000 - - 60000 - - HALT - - ${GLOBAL_MAX_SIZE} - - - - true - - ${MESSAGE_EXPIRY_SCAN_PERIOD} - - - tcp://127.0.0.1:61616?tcpSendBufferSize=1048576;tcpReceiveBufferSize=1048576;protocols=CORE - tcp://0.0.0.0:5673?protocols=AMQP;sslEnabled=true;keyStorePath=${KEYSTORE_PATH};keyStorePassword=enmasse;trustStorePath=${TRUSTSTORE_PATH};trustStorePassword=enmasse;verifyHost=false;needClientAuth=true - - - - - - - - - - - - - - -1 - 1 - 1.5 - 10000 - !!GLOBAL_DLQ - - -1 - 10 - ${ADDRESS_FULL_POLICY} - - - probe - - - -
- -
-
- -
-
- - - -
-
- - - -
-
- - true - / - # - + - - - -
$TOPIC_NAME
- $TOPIC_NAME/$HOSTNAME - ANYCAST - false -
-
- - - - org.apache.activemq.artemis.integration.amqp.AMQPConnectorServiceFactory - - - - - - - org.apache.activemq.artemis.integration.amqp.AMQPConnectorServiceFactory - - - - - - - - - org.apache.activemq.artemis.integration.amqp.AMQPConnectorServiceFactory - - - - - - - - - org.apache.activemq.artemis.integration.amqp.AMQPConnectorServiceFactory - - - - - - - - - org.apache.activemq.artemis.integration.amqp.AMQPConnectorServiceFactory - - - - - - - - - - - org.apache.activemq.artemis.integration.amqp.AMQPConnectorServiceFactory - - - - - - - - -
-
- diff --git a/broker-plugin/pom.xml b/broker-plugin/pom.xml index f755836909f..878ceba7d63 100644 --- a/broker-plugin/pom.xml +++ b/broker-plugin/pom.xml @@ -4,7 +4,7 @@ io.enmasse enmasse - 0.32-SNAPSHOT + 1.0-SNAPSHOT pom 4.0.0 @@ -14,8 +14,6 @@ 8 - amqp-connector - sasl-delegation tcnative broker-cli plugin diff --git a/broker-plugin/sasl-delegation/pom.xml b/broker-plugin/sasl-delegation/pom.xml deleted file mode 100644 index 18d2c7dd3c2..00000000000 --- a/broker-plugin/sasl-delegation/pom.xml +++ /dev/null @@ -1,128 +0,0 @@ - - - - io.enmasse - broker-plugin - 0.32-SNAPSHOT - - 4.0.0 - sasl-delegation - - - org.apache.activemq - artemis-amqp-protocol - provided - - - org.apache.activemq - artemis-selector - - - org.apache.activemq - artemis-jdbc-store - - - org.apache.activemq - artemis-native - - - org.apache.activemq - artemis-jms-client - - - org.apache.activemq - artemis-commons - - - org.apache.activemq - artemis-journal - - - org.jgroups - jgroups - - - commons-beanutils - commons-beanutils - - - commons-io - commons-io - - - org.apache.johnzon - johnzon-core - - - org.apache.geronimo.specs - geronimo-json_1.0_spec - - - io.netty - * - - - - - org.apache.qpid - proton-j - compile - - - org.junit.jupiter - junit-jupiter-api - test - - - org.junit.jupiter - junit-jupiter-engine - test - - - org.junit.platform - junit-platform-launcher - test - - - io.netty - netty-common - test - - - io.netty - netty-transport - test - - - io.netty - netty-handler - test - - - org.mockito - mockito-core - test - - - io.vertx - vertx-core - test - - - io.netty - * - - - - - io.vertx - vertx-proton - - - ch.qos.logback - logback-classic - test - - - diff --git a/broker-plugin/sasl-delegation/src/main/java/io/enmasse/artemis/sasl_delegation/PlainSaslMechanismFactory.java b/broker-plugin/sasl-delegation/src/main/java/io/enmasse/artemis/sasl_delegation/PlainSaslMechanismFactory.java deleted file mode 100644 index fdacb091e7d..00000000000 --- a/broker-plugin/sasl-delegation/src/main/java/io/enmasse/artemis/sasl_delegation/PlainSaslMechanismFactory.java +++ /dev/null @@ -1,79 +0,0 @@ -/* - * Copyright 2017-2018, EnMasse authors. - * License: Apache License 2.0 (see the file LICENSE or http://apache.org/licenses/LICENSE-2.0.html). - */ -package io.enmasse.artemis.sasl_delegation; - -import java.io.IOException; -import java.nio.ByteBuffer; -import java.nio.CharBuffer; -import java.nio.charset.StandardCharsets; -import java.util.Map; -import javax.security.auth.callback.Callback; -import javax.security.auth.callback.CallbackHandler; -import javax.security.auth.callback.NameCallback; -import javax.security.auth.callback.PasswordCallback; -import javax.security.auth.callback.UnsupportedCallbackException; - -public class PlainSaslMechanismFactory implements SaslMechanismFactory { - - @Override - public String getName() { - return "PLAIN"; - } - - @Override - public boolean isSecure() { - return false; - } - - @Override - public SaslMechanism newInstance(CallbackHandler callbackHandler, - Map sharedState, - Map options) { - - - NameCallback nameCallback = new NameCallback("Username: "); - PasswordCallback passwordCallback = new PasswordCallback("Password: ", false); - try { - callbackHandler.handle(new Callback[] { nameCallback, passwordCallback }); - } catch (IOException ioe) { - return null; - } catch (UnsupportedCallbackException e) { - return null; - } - - return new PlainMechanism(nameCallback.getName(), passwordCallback.getPassword()); - } - - private static class PlainMechanism implements SaslMechanism { - - private final byte[] initialResponse; - private boolean complete; - - public PlainMechanism(String name, char[] password) { - ByteBuffer encodedPassword = StandardCharsets.UTF_8.encode(CharBuffer.wrap(password)); - ByteBuffer nameBytes = StandardCharsets.UTF_8.encode(name); - final int encodedNameLength = nameBytes.remaining(); - initialResponse = new byte[2 + encodedPassword.remaining() + encodedNameLength]; - nameBytes.get(initialResponse, 1, encodedNameLength); - encodedPassword.get(initialResponse, 2+encodedNameLength, encodedPassword.remaining()); - } - - @Override - public byte[] getResponse(byte[] challenge) { - if(complete) { - return new byte[0]; - } else { - complete = true; - return initialResponse; - } - - } - - @Override - public boolean completedSuccessfully() { - return complete; - } - } -} diff --git a/broker-plugin/sasl-delegation/src/main/java/io/enmasse/artemis/sasl_delegation/SaslDelegatingLogin.java b/broker-plugin/sasl-delegation/src/main/java/io/enmasse/artemis/sasl_delegation/SaslDelegatingLogin.java deleted file mode 100644 index f3d97ae67a0..00000000000 --- a/broker-plugin/sasl-delegation/src/main/java/io/enmasse/artemis/sasl_delegation/SaslDelegatingLogin.java +++ /dev/null @@ -1,593 +0,0 @@ -/* - * Copyright 2017-2018, EnMasse authors. - * License: Apache License 2.0 (see the file LICENSE or http://apache.org/licenses/LICENSE-2.0.html). - */ - -package io.enmasse.artemis.sasl_delegation; - -import org.apache.activemq.artemis.spi.core.protocol.RemotingConnection; -import org.apache.activemq.artemis.spi.core.security.jaas.CertificateCallback; -import org.apache.activemq.artemis.spi.core.security.jaas.JaasCallbackHandler; -import org.apache.activemq.artemis.spi.core.security.jaas.RolePrincipal; -import org.apache.activemq.artemis.spi.core.security.jaas.UserPrincipal; -import org.apache.qpid.proton.Proton; -import org.apache.qpid.proton.amqp.Symbol; -import org.apache.qpid.proton.engine.Connection; -import org.apache.qpid.proton.engine.EndpointState; -import org.apache.qpid.proton.engine.Sasl; -import org.apache.qpid.proton.engine.Transport; -import org.apache.qpid.proton.engine.TransportResult; -import org.jboss.logging.Logger; - -import java.io.FileInputStream; -import java.io.IOException; -import java.io.InputStream; -import java.io.OutputStream; -import java.lang.reflect.Field; -import java.net.Socket; -import java.nio.ByteBuffer; -import java.nio.file.Files; -import java.nio.file.Paths; -import java.security.GeneralSecurityException; -import java.security.KeyStore; -import java.security.NoSuchAlgorithmException; -import java.security.Principal; -import java.util.*; -import java.util.function.BooleanSupplier; -import javax.naming.InvalidNameException; -import javax.naming.ldap.LdapName; -import javax.net.ssl.SSLContext; -import javax.net.ssl.TrustManager; -import javax.net.ssl.TrustManagerFactory; -import javax.security.auth.Subject; -import javax.security.auth.callback.Callback; -import javax.security.auth.callback.CallbackHandler; -import javax.security.auth.callback.NameCallback; -import javax.security.auth.callback.PasswordCallback; -import javax.security.auth.callback.UnsupportedCallbackException; -import javax.security.auth.login.LoginException; -import javax.security.auth.spi.LoginModule; -import javax.security.cert.X509Certificate; - -import static org.apache.qpid.proton.engine.Sasl.SaslState.PN_SASL_FAIL; -import static org.apache.qpid.proton.engine.Sasl.SaslState.PN_SASL_IDLE; -import static org.apache.qpid.proton.engine.Sasl.SaslState.PN_SASL_PASS; -import static org.apache.qpid.proton.engine.Sasl.SaslState.PN_SASL_STEP; - -public class SaslDelegatingLogin implements LoginModule { - - private static final Logger LOG = Logger.getLogger(SaslDelegatingLogin.class); - - private static final String[] TLS_PROTOCOL_PREFERENCES = new String[]{"TLSv1.2", "TLSv1.1", "TLS", "TLSv1"}; - private static final Symbol AUTHENTICATED_IDENTITY = Symbol.valueOf("authenticated-identity"); - private static final Set SPECIAL_AUTHZ_GROUPS = Collections.unmodifiableSet(new HashSet<>(Arrays.asList("admin", "manage"))); - public static final String PREFERRED_USERNAME = "preferred_username"; - public static final String SUB = "sub"; - public static final String GROUPS = "groups"; - public static final String PROP_ADDRESS_AUTHZ = "address-authz"; - public static final Symbol CAPABILITY_ADDRESS_AUTHZ = Symbol.valueOf("ADDRESS-AUTHZ"); - public static final String SUPPORT_USER_PATH = "support_user_path"; - public static final String SUPPORT_PASSWORD_PATH = "support_password_path"; - - private final Set principals = new HashSet<>(); - private final Set roles = new HashSet<>(); - - private String host = "localhost"; - private int port = 5672; - - private Subject subject; - private CallbackHandler callbackHandler; - private boolean loginSucceeded; - private String user; - private String container; - private String saslHostname; - private SaslMechanismFactory[] saslFactories; - private Map sharedState; - private Map options; - private boolean useTls; - private String trustStorePath; - private SSLContext sslContext; - private char[] trustStorePassword; - private Map> validCertUsers = new HashMap<>(); - private List defaultRolesAuthenticated = new ArrayList<>(); - private List defaultRolesUnauthenticated = new ArrayList<>(); - private String securitySettings; - private Subject existingSubject; - private RemotingConnection remoteConnection; - - private static volatile Field REMOTING_CONNECTION_FIELD; - private static final Map AUTHENTICATED_CONNECTIONS = new WeakHashMap<>(); - - static { - try { - Class jassCallbackHandlerClass = Class.forName("org.apache.activemq.artemis.spi.core.security.jaas.JaasCallbackHandler"); - REMOTING_CONNECTION_FIELD = jassCallbackHandlerClass.getField("remotingConnection"); - REMOTING_CONNECTION_FIELD.setAccessible(true); - } catch (ClassNotFoundException | NoSuchFieldException ignored) { - - } - } - - private Optional supportUserName; - private Optional supportPassword; - - @Override - public void initialize(Subject subject, - CallbackHandler callbackHandler, - Map sharedState, - Map options) { - this.subject = subject; - this.callbackHandler = callbackHandler; - this.sharedState = sharedState; - - if(options.containsKey("hostname")) { - this.host = String.valueOf(options.get("hostname")).trim(); - } - if(options.containsKey("port")) { - this.port = Integer.parseInt(String.valueOf(options.get("port")).trim()); - } - if(options.containsKey("sasl_hostname")) { - this.saslHostname = String.valueOf(options.get("sasl_hostname")).trim(); - } else { - this.saslHostname = this.host; - } - if(options.containsKey("container_id")) { - this.container = String.valueOf(options.get("container_id")).trim(); - } else { - this.container = this.saslHostname; - } - if(options.containsKey("use_tls")) { - this.useTls = Boolean.parseBoolean(String.valueOf(options.get("use_tls")).trim()); - } - if(options.containsKey("truststore_path")) { - this.trustStorePath = String.valueOf(options.get("truststore_path")).trim(); - } - if(options.containsKey("truststore_password")) { - this.trustStorePassword = String.valueOf(options.get("truststore_password")).trim().toCharArray(); - } - if(options.containsKey("valid_cert_users")) { - validCertUsers.clear(); - String[] userDetails = String.valueOf(options.get("valid_cert_users")).trim().split(";"); - for(String userDetail : userDetails) { - List roles = new ArrayList<>(); - String[] userRoles = userDetail.split(":", 2); - if(userRoles.length == 2) { - for(String role : userRoles[1].split(",")) { - roles.add(role.trim()); - } - } - validCertUsers.put(userRoles[0], roles); - } - } - if(options.containsKey("default_roles_authenticated")) { - defaultRolesAuthenticated.clear(); - String [] roles = String.valueOf(options.get("default_roles_authenticated")).split(","); - for (String role : roles) { - defaultRolesAuthenticated.add(role.trim()); - } - } - if(options.containsKey("default_roles_unauthenticated")) { - defaultRolesUnauthenticated.clear(); - String [] roles = String.valueOf(options.get("default_roles_unauthenticated")).split(","); - for (String role : roles) { - defaultRolesUnauthenticated.add(role.trim()); - } - } - if(options.containsKey("security_settings")) { - this.securitySettings = String.valueOf(options.get("security_settings")).trim(); - } - - if(useTls) { - try { - this.sslContext = createSSLContext(); - } catch (GeneralSecurityException | IOException e) { - LOG.error("Unable to create SSL context", e); - } - } - - if(options.containsKey(SUPPORT_USER_PATH) && options.containsKey(SUPPORT_PASSWORD_PATH)) { - String supportUserPath = String.valueOf(options.get(SUPPORT_USER_PATH)); - String supportPasswordPath = String.valueOf(options.get(SUPPORT_PASSWORD_PATH)); - - try { - this.supportUserName = Files.readAllLines(Paths.get(supportUserPath)).stream().findFirst(); - } catch (IOException e) { - LOG.warnf(e,"Failed to load support username from file : %s", supportUserPath); - this.supportUserName = Optional.empty(); - } - try { - this.supportPassword = Files.readAllLines(Paths.get(supportPasswordPath)).stream().findFirst(); - } catch (IOException e) { - LOG.warnf(e,"Failed to load support password from file : %s", supportPasswordPath); - this.supportPassword = Optional.empty(); - } - - if (this.supportPassword.isPresent() && this.supportUserName.isPresent()) { - LOG.debug("Support access configured"); - } - - } else { - LOG.debug("Support access not configured"); - this.supportUserName = Optional.empty(); - this.supportPassword = Optional.empty(); - } - - this.options = options; - loginSucceeded = false; - saslFactories = new SaslMechanismFactory[] { new PlainSaslMechanismFactory() }; - } - - - @Override - public boolean login() throws LoginException { - - boolean success = false; - try { - if (this.callbackHandler instanceof JaasCallbackHandler) { - try { - remoteConnection = - (RemotingConnection) REMOTING_CONNECTION_FIELD.get(this.callbackHandler); - existingSubject = AUTHENTICATED_CONNECTIONS.get(remoteConnection); - success = existingSubject != null; - } catch (IllegalAccessException e) { - - } - } - if (!success) { - List certs = new ArrayList<>(); - - NameCallback nameCallback = new NameCallback("user:"); - PasswordCallback passwordCallback = new PasswordCallback("password:", false); - CertificateCallback certificateCallback = new CertificateCallback(); - callbackHandler.handle(new Callback[] { nameCallback, certificateCallback, passwordCallback}); - X509Certificate[] certArray = certificateCallback.getCertificates(); - - if(certArray != null) { - certs.addAll(Arrays.asList(certArray)); - } - - if (nameCallback.getName() == null && !certs.isEmpty()) { - success = populateUserAndRolesFromCert(certs.get(0)); - } else if (this.supportPassword.isPresent() - && this.supportUserName.isPresent() - && this.supportUserName.get().equals(nameCallback.getName())) { - boolean passwordMatched = this.supportPassword.get().equals(new String(passwordCallback.getPassword())); - if (passwordMatched) { - String hawtioRole = System.getProperty("hawtio.role", "amq"); - user = nameCallback.getName(); - roles.addAll(defaultRolesAuthenticated); - roles.add("admin"); - roles.add(hawtioRole); - success = true; - } else { - LOG.debugf("Login failed for user : %s", nameCallback.getName()); - } - } else { - Transport transport = Proton.transport(); - Connection connection = Proton.connection(); - transport.bind(connection); - Sasl sasl = transport.sasl(); - sasl.client(); - - Socket socket = createSocket(); - - InputStream in = socket.getInputStream(); - OutputStream out = socket.getOutputStream(); - - transport.open(); - - // write Headers - writeToNetwork(connection, out); - - SaslMechanism mechanism = chooseSaslMechanismAndSendInit(connection, in, out); - - performSaslSteps(connection, in, out, mechanism); - - if (isSaslAuthenticated(connection, mechanism)) { - performConnectionOpen(connection, in, out); - getUserAndRolesFromConnection(connection); - success = true; - } else { - LOG.debug("Login failed"); - } - - connection.close(); - transport.close(); - socket.close(); - } - } - } catch(IOException | UnsupportedCallbackException | InvalidNameException e){ - LoginException loginException = new LoginException("Exception attempting to authenticate using SASL delegation"); - loginException.initCause(e); - LOG.warn(e); - throw loginException; - } - - loginSucceeded = success; - return success; - } - - @Override - public boolean commit() throws LoginException { - boolean result = loginSucceeded; - try { - if (result) { - if (existingSubject == null) { - UserPrincipal userPrincipal = new UserPrincipal(user); - principals.add(userPrincipal); - LOG.debugv("Adding user principal for: {0}", user); - for (String entry : roles) { - LOG.debugv("Adding role principal for: {0}", entry); - principals.add(new RolePrincipal(entry)); - } - - subject.getPrincipals().addAll(principals); - if(remoteConnection != null) { - AUTHENTICATED_CONNECTIONS.put(remoteConnection, subject); - } - } else { - subject.getPrincipals().addAll(existingSubject.getPrincipals()); - } - } - clear(); - - LOG.debugv("commit, result: {0}", result); - - } catch (RuntimeException | Error e) { - LOG.error(e); - throw e; - } - return result; - } - - @Override - public boolean abort() throws LoginException { - clear(); - - LOG.debug("abort"); - return true; - } - - @Override - public boolean logout() throws LoginException { - subject.getPrincipals().removeAll(principals); - clear(); - LOG.debug("logout"); - return true; - } - - private void clear() { - user = null; - principals.clear(); - roles.clear(); - loginSucceeded = false; - remoteConnection = null; - existingSubject = null; - } - - - - private boolean populateUserAndRolesFromCert(X509Certificate certificate) throws InvalidNameException { - LdapName ldapName = new LdapName(certificate.getSubjectDN().getName()); - - // Find the first CN that maps to a valid user as supplied in the config - Optional validCN = ldapName.getRdns().stream() - .filter(rdn -> rdn.getType().equalsIgnoreCase("CN")) - .map(rdn -> String.valueOf(rdn.getValue())) - .filter(n -> validCertUsers.containsKey(n)).findFirst(); - - if(validCN.isPresent()) { - String name = validCN.get(); - user = name; roles.addAll(validCertUsers.get(name)); - return true; - } else { - return false; - } - } - - - private void getUserAndRolesFromConnection(Connection connection) { - final Map remoteProperties = connection.getRemoteProperties(); - Symbol[] supportedCapabilities = connection.getRemoteOfferedCapabilities(); - boolean supportsAuthz = supportedCapabilities != null && Arrays.asList(supportedCapabilities).contains(CAPABILITY_ADDRESS_AUTHZ); - if (remoteProperties != null && remoteProperties.get(AUTHENTICATED_IDENTITY) instanceof Map) { - Map identity = (Map) remoteProperties.get(AUTHENTICATED_IDENTITY); - if (identity.containsKey(PREFERRED_USERNAME)) { - user = String.valueOf(identity.get(PREFERRED_USERNAME)).trim(); - } else { - user = String.valueOf(identity.get(SUB)).trim(); - } - roles.addAll(defaultRolesAuthenticated); - - if (remoteProperties.get(Symbol.valueOf(GROUPS)) instanceof List) { - List groups = new ArrayList<>(((List) ((List) remoteProperties.get(Symbol.valueOf(GROUPS))))); - groups.retainAll(SPECIAL_AUTHZ_GROUPS); - roles.addAll(groups); - for (String group : groups) { - roles.add(group + "_#"); - } - } - } - - if(supportsAuthz) { - if (remoteProperties != null && remoteProperties.get(Symbol.valueOf(PROP_ADDRESS_AUTHZ)) instanceof Map) { - Map authz = (Map) remoteProperties.get(Symbol.valueOf(PROP_ADDRESS_AUTHZ)); - List groups = new ArrayList<>(); - for(Map.Entry entry : authz.entrySet()) { - for(String permission : entry.getValue()) { - groups.add(permission + "_" + entry.getKey().replace('*', '#')); - } - } - roles.addAll(groups); - if(this.securitySettings != null) { - SaslGroupBasedSecuritySettingsPlugin securitySettingPlugin = SaslGroupBasedSecuritySettingsPlugin.getInstance(this.securitySettings); - if(securitySettingPlugin != null) { - securitySettingPlugin.addGroups(groups); - } - } - } - - } else { - if(this.securitySettings != null) { - SaslGroupBasedSecuritySettingsPlugin securitySettingPlugin = SaslGroupBasedSecuritySettingsPlugin.getInstance(this.securitySettings); - if(securitySettingPlugin != null) { - LOG.infov("Using sasl delegation for authz, but delegate does offer support, adding admin role to : {0}", user); - roles.addAll(defaultRolesUnauthenticated); - } - } - } - - } - - private void performConnectionOpen(Connection connection, InputStream in, OutputStream out) throws IOException, LoginException { - connection.setHostname(saslHostname); - connection.setContainer(container); - connection.setDesiredCapabilities(new Symbol[] {CAPABILITY_ADDRESS_AUTHZ}); - connection.open(); - writeToNetwork(connection, out); - readFromNetwork(connection, in, () -> connection.getRemoteState() == EndpointState.UNINITIALIZED); - } - - private void performSaslSteps(Connection connection, InputStream in, - OutputStream out, - SaslMechanism mechanism) throws IOException, LoginException { - Transport transport = connection.getTransport(); - Sasl sasl = transport.sasl(); - do { - - readFromNetwork(connection, in, () -> - !(EnumSet.of(PN_SASL_PASS, PN_SASL_FAIL).contains(sasl.getState()) - || (sasl.getState() == PN_SASL_STEP && sasl.pending() > 0))); - - if (sasl.pending() > 0) { - byte[] challenge = new byte[sasl.pending()]; - byte[] response = mechanism.getResponse(challenge); - if (sasl.getState() == PN_SASL_STEP) { - sasl.send(response, 0, response.length); - writeToNetwork(connection, out); - } - } - - } while (sasl.getState() == PN_SASL_STEP); - } - - private SaslMechanism chooseSaslMechanismAndSendInit(Connection connection, InputStream in, OutputStream out) throws LoginException, IOException { - - Transport transport = connection.getTransport(); - Sasl sasl = transport.sasl(); - SaslMechanism mechanism = null; - // read from network until we get a sasl-mechanisms - readFromNetwork(connection, in, () -> sasl.getState() == PN_SASL_IDLE && sasl.getRemoteMechanisms().length == 0); - - for (SaslMechanismFactory factory : saslFactories) { - if (Arrays.asList(sasl.getRemoteMechanisms()).contains(factory.getName())) { - mechanism = factory.newInstance(callbackHandler, sharedState, options); - if (mechanism != null) { - sasl.setRemoteHostname(saslHostname); - sasl.setMechanisms(factory.getName()); - byte[] initialResponse = mechanism.getResponse(null); - if (initialResponse != null && initialResponse.length != 0) { - sasl.send(initialResponse, 0, initialResponse.length); - } - break; - } - } - } - - if (mechanism == null) { - throw new LoginException("Unable to authenticate using SASL delegation, no supported mechanisms"); - } - - writeToNetwork(connection, out); - return mechanism; - } - - private boolean isSaslAuthenticated(Connection connection, SaslMechanism mechanism) { - Transport transport = connection.getTransport(); - Sasl sasl = transport.sasl(); - return sasl.getState() == PN_SASL_PASS && mechanism.completedSuccessfully(); - } - - private Socket createSocket() throws IOException, LoginException { - if(this.useTls) { - if(sslContext == null) { - throw new LoginException("Unable to establish SSL connection due to configuration errors"); - } - return sslContext.getSocketFactory().createSocket(host, port); - } else { - return new Socket(host, port); - } - } - - private void readFromNetwork(Connection connection, InputStream in, BooleanSupplier test) throws IOException, LoginException { - Transport transport = connection.getTransport(); - while(test.getAsBoolean()) { - ByteBuffer buf = transport.getInputBuffer(); - byte[] tmpBuf = new byte[buf.remaining()]; - int bytesRead = in.read(tmpBuf); - LOG.tracev("read {0} bytes", bytesRead); - if (bytesRead == -1) { - throw new LoginException("Unexpected EOS experienced when authenticating using SASL delegation"); - } else { - buf.put(tmpBuf, 0, bytesRead); - TransportResult result = transport.processInput(); - if(!result.isOk()) { - LoginException e = new LoginException("Unexpected error when authenticating using SASL delegation"); - e.initCause(result.getException()); - throw e; - } - } - - } - - } - - private void writeToNetwork(Connection connection, OutputStream out) throws IOException { - Transport transport = connection.getTransport(); - while(transport.pending() > 0) - { - ByteBuffer outputBuf = transport.head(); - final int size = outputBuf.remaining(); - byte[] tmpBuf = new byte[size]; - outputBuf.get(tmpBuf); - LOG.tracev("writing {0} bytes", size); - out.write(tmpBuf); - transport.pop(size); - } - } - - private SSLContext createSSLContext() throws GeneralSecurityException, IOException { - SSLContext sslContext = tryGetSSLContext(); - TrustManager[] trustManagers = null; - if(this.trustStorePath != null) { - KeyStore ts = KeyStore.getInstance("JKS"); - try(InputStream in = new FileInputStream(trustStorePath)) - { - ts.load(in, trustStorePassword); - } - TrustManagerFactory tmf = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm()); - tmf.init(ts); - trustManagers = tmf.getTrustManagers(); - } - sslContext.init(null, trustManagers, null); - - return sslContext; - } - - private static SSLContext tryGetSSLContext() throws NoSuchAlgorithmException { - return tryGetSSLContext(TLS_PROTOCOL_PREFERENCES); - } - - private static SSLContext tryGetSSLContext(final String[] protocols) throws NoSuchAlgorithmException { - for (String protocol : protocols) { - try { - return SSLContext.getInstance(protocol); - } catch (NoSuchAlgorithmException e) { - // pass and try the next protocol in the list - } - } - throw new NoSuchAlgorithmException(String.format("Could not create SSLContext with one of the requested protocols: %s", - Arrays.toString(protocols))); - } - -} diff --git a/broker-plugin/sasl-delegation/src/main/java/io/enmasse/artemis/sasl_delegation/SaslGroupBasedSecuritySettingsPlugin.java b/broker-plugin/sasl-delegation/src/main/java/io/enmasse/artemis/sasl_delegation/SaslGroupBasedSecuritySettingsPlugin.java deleted file mode 100644 index 3a33bb0b8e9..00000000000 --- a/broker-plugin/sasl-delegation/src/main/java/io/enmasse/artemis/sasl_delegation/SaslGroupBasedSecuritySettingsPlugin.java +++ /dev/null @@ -1,175 +0,0 @@ -/* - * Copyright 2017-2018, EnMasse authors. - * License: Apache License 2.0 (see the file LICENSE or http://apache.org/licenses/LICENSE-2.0.html). - */ - -package io.enmasse.artemis.sasl_delegation; - -import java.util.Collections; -import java.util.HashSet; -import java.util.List; -import java.util.Map; -import java.util.Set; -import java.util.concurrent.ConcurrentHashMap; - -import org.apache.activemq.artemis.core.security.Role; -import org.apache.activemq.artemis.core.server.SecuritySettingPlugin; -import org.apache.activemq.artemis.core.settings.HierarchicalRepository; -import org.jboss.logging.Logger; - -@SuppressWarnings("serial") -public class SaslGroupBasedSecuritySettingsPlugin implements SecuritySettingPlugin { - - private static final Logger LOG = Logger.getLogger(SaslGroupBasedSecuritySettingsPlugin.class); - - private static final Map INSTANCES = new ConcurrentHashMap<>(); - - private static final String NAME = "name"; - private static final String USE_GROUPS_FROM_SASL_DELEGATION = "useGroupsFromSaslDelegation"; - private static final String ADMIN_GROUP = "admin"; - private static final String MANAGE_GROUP = "manage"; - private static final String ALL_GROUP = "all"; - private String name; - private HierarchicalRepository> securityRepository; - private final Set knownAddresses = new HashSet<>(); - private Set standardRoles; - private boolean useGroupsFromSaslDelegation; - - @Override - public SecuritySettingPlugin init(Map map) { - this.name = map.get(NAME); - if(this.name != null) { - INSTANCES.put(this.name, this); - } - this.useGroupsFromSaslDelegation = "true".equalsIgnoreCase(map.get(USE_GROUPS_FROM_SASL_DELEGATION)); - Set roles = new HashSet<>(); - - // "admin" (console or other internal process) can do anything - roles.add(new Role(ADMIN_GROUP, true, true, true, true, true, true, true, true, true, true)); - - if(!useGroupsFromSaslDelegation) { - // "all" users can create/delete queues (but not addresses) - roles.add(new Role(ALL_GROUP, true, true, true, true, true, true, false, true, false, false)); - roles.add(new Role(MANAGE_GROUP, true, true, true, true, true, true, true, true, false, false)); - } - - this.standardRoles = Collections.unmodifiableSet(roles); - - return this; - } - - @Override - public SecuritySettingPlugin stop() { - if(this.name != null) { - INSTANCES.remove(this.name); - } - return this; - } - - @Override - public Map> getSecurityRoles() { - - Map> securityRoles = Collections.singletonMap("#", this.standardRoles); - return securityRoles; - } - - @Override - public void setSecurityRepository(HierarchicalRepository> hierarchicalRepository) { - this.securityRepository = hierarchicalRepository; - } - - static SaslGroupBasedSecuritySettingsPlugin getInstance(String name) { - return INSTANCES.get(name); - } - - synchronized void addGroups(List groups) { - if(useGroupsFromSaslDelegation) { - LOG.debugv("Adding groups: {0}", groups); - for (String group : groups) { - addGroup(group); - } - } - } - - private static final char DEFAULT_SINGLE_WORD = '+'; - - private static final char DEFAULT_ANY_WORDS = '#'; - - private static final char DEFAULT_DELIMITER = '/'; - - private void addGroup(String group) { - String[] parts = group.split("_", 2); - if (parts.length == 2) { - char singleWord = DEFAULT_SINGLE_WORD; - char anyWords = DEFAULT_ANY_WORDS; - char delimeter = DEFAULT_DELIMITER; - try { - String address = parts[1].replace('*', '#'); - if(knownAddresses.add(address)) { - - String singleWordString = String.valueOf(singleWord); - String delimeterString = String.valueOf(delimeter); - String anyWordsString = String.valueOf(anyWords); - - Set roles = new HashSet<>(); - LOG.debugv("Adding permissions for address {0} due to group {1}", address, group); - roles.add(new Role("send_" + address, true, false, false, false, true, false, false, false, true, false)); - roles.add(new Role("recv_" + address, false, true, true, true, true, true, false, true, true, false)); - roles.add(new Role("browse_" + address, false, false, false, false, false, false, false, true, false, false)); - roles.add(new Role("create_" + address, false, false, true, false, true, false, false, false, true, false)); - roles.add(new Role("delete_" + address, false, false, false, true, false, true, false, false, false, true)); - roles.add(new Role("manage_" + address, false, false, false, false, false, false, true, false, false, false)); - - Set allRoles = new HashSet<>(securityRepository.getMatch(address)); - allRoles.addAll(roles); - securityRepository.addMatch(address, allRoles); - - if (address.equals(anyWordsString)) { - for (String existingAddress : knownAddresses) { - if (!existingAddress.equals(address)) { - Set updatedRoles = new HashSet<>(securityRepository.getMatch(existingAddress)); - updatedRoles.addAll(roles); - securityRepository.addMatch(existingAddress, updatedRoles); - } - } - } else if (address.equals(singleWordString)) { - for (String existingAddress : knownAddresses) { - if (!existingAddress.equals(address) && !existingAddress.contains(delimeterString)) { - Set updatedRoles = new HashSet<>(securityRepository.getMatch(existingAddress)); - updatedRoles.addAll(roles); - securityRepository.addMatch(existingAddress, updatedRoles); - } - } - } else if (address.endsWith(delimeterString + singleWordString)) { - String stem = address.substring(0, address.length() - 2); - for (String existingAddress : knownAddresses) { - if (!existingAddress.equals(address) - && existingAddress.startsWith(stem) - && !existingAddress.substring(stem.length() + 1, existingAddress.length()).contains(delimeterString) - && !existingAddress.substring(stem.length() + 1, existingAddress.length()).equals(anyWordsString)) { - Set updatedRoles = new HashSet<>(securityRepository.getMatch(existingAddress)); - updatedRoles.addAll(roles); - securityRepository.addMatch(existingAddress, updatedRoles); - } - } - } else if (address.endsWith(delimeterString + anyWordsString)) { - String stem = address.substring(0, address.length() - 2); - for (String existingAddress : knownAddresses) { - if (!existingAddress.equals(address) - && existingAddress.startsWith(stem)) { - Set updatedRoles = new HashSet<>(securityRepository.getMatch(existingAddress)); - updatedRoles.addAll(roles); - securityRepository.addMatch(existingAddress, updatedRoles); - } - } - } - } - - } catch (IllegalArgumentException e) { - LOG.infov("Unable to parse implied address from group {0}: {1}", group, e.getMessage(), e); - e.printStackTrace(); - } - } - - } -} diff --git a/broker-plugin/sasl-delegation/src/main/java/io/enmasse/artemis/sasl_delegation/SaslMechanism.java b/broker-plugin/sasl-delegation/src/main/java/io/enmasse/artemis/sasl_delegation/SaslMechanism.java deleted file mode 100644 index 15ef38ad3c6..00000000000 --- a/broker-plugin/sasl-delegation/src/main/java/io/enmasse/artemis/sasl_delegation/SaslMechanism.java +++ /dev/null @@ -1,11 +0,0 @@ -/* - * Copyright 2017-2018, EnMasse authors. - * License: Apache License 2.0 (see the file LICENSE or http://apache.org/licenses/LICENSE-2.0.html). - */ -package io.enmasse.artemis.sasl_delegation; - -interface SaslMechanism { - - byte[] getResponse(byte[] challenge); - boolean completedSuccessfully(); -} diff --git a/broker-plugin/sasl-delegation/src/main/java/io/enmasse/artemis/sasl_delegation/SaslMechanismFactory.java b/broker-plugin/sasl-delegation/src/main/java/io/enmasse/artemis/sasl_delegation/SaslMechanismFactory.java deleted file mode 100644 index e089147c742..00000000000 --- a/broker-plugin/sasl-delegation/src/main/java/io/enmasse/artemis/sasl_delegation/SaslMechanismFactory.java +++ /dev/null @@ -1,14 +0,0 @@ -/* - * Copyright 2017-2018, EnMasse authors. - * License: Apache License 2.0 (see the file LICENSE or http://apache.org/licenses/LICENSE-2.0.html). - */ -package io.enmasse.artemis.sasl_delegation; - -import java.util.Map; -import javax.security.auth.callback.CallbackHandler; - -interface SaslMechanismFactory { - String getName(); - boolean isSecure(); - SaslMechanism newInstance(CallbackHandler callbackHandler, Map sharedState, Map options); -} diff --git a/broker-plugin/sasl-delegation/src/test/java/io/enmasse/artemis/sasl_delegation/SaslDelegatingLoginTest.java b/broker-plugin/sasl-delegation/src/test/java/io/enmasse/artemis/sasl_delegation/SaslDelegatingLoginTest.java deleted file mode 100644 index 1eaf3a7ef86..00000000000 --- a/broker-plugin/sasl-delegation/src/test/java/io/enmasse/artemis/sasl_delegation/SaslDelegatingLoginTest.java +++ /dev/null @@ -1,540 +0,0 @@ -/* - * Copyright 2017-2018, EnMasse authors. - * License: Apache License 2.0 (see the file LICENSE or http://apache.org/licenses/LICENSE-2.0.html). - */ - -package io.enmasse.artemis.sasl_delegation; - -import io.vertx.core.AbstractVerticle; -import io.vertx.core.Handler; -import io.vertx.core.Vertx; -import io.vertx.core.net.NetSocket; -import io.vertx.proton.ProtonConnection; -import io.vertx.proton.ProtonServer; -import io.vertx.proton.ProtonServerOptions; -import io.vertx.proton.sasl.ProtonSaslAuthenticator; -import io.vertx.proton.sasl.impl.ProtonSaslPlainImpl; -import org.apache.activemq.artemis.core.security.Role; -import org.apache.activemq.artemis.core.settings.HierarchicalRepository; -import org.apache.activemq.artemis.core.settings.impl.HierarchicalObjectRepository; -import org.apache.activemq.artemis.spi.core.security.jaas.CertificateCallback; -import org.apache.activemq.artemis.spi.core.security.jaas.RolePrincipal; -import org.apache.activemq.artemis.spi.core.security.jaas.UserPrincipal; -import org.apache.qpid.proton.amqp.Symbol; -import org.apache.qpid.proton.engine.Sasl; -import org.apache.qpid.proton.engine.Transport; -import org.junit.jupiter.api.AfterEach; -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Test; - -import javax.security.auth.Subject; -import javax.security.auth.callback.Callback; -import javax.security.auth.callback.CallbackHandler; -import javax.security.auth.callback.NameCallback; -import javax.security.auth.callback.PasswordCallback; -import javax.security.auth.login.LoginException; -import javax.security.cert.CertificateException; -import javax.security.cert.X509Certificate; -import java.io.IOException; -import java.nio.charset.StandardCharsets; -import java.nio.file.Files; -import java.nio.file.Path; -import java.security.Principal; -import java.util.*; -import java.util.concurrent.CompletableFuture; -import java.util.concurrent.ExecutionException; -import java.util.stream.Collectors; - -import static org.junit.jupiter.api.Assertions.*; - -public class SaslDelegatingLoginTest { - - private SaslDelegatingLogin loginModule; - private Map options = new HashMap<>(); - private Map validLogins = new HashMap<>(); - private Map> groups = new HashMap<>(); - private int port; - private AuthServer authServer; - private Vertx vertx; - @SuppressWarnings("unused") - private String verticleId; - private String mechName; - private SaslGroupBasedSecuritySettingsPlugin securityPlugin = new SaslGroupBasedSecuritySettingsPlugin(); - private Path userFile; - private Path passwordFile; - - @BeforeEach - public void setup() throws ExecutionException, InterruptedException { - options.clear(); - loginModule = new SaslDelegatingLogin(); - - Map initMap = new HashMap<>(); - initMap.put("name", "test"); - initMap.put("useGroupsFromSaslDelegation", "true"); - securityPlugin.init(initMap); - HierarchicalRepository> repo = new HierarchicalObjectRepository<>(); - repo.setDefault(Collections.emptySet()); - securityPlugin.setSecurityRepository(repo); - - CompletableFuture portFuture = new CompletableFuture<>(); - mechName = ProtonSaslPlainImpl.MECH_NAME; - authServer = new AuthServer(portFuture); - vertx = Vertx.vertx(); - CompletableFuture idFuture = new CompletableFuture<>(); - vertx.deployVerticle(authServer, result -> { - if (result.succeeded()) { - idFuture.complete(result.result()); - } else { - idFuture.completeExceptionally(result.cause()); - } - }); - verticleId = idFuture.get(); - port = portFuture.get(); - options.put("hostname", "127.0.0.1"); - options.put("port", port); - options.put("security_settings", "test"); - options.put("default_roles_authenticated", "all"); - } - - @AfterEach - public void tearDown() { - - try { - vertx.close(); - } finally { - if (userFile != null) { - try { - Files.deleteIfExists(userFile); - } catch (IOException e) { - // pass - } - } - if (passwordFile != null) { - try { - Files.deleteIfExists(passwordFile); - } catch (IOException e) { - // pass - } - } - } - } - - // successful credentials login with roles - @Test - public void testSuccessfulCredentialLogin() throws Exception { - Subject subject = new Subject(); - validLogins.put("user", "password"); - groups.put("user", Arrays.asList("send_a", "recv_b")); - loginModule.initialize(subject, createCallbackHandler("user", "password".toCharArray()), Collections.emptyMap(), options); - assertTrue(loginModule.login(), "Login unexpectedly failed"); - assertEquals(0, subject.getPrincipals().size(), "No principals should be added until after the commit"); - assertTrue(loginModule.commit(), "Commit unexpectedly failed"); - assertEquals(Collections.singleton("user"), subject.getPrincipals(UserPrincipal.class).stream().map(Principal::getName).collect(Collectors.toSet()), "Unexpected user principal names"); - assertEquals(new HashSet<>(Arrays.asList("send_a", "recv_b", "all")), subject.getPrincipals(RolePrincipal.class).stream().map(Principal::getName).collect(Collectors.toSet()), "Unexpected role principal names"); - } - - // unsuccessful credentials login - @Test - public void testUnsuccessfulCredentialLogin() throws Exception { - Subject subject = new Subject(); - validLogins.put("user", "password2"); - groups.put("user", Arrays.asList("a", "b")); - loginModule.initialize(subject, createCallbackHandler("user", "password".toCharArray()), Collections.emptyMap(), options); - assertFalse(loginModule.login(), "Login unexpectedly succeeded"); - assertEquals(0, subject.getPrincipals().size(), "No principals should be added until after the commit"); - loginModule.commit(); - assertEquals(Collections.emptySet(), subject.getPrincipals(UserPrincipal.class).stream().map(Principal::getName).collect(Collectors.toSet()), "Unexpected user principal names"); - assertEquals(Collections.emptySet(), subject.getPrincipals(RolePrincipal.class).stream().map(Principal::getName).collect(Collectors.toSet()), "Unexpected role principal names"); - - } - - // no matching sasl mechanism - @Test - public void testUnsuccessfulCredentialLoginWithBadMechanism() throws Exception { - mechName = "WIBBLE"; - Subject subject = new Subject(); - validLogins.put("user", "password"); - groups.put("user", Arrays.asList("a", "b")); - loginModule.initialize(subject, createCallbackHandler("user", "password".toCharArray()), Collections.emptyMap(), options); - try { - loginModule.login(); - fail("Login should not succeed if there are no matching mechanisms"); - } catch (LoginException e) { - // pass - } - } - - // successful cert login - @Test - public void testSuccessfulCertLogin() throws Exception { - - options.put("valid_cert_users", "foo:a,b;bar:c"); - - Subject subject = new Subject(); - - loginModule.initialize(subject, createCallbackHandler(null, null, generateCertificate(CERT_FOO)), Collections.emptyMap(), options); - assertTrue(loginModule.login(), "Login unexpectedly failed"); - assertEquals(0, subject.getPrincipals().size(), "No principals should be added until after the commit"); - assertTrue(loginModule.commit(), "Commit unexpectedly failed"); - assertEquals(Collections.singleton("foo"), subject.getPrincipals(UserPrincipal.class).stream().map(Principal::getName).collect(Collectors.toSet()), "Unexpected user principal names"); - assertEquals(new HashSet<>(Arrays.asList("a", "b")), subject.getPrincipals(RolePrincipal.class).stream().map(Principal::getName).collect(Collectors.toSet()), "Unexpected role principal names"); - - subject = new Subject(); - - loginModule.initialize(subject, createCallbackHandler(null, null, generateCertificate(CERT_BAR)), Collections.emptyMap(), options); - assertTrue(loginModule.login(), "Login unexpectedly failed"); - assertEquals(0, subject.getPrincipals().size(), "No principals should be added until after the commit"); - assertTrue(loginModule.commit(), "Commit unexpectedly failed"); - assertEquals(Collections.singleton("bar"), subject.getPrincipals(UserPrincipal.class).stream().map(Principal::getName).collect(Collectors.toSet()), "Unexpected user principal names"); - assertEquals(Collections.singleton("c"), subject.getPrincipals(RolePrincipal.class).stream().map(Principal::getName).collect(Collectors.toSet()), "Unexpected role principal names"); - - } - - // unsuccessful cert login - @Test - public void testUnsuccessfulCertLogin() throws Exception { - - Map options = new HashMap<>(); - options.put("valid_cert_users", "foo:a,b;bar:c"); - - Subject subject = new Subject(); - - loginModule.initialize(subject, createCallbackHandler(null, null, generateCertificate(CERT_BANANA)), Collections.emptyMap(), options); - assertFalse(loginModule.login(), "Login unexpectedly succeeded"); - assertEquals(0, subject.getPrincipals().size(), "No principals should be added until after the commit"); - loginModule.commit(); - assertEquals(Collections.emptySet(), subject.getPrincipals(UserPrincipal.class).stream().map(Principal::getName).collect(Collectors.toSet()), "Unexpected user principal names"); - assertEquals(Collections.emptySet(), subject.getPrincipals(RolePrincipal.class).stream().map(Principal::getName).collect(Collectors.toSet()), "Unexpected role principal names"); - - } - - @Test - public void testSuccessfulSupportAccess() throws Exception { - - String username = "support"; - String password = "pwd" + UUID.randomUUID().toString(); - userFile = Files.createTempFile("user", "dat"); - Files.deleteIfExists(userFile); - passwordFile = Files.createTempFile("passwd", "dat"); - Files.deleteIfExists(passwordFile); - Files.write(userFile, Collections.singletonList(username)); - Files.write(passwordFile, Collections.singletonList(password)); - - options.put("support_user_path", userFile.toString()); - options.put("support_password_path", passwordFile.toString()); - - Subject subject = new Subject(); - - loginModule.initialize(subject, createCallbackHandler(username, password.toCharArray()), Collections.emptyMap(), options); - assertTrue(loginModule.login(), "Login unexpectedly failed"); - assertEquals(0, subject.getPrincipals().size(), "No principals should be added until after the commit"); - assertTrue(loginModule.commit(), "Commit unexpectedly failed"); - assertEquals(Collections.singleton(username), subject.getPrincipals(UserPrincipal.class).stream().map(Principal::getName).collect(Collectors.toSet()), "Unexpected user principal names"); - assertEquals(new HashSet<>(Arrays.asList("all", "amq", "admin")), subject.getPrincipals(RolePrincipal.class).stream().map(Principal::getName).collect(Collectors.toSet()), "Unexpected role principal names"); - } - - @Test - public void testUnsuccessfulSupportAccess() throws Exception { - String username = "support"; - String password = "pwd" + UUID.randomUUID().toString(); - userFile = Files.createTempFile("user", "dat"); - Files.deleteIfExists(userFile); - passwordFile = Files.createTempFile("passwd", "dat"); - Files.deleteIfExists(passwordFile); - Files.write(userFile, Collections.singletonList(username)); - Files.write(passwordFile, Collections.singletonList(password)); - - options.put("support_user_path", userFile.toString()); - options.put("support_password_path", passwordFile.toString()); - - Subject subject = new Subject(); - - loginModule.initialize(subject, createCallbackHandler(username, "bad".toCharArray()), Collections.emptyMap(), options); - assertFalse(loginModule.login(), "Login unexpectedly failed"); - assertEquals(0, subject.getPrincipals().size(), "No principals should be added until after the commit"); - assertFalse(loginModule.commit(), "Commit unexpectedly failed"); - assertEquals(Collections.emptySet(), subject.getPrincipals(UserPrincipal.class).stream().map(Principal::getName).collect(Collectors.toSet()), "Unexpected user principal names"); - assertEquals(Collections.emptySet(), subject.getPrincipals(RolePrincipal.class).stream().map(Principal::getName).collect(Collectors.toSet()), "Unexpected role principal names"); - } - - - private CallbackHandler createCallbackHandler(String user, char[] password, X509Certificate... certificates) { - return callbacks -> { - for (Callback callback : callbacks) { - if (callback instanceof NameCallback && user != null) { - ((NameCallback) callback).setName(user); - } else if (callback instanceof PasswordCallback && password != null) { - ((PasswordCallback) callback).setPassword(password); - } else if (callback instanceof CertificateCallback && (certificates.length != 0)) { - ((CertificateCallback) callback).setCertificates(certificates); - } - } - }; - } - - private X509Certificate generateCertificate(String cert) throws CertificateException { - return X509Certificate.getInstance(cert.getBytes(StandardCharsets.US_ASCII)); - } - - private static final Symbol ADDRESS_AUTHZ_CAPABILITY = Symbol.valueOf("ADDRESS-AUTHZ"); - private static final Symbol ADDRESS_AUTHZ_PROPERTY = Symbol.valueOf("address-authz"); - - private final class AuthServer extends AbstractVerticle { - - - private ProtonServer server; - private final CompletableFuture portFuture; - private final SaslAuthenticator saslAuthenticator; - - private AuthServer(CompletableFuture portFuture) { - this.portFuture = portFuture; - saslAuthenticator = new SaslAuthenticator(); - } - - private void connectHandler(ProtonConnection connection) { - String containerId = "auth-server"; - connection.setContainer(containerId); - connection.openHandler(conn -> { - Map props = new HashMap<>(); - Map claims = new HashMap<>(); - claims.put("sub", saslAuthenticator.getUser()); - claims.put("preferred_username", saslAuthenticator.getUser()); - props.put(Symbol.valueOf("authenticated-identity"), claims); - if (connection.getRemoteDesiredCapabilities() != null && Arrays.asList(connection.getRemoteDesiredCapabilities()).contains(ADDRESS_AUTHZ_CAPABILITY)) { - connection.setOfferedCapabilities(new Symbol[]{ADDRESS_AUTHZ_CAPABILITY}); - if (groups.containsKey(saslAuthenticator.getUser())) { - props.put(ADDRESS_AUTHZ_PROPERTY, getPermissionsFromGroups(groups.get(saslAuthenticator.getUser()))); - } - } - - if (groups.containsKey(saslAuthenticator.getUser())) { - props.put(Symbol.valueOf("groups"), groups.get(saslAuthenticator.getUser())); - } - connection.setProperties(props); - connection.open(); - connection.close(); - }).closeHandler(conn -> { - connection.close(); - connection.disconnect(); - }).disconnectHandler(protonConnection -> { - connection.disconnect(); - }); - - } - - Map getPermissionsFromGroups(List groups) { - Map> authMap = new HashMap<>(); - for (String group : groups) { - String[] parts = group.split("_", 2); - if (parts[0] != null) { - Set permissions = authMap.computeIfAbsent(parts[1], a -> new HashSet<>()); - permissions.add(parts[0]); - } - } - return authMap.entrySet().stream().collect(Collectors.toMap(Map.Entry::getKey, e -> e.getValue().toArray(new String[e.getValue().size()]))); - } - - - @Override - public void start() { - ProtonServerOptions options = new ProtonServerOptions(); - - server = ProtonServer.create(vertx, options); - server.saslAuthenticatorFactory(() -> saslAuthenticator); - server.connectHandler(this::connectHandler); - - server.listen(0, "127.0.0.1", event -> { - if (event.failed()) { - portFuture.completeExceptionally(event.cause()); - } else { - portFuture.complete(server.actualPort()); - } - }); - } - - @Override - public void stop() { - if (server != null) { - server.close(); - } - } - - } - - private static final String CERT_FOO = "-----BEGIN CERTIFICATE-----\n" + - "MIIFDjCCAvagAwIBAgIJAJjPI7TtVOCpMA0GCSqGSIb3DQEBBQUAMA4xDDAKBgNV\n" + - "BAMTA2ZvbzAeFw0xNzEwMTIwODU4NTNaFw0xODEwMTIwODU4NTNaMA4xDDAKBgNV\n" + - "BAMTA2ZvbzCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBALsasR9eE/77\n" + - "qmt1Gjz+/wJzu3V9pgde1iBuwoXHKGZyayMHLucnKXVF3o7DEk0R/1m3hKh5QoNV\n" + - "BHgiUzfjxKFxk85F00G/B1FkDXGjxqUz84hsSnOZS7917AdVHQ4FNfbUIeWgkRe3\n" + - "EqK/C3QiyVPr0Uou9jU+YLxOItB22KXVKO3gtBaZfaj8MpzPi8XOxesfpTB+1Fws\n" + - "336aYeeDxH78t05EnwqEd+bX7BPs+0DsReARKVi1v9YNRUX2AAQDBGqLKz39Eg0M\n" + - "0BaiRLv7O4i0DEJlGYC05P8L5Nb8JwZ8XQV0jlZrqNiqSqL2Glt+m4LkBLFwDfZz\n" + - "Q95FVeSSD4AlUB1eHnv41lT3SDOvLV8e6c222MDuC0V7eKDdCWVNlzQpwMyZKeBR\n" + - "HOl5IRjVAi2+JEE7YyzgIn68ZY2YLOBzoUKawPeThak6hJEzDaXzpUVJtKH9LnIW\n" + - "Yyt+K+c9/Sm4Cs95tCALA005r4GSypcTIdAdOpO9UcS4ObeKRhdCFHgkNitkw4i0\n" + - "X2i80y7lzoEV64ZbzMpqq0jWN2LzkHNU5zl5AoL7s8pFMSoQruOEOhIq3yHlDJ9W\n" + - "0cGgzz2XvTQgXtCi4H/pVSXo7ojLffGW5CP4+k8iFAO4uIsqAUKRG2kCSNv3KTMj\n" + - "f6d00flOm6S2+zIBCSz8UF8RmUknf6Y/AgMBAAGjbzBtMB0GA1UdDgQWBBTlJ705\n" + - "R48NTZMfuQyn9D4TeEeDvjA+BgNVHSMENzA1gBTlJ705R48NTZMfuQyn9D4TeEeD\n" + - "vqESpBAwDjEMMAoGA1UEAxMDZm9vggkAmM8jtO1U4KkwDAYDVR0TBAUwAwEB/zAN\n" + - "BgkqhkiG9w0BAQUFAAOCAgEAB0Xamx2uhf7kixCyS2RVGubxTjgu0r+jz9fk6iPW\n" + - "PzzAAb364NkgtWqOg4yOD88VjtRy1meNhOYs/lZ4Eo7STBpsb+zNF1fB9Yg9cNqy\n" + - "A3W6Wjuon7pMWXZDmXFlehPSibbO2tnaiBuBe5j6EL5BWrtHHWlvALUrIG9aB9sI\n" + - "oWRg2WK9GJcKQ90huM8z7ZeA8h0tjEhB5UXc9eAtNmsXnIQ1aWq2mb3/Fg9ALrkl\n" + - "9cHXkdGRty1INXi0b3NSa2lK7m0Rt1iWRlIv5Q9DNAGADx6azrWdbSm/DppOQCrm\n" + - "0KgdzpESIt/ZcibWYVkCOkY3KAl3MmgFc/ezOmYhuBMPzxxi+2omt+B1AFaubsRc\n" + - "65FBKjIzLEB+SuNAgfrk1kdttqMNT3JyY7hYLoMM/wryAZ7pxMCJ4ylJ85LTuoFM\n" + - "l9EPPXJI7cnplgsifDwAI0VfCu7VNIYKMUSk5UhJdJIQMPajThjg4EyXtoETcOZR\n" + - "TIN0PGC2W5n7CFO5WLNsQcWi0Rwq/RVy1y6EDYsZBqwvjUTWw9CkLXtJo1Naizb3\n" + - "qS7WskaD+kciXQ/t/aAXCd1zBZU85tjbaiMzAln8LG5jHChGi3lfNR/V24KxmNyY\n" + - "9KoXswCfB65shysC8t9FaJ4DFtcZxxY5jj9DNuzTiIYvdkpHZG+MYjesQDrxX/cp\n" + - "W0s=\n" + - "-----END CERTIFICATE-----\n"; - - private static final String CERT_BAR = "-----BEGIN CERTIFICATE-----\n" + - "MIIFDjCCAvagAwIBAgIJAIoMqv3u2Y9OMA0GCSqGSIb3DQEBBQUAMA4xDDAKBgNV\n" + - "BAMTA2JhcjAeFw0xNzEwMTIwODU5MTNaFw0xODEwMTIwODU5MTNaMA4xDDAKBgNV\n" + - "BAMTA2JhcjCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBAKjew9A5JoJ/\n" + - "PC7fQsUokG9jE+PVwpiXIFXF3dR8pARnMyjq8YXIV66wFKmcRQlM0WvOYBbxVfFT\n" + - "vjo3ASNsoaAt6HEDeCI6BwfJjXnnw3ko7o5TSPVmgS39G9vjGqZEGmbmM/Elrnc/\n" + - "VML5+wGw9g8I6YKFRfs70QxL7BDlO4YSbEki7lppyYSWGMGW7YrByCpgZaO21e5V\n" + - "CyTlREAKbcnchqtIheDjra2syna5G3L7OYGI4k/Y1D1VyShooV2FsJFB6M4TYaGO\n" + - "/T5ewEStMU+E2O79iHWsVUj/Z2gnlnbzPvk6Sy/wWAUP14B3WUPHyzBynRAwYA7u\n" + - "zHodcMLcpcHjBRZGnqEvuSB2muycRrLDtgIlvKxVZ6Da1Y75YGdgRGdxedeuhQky\n" + - "TTiGwctq6FVHH9uV4MTdVNe5eE6pzkuuSBX+X8Hv3LsbqKW0wMvyyZZtTlD19tTe\n" + - "QhJsacpIbCvm/QP+JMOKZtIMaSRdRCj11EtA5bbHJKvHilt1rpufXL9OHnqSr3lp\n" + - "vUiNWq9sKqvxkRvZbKVUVRrjDMA8oOmhMc0llAHunC1m84fwI/SHZ6l6ceqAmWWc\n" + - "JlSLT6vsrfzavibJjVJnXwIsr3Jy3itjpudvFbEz7mB30i9YCjr8OXZr7/x1KYU8\n" + - "SePtV5ZYF3COFE9PomWHwq76VBgtcrKbAgMBAAGjbzBtMB0GA1UdDgQWBBSHMfZS\n" + - "fov+3UCqlB+9oKmDr32SiDA+BgNVHSMENzA1gBSHMfZSfov+3UCqlB+9oKmDr32S\n" + - "iKESpBAwDjEMMAoGA1UEAxMDYmFyggkAigyq/e7Zj04wDAYDVR0TBAUwAwEB/zAN\n" + - "BgkqhkiG9w0BAQUFAAOCAgEAPBq/1p7l0u3vyBAjnmuBPvMEg7UaaNxcrDbl1gf0\n" + - "DpxprCJifZWYp4vsPdCE7zLXyJNfR1Cn0BKJ1hoAGU53Q689KSqRMgRb/lyU1y7s\n" + - "m7nphXR39kTN06YPkQpPs4WGKQc79FIeIxxWBYJzi3wkK2nBAyRXswbtDfVS46j1\n" + - "+kCYENiXFoIQboE4HjZ0G3Wh0Ct8au4oTFiTZ1XyxeKri7TykOwvJRIh3m6nByQi\n" + - "UPYthIWcrcZDk6c0kTOFapS0ufQ7AeynFHiveIT4CL75jdME9Ll/3Re/EgHkOJ/1\n" + - "dElESlcnjdkRzHyHCdXWnDs0gfWj1F56GGUFh0HhrIABl0Uzd/ukCHDzUOLTGRQg\n" + - "eJ3eFLMgjY3oDpbFgEupBRP266e/XbnmzXhDMdguL7+Pp9gka5tA7a67SL05FG0Z\n" + - "nWU5XXLLr+jxh7hIIdpVe+TZxdVMD0znn68MxOz8liJcGmdO+9Gnwg3FLZR0e02L\n" + - "iehS0b/oddF5xMUyDdwt/dIyU1gOc1ozLld1U7RTZIKa2Y0M65vD/sMjfsWku1Kf\n" + - "xaFEMI94vAWa9WyzveDrvFCyQNfqgmdiyh7t81TTMbFMvaGY+VUUT5UsKZX5FPFS\n" + - "AiYa8rwmnlKLVoNC6s9WSrlaPXfpWvucT4YE3L7RJ/WuP8t2oXgLtR/ATjhTnveg\n" + - "d4s=\n" + - "-----END CERTIFICATE-----\n"; - - private static final String CERT_BANANA = "-----BEGIN CERTIFICATE-----\n" + - "MIIFFzCCAv+gAwIBAgIJAOk9W2pKKYDVMA0GCSqGSIb3DQEBBQUAMBExDzANBgNV\n" + - "BAMTBmJhbmFuYTAeFw0xNzEwMTIwODU5MzBaFw0xODEwMTIwODU5MzBaMBExDzAN\n" + - "BgNVBAMTBmJhbmFuYTCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBAJ5L\n" + - "SDzBElCjuFf7jXYaOLDPL3ifeuM01k56oHdBjwmRBPLc/c0Tz5609Q9ODn5PglnZ\n" + - "zhpGayGh/XfJ5iQtP20xKd5zkP/+RHKI9SU/r682nmkLW7KDWfizKP7pmUuHkXz7\n" + - "VxtBTLtQLdEfquKUlPibxKfbGARmRaLv6i1o9nh2z4QotFB++ionlUicmGKbC6aR\n" + - "boCaEydDYP/vvftwcKucfRRKrxJqQ4dlBQ3wwTaJ5eqjXTHTHn0d5gg36xAcUxwr\n" + - "azc7TMeRd3QYhB7hOaJSh5H24X9khrzWSawho6eKk4QrvlXvzGV9Q2QDpsi4jYv5\n" + - "K/+jHgCukLsLW2aRRkFbKbCE4ghuVyOmfedUSj06/obVg+W3XW3dndwRHmc21M5q\n" + - "ECpZqBsitNWj7vaunCgIgBzJjL1IdcMA4gaEt8M0sslPyOrZUNMj1LL+izoN7K7G\n" + - "aD/RqepNEBU5OKVNmZgFyXgZWADIrpPQxGVgrEY0j9Tagw5GgeHvdJNNIeGcVbci\n" + - "/8osHlH4us2/lphUXPrwIAgh4dVIygKhkH6aSRbrq84urqJ57Zghnz9XPDosgdRY\n" + - "l0fMvReMX9J9Tjk3mWfTYlmul19I4LTrWRS1kHlS6KjUweQ/QaMqo1kPD4Kae0SA\n" + - "lLAeDq4Jntj/b1v29kIkhNdCbm1jMUIKhgLaL9l3AgMBAAGjcjBwMB0GA1UdDgQW\n" + - "BBRBDIZnqyn/lKPhosxwYC6eRnSy7DBBBgNVHSMEOjA4gBRBDIZnqyn/lKPhosxw\n" + - "YC6eRnSy7KEVpBMwETEPMA0GA1UEAxMGYmFuYW5hggkA6T1bakopgNUwDAYDVR0T\n" + - "BAUwAwEB/zANBgkqhkiG9w0BAQUFAAOCAgEAMABQyp8wXQnO16+2hSFY2EdO0UGi\n" + - "A/1O6NwiVTMt9A6Ltjk5nQ5t4cgonaHBiMa25HeQ6BJ2iAKGvmPr9R5S9XIjD2BI\n" + - "3ov02bglUCD40EJE1krFJOj3elCBjN6991OOzvWOK38FTlpJTXI9FzTxIqIcjlGJ\n" + - "SB4dbpnJH7Nat3T73n0MrHguGx7SQ+3MS5wvy3ZWO5Eg7Pu1pATfHgQHUwf+uv1T\n" + - "FDD8Ulct/hAGuNvRlRv1nkzplXCM9nY9aW3316Rg1HV/P5aBKaW23Sa6pJlIDEwa\n" + - "whNw8pQlTtahgM36K7AZbGSDBrkIN5+cJd93Orbu4dg2RCVPxLi2/yvNP0t4lBrW\n" + - "5e/5Ye/kLt23Z6Tv/d3BhbM3RwgUVmsTbdjUDChed5u7lwWf77LXW7ptEbDC3Vlk\n" + - "TOeCRsgFzV7OzXEsSlpqXTsR6+Q8a9ffg5vaaaWcwqUQIBfPdFFLmYvDpcJQXOc6\n" + - "rnKe6CnAfpE17tczCQ0TBHp4MkPyuO9WfoCb0+5Lw+O4Ny59NKLKV1zFHSELzaKt\n" + - "4msENSPdy+6YxKMJUlWMe+Z26aj8FCGuFoC17kzXlw9QP/aYPBhh2E0diOpgfs1R\n" + - "0XZg9N4M+d9oJFjK43wdDjUcPdjpfqbrc5IzBY01j7V/r+zFTn+yuLIc8B4MF6Dn\n" + - "DQkhI+BdvtzGTwE=\n" + - "-----END CERTIFICATE-----\n"; - - private class SaslAuthenticator implements ProtonSaslAuthenticator { - - private Sasl sasl; - private String user; - private boolean succeeded; - - private SaslAuthenticator() { - } - - @Override - public void init(NetSocket socket, ProtonConnection protonConnection, Transport transport) { - sasl = transport.sasl(); - sasl.server(); - sasl.allowSkip(false); - - sasl.setMechanisms(mechName); - - } - - @Override - public void process(Handler completionHandler) { - String[] remoteMechanisms = sasl.getRemoteMechanisms(); - - if (remoteMechanisms.length > 0) { - byte[] response; - if (sasl.pending() > 0) { - response = new byte[sasl.pending()]; - sasl.recv(response, 0, response.length); - } else { - response = new byte[0]; - } - - int authzidNullPosition = findNullPosition(response, 0); - if (authzidNullPosition < 0) { - throw new IllegalArgumentException("Invalid PLAIN encoding, authzid null terminator not found"); - } - - int authcidNullPosition = findNullPosition(response, authzidNullPosition + 1); - if (authcidNullPosition < 0) { - throw new IllegalArgumentException("Invalid PLAIN encoding, authcid null terminator not found"); - } - - String username = new String(response, authzidNullPosition + 1, authcidNullPosition - authzidNullPosition - 1, StandardCharsets.UTF_8); - int passwordLen = response.length - authcidNullPosition - 1; - String password = new String(response, authcidNullPosition + 1, passwordLen, StandardCharsets.UTF_8); - - if (validLogins.containsKey(username)) { - if (password.equals(validLogins.get(username))) { - this.user = username; - succeeded = true; - sasl.done(Sasl.SaslOutcome.PN_SASL_OK); - } else { - sasl.done(Sasl.SaslOutcome.PN_SASL_AUTH); - } - } else { - sasl.done(Sasl.SaslOutcome.PN_SASL_AUTH); - } - completionHandler.handle(true); - } else { - completionHandler.handle(false); - } - } - - private int findNullPosition(byte[] response, int startPosition) { - int position = startPosition; - while (position < response.length) { - if (response[position] == (byte) 0) { - return position; - } - position++; - } - return -1; - } - - @Override - public boolean succeeded() { - return succeeded; - } - - public String getUser() { - return user; - } - } -} diff --git a/broker-plugin/sasl-delegation/src/test/resources/logback-test.xml b/broker-plugin/sasl-delegation/src/test/resources/logback-test.xml deleted file mode 100644 index 0037f119770..00000000000 --- a/broker-plugin/sasl-delegation/src/test/resources/logback-test.xml +++ /dev/null @@ -1,17 +0,0 @@ - - - - - - - %d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n - - - - - - - - - - \ No newline at end of file diff --git a/broker-plugin/tcnative/pom.xml b/broker-plugin/tcnative/pom.xml index 5b17b8685cd..8836053531f 100644 --- a/broker-plugin/tcnative/pom.xml +++ b/broker-plugin/tcnative/pom.xml @@ -4,7 +4,7 @@ io.enmasse broker-plugin - 0.32-SNAPSHOT + 1.0-SNAPSHOT 4.0.0 tcnative diff --git a/bundle/pom.xml b/bundle/pom.xml index 1f845e5bb83..f28582d5d61 100644 --- a/bundle/pom.xml +++ b/bundle/pom.xml @@ -5,7 +5,7 @@ enmasse io.enmasse - 0.32-SNAPSHOT + 1.0-SNAPSHOT pom EnMasse Bundles diff --git a/cmd/controller-manager/main.go b/cmd/controller-manager/main.go index 6343ac979f2..e380b09c8d4 100644 --- a/cmd/controller-manager/main.go +++ b/cmd/controller-manager/main.go @@ -131,47 +131,34 @@ func main() { }() } - globalGvks := []schema.GroupVersionKind{ - schema.GroupVersionKind{ - Group: "user.enmasse.io", - Version: "v1beta1", - Kind: "MessagingUser", - }, - schema.GroupVersionKind{ - Group: "user.enmasse.io", - Version: "v1beta1", - Kind: "MessagingUserList", - }, - schema.GroupVersionKind{ - Group: "enmasse.io", - Version: "v1beta1", - Kind: "AddressSpace", - }, - schema.GroupVersionKind{ - Group: "enmasse.io", - Version: "v1beta1", - Kind: "AddressSpaceList", - }, - schema.GroupVersionKind{ - Group: "enmasse.io", - Version: "v1beta1", - Kind: "Address", - }, - schema.GroupVersionKind{ - Group: "enmasse.io", - Version: "v1beta1", - Kind: "AddressList", - }, - schema.GroupVersionKind{ - Group: "iot.enmasse.io", - Version: "v1alpha1", - Kind: "IoTProject", - }, - schema.GroupVersionKind{ - Group: "iot.enmasse.io", - Version: "v1alpha1", - Kind: "IoTProjectList", - }, + globalGvks := make([]schema.GroupVersionKind, 0) + + if util.IsModuleEnabled("IOT_CONFIG") { + globalGvks = append(globalGvks, + schema.GroupVersionKind{ + Group: "iot.enmasse.io", + Version: "v1alpha1", + Kind: "IoTConfig", + }, + schema.GroupVersionKind{ + Group: "iot.enmasse.io", + Version: "v1alpha1", + Kind: "IoTConfigList", + }) + } + + if util.IsModuleEnabled("IOT_PROJECT") { + globalGvks = append(globalGvks, + schema.GroupVersionKind{ + Group: "iot.enmasse.io", + Version: "v1alpha1", + Kind: "IoTProject", + }, + schema.GroupVersionKind{ + Group: "iot.enmasse.io", + Version: "v1alpha1", + Kind: "IoTProjectList", + }) } if util.IsModuleEnabled("MESSAGING_INFRASTRUCTURE") { @@ -234,6 +221,36 @@ func main() { ) } + if util.IsModuleEnabled("MESSAGING_PLAN") { + globalGvks = append(globalGvks, + schema.GroupVersionKind{ + Group: "enmasse.io", + Version: "v1beta2", + Kind: "MessagingPlan", + }, + schema.GroupVersionKind{ + Group: "enmasse.io", + Version: "v1beta2", + Kind: "MessagingPlanList", + }, + ) + } + + if util.IsModuleEnabled("MESSAGING_ADDRESS_PLAN") { + globalGvks = append(globalGvks, + schema.GroupVersionKind{ + Group: "enmasse.io", + Version: "v1beta2", + Kind: "MessagingAddressPlan", + }, + schema.GroupVersionKind{ + Group: "enmasse.io", + Version: "v1beta2", + Kind: "MessagingAddressPlanList", + }, + ) + } + // Create a new Cmd to provide shared dependencies and start components mgr, err := manager.New(cfg, manager.Options{ Namespace: namespace, @@ -352,7 +369,9 @@ func serveCRMetrics(cfg *rest.Config) error { if (!util.IsModuleEnabled("MESSAGING_INFRASTRUCTURE") && strings.HasPrefix(gvk.Kind, "MessagingInfrastructure")) || (!util.IsModuleEnabled("MESSAGING_TENANT") && strings.HasPrefix(gvk.Kind, "MessagingTenant")) || (!util.IsModuleEnabled("MESSAGING_ENDPOINT") && strings.HasPrefix(gvk.Kind, "MessagingEndpoint")) || - (!util.IsModuleEnabled("MESSAGING_ADDRESS") && strings.HasPrefix(gvk.Kind, "MessagingAddress")) { + (!util.IsModuleEnabled("MESSAGING_ADDRESS") && strings.HasPrefix(gvk.Kind, "MessagingAddress")) || + (!util.IsModuleEnabled("MESSAGING_PLAN") && strings.HasPrefix(gvk.Kind, "MessagingPlan")) || + (!util.IsModuleEnabled("MESSAGING_ADDRESS_PLAN") && strings.HasPrefix(gvk.Kind, "MessagingAddressPlan")) { log.Info("Skipping adding metric because module is not enabled", "gkv", gvk) } else { filteredGVK = append(filteredGVK, gvk) diff --git a/console/console-init/pom.xml b/console/console-init/pom.xml index a7f5e5eceb4..85538679e00 100644 --- a/console/console-init/pom.xml +++ b/console/console-init/pom.xml @@ -5,7 +5,7 @@ io.enmasse console - 0.32-SNAPSHOT + 1.0-SNAPSHOT 4.0.0 io.enmasse diff --git a/console/console-server/pom.xml b/console/console-server/pom.xml index 6ee2f7c80fe..1c41b1f9fe3 100644 --- a/console/console-server/pom.xml +++ b/console/console-server/pom.xml @@ -4,7 +4,7 @@ io.enmasse console - 0.32-SNAPSHOT + 1.0-SNAPSHOT pom 4.0.0 diff --git a/console/pom.xml b/console/pom.xml index f53462f6171..379192c5654 100644 --- a/console/pom.xml +++ b/console/pom.xml @@ -5,7 +5,7 @@ io.enmasse enmasse - 0.32-SNAPSHOT + 1.0-SNAPSHOT 4.0.0 io.enmasse diff --git a/controller-manager/pom.xml b/controller-manager/pom.xml index 62bdb167a18..a2cb9ca0eaf 100644 --- a/controller-manager/pom.xml +++ b/controller-manager/pom.xml @@ -4,7 +4,7 @@ io.enmasse enmasse - 0.32-SNAPSHOT + 1.0-SNAPSHOT pom 4.0.0 diff --git a/discovery-lib/README.md b/discovery-lib/README.md deleted file mode 100644 index d3e4436b0c3..00000000000 --- a/discovery-lib/README.md +++ /dev/null @@ -1 +0,0 @@ -THe discovery library is for discovering pods in kubernetes using the configserv podsense AMQP API diff --git a/discovery-lib/pom.xml b/discovery-lib/pom.xml deleted file mode 100644 index 60ba254ec0f..00000000000 --- a/discovery-lib/pom.xml +++ /dev/null @@ -1,53 +0,0 @@ - - - - io.enmasse - enmasse - 0.32-SNAPSHOT - - 4.0.0 - discovery-lib - - - io.enmasse - k8s-api - compile - - - io.fabric8 - kubernetes-client - compile - - - org.slf4j - slf4j-api - compile - - - org.junit.jupiter - junit-jupiter-api - test - - - org.junit.jupiter - junit-jupiter-engine - test - - - org.junit.platform - junit-platform-launcher - test - - - org.mockito - mockito-core - test - - - ch.qos.logback - logback-classic - test - - - diff --git a/discovery-lib/src/main/java/enmasse/discovery/DiscoveryClient.java b/discovery-lib/src/main/java/enmasse/discovery/DiscoveryClient.java deleted file mode 100644 index 492ef41df7b..00000000000 --- a/discovery-lib/src/main/java/enmasse/discovery/DiscoveryClient.java +++ /dev/null @@ -1,131 +0,0 @@ -/* - * Copyright 2016-2018, EnMasse authors. - * License: Apache License 2.0 (see the file LICENSE or http://apache.org/licenses/LICENSE-2.0.html). - */ - -package enmasse.discovery; - -import io.enmasse.k8s.api.cache.*; -import io.fabric8.kubernetes.api.model.PodList; -import io.fabric8.kubernetes.client.DefaultKubernetesClient; -import io.fabric8.kubernetes.client.KubernetesClient; -import io.fabric8.kubernetes.client.Watch; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import java.time.Clock; -import java.time.Duration; -import java.util.*; -import java.util.stream.Collectors; - -public class DiscoveryClient implements ListerWatcher { - private final List listeners = new ArrayList<>(); - private final Logger log = LoggerFactory.getLogger(DiscoveryClient.class.getName()); - private final String containerName; - private final Map labelFilter; - private final Map annotationFilter; - private Set currentHosts = new LinkedHashSet<>(); - private final KubernetesClient client; - private final Controller controller; - - public DiscoveryClient(KubernetesClient client, Map labelFilter, Map annotationFilter, String containerName) { - this.client = client; - this.labelFilter = labelFilter; - this.annotationFilter = annotationFilter; - this.containerName = containerName; - WorkQueue queue = new EventCache<>(new HasMetadataFieldExtractor<>()); - Reflector.Config config = new Reflector.Config<>(); - config.setClock(Clock.systemUTC()); - config.setExpectedType(io.fabric8.kubernetes.api.model.Pod.class); - config.setListerWatcher(this); - config.setResyncInterval(Duration.ofMinutes(5)); - config.setWorkQueue(queue); - config.setProcessor(map -> { - if (queue.hasSynced()) { - resourcesUpdated(queue.list().stream() - .map(Pod::new) - .filter(this::filterPod) - .collect(Collectors.toList())); - } - }); - - Reflector reflector = new Reflector<>(config); - controller = new Controller(reflector); - } - - public DiscoveryClient(Map labelFilter, Map annotationFilter, String containerName) { - this(new DefaultKubernetesClient(), labelFilter, annotationFilter, containerName); - } - - public void addListener(DiscoveryListener listener) { - this.listeners.add(listener); - } - - private void notifyListeners(Set hosts) { - if (currentHosts.equals(hosts)) { - return; - } - currentHosts = new LinkedHashSet<>(hosts); - log.info("Received new set of hosts: " + hosts); - for (DiscoveryListener listener : listeners) { - listener.hostsChanged(hosts); - } - } - - public void start() { - controller.start(); - } - - void resourcesUpdated(List resources) { - Set hosts = new HashSet<>(); - for (Pod pod : resources) { - - String host = pod.getHost(); - String ready = pod.getReady(); - String phase = pod.getPhase(); - if ("True".equals(ready) && "Running".equals(phase)) { - Map> portMap = pod.getPortMap(); - if (containerName != null) { - hosts.add(new Host(host, portMap.get(containerName))); - } else { - hosts.add(new Host(host, portMap.values().iterator().next())); - } - } - } - - notifyListeners(hosts); - } - - public void stop() throws InterruptedException { - controller.stop(); - } - - private boolean filterPod(Pod pod) { - Map annotations = pod.getAnnotations(); - if (annotationFilter.isEmpty()) { - return true; - } - - if (annotations == null) { - return false; - } - - for (Map.Entry filterEntry : annotationFilter.entrySet()) { - String annotationValue = annotations.get(filterEntry.getKey()); - if (annotationValue == null || !annotationValue.equals(filterEntry.getValue())) { - return false; - } - } - return true; - } - - @Override - public PodList list(ListOptions listOptions) { - return client.pods().withLabels(labelFilter).list(); - } - - @Override - public Watch watch(io.fabric8.kubernetes.client.Watcher watcher, ListOptions listOptions) { - return client.pods().withLabels(labelFilter).withResourceVersion(listOptions.getResourceVersion()).watch(watcher); - } -} diff --git a/discovery-lib/src/main/java/enmasse/discovery/DiscoveryListener.java b/discovery-lib/src/main/java/enmasse/discovery/DiscoveryListener.java deleted file mode 100644 index d965932035f..00000000000 --- a/discovery-lib/src/main/java/enmasse/discovery/DiscoveryListener.java +++ /dev/null @@ -1,15 +0,0 @@ -/* - * Copyright 2016-2018, EnMasse authors. - * License: Apache License 2.0 (see the file LICENSE or http://apache.org/licenses/LICENSE-2.0.html). - */ - -package enmasse.discovery; - -import java.util.Set; - -/** - * Interface for listeners on host set changes. - */ -public interface DiscoveryListener { - void hostsChanged(Set hosts); -} diff --git a/discovery-lib/src/main/java/enmasse/discovery/Endpoint.java b/discovery-lib/src/main/java/enmasse/discovery/Endpoint.java deleted file mode 100644 index eb5f9a824a1..00000000000 --- a/discovery-lib/src/main/java/enmasse/discovery/Endpoint.java +++ /dev/null @@ -1,27 +0,0 @@ -/* - * Copyright 2016-2018, EnMasse authors. - * License: Apache License 2.0 (see the file LICENSE or http://apache.org/licenses/LICENSE-2.0.html). - */ - -package enmasse.discovery; - -/** - * An endpoint for a host and port; - */ -public class Endpoint { - private final String hostname; - private final int port; - - public Endpoint(String hostname, int port) { - this.hostname = hostname; - this.port = port; - } - - public String hostname() { - return hostname; - } - - public int port() { - return port; - } -} diff --git a/discovery-lib/src/main/java/enmasse/discovery/Host.java b/discovery-lib/src/main/java/enmasse/discovery/Host.java deleted file mode 100644 index 00c7ab75bda..00000000000 --- a/discovery-lib/src/main/java/enmasse/discovery/Host.java +++ /dev/null @@ -1,62 +0,0 @@ -/* - * Copyright 2016-2018, EnMasse authors. - * License: Apache License 2.0 (see the file LICENSE or http://apache.org/licenses/LICENSE-2.0.html). - */ - -package enmasse.discovery; - -import java.util.Map; - -/** - * Represents a host with a name and a set of ports. - */ -public class Host { - private final String hostname; - private final Map portMap; - - public Host(String hostname, Map portMap) { - this.hostname = hostname; - this.portMap = portMap; - } - - public String getHostname() { - return hostname; - } - - public Endpoint amqpEndpoint() { - return getEndpoint("amqp"); - } - - public Endpoint getEndpoint(String portName) { - return new Endpoint(hostname, portMap.get(portName)); - } - - @Override - public boolean equals(Object o) { - if (this == o) return true; - if (o == null || getClass() != o.getClass()) return false; - - Host host = (Host) o; - - if (!hostname.equals(host.hostname)) return false; - return (portMap.containsKey("amqp") == host.portMap.containsKey("amqp")) && portMap.get("amqp").equals(host.portMap.get("amqp")); - - } - - @Override - public int hashCode() { - int result = hostname.hashCode(); - if (portMap.containsKey("amqp")) { - result = 31 * result + portMap.get("amqp"); - } - return result; - } - - @Override - public String toString() { - StringBuilder builder = new StringBuilder(); - builder.append("host=").append(hostname).append(", "); - builder.append("ports=").append(portMap.toString()); - return builder.toString(); - } -} diff --git a/discovery-lib/src/main/java/enmasse/discovery/Pod.java b/discovery-lib/src/main/java/enmasse/discovery/Pod.java deleted file mode 100644 index c2e99e5adf8..00000000000 --- a/discovery-lib/src/main/java/enmasse/discovery/Pod.java +++ /dev/null @@ -1,124 +0,0 @@ -/* - * Copyright 2018, EnMasse authors. - * License: Apache License 2.0 (see the file LICENSE or http://apache.org/licenses/LICENSE-2.0.html). - */ -package enmasse.discovery; - -import io.fabric8.kubernetes.api.model.Container; -import io.fabric8.kubernetes.api.model.ContainerPort; -import io.fabric8.kubernetes.api.model.PodCondition; - -import java.util.LinkedHashMap; -import java.util.List; -import java.util.Map; - -/** - * Represents a podsense resource. - */ -public class Pod { - private final String name; - private final String kind; - private final String host; - private final String ready; - private final String phase; - private final Map> portMap; - private final Map annotations = new LinkedHashMap<>(); - - public Pod(io.fabric8.kubernetes.api.model.Pod pod) { - this.name = pod.getMetadata().getName(); - if (pod.getMetadata().getAnnotations() != null) { - this.annotations.putAll(pod.getMetadata().getAnnotations()); - } - this.kind = pod.getKind(); - this.host = pod.getStatus().getPodIP(); - this.phase = pod.getStatus().getPhase(); - this.ready = getReadyCondition(pod.getStatus().getConditions()); - this.portMap = getPortMap(pod.getSpec().getContainers()); - } - - private String getReadyCondition(List conditions) { - for (PodCondition condition : conditions) { - if ("Ready".equals(condition.getType())) { - return condition.getStatus(); - } - } - return "Unknown"; - } - - private static Map> getPortMap(List containers) { - Map> portMap = new LinkedHashMap<>(); - for (Container container : containers) { - Map ports = new LinkedHashMap<>(); - for (ContainerPort port : container.getPorts()) { - ports.put(port.getName(), port.getContainerPort()); - } - portMap.put(container.getName(), ports); - } - return portMap; - } - - @Override - public boolean equals(Object o) { - if (this == o) return true; - if (o == null || getClass() != o.getClass()) return false; - - Pod that = (Pod) o; - - if (!name.equals(that.name)) return false; - if (!kind.equals(that.kind)) return false; - if (host != null ? !host.equals(that.host) : that.host != null) return false; - if (phase != null ? !phase.equals(that.phase) : that.phase != null) return false; - if (ready != null ? !ready.equals(that.ready) : that.ready != null) return false; - return portMap != null ? portMap.equals(that.portMap) : that.portMap == null; - } - - public String getName() { - return name; - } - - public String getKind() { - return kind; - } - - @Override - public String toString() { - StringBuilder builder = new StringBuilder(); - builder.append("kind=").append(kind); - builder.append(", name=").append(name); - builder.append(", host=").append(host); - builder.append(", phase=").append(phase); - builder.append(", ready=").append(ready); - return builder.toString(); - } - - @Override - public int hashCode() { - int result = name.hashCode(); - result = 31 * result + kind.hashCode(); - result = 31 * result + (host != null ? host.hashCode() : 0); - result = 31 * result + (phase != null ? phase.hashCode() : 0); - result = 31 * result + (ready != null ? ready.hashCode() : 0); - result = 31 * result + (portMap != null ? portMap.hashCode() : 0); - return result; - } - - public String getPhase() { - return phase; - } - - public String getReady() { - return ready; - } - - public String getHost() { - return host; - } - - public Map> getPortMap() { - return portMap; - } - - public Map getAnnotations() { - return annotations; - } -} diff --git a/discovery-lib/src/test/java/enmasse/discovery/DiscoveryTest.java b/discovery-lib/src/test/java/enmasse/discovery/DiscoveryTest.java deleted file mode 100644 index fdd8ec0f7f2..00000000000 --- a/discovery-lib/src/test/java/enmasse/discovery/DiscoveryTest.java +++ /dev/null @@ -1,87 +0,0 @@ -/* - * Copyright 2018, EnMasse authors. - * License: Apache License 2.0 (see the file LICENSE or http://apache.org/licenses/LICENSE-2.0.html). - */ -package enmasse.discovery; - -import io.fabric8.kubernetes.api.model.ContainerBuilder; -import io.fabric8.kubernetes.api.model.ContainerPortBuilder; -import io.fabric8.kubernetes.api.model.PodBuilder; -import io.fabric8.kubernetes.client.KubernetesClient; -import org.junit.jupiter.api.Test; - -import java.util.Collections; -import java.util.Map; -import java.util.Set; -import java.util.concurrent.CompletableFuture; -import java.util.concurrent.TimeUnit; -import java.util.concurrent.TimeoutException; - -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.fail; -import static org.mockito.Mockito.mock; - -public class DiscoveryTest { - - @Test - public void testDiscovery() throws Exception { - Map expectedLabelFilter = Collections.singletonMap("my", "key"); - Map expectedAnnotationFilter = Collections.singletonMap("my", "annotation"); - CompletableFuture> changedHosts = new CompletableFuture<>(); - KubernetesClient kubeClient = mock(KubernetesClient.class); - - System.out.println("Deploying server verticle"); - DiscoveryClient client = new DiscoveryClient(kubeClient, expectedLabelFilter, expectedAnnotationFilter, null); - client.addListener(changedHosts::complete); - - System.out.println("Waiting for subscriber to be created"); - client.resourcesUpdated(Collections.singletonList(createPod("False", "Pending"))); - - System.out.println("Sending second response"); - client.resourcesUpdated(Collections.singletonList(createPod("False", "Running"))); - try { - changedHosts.get(10, TimeUnit.SECONDS); - fail("Ready must be true before returning host"); - } catch (TimeoutException ignored) { - } - - System.out.println("Sending third response"); - client.resourcesUpdated(Collections.singletonList(createPod("True", "Running"))); - try { - Set actual = changedHosts.get(2, TimeUnit.MINUTES); - assertEquals(actual.size(), 1); - Host actualHost = actual.iterator().next(); - assertEquals(actualHost.getHostname(), "10.0.0.1"); - } catch (Exception e) { - fail("Unexpected exception" + e.getMessage()); - e.printStackTrace(); - } - } - - public enmasse.discovery.Pod createPod(String ready, String phase) { - return new enmasse.discovery.Pod(new PodBuilder() - .editOrNewMetadata() - .withName("mypod") - .withLabels(Collections.singletonMap("my", "key")) - .withAnnotations(Collections.singletonMap("my", "annotation")) - .endMetadata() - .editOrNewStatus() - .withPhase(phase) - .withPodIP("10.0.0.1") - .addNewCondition() - .withType("Ready") - .withStatus(ready) - .endCondition() - .endStatus() - .withNewSpec() - .addToContainers(new ContainerBuilder() - .withName("c") - .addToPorts(new ContainerPortBuilder() - .withName("http") - .withContainerPort(1234) - .build()) - .build()) - .endSpec() - .build()); - } -} diff --git a/discovery-lib/src/test/resources/logback-test.xml b/discovery-lib/src/test/resources/logback-test.xml deleted file mode 100644 index 0037f119770..00000000000 --- a/discovery-lib/src/test/resources/logback-test.xml +++ /dev/null @@ -1,17 +0,0 @@ - - - - - - - %d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n - - - - - - - - - - \ No newline at end of file diff --git a/documentation/design/iot/README.adoc b/documentation/design/iot/README.adoc index f6b496c5650..d9011808387 100644 --- a/documentation/design/iot/README.adoc +++ b/documentation/design/iot/README.adoc @@ -1,18 +1,82 @@ +:icons: font = IoT support +[.lead] This document describes how IoT is added into the EnMasse system. +NOTE: This is the updated version for EnMasse Core v1. + == Overview -EnMasse IoT support is an additional layer on top of the EnMasse system. It supports IoT -specific use cases and protocols, like "telemetry ingestion", "command & control", "MQTT", "HTTP", ... +EnMasse IoT support is an additional layer on top of the +EnMasse system. It supports IoT specific use cases and +protocols, like "telemetry ingestion", "command & control", +"MQTT", "HTTP", … This is implemented based on the Eclipse Hono project. +== IoT Config CRD + +[.lead] +An "IoT Config" is an IoT infrastructure which can serve multiple IoT tenants (IoTProject). + +An IoT Config: + +* Is part of the EnMasse infrastructure namespace. +* Is configured to bind to exactly one _messaging infrastructure_. +* Has the name `default`. +* Can currently only exist once in the cluster. ++ +NOTE: The goal is to lift this limitation in a future version and +allow for multiple IoT infrastructure instances. Each IoT infrastructure +pointing to exactly one messaging infrastructure. +* Serves all IoT projects assigned to the IoT infrastructure. ++ +This is an implicit assignment. An IoT project resides in a Kubernetes +namespace, which is assigned to a messaging tenant, which is assigned +to a messaging infrastructure. + +*Target persona:* IoT service admin + +*Proposal:* Change the name from `IoTConfig` to `IoTInfrastructure` +when we make allow for multiple IoT infrastructures. + +=== Hono services + +Each IoT infrastructure has a set of Hono services (protocol adapters, +device registry, …) which will be shared between all IoT projects, +assigned to this infrastructure. + +=== Protocol adapter addressing + +The protocol adapters will directly connect to the messaging +infrastructure, using an (to be defined) internal +authentication/authorization mechanism. + +For this to work, the protocol adapters must be able to prefix +the address resources with the _vhost_ prefix. The _vhost_ prefix +is the Kubernetes namespace. + +*Example:* Assuming an IoT project `iot1` in the namespace `ns1`. +The operator would request an address `telemetry/ns1.iot1` in the +namespace `ns1`. As the protocol adapters would connect to the +messaging infrastructure, they would prefix the address with the +namespace and would use the address `ns1/telemetry/ns1.iot1` instead. + +IMPORTANT: Currently we are using the QPID dispatch router sidecar to +split the single AMQP connection coming from Hono protocol adapters +into multiple connections for the different address spaces. This +sidecar setup would be removed. + +IMPORTANT: This requires an upstream change in Hono, so that we +have the ability to prefix the addresses in Hono with a custom +(tenant specific) prefix. + == IoT Project CRD -An "IoT project" is a logical entity which scopes a set of devices into a single, IoT specific, namespace. +[.lead] +An "IoT project" is a logical entity, which scopes a set of devices into a single, IoT specific, namespace. An IoT project: @@ -20,19 +84,65 @@ An IoT project: * Has a name which adheres to the requirements of `.metadata.name` * Translates into a Hono tenant name by: `.` * Can be described in a Kubernetes CRD -* Connects to one EnMasse standard address space -* Has its own set of addresses ("telemtry/", "event/", ...) inside that address space +* Connects implicitly to one EnMasse IoT infrastructure (IoTConfig) +* Manages its own set of addresses ("telemetry/", "event/", ...) +* Requires a _messaging tenant_ to be created first + +IMPORTANT: This will remove the three current _downstream strategies_ +and remain with only the _managed_ one. The "external" strategy can +be achieved by using _EnMasse connectors_. + +*Target persona:* IoT tenant + +=== Missing messaging tenant + +*Aspect:* Getting started + +If an IoT project gets created in a namespace which does not have +a _message tenant_ created, then the IoT project will: + +* Get the field `.status.phase` set to `Failed` +* Provide a helpful message in the field `.status.message` +* Get the condition `HasMessagingTenant` set to `false` + +As soon as the messaging tenant gets created, the project will +try to become ready automatically. + +The Web UI should alert the user that it is not possible to create +an IoT project without having a messaging tenant. It _may_ offer to +create a messaging tenant on the fly. + +=== IoT infrastructure assignment === + +As mentioned earlier, each IoT project is implicitly assigned to a +messaging infrastructure. And each IoT infrastructure is assigned +to exactly one messaging infrastructure. + +This may lead to a situation where an IoT projects gets created, +pointing to a messaging infrastructure, which has no IoT infrastructure +assigned. + +In this case the IoT project will: + +* Get the field `.status.phase` set to `Failed` +* Provide a helpful message in the field `.status.message` +* Get the condition `HasMessagingInfrastructure` set to `false` -IoT project know three modes of operation: +The controller will *not* automatically re-try to bring the IoT project +into a ready state. -* **External**: All messaging resources (address space, addresses, users) are provided by the user (external - to the IoT project). The configuration of the IoT project contains all necessary information. -* **Providd**: to be written -* **Managed**: The EnMasse system fully managed the messaging resources. +IMPORTANT: This scenario is more important in the first version, as we +currently only support *one* IoT infrastructure. It still is a valid +scenario once multiple IoT infrastructures are supported. -== Protocol adapters +== Getting started -The Hono protocol adapters are shared, infrastructure components, which will be re-used by all IoT projects. -Therefore the messages of each Hono tenant (EnMasse IoT project) will be proxies using the Qpid dispatcher router, -from the Hono protocol adapters, to the target messaging resources of the IoT project. +The minimum steps to get started are: +* Deploy EnMasse +* Create a messaging infrastructure +* Create an IoT infrastructure + ** Deploy PostgreSQL + ** Create a database schema +* Create a messaging tenant +* Create an IoT project diff --git a/documentation/pom.xml b/documentation/pom.xml index ba20763d237..e931bb1dd00 100644 --- a/documentation/pom.xml +++ b/documentation/pom.xml @@ -5,7 +5,7 @@ io.enmasse enmasse - 0.32-SNAPSHOT + 1.0-SNAPSHOT 4.0.0 documentation diff --git a/iot/images/iot-adapters/pom.xml b/iot/images/iot-adapters/pom.xml index f53adbc85a5..fb350238334 100644 --- a/iot/images/iot-adapters/pom.xml +++ b/iot/images/iot-adapters/pom.xml @@ -8,7 +8,7 @@ io.enmasse iot - 0.32-SNAPSHOT + 1.0-SNAPSHOT ../../ diff --git a/iot/images/iot-device-registry/pom.xml b/iot/images/iot-device-registry/pom.xml index 8ce64f8273d..087ed43c1d7 100644 --- a/iot/images/iot-device-registry/pom.xml +++ b/iot/images/iot-device-registry/pom.xml @@ -8,7 +8,7 @@ io.enmasse iot - 0.32-SNAPSHOT + 1.0-SNAPSHOT ../../ diff --git a/iot/iot-application-base/pom.xml b/iot/iot-application-base/pom.xml index e726106e4af..6d77f1c35e2 100644 --- a/iot/iot-application-base/pom.xml +++ b/iot/iot-application-base/pom.xml @@ -8,7 +8,7 @@ io.enmasse iot - 0.32-SNAPSHOT + 1.0-SNAPSHOT iot-application-base diff --git a/iot/iot-auth-service/pom.xml b/iot/iot-auth-service/pom.xml index 167f4e3ff47..7a1fdb26209 100644 --- a/iot/iot-auth-service/pom.xml +++ b/iot/iot-auth-service/pom.xml @@ -8,7 +8,7 @@ io.enmasse iot-application-base - 0.32-SNAPSHOT + 1.0-SNAPSHOT ../iot-application-base diff --git a/iot/iot-device-connection-infinispan/pom.xml b/iot/iot-device-connection-infinispan/pom.xml index 96293592c94..ffd013750d3 100644 --- a/iot/iot-device-connection-infinispan/pom.xml +++ b/iot/iot-device-connection-infinispan/pom.xml @@ -8,7 +8,7 @@ io.enmasse iot-application-base - 0.32-SNAPSHOT + 1.0-SNAPSHOT ../iot-application-base diff --git a/iot/iot-device-registry-base/pom.xml b/iot/iot-device-registry-base/pom.xml index 0dba3a71ab4..96d22b2580b 100644 --- a/iot/iot-device-registry-base/pom.xml +++ b/iot/iot-device-registry-base/pom.xml @@ -8,7 +8,7 @@ io.enmasse iot - 0.32-SNAPSHOT + 1.0-SNAPSHOT iot-device-registry-base diff --git a/iot/iot-device-registry-base/src/main/java/io/enmasse/iot/registry/autowire/AutowiredCredentialsAdapter.java b/iot/iot-device-registry-base/src/main/java/io/enmasse/iot/registry/autowire/AutowiredCredentialsAdapter.java deleted file mode 100644 index 66f65e979af..00000000000 --- a/iot/iot-device-registry-base/src/main/java/io/enmasse/iot/registry/autowire/AutowiredCredentialsAdapter.java +++ /dev/null @@ -1,36 +0,0 @@ -/* - * Copyright 2019-2020, EnMasse authors. - * License: Apache License 2.0 (see the file LICENSE or http://apache.org/licenses/LICENSE-2.0.html). - */ - -package io.enmasse.iot.registry.autowire; - -import org.eclipse.hono.service.credentials.CredentialsService; -import org.eclipse.hono.service.credentials.EventBusCredentialsAdapter; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.boot.autoconfigure.condition.ConditionalOnBean; -import org.springframework.stereotype.Component; - -/** - * A default event bus based service implementation of the {@link CredentialsService}. - *

- * This wires up the actual service instance with the mapping to the event bus implementation. It is intended to be used - * in a Spring Boot environment. - */ -@Component -@ConditionalOnBean(CredentialsService.class) -public final class AutowiredCredentialsAdapter extends EventBusCredentialsAdapter { - - private CredentialsService service; - - @Autowired - public void setService(final CredentialsService service) { - this.service = service; - } - - @Override - protected CredentialsService getService() { - return this.service; - } - -} diff --git a/iot/iot-device-registry-base/src/main/java/io/enmasse/iot/registry/autowire/AutowiredCredentialsManagementAdapter.java b/iot/iot-device-registry-base/src/main/java/io/enmasse/iot/registry/autowire/AutowiredCredentialsManagementAdapter.java deleted file mode 100644 index dc464b434dc..00000000000 --- a/iot/iot-device-registry-base/src/main/java/io/enmasse/iot/registry/autowire/AutowiredCredentialsManagementAdapter.java +++ /dev/null @@ -1,37 +0,0 @@ -/* - * Copyright 2019, EnMasse authors. - * License: Apache License 2.0 (see the file LICENSE or http://apache.org/licenses/LICENSE-2.0.html). - */ - -package io.enmasse.iot.registry.autowire; - -import org.eclipse.hono.service.management.credentials.CredentialsManagementService; -import org.eclipse.hono.service.management.credentials.EventBusCredentialsManagementAdapter; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.boot.autoconfigure.condition.ConditionalOnBean; -import org.springframework.stereotype.Component; - -/** - * A default event bus based service implementation of the {@link CredentialsManagementService}. - *

- * This wires up the actual service instance with the mapping to the event bus implementation. It is intended to be used - * in a Spring Boot environment. - */ -@Component -@ConditionalOnBean(CredentialsManagementService.class) -public final class AutowiredCredentialsManagementAdapter extends EventBusCredentialsManagementAdapter { - - private CredentialsManagementService service; - - @Autowired - public void setService(final CredentialsManagementService service) { - this.service = service; - } - - @Override - protected CredentialsManagementService getService() { - return this.service; - } - -} - diff --git a/iot/iot-device-registry-base/src/main/java/io/enmasse/iot/registry/autowire/AutowiredDeviceManagementAdapter.java b/iot/iot-device-registry-base/src/main/java/io/enmasse/iot/registry/autowire/AutowiredDeviceManagementAdapter.java deleted file mode 100644 index da37f1c66ca..00000000000 --- a/iot/iot-device-registry-base/src/main/java/io/enmasse/iot/registry/autowire/AutowiredDeviceManagementAdapter.java +++ /dev/null @@ -1,36 +0,0 @@ -/* - * Copyright 2019, EnMasse authors. - * License: Apache License 2.0 (see the file LICENSE or http://apache.org/licenses/LICENSE-2.0.html). - */ - -package io.enmasse.iot.registry.autowire; - -import org.eclipse.hono.service.management.device.DeviceManagementService; -import org.eclipse.hono.service.management.device.EventBusDeviceManagementAdapter; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.boot.autoconfigure.condition.ConditionalOnBean; -import org.springframework.stereotype.Component; - -/** - * A default event bus based service implementation of the {@link DeviceManagementService}. - *

- * This wires up the actual service instance with the mapping to the event bus implementation. It is intended to be used - * in a Spring Boot environment. - */ -@Component -@ConditionalOnBean(DeviceManagementService.class) -public final class AutowiredDeviceManagementAdapter extends EventBusDeviceManagementAdapter { - - private DeviceManagementService service; - - @Autowired - public void setService(final DeviceManagementService service) { - this.service = service; - } - - @Override - protected DeviceManagementService getService() { - return this.service; - } - -} diff --git a/iot/iot-device-registry-base/src/main/java/io/enmasse/iot/registry/autowire/AutowiredRegistrationAdapter.java b/iot/iot-device-registry-base/src/main/java/io/enmasse/iot/registry/autowire/AutowiredRegistrationAdapter.java deleted file mode 100644 index c839954b7f2..00000000000 --- a/iot/iot-device-registry-base/src/main/java/io/enmasse/iot/registry/autowire/AutowiredRegistrationAdapter.java +++ /dev/null @@ -1,37 +0,0 @@ -/* - * Copyright 2019-2020, EnMasse authors. - * License: Apache License 2.0 (see the file LICENSE or http://apache.org/licenses/LICENSE-2.0.html). - */ - -package io.enmasse.iot.registry.autowire; - -import org.eclipse.hono.service.management.device.DeviceManagementService; -import org.eclipse.hono.service.registration.EventBusRegistrationAdapter; -import org.eclipse.hono.service.registration.RegistrationService; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.boot.autoconfigure.condition.ConditionalOnBean; -import org.springframework.stereotype.Component; - -/** - * A default event bus based service implementation of the {@link DeviceManagementService}. - *

- * This wires up the actual service instance with the mapping to the event bus implementation. It is intended to be used - * in a Spring Boot environment. - */ -@Component -@ConditionalOnBean(RegistrationService.class) -public final class AutowiredRegistrationAdapter extends EventBusRegistrationAdapter { - - private RegistrationService service; - - @Autowired - public void setService(final RegistrationService service) { - this.service = service; - } - - @Override - protected RegistrationService getService() { - return this.service; - } - -} diff --git a/iot/iot-device-registry-base/src/main/java/io/enmasse/iot/registry/devcon/AbstractDeviceConnectionService.java b/iot/iot-device-registry-base/src/main/java/io/enmasse/iot/registry/devcon/AbstractDeviceConnectionService.java index 8fd99af22bf..f5352137a95 100644 --- a/iot/iot-device-registry-base/src/main/java/io/enmasse/iot/registry/devcon/AbstractDeviceConnectionService.java +++ b/iot/iot-device-registry-base/src/main/java/io/enmasse/iot/registry/devcon/AbstractDeviceConnectionService.java @@ -5,6 +5,7 @@ package io.enmasse.iot.registry.devcon; +import java.time.Duration; import java.util.ArrayList; import java.util.List; import java.util.Map; @@ -24,7 +25,7 @@ public abstract class AbstractDeviceConnectionService implements DeviceConnectio protected abstract Future processGetLastKnownGatewayForDevice(final DeviceConnectionKey key, final Span span); - protected abstract Future processSetCommandHandlingAdapterInstance(final DeviceConnectionKey key, final String adapterInstanceId, final Span span); + protected abstract Future processSetCommandHandlingAdapterInstance(final DeviceConnectionKey key, final String adapterInstanceId, Duration lifespan, final Span span); protected abstract Future processGetCommandHandlingAdapterInstances(final DeviceConnectionKey key, final List viaGateways, final Span span); @@ -43,8 +44,8 @@ public Future setLastKnownGatewayForDevice(final String } @Override - public Future setCommandHandlingAdapterInstance(String tenantId, String deviceId, String adapterInstanceId, Span span) { - return processSetCommandHandlingAdapterInstance(DeviceConnectionKey.deviceConnectionKey(tenantId, deviceId), adapterInstanceId, span); + public Future setCommandHandlingAdapterInstance(String tenantId, String deviceId, String adapterInstanceId, Duration lifespan, Span span) { + return processSetCommandHandlingAdapterInstance(DeviceConnectionKey.deviceConnectionKey(tenantId, deviceId), adapterInstanceId, lifespan, span); } @Override diff --git a/iot/iot-device-registry-base/src/main/java/io/enmasse/iot/registry/tenant/KubernetesTenantInformationService.java b/iot/iot-device-registry-base/src/main/java/io/enmasse/iot/registry/tenant/KubernetesTenantInformationService.java index c7ace5045fb..10250819846 100644 --- a/iot/iot-device-registry-base/src/main/java/io/enmasse/iot/registry/tenant/KubernetesTenantInformationService.java +++ b/iot/iot-device-registry-base/src/main/java/io/enmasse/iot/registry/tenant/KubernetesTenantInformationService.java @@ -30,7 +30,6 @@ import io.opentracing.Span; import io.vertx.core.Future; -@Component public class KubernetesTenantInformationService extends AbstractProjectBasedService implements TenantInformationService { private static final Logger log = LoggerFactory.getLogger(KubernetesTenantInformationService.class); diff --git a/iot/iot-device-registry-base/src/test/java/io/enmasse/iot/registry/device/AbstractCredentialsManagementServiceTest.java b/iot/iot-device-registry-base/src/test/java/io/enmasse/iot/registry/device/AbstractCredentialsManagementServiceTest.java deleted file mode 100644 index 6410838ae97..00000000000 --- a/iot/iot-device-registry-base/src/test/java/io/enmasse/iot/registry/device/AbstractCredentialsManagementServiceTest.java +++ /dev/null @@ -1,96 +0,0 @@ -/* - * Copyright 2019, EnMasse authors. - * License: Apache License 2.0 (see the file LICENSE or http://apache.org/licenses/LICENSE-2.0.html). - */ - -package io.enmasse.iot.registry.device; - -import static org.hamcrest.MatcherAssert.assertThat; -import static org.hamcrest.core.IsNull.notNullValue; - -import java.net.HttpURLConnection; -import java.util.Collections; -import java.util.List; -import java.util.Optional; - -import org.eclipse.hono.deviceregistry.service.credentials.AbstractCredentialsManagementService; -import org.eclipse.hono.deviceregistry.service.device.DeviceKey; -import org.eclipse.hono.service.management.OperationResult; -import org.eclipse.hono.service.management.credentials.CommonCredential; -import org.eclipse.hono.service.management.credentials.PasswordCredential; -import org.hamcrest.core.Is; -import org.junit.jupiter.api.AfterEach; -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Disabled; -import org.junit.jupiter.api.Test; -import org.junit.jupiter.api.extension.ExtendWith; - -import io.enmasse.iot.registry.tenant.NoopTenantInformationService; -import io.opentracing.Span; -import io.opentracing.noop.NoopSpan; -import io.vertx.core.Future; -import io.vertx.core.Vertx; -import io.vertx.junit5.VertxExtension; -import io.vertx.junit5.VertxTestContext; - -/** - * Tests for {@link AbstractCredentialsManagementService}. - * - */ -@ExtendWith(VertxExtension.class) -public class AbstractCredentialsManagementServiceTest { - - private Vertx vertx; - private AbstractCredentialsManagementService service; - - @BeforeEach - public void setup() { - this.vertx = Vertx.factory.vertx(); - this.service = new AbstractCredentialsManagementService(this.vertx) { - - @Override - protected Future> processUpdateCredentials(DeviceKey key, Optional resourceVersion, List credentials, Span span) { - return Future.succeededFuture(); - } - - @Override - protected Future>> processReadCredentials(DeviceKey key, Span span) { - return Future.succeededFuture(); - } - }; - - this.service.setTenantInformationService(new NoopTenantInformationService()); - } - - @AfterEach - public void cleanup() { - vertx.close(); - } - - /** - * Test if the abstract implementation detects the invalid credential information. - *
- * This test checks if the {@link AbstractCredentialsManagementService} detects, and rejects an - * invalid credential set. For that the actual implementation of the service can simply return - * {@code null}. - * - * @throws Exception if something goes wrong. It should not. - */ - @Test - @Disabled("Update credentials throws exception instead of returning bad request") - public void testValidationFailure(final VertxTestContext ctx) throws Exception { - final PasswordCredential invalidCredentials = new PasswordCredential(); - final List credentials = Collections.singletonList(invalidCredentials); - - final Future> f = this.service.updateCredentials("foo", "bar", credentials, Optional.empty(), NoopSpan.INSTANCE) - .setHandler(ctx.succeeding(r -> { - ctx.verify(() -> { - assertThat(r, notNullValue()); - assertThat(r.isError(), Is.is(true)); - assertThat(r.getStatus(), Is.is(HttpURLConnection.HTTP_BAD_REQUEST)); - } - ); - })); - } - -} diff --git a/iot/iot-device-registry-infinispan/pom.xml b/iot/iot-device-registry-infinispan/pom.xml index 63be9dc268c..2ff82f31454 100644 --- a/iot/iot-device-registry-infinispan/pom.xml +++ b/iot/iot-device-registry-infinispan/pom.xml @@ -8,7 +8,7 @@ io.enmasse iot-application-base - 0.32-SNAPSHOT + 1.0-SNAPSHOT ../iot-application-base @@ -86,13 +86,6 @@ vertx-junit5 test - - org.eclipse.hono - hono-service-base - tests - test-jar - test - org.infinispan diff --git a/iot/iot-device-registry-infinispan/src/main/java/io/enmasse/iot/registry/infinispan/Application.java b/iot/iot-device-registry-infinispan/src/main/java/io/enmasse/iot/registry/infinispan/Application.java index 80322abd163..539e3b0bedb 100644 --- a/iot/iot-device-registry-infinispan/src/main/java/io/enmasse/iot/registry/infinispan/Application.java +++ b/iot/iot-device-registry-infinispan/src/main/java/io/enmasse/iot/registry/infinispan/Application.java @@ -21,11 +21,8 @@ import io.vertx.core.Verticle; @ComponentScan("org.eclipse.hono.service.auth") -@ComponentScan("org.eclipse.hono.deviceregistry.service.deviceconnection") @ComponentScan("org.eclipse.hono.service.metric") -@ComponentScan("io.enmasse.iot.registry") -@ComponentScan("io.enmasse.iot.service.base") -@ComponentScan("io.enmasse.iot.infinispan") +@ComponentScan("io.enmasse.iot.registry.infinispan.config") @EnableAutoConfiguration public class Application extends AbstractBaseApplication { diff --git a/iot/iot-device-registry-infinispan/src/main/java/io/enmasse/iot/registry/infinispan/config/AmqpEndpointConfiguration.java b/iot/iot-device-registry-infinispan/src/main/java/io/enmasse/iot/registry/infinispan/config/AmqpEndpointConfiguration.java index 6c7fb7388ac..442cbe49804 100644 --- a/iot/iot-device-registry-infinispan/src/main/java/io/enmasse/iot/registry/infinispan/config/AmqpEndpointConfiguration.java +++ b/iot/iot-device-registry-infinispan/src/main/java/io/enmasse/iot/registry/infinispan/config/AmqpEndpointConfiguration.java @@ -11,7 +11,9 @@ import org.springframework.boot.context.properties.ConfigurationProperties; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; +import org.springframework.context.annotation.Scope; +import io.enmasse.iot.registry.server.DeviceRegistryAmqpServer; import io.enmasse.iot.utils.ConfigBase; @Configuration @@ -29,4 +31,15 @@ public ServiceConfigProperties amqpProperties() { return new ServiceConfigProperties(); } + /** + * Creates a new server for exposing the device registry's AMQP 1.0 based + * endpoints. + * + * @return The server. + */ + @Bean + public DeviceRegistryAmqpServer amqpServer() { + return new DeviceRegistryAmqpServer(); + } + } diff --git a/iot/iot-device-registry-infinispan/src/main/java/io/enmasse/iot/registry/infinispan/config/DeviceConnectionServiceConfiguration.java b/iot/iot-device-registry-infinispan/src/main/java/io/enmasse/iot/registry/infinispan/config/DeviceConnectionServiceConfiguration.java index d9d22b16f48..211ff18020c 100644 --- a/iot/iot-device-registry-infinispan/src/main/java/io/enmasse/iot/registry/infinispan/config/DeviceConnectionServiceConfiguration.java +++ b/iot/iot-device-registry-infinispan/src/main/java/io/enmasse/iot/registry/infinispan/config/DeviceConnectionServiceConfiguration.java @@ -5,37 +5,50 @@ package io.enmasse.iot.registry.infinispan.config; +import static io.enmasse.iot.registry.infinispan.Profiles.PROFILE_DEVICE_CONNECTION; +import static io.vertx.core.Vertx.vertx; + +import org.eclipse.hono.deviceregistry.service.deviceconnection.MapBasedDeviceConnectionService; import org.eclipse.hono.deviceregistry.service.deviceconnection.MapBasedDeviceConnectionsConfigProperties; -import org.eclipse.hono.service.deviceconnection.DeviceConnectionAmqpEndpoint; +import org.eclipse.hono.service.amqp.AmqpEndpoint; +import org.eclipse.hono.service.deviceconnection.DelegatingDeviceConnectionAmqpEndpoint; import org.eclipse.hono.service.deviceconnection.DeviceConnectionService; -import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.autoconfigure.condition.ConditionalOnBean; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; - -import io.vertx.core.Vertx; +import org.springframework.context.annotation.Profile; @Configuration -//@Profile(PROFILE_DEVICE_CONNECTION) -//TODO - enable it again when https://github.com/EnMasseProject/enmasse/issues/4338 is implemented +@Profile(PROFILE_DEVICE_CONNECTION) public class DeviceConnectionServiceConfiguration { /** * Creates a new instance of an AMQP 1.0 protocol handler for Hono's Device Connection API. * + * @param service The service instance to delegate to. * @return The handler. */ - @Autowired @Bean @ConditionalOnBean(DeviceConnectionService.class) - public DeviceConnectionAmqpEndpoint deviceConnectionAmqpEndpoint(final Vertx vertx) { - return new DeviceConnectionAmqpEndpoint(vertx); + public AmqpEndpoint deviceConnectionAmqpEndpoint(final DeviceConnectionService service) { + return new DelegatingDeviceConnectionAmqpEndpoint(vertx(), service); } @Bean - //TODO - remove when https://github.com/EnMasseProject/enmasse/issues/4338 is implemented - public MapBasedDeviceConnectionsConfigProperties deviceConnectionsProperties() { + public MapBasedDeviceConnectionsConfigProperties deviceConnectionProperties() { return new MapBasedDeviceConnectionsConfigProperties(); } + /** + * Creates an instance of the file based service for managing device connection information. + * + * @return The service. + */ + @Bean + public DeviceConnectionService deviceConnectionService() { + final MapBasedDeviceConnectionService service = new MapBasedDeviceConnectionService(); + service.setConfig(deviceConnectionProperties()); + return service; + } + } diff --git a/iot/iot-device-registry-infinispan/src/main/java/io/enmasse/iot/registry/infinispan/config/DeviceServiceConfiguration.java b/iot/iot-device-registry-infinispan/src/main/java/io/enmasse/iot/registry/infinispan/config/DeviceServiceConfiguration.java index 35818d7aa47..c94064aa7b3 100644 --- a/iot/iot-device-registry-infinispan/src/main/java/io/enmasse/iot/registry/infinispan/config/DeviceServiceConfiguration.java +++ b/iot/iot-device-registry-infinispan/src/main/java/io/enmasse/iot/registry/infinispan/config/DeviceServiceConfiguration.java @@ -5,80 +5,143 @@ package io.enmasse.iot.registry.infinispan.config; +import io.enmasse.iot.infinispan.cache.DeviceManagementCacheProvider; +import io.enmasse.iot.infinispan.config.InfinispanProperties; import static io.enmasse.iot.registry.infinispan.Profiles.PROFILE_DEVICE_REGISTRY; +import io.enmasse.iot.registry.infinispan.device.impl.CredentialsManagementServiceImpl; +import io.enmasse.iot.registry.infinispan.device.impl.CredentialsServiceImpl; +import io.enmasse.iot.registry.infinispan.device.impl.DeviceManagementServiceImpl; +import io.enmasse.iot.registry.infinispan.device.impl.RegistrationServiceImpl; +import io.enmasse.iot.registry.tenant.KubernetesTenantInformationService; +import io.enmasse.iot.registry.util.DeviceRegistryTokenAuthHandler; +import io.enmasse.iot.registry.util.DeviceRegistryTokenAuthProvider; +import io.opentracing.Tracer; +import io.vertx.core.Vertx; +import static io.vertx.core.Vertx.vertx; +import io.vertx.ext.auth.AuthProvider; +import io.vertx.ext.web.handler.AuthHandler; import org.eclipse.hono.auth.HonoPasswordEncoder; import org.eclipse.hono.auth.SpringBasedHonoPasswordEncoder; -import org.eclipse.hono.service.credentials.CredentialsAmqpEndpoint; +import org.eclipse.hono.deviceregistry.service.tenant.TenantInformationService; +import org.eclipse.hono.service.amqp.AmqpEndpoint; import org.eclipse.hono.service.credentials.CredentialsService; -import org.eclipse.hono.service.management.credentials.CredentialsManagementHttpEndpoint; +import org.eclipse.hono.service.credentials.DelegatingCredentialsAmqpEndpoint; +import org.eclipse.hono.service.http.HttpEndpoint; import org.eclipse.hono.service.management.credentials.CredentialsManagementService; -import org.eclipse.hono.service.management.device.DeviceManagementHttpEndpoint; +import org.eclipse.hono.service.management.credentials.DelegatingCredentialsManagementHttpEndpoint; +import org.eclipse.hono.service.management.device.DelegatingDeviceManagementHttpEndpoint; import org.eclipse.hono.service.management.device.DeviceManagementService; -import org.eclipse.hono.service.registration.RegistrationAmqpEndpoint; +import org.eclipse.hono.service.registration.DelegatingRegistrationAmqpEndpoint; import org.eclipse.hono.service.registration.RegistrationService; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.autoconfigure.condition.ConditionalOnBean; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Profile; - -import io.enmasse.iot.registry.util.DeviceRegistryTokenAuthHandler; -import io.enmasse.iot.registry.util.DeviceRegistryTokenAuthProvider; -import io.opentracing.Tracer; -import io.vertx.core.Vertx; -import io.vertx.ext.auth.AuthProvider; -import io.vertx.ext.web.handler.AuthHandler; +import org.springframework.context.annotation.Scope; @Configuration +@Profile({PROFILE_DEVICE_REGISTRY}) public class DeviceServiceConfiguration { + @Bean + public DeviceManagementCacheProvider deviceManagementCacheProvider(final InfinispanProperties infinispanProperties) { + return new DeviceManagementCacheProvider(infinispanProperties); + } + + /** + * Exposes the Infinispan registration service as a Spring bean. + * + * @return The Infinispan registration service. + */ + @Bean + public RegistrationService registrationService(final DeviceManagementCacheProvider cacheProvider) { + return new RegistrationServiceImpl(cacheProvider); + } + + /** + * Exposes the Infinispan credentials service as a Spring bean. + * + * @return The Infinispan credentials service. + */ + @Bean + public CredentialsService credentialsService(final DeviceManagementCacheProvider cacheProvider, final DeviceServiceProperties properties) { + return new CredentialsServiceImpl(cacheProvider, properties); + } + + /** + * Exposes Infinispan device management service as a Spring bean + * + * @param cacheProvider The cache provider. + * @return The Infinispan device management service + */ + @Bean + public DeviceManagementService deviceManagementService(final DeviceManagementCacheProvider cacheProvider) { + return new DeviceManagementServiceImpl(cacheProvider); + } + + /** + * Exposes Infinispan credential management service as a Spring bean + * + * @param cacheProvider The cache provider. + * @return The Infinispan credential management service + */ + @Bean + public CredentialsManagementService credentialsManagementService(final Vertx vertx, HonoPasswordEncoder passwordEncoder, final DeviceManagementCacheProvider cacheProvider) { + return new CredentialsManagementServiceImpl(vertx, passwordEncoder, cacheProvider); + } + /** * Creates a new instance of an AMQP 1.0 protocol handler for Hono's Device Registration API. * + * @param service The service instance to delegate to. * @return The handler. */ - @Autowired @Bean @ConditionalOnBean(RegistrationService.class) - public RegistrationAmqpEndpoint registrationAmqpEndpoint(final Vertx vertx) { - return new RegistrationAmqpEndpoint(vertx); + public AmqpEndpoint registrationAmqpEndpoint(final RegistrationService service) { + return new DelegatingRegistrationAmqpEndpoint(vertx(), service); } /** * Creates a new instance of an AMQP 1.0 protocol handler for Hono's Credentials API. * + * @param service The service instance to delegate to. * @return The handler. */ - @Autowired @Bean @ConditionalOnBean(CredentialsService.class) - public CredentialsAmqpEndpoint credentialsAmqpEndpoint(final Vertx vertx) { - return new CredentialsAmqpEndpoint(vertx); + public AmqpEndpoint credentialsAmqpEndpoint(final CredentialsService service) { + return new DelegatingCredentialsAmqpEndpoint(vertx(), service); } /** - * Creates a new instance of an HTTP protocol handler for Hono's Device Registration API. + * Creates a new instance of an HTTP protocol handler for the devices resources + * of Hono's Device Registry Management API's. * + * @param vertx The vert.x instance to run on. + * @param service The service instance to delegate to. * @return The handler. */ - @Autowired @Bean @ConditionalOnBean(DeviceManagementService.class) - public DeviceManagementHttpEndpoint registrationHttpEndpoint(final Vertx vertx) { - return new DeviceManagementHttpEndpoint(vertx); + public HttpEndpoint deviceHttpEndpoint(final Vertx vertx, final DeviceManagementService service) { + return new DelegatingDeviceManagementHttpEndpoint(vertx, service); } /** - * Creates a new instance of an HTTP protocol handler for Hono's Credentials API. + * Creates a new instance of an HTTP protocol handler for the credentials resources + * of Hono's Device Registry Management API's. * + * @param vertx The vert.x instance to run on. + * @param service The service instance to delegate to. * @return The handler. */ - @Autowired @Bean @ConditionalOnBean(CredentialsManagementService.class) - public CredentialsManagementHttpEndpoint credentialsHttpEndpoint(final Vertx vertx) { - return new CredentialsManagementHttpEndpoint(vertx); + public HttpEndpoint credentialsHttpEndpoint(final Vertx vertx, final CredentialsManagementService service) { + return new DelegatingCredentialsManagementHttpEndpoint(vertx, service); } /** @@ -87,13 +150,11 @@ public CredentialsManagementHttpEndpoint credentialsHttpEndpoint(final Vertx ver * @return The handler. */ @Bean - @Autowired public AuthHandler authHandler(final Tracer tracer, final RestEndpointConfiguration restEndpointConfiguration) { return new DeviceRegistryTokenAuthHandler(tracer, authProvider(tracer, restEndpointConfiguration)); } @Bean - @Autowired public AuthProvider authProvider(final Tracer tracer, final RestEndpointConfiguration restEndpointConfiguration) { return new DeviceRegistryTokenAuthProvider(tracer, restEndpointConfiguration.getAuthTokenCacheExpiration()); } @@ -105,10 +166,14 @@ public AuthProvider authProvider(final Tracer tracer, final RestEndpointConfigur * @return The encoder. */ @Bean - @Autowired @Profile(PROFILE_DEVICE_REGISTRY) public HonoPasswordEncoder passwordEncoder(DeviceServiceProperties deviceServiceProperties) { return new SpringBasedHonoPasswordEncoder(deviceServiceProperties.getMaxBcryptIterations()); } + @Bean + public KubernetesTenantInformationService tenantInformationService() { + return new KubernetesTenantInformationService(); + } + } diff --git a/iot/iot-device-registry-infinispan/src/main/java/io/enmasse/iot/registry/infinispan/config/RestEndpointConfiguration.java b/iot/iot-device-registry-infinispan/src/main/java/io/enmasse/iot/registry/infinispan/config/RestEndpointConfiguration.java index 5919a62071a..38877ffe7b6 100644 --- a/iot/iot-device-registry-infinispan/src/main/java/io/enmasse/iot/registry/infinispan/config/RestEndpointConfiguration.java +++ b/iot/iot-device-registry-infinispan/src/main/java/io/enmasse/iot/registry/infinispan/config/RestEndpointConfiguration.java @@ -9,6 +9,7 @@ import java.time.temporal.ChronoUnit; import org.eclipse.hono.config.ServiceConfigProperties; +import org.eclipse.hono.deviceregistry.server.DeviceRegistryHttpServer; import org.eclipse.hono.util.Constants; import org.springframework.beans.factory.annotation.Qualifier; import org.springframework.boot.context.properties.ConfigurationProperties; @@ -47,4 +48,15 @@ public Duration getAuthTokenCacheExpiration() { public void setAuthTokenCacheExpiration(Duration authTokenCacheExpiration) { this.authTokenCacheExpiration = authTokenCacheExpiration; } + + /** + * Creates a new server for exposing the device registry's AMQP 1.0 based + * endpoints. + * + * @return The server. + */ + @Bean + public DeviceRegistryHttpServer httpServer() { + return new DeviceRegistryHttpServer(); + } } diff --git a/iot/iot-device-registry-infinispan/src/main/java/io/enmasse/iot/registry/infinispan/config/TenantServiceConfiguration.java b/iot/iot-device-registry-infinispan/src/main/java/io/enmasse/iot/registry/infinispan/config/TenantServiceConfiguration.java deleted file mode 100644 index 27938742e97..00000000000 --- a/iot/iot-device-registry-infinispan/src/main/java/io/enmasse/iot/registry/infinispan/config/TenantServiceConfiguration.java +++ /dev/null @@ -1,46 +0,0 @@ -/* - * Copyright 2019, EnMasse authors. - * License: Apache License 2.0 (see the file LICENSE or http://apache.org/licenses/LICENSE-2.0.html). - */ - -package io.enmasse.iot.registry.infinispan.config; - -import org.eclipse.hono.service.management.tenant.TenantManagementHttpEndpoint; -import org.eclipse.hono.service.management.tenant.TenantManagementService; -import org.eclipse.hono.service.tenant.TenantAmqpEndpoint; -import org.eclipse.hono.service.tenant.TenantService; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.boot.autoconfigure.condition.ConditionalOnBean; -import org.springframework.context.annotation.Bean; -import org.springframework.context.annotation.Configuration; - -import io.vertx.core.Vertx; - -@Configuration -public class TenantServiceConfiguration { - - /** - * Creates a new instance of an AMQP 1.0 protocol handler for Hono's Tenant API. - * - * @return The handler. - */ - @Bean - @ConditionalOnBean(TenantService.class) - @Autowired - public TenantAmqpEndpoint tenantAmqpEndpoint(final Vertx vertx) { - return new TenantAmqpEndpoint(vertx); - } - - /** - * Creates a new instance of an HTTP protocol handler for Hono's Tenant API. - * - * @return The handler. - */ - @Bean - @ConditionalOnBean(TenantManagementService.class) - @Autowired - public TenantManagementHttpEndpoint tenantHttpEndpoint(final Vertx vertx) { - return new TenantManagementHttpEndpoint(vertx); - } - -} diff --git a/iot/iot-device-registry-infinispan/src/main/java/io/enmasse/iot/registry/infinispan/device/impl/CredentialsManagementServiceImpl.java b/iot/iot-device-registry-infinispan/src/main/java/io/enmasse/iot/registry/infinispan/device/impl/CredentialsManagementServiceImpl.java index d58ec363f6c..0021b1b94a8 100644 --- a/iot/iot-device-registry-infinispan/src/main/java/io/enmasse/iot/registry/infinispan/device/impl/CredentialsManagementServiceImpl.java +++ b/iot/iot-device-registry-infinispan/src/main/java/io/enmasse/iot/registry/infinispan/device/impl/CredentialsManagementServiceImpl.java @@ -30,6 +30,7 @@ import java.util.Set; import java.util.concurrent.CompletableFuture; +import org.eclipse.hono.auth.HonoPasswordEncoder; import org.eclipse.hono.client.ServiceInvocationException; import org.eclipse.hono.deviceregistry.service.credentials.AbstractCredentialsManagementService; import org.eclipse.hono.deviceregistry.service.device.DeviceKey; @@ -61,8 +62,8 @@ public class CredentialsManagementServiceImpl extends AbstractCredentialsManagem private final RemoteCache managementCache; @Autowired - public CredentialsManagementServiceImpl(final Vertx vertx, final DeviceManagementCacheProvider cacheProvider) { - super(vertx); + public CredentialsManagementServiceImpl(final Vertx vertx, HonoPasswordEncoder passwordEncoder, final DeviceManagementCacheProvider cacheProvider) { + super(vertx, passwordEncoder); this.managementCache = cacheProvider .getDeviceManagementCache() .orElseThrow(() -> new NoSuchElementException("Missing device management cache")); diff --git a/iot/iot-device-registry-jdbc/pom.xml b/iot/iot-device-registry-jdbc/pom.xml index 4ebcf4916e5..8842308e2a6 100644 --- a/iot/iot-device-registry-jdbc/pom.xml +++ b/iot/iot-device-registry-jdbc/pom.xml @@ -8,7 +8,7 @@ io.enmasse iot-application-base - 0.32-SNAPSHOT + 1.0-SNAPSHOT ../iot-application-base @@ -91,13 +91,6 @@ vertx-junit5 test - - org.eclipse.hono - hono-service-base - tests - test-jar - test - diff --git a/iot/iot-device-registry-jdbc/src/main/java/io/enmasse/iot/registry/jdbc/Application.java b/iot/iot-device-registry-jdbc/src/main/java/io/enmasse/iot/registry/jdbc/Application.java index 14965fcff3d..0d9506abdb7 100644 --- a/iot/iot-device-registry-jdbc/src/main/java/io/enmasse/iot/registry/jdbc/Application.java +++ b/iot/iot-device-registry-jdbc/src/main/java/io/enmasse/iot/registry/jdbc/Application.java @@ -22,8 +22,7 @@ @ComponentScan("org.eclipse.hono.service.auth") @ComponentScan("org.eclipse.hono.service.metric") -@ComponentScan("io.enmasse.iot.registry") -@ComponentScan("io.enmasse.iot.service.base") +@ComponentScan("io.enmasse.iot.registry.jdbc.config") @EnableAutoConfiguration public class Application extends AbstractBaseApplication { diff --git a/iot/iot-device-registry-jdbc/src/main/java/io/enmasse/iot/registry/jdbc/config/AmqpEndpointConfiguration.java b/iot/iot-device-registry-jdbc/src/main/java/io/enmasse/iot/registry/jdbc/config/AmqpEndpointConfiguration.java index 2d47c638841..1cd61fcd1e4 100644 --- a/iot/iot-device-registry-jdbc/src/main/java/io/enmasse/iot/registry/jdbc/config/AmqpEndpointConfiguration.java +++ b/iot/iot-device-registry-jdbc/src/main/java/io/enmasse/iot/registry/jdbc/config/AmqpEndpointConfiguration.java @@ -11,7 +11,11 @@ import org.springframework.boot.context.properties.ConfigurationProperties; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; +import org.springframework.context.annotation.Profile; +import static io.enmasse.iot.registry.jdbc.Profiles.PROFILE_DEVICE_CONNECTION; +import static io.enmasse.iot.registry.jdbc.Profiles.PROFILE_REGISTRY_ADAPTER; +import io.enmasse.iot.registry.server.DeviceRegistryAmqpServer; import io.enmasse.iot.utils.ConfigBase; @Configuration @@ -29,4 +33,16 @@ public ServiceConfigProperties amqpProperties() { return new ServiceConfigProperties(); } + /** + * Creates a new server for exposing the device registry's AMQP 1.0 based + * endpoints. + * + * @return The server. + */ + @Bean + @Profile({PROFILE_REGISTRY_ADAPTER, PROFILE_DEVICE_CONNECTION}) + public DeviceRegistryAmqpServer amqpServer() { + return new DeviceRegistryAmqpServer(); + } + } diff --git a/iot/iot-device-registry-jdbc/src/main/java/io/enmasse/iot/registry/jdbc/config/DeviceConnectionServiceConfiguration.java b/iot/iot-device-registry-jdbc/src/main/java/io/enmasse/iot/registry/jdbc/config/DeviceConnectionServiceConfiguration.java index e93d0b2ef4a..d5d938b0e51 100644 --- a/iot/iot-device-registry-jdbc/src/main/java/io/enmasse/iot/registry/jdbc/config/DeviceConnectionServiceConfiguration.java +++ b/iot/iot-device-registry-jdbc/src/main/java/io/enmasse/iot/registry/jdbc/config/DeviceConnectionServiceConfiguration.java @@ -5,33 +5,44 @@ package io.enmasse.iot.registry.jdbc.config; +import io.enmasse.iot.jdbc.store.devcon.Store; import static io.enmasse.iot.registry.jdbc.Profiles.PROFILE_DEVICE_CONNECTION; +import io.enmasse.iot.registry.jdbc.devcon.impl.DeviceConnectionServiceImpl; +import static io.vertx.core.Vertx.vertx; -import org.eclipse.hono.deviceregistry.service.deviceconnection.MapBasedDeviceConnectionsConfigProperties; -import org.eclipse.hono.service.deviceconnection.DeviceConnectionAmqpEndpoint; +import org.eclipse.hono.service.amqp.AmqpEndpoint; +import org.eclipse.hono.service.deviceconnection.DelegatingDeviceConnectionAmqpEndpoint; import org.eclipse.hono.service.deviceconnection.DeviceConnectionService; -import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.autoconfigure.condition.ConditionalOnBean; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Profile; -import io.vertx.core.Vertx; - @Configuration @Profile(PROFILE_DEVICE_CONNECTION) public class DeviceConnectionServiceConfiguration { + /** + * Creates and instance of the JDBC device connection service. + * + * @param store The device information store. + * @return The JDBC device connection service. + */ + @Bean + public DeviceConnectionService deviceConnectionService(Store store) { + return new DeviceConnectionServiceImpl(store); + } + /** * Creates a new instance of an AMQP 1.0 protocol handler for Hono's Device Connection API. * + * @param service The service instance to delegate to. * @return The handler. */ - @Autowired @Bean @ConditionalOnBean(DeviceConnectionService.class) - public DeviceConnectionAmqpEndpoint deviceConnectionAmqpEndpoint(final Vertx vertx) { - return new DeviceConnectionAmqpEndpoint(vertx); + public AmqpEndpoint deviceConnectionAmqpEndpoint(final DeviceConnectionService service) { + return new DelegatingDeviceConnectionAmqpEndpoint(vertx(), service); } } diff --git a/iot/iot-device-registry-jdbc/src/main/java/io/enmasse/iot/registry/jdbc/config/DeviceServiceConfiguration.java b/iot/iot-device-registry-jdbc/src/main/java/io/enmasse/iot/registry/jdbc/config/DeviceServiceConfiguration.java index 4de3c61acc1..63115153a44 100644 --- a/iot/iot-device-registry-jdbc/src/main/java/io/enmasse/iot/registry/jdbc/config/DeviceServiceConfiguration.java +++ b/iot/iot-device-registry-jdbc/src/main/java/io/enmasse/iot/registry/jdbc/config/DeviceServiceConfiguration.java @@ -5,18 +5,34 @@ package io.enmasse.iot.registry.jdbc.config; +import io.enmasse.iot.jdbc.store.device.AbstractDeviceAdapterStore; +import io.enmasse.iot.jdbc.store.device.AbstractDeviceManagementStore; import static io.enmasse.iot.registry.jdbc.Profiles.PROFILE_REGISTRY_ADAPTER; import static io.enmasse.iot.registry.jdbc.Profiles.PROFILE_REGISTRY_MANAGEMENT; +import io.enmasse.iot.registry.jdbc.device.impl.CredentialsManagementServiceImpl; +import io.enmasse.iot.registry.jdbc.device.impl.CredentialsServiceImpl; +import io.enmasse.iot.registry.jdbc.device.impl.DeviceManagementServiceImpl; +import io.enmasse.iot.registry.jdbc.device.impl.RegistrationServiceImpl; +import io.enmasse.iot.registry.tenant.KubernetesTenantInformationService; +import io.enmasse.iot.registry.util.DeviceRegistryTokenAuthHandler; +import io.enmasse.iot.registry.util.DeviceRegistryTokenAuthProvider; +import io.opentracing.Tracer; +import io.vertx.core.Vertx; +import static io.vertx.core.Vertx.vertx; +import io.vertx.ext.auth.AuthProvider; +import io.vertx.ext.web.handler.AuthHandler; import org.eclipse.hono.auth.HonoPasswordEncoder; import org.eclipse.hono.auth.SpringBasedHonoPasswordEncoder; -import org.eclipse.hono.service.credentials.CredentialsAmqpEndpoint; +import org.eclipse.hono.service.amqp.AmqpEndpoint; import org.eclipse.hono.service.credentials.CredentialsService; -import org.eclipse.hono.service.management.credentials.CredentialsManagementHttpEndpoint; +import org.eclipse.hono.service.credentials.DelegatingCredentialsAmqpEndpoint; +import org.eclipse.hono.service.http.HttpEndpoint; import org.eclipse.hono.service.management.credentials.CredentialsManagementService; -import org.eclipse.hono.service.management.device.DeviceManagementHttpEndpoint; +import org.eclipse.hono.service.management.credentials.DelegatingCredentialsManagementHttpEndpoint; +import org.eclipse.hono.service.management.device.DelegatingDeviceManagementHttpEndpoint; import org.eclipse.hono.service.management.device.DeviceManagementService; -import org.eclipse.hono.service.registration.RegistrationAmqpEndpoint; +import org.eclipse.hono.service.registration.DelegatingRegistrationAmqpEndpoint; import org.eclipse.hono.service.registration.RegistrationService; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.autoconfigure.condition.ConditionalOnBean; @@ -24,63 +40,111 @@ import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Profile; -import io.enmasse.iot.registry.util.DeviceRegistryTokenAuthHandler; -import io.enmasse.iot.registry.util.DeviceRegistryTokenAuthProvider; -import io.opentracing.Tracer; -import io.vertx.core.Vertx; -import io.vertx.ext.auth.AuthProvider; -import io.vertx.ext.web.handler.AuthHandler; - @Configuration @Profile({PROFILE_REGISTRY_ADAPTER, PROFILE_REGISTRY_MANAGEMENT}) public class DeviceServiceConfiguration { + /** + * Exposes the JDBC registration service as a Spring bean. + * + * @param store The JDBC store. + * @return The JDBC registration service. + */ + @Bean + @Profile(PROFILE_REGISTRY_ADAPTER) + public RegistrationService registrationService(AbstractDeviceAdapterStore store) { + return new RegistrationServiceImpl(store); + } + + /** + * Exposes the JDBC credentials service as a Spring bean. + * + * @param store The JDBC store. + * @return The JDBC credentials service. + */ + @Bean + @Profile(PROFILE_REGISTRY_ADAPTER) + public CredentialsService credentialsService(AbstractDeviceAdapterStore store) { + return new CredentialsServiceImpl(store); + } + + /** + * Exposes JDBC device management service as a Spring bean + * + * @param store The JDBC store. + * @param properties The service properties. + * @return The JDBC device management service + */ + @Bean + @Profile(PROFILE_REGISTRY_MANAGEMENT) + public DeviceManagementService deviceManagementService(final AbstractDeviceManagementStore store, final DeviceServiceProperties properties) { + return new DeviceManagementServiceImpl(store, properties); + } + + /** + * Exposes JDBC credential management service as a Spring bean + * + * @param vertx The Verx instance. + * @param store The JDBC store + * @param properties The service properties. + * @return The JDBC credential management service + */ + @Bean + @Profile(PROFILE_REGISTRY_MANAGEMENT) + public CredentialsManagementService credentialsManagementService(final Vertx vertx, HonoPasswordEncoder passwordEncoder, final AbstractDeviceManagementStore store, final DeviceServiceProperties properties) { + return new CredentialsManagementServiceImpl(vertx, passwordEncoder, store, properties); + } + /** * Creates a new instance of an AMQP 1.0 protocol handler for Hono's Device Registration API. * + * @param service The service instance to delegate to. * @return The handler. */ - @Autowired @Bean @ConditionalOnBean(RegistrationService.class) - public RegistrationAmqpEndpoint registrationAmqpEndpoint(final Vertx vertx) { - return new RegistrationAmqpEndpoint(vertx); + public AmqpEndpoint registrationAmqpEndpoint(final RegistrationService service) { + return new DelegatingRegistrationAmqpEndpoint(vertx(), service); } /** * Creates a new instance of an AMQP 1.0 protocol handler for Hono's Credentials API. * + * @param service The service instance to delegate to. * @return The handler. */ - @Autowired @Bean @ConditionalOnBean(CredentialsService.class) - public CredentialsAmqpEndpoint credentialsAmqpEndpoint(final Vertx vertx) { - return new CredentialsAmqpEndpoint(vertx); + public AmqpEndpoint credentialsAmqpEndpoint(final CredentialsService service) { + return new DelegatingCredentialsAmqpEndpoint(vertx(), service); } /** - * Creates a new instance of an HTTP protocol handler for Hono's Device Registration API. + * Creates a new instance of an HTTP protocol handler for the devices resources + * of Hono's Device Registry Management API's. * + * @param vertx The vert.x instance to run on. + * @param service The service instance to delegate to. * @return The handler. */ - @Autowired @Bean @ConditionalOnBean(DeviceManagementService.class) - public DeviceManagementHttpEndpoint registrationHttpEndpoint(final Vertx vertx) { - return new DeviceManagementHttpEndpoint(vertx); + public HttpEndpoint deviceHttpEndpoint(final Vertx vertx, final DeviceManagementService service) { + return new DelegatingDeviceManagementHttpEndpoint(vertx, service); } /** - * Creates a new instance of an HTTP protocol handler for Hono's Credentials API. + * Creates a new instance of an HTTP protocol handler for the credentials resources + * of Hono's Device Registry Management API's. * + * @param vertx The vert.x instance to run on. + * @param service The service instance to delegate to. * @return The handler. */ - @Autowired @Bean @ConditionalOnBean(CredentialsManagementService.class) - public CredentialsManagementHttpEndpoint credentialsHttpEndpoint(final Vertx vertx) { - return new CredentialsManagementHttpEndpoint(vertx); + public HttpEndpoint credentialsHttpEndpoint(final Vertx vertx, final CredentialsManagementService service) { + return new DelegatingCredentialsManagementHttpEndpoint(vertx, service); } /** @@ -113,4 +177,9 @@ public HonoPasswordEncoder passwordEncoder(final DeviceServiceProperties deviceS return new SpringBasedHonoPasswordEncoder(deviceServiceProperties.getMaxBcryptIterations()); } + @Bean + public KubernetesTenantInformationService tenantInformationService() { + return new KubernetesTenantInformationService(); + } + } diff --git a/iot/iot-device-registry-jdbc/src/main/java/io/enmasse/iot/registry/jdbc/config/RestEndpointConfiguration.java b/iot/iot-device-registry-jdbc/src/main/java/io/enmasse/iot/registry/jdbc/config/RestEndpointConfiguration.java index 517528ce8cd..409fac722e9 100644 --- a/iot/iot-device-registry-jdbc/src/main/java/io/enmasse/iot/registry/jdbc/config/RestEndpointConfiguration.java +++ b/iot/iot-device-registry-jdbc/src/main/java/io/enmasse/iot/registry/jdbc/config/RestEndpointConfiguration.java @@ -9,13 +9,16 @@ import java.time.temporal.ChronoUnit; import org.eclipse.hono.config.ServiceConfigProperties; +import org.eclipse.hono.deviceregistry.server.DeviceRegistryHttpServer; import org.eclipse.hono.util.Constants; import org.springframework.beans.factory.annotation.Qualifier; import org.springframework.boot.context.properties.ConfigurationProperties; import org.springframework.boot.convert.DurationUnit; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; +import org.springframework.context.annotation.Profile; +import static io.enmasse.iot.registry.jdbc.Profiles.PROFILE_REGISTRY_MANAGEMENT; import io.enmasse.iot.utils.ConfigBase; @Configuration @@ -47,4 +50,16 @@ public Duration getAuthTokenCacheExpiration() { public void setAuthTokenCacheExpiration(Duration authTokenCacheExpiration) { this.authTokenCacheExpiration = authTokenCacheExpiration; } + + /** + * Creates a new server for exposing the device registry's AMQP 1.0 based + * endpoints. + * + * @return The server. + */ + @Bean + @Profile(PROFILE_REGISTRY_MANAGEMENT) + public DeviceRegistryHttpServer httpServer() { + return new DeviceRegistryHttpServer(); + } } diff --git a/iot/iot-device-registry-jdbc/src/main/java/io/enmasse/iot/registry/jdbc/config/TenantServiceConfiguration.java b/iot/iot-device-registry-jdbc/src/main/java/io/enmasse/iot/registry/jdbc/config/TenantServiceConfiguration.java deleted file mode 100644 index eea47fbddf2..00000000000 --- a/iot/iot-device-registry-jdbc/src/main/java/io/enmasse/iot/registry/jdbc/config/TenantServiceConfiguration.java +++ /dev/null @@ -1,46 +0,0 @@ -/* - * Copyright 2019, EnMasse authors. - * License: Apache License 2.0 (see the file LICENSE or http://apache.org/licenses/LICENSE-2.0.html). - */ - -package io.enmasse.iot.registry.jdbc.config; - -import org.eclipse.hono.service.management.tenant.TenantManagementHttpEndpoint; -import org.eclipse.hono.service.management.tenant.TenantManagementService; -import org.eclipse.hono.service.tenant.TenantAmqpEndpoint; -import org.eclipse.hono.service.tenant.TenantService; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.boot.autoconfigure.condition.ConditionalOnBean; -import org.springframework.context.annotation.Bean; -import org.springframework.context.annotation.Configuration; - -import io.vertx.core.Vertx; - -@Configuration -public class TenantServiceConfiguration { - - /** - * Creates a new instance of an AMQP 1.0 protocol handler for Hono's Tenant API. - * - * @return The handler. - */ - @Autowired - @Bean - @ConditionalOnBean(TenantService.class) - public TenantAmqpEndpoint tenantAmqpEndpoint(final Vertx vertx) { - return new TenantAmqpEndpoint(vertx); - } - - /** - * Creates a new instance of an HTTP protocol handler for Hono's Tenant API. - * - * @return The handler. - */ - @Autowired - @Bean - @ConditionalOnBean(TenantManagementService.class) - public TenantManagementHttpEndpoint tenantHttpEndpoint(final Vertx vertx) { - return new TenantManagementHttpEndpoint(vertx); - } - -} diff --git a/iot/iot-device-registry-jdbc/src/main/java/io/enmasse/iot/registry/jdbc/devcon/impl/AutowiredDeviceConnectionAdapter.java b/iot/iot-device-registry-jdbc/src/main/java/io/enmasse/iot/registry/jdbc/devcon/impl/AutowiredDeviceConnectionAdapter.java deleted file mode 100644 index 713666f631b..00000000000 --- a/iot/iot-device-registry-jdbc/src/main/java/io/enmasse/iot/registry/jdbc/devcon/impl/AutowiredDeviceConnectionAdapter.java +++ /dev/null @@ -1,35 +0,0 @@ -/* - * Copyright 2019-2020, EnMasse authors. - * License: Apache License 2.0 (see the file LICENSE or http://apache.org/licenses/LICENSE-2.0.html). - */ -package io.enmasse.iot.registry.jdbc.devcon.impl; - -import org.eclipse.hono.service.deviceconnection.DeviceConnectionService; -import org.eclipse.hono.service.deviceconnection.EventBusDeviceConnectionAdapter; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.boot.autoconfigure.condition.ConditionalOnBean; -import org.springframework.stereotype.Component; - -/** - * A default event bus based service implementation of the {@link DeviceConnectionService}. - *

- * This wires up the actual service instance with the mapping to the event bus implementation. It is intended to be used - * in a Spring Boot environment. - */ -@Component -@ConditionalOnBean(DeviceConnectionService.class) -public final class AutowiredDeviceConnectionAdapter extends EventBusDeviceConnectionAdapter { - - private DeviceConnectionService service; - - @Autowired - public void setService(final DeviceConnectionService service) { - this.service = service; - } - - @Override - protected DeviceConnectionService getService() { - return this.service; - } - -} diff --git a/iot/iot-device-registry-jdbc/src/main/java/io/enmasse/iot/registry/jdbc/devcon/impl/DeviceConnectionServiceImpl.java b/iot/iot-device-registry-jdbc/src/main/java/io/enmasse/iot/registry/jdbc/devcon/impl/DeviceConnectionServiceImpl.java index d101a0dac9e..7ad0b22cab3 100644 --- a/iot/iot-device-registry-jdbc/src/main/java/io/enmasse/iot/registry/jdbc/devcon/impl/DeviceConnectionServiceImpl.java +++ b/iot/iot-device-registry-jdbc/src/main/java/io/enmasse/iot/registry/jdbc/devcon/impl/DeviceConnectionServiceImpl.java @@ -8,6 +8,7 @@ import static io.enmasse.iot.registry.jdbc.Profiles.PROFILE_DEVICE_CONNECTION; import java.net.HttpURLConnection; +import java.time.Duration; import java.util.List; import org.eclipse.hono.service.deviceconnection.DeviceConnectionService; @@ -27,14 +28,11 @@ /** * A {@link DeviceConnectionService} that use an JDBC as a backend service. */ -@Component -@Profile(PROFILE_DEVICE_CONNECTION) public class DeviceConnectionServiceImpl extends AbstractDeviceConnectionService { private Store store; - @Autowired - protected DeviceConnectionServiceImpl(final Store store) { + public DeviceConnectionServiceImpl(final Store store) { this.store = store; } @@ -65,7 +63,8 @@ protected Future processSetLastKnownGatewayForDevice(fin } @Override - protected Future processSetCommandHandlingAdapterInstance(DeviceConnectionKey key, String adapterInstanceId, Span span) { + protected Future processSetCommandHandlingAdapterInstance(DeviceConnectionKey key, String adapterInstanceId, Duration lifespan, Span span) { + //TODO handle lifespan return this.store .processSetCommandHandlingAdapterInstance(key, adapterInstanceId, span.context()) .map(r -> DeviceConnectionResult.from(HttpURLConnection.HTTP_NO_CONTENT)); diff --git a/iot/iot-device-registry-jdbc/src/main/java/io/enmasse/iot/registry/jdbc/device/impl/CredentialsManagementServiceImpl.java b/iot/iot-device-registry-jdbc/src/main/java/io/enmasse/iot/registry/jdbc/device/impl/CredentialsManagementServiceImpl.java index 02c02e44568..79b35eb0b84 100644 --- a/iot/iot-device-registry-jdbc/src/main/java/io/enmasse/iot/registry/jdbc/device/impl/CredentialsManagementServiceImpl.java +++ b/iot/iot-device-registry-jdbc/src/main/java/io/enmasse/iot/registry/jdbc/device/impl/CredentialsManagementServiceImpl.java @@ -39,8 +39,8 @@ public class CredentialsManagementServiceImpl extends AbstractCredentialsManagem private final Optional ttl; @Autowired - public CredentialsManagementServiceImpl(final Vertx vertx, final AbstractDeviceManagementStore store, final DeviceServiceProperties properties) { - super(vertx); + public CredentialsManagementServiceImpl(final Vertx vertx, HonoPasswordEncoder passwordEncoder, final AbstractDeviceManagementStore store, final DeviceServiceProperties properties) { + super(vertx, passwordEncoder); this.store = store; this.ttl = Optional.of(CacheDirective.maxAgeDirective(properties.getCredentialsTtl().toSeconds())); } diff --git a/iot/iot-http-adapter/pom.xml b/iot/iot-http-adapter/pom.xml index 850f0558000..8e0c17ba3c3 100644 --- a/iot/iot-http-adapter/pom.xml +++ b/iot/iot-http-adapter/pom.xml @@ -8,7 +8,7 @@ io.enmasse iot-application-base - 0.32-SNAPSHOT + 1.0-SNAPSHOT ../iot-application-base diff --git a/iot/iot-infinispan-base/pom.xml b/iot/iot-infinispan-base/pom.xml index b034d947694..d81718ef78e 100644 --- a/iot/iot-infinispan-base/pom.xml +++ b/iot/iot-infinispan-base/pom.xml @@ -8,7 +8,7 @@ io.enmasse iot - 0.32-SNAPSHOT + 1.0-SNAPSHOT iot-infinispan-base diff --git a/iot/iot-jdbc-base/pom.xml b/iot/iot-jdbc-base/pom.xml index c9c188464ec..fa5713ed71b 100644 --- a/iot/iot-jdbc-base/pom.xml +++ b/iot/iot-jdbc-base/pom.xml @@ -8,7 +8,7 @@ io.enmasse iot - 0.32-SNAPSHOT + 1.0-SNAPSHOT iot-jdbc-base diff --git a/iot/iot-lorawan-adapter/pom.xml b/iot/iot-lorawan-adapter/pom.xml index 14ace593977..37af7e44a71 100644 --- a/iot/iot-lorawan-adapter/pom.xml +++ b/iot/iot-lorawan-adapter/pom.xml @@ -8,7 +8,7 @@ io.enmasse iot-application-base - 0.32-SNAPSHOT + 1.0-SNAPSHOT ../iot-application-base diff --git a/iot/iot-mqtt-adapter/pom.xml b/iot/iot-mqtt-adapter/pom.xml index 9d394c201f0..ebecf0c6e36 100644 --- a/iot/iot-mqtt-adapter/pom.xml +++ b/iot/iot-mqtt-adapter/pom.xml @@ -8,7 +8,7 @@ io.enmasse iot-application-base - 0.32-SNAPSHOT + 1.0-SNAPSHOT ../iot-application-base diff --git a/iot/iot-proxy-configurator/pom.xml b/iot/iot-proxy-configurator/pom.xml index 918fe3a2ec8..141c29f5d0f 100644 --- a/iot/iot-proxy-configurator/pom.xml +++ b/iot/iot-proxy-configurator/pom.xml @@ -4,7 +4,7 @@ io.enmasse iot-application-base - 0.32-SNAPSHOT + 1.0-SNAPSHOT ../iot-application-base pom diff --git a/iot/iot-service-base/pom.xml b/iot/iot-service-base/pom.xml index 6f8f61ae32f..29707d95f07 100644 --- a/iot/iot-service-base/pom.xml +++ b/iot/iot-service-base/pom.xml @@ -8,7 +8,7 @@ io.enmasse iot - 0.32-SNAPSHOT + 1.0-SNAPSHOT iot-service-base @@ -45,6 +45,13 @@ kubernetes-client + + io.quarkus + quarkus-config-yaml + ${quarkus.version} + true + + org.junit.jupiter diff --git a/iot/iot-service-base/src/main/java/io/enmasse/iot/service/base/AbstractKubernetesBasedService.java b/iot/iot-service-base/src/main/java/io/enmasse/iot/service/base/AbstractKubernetesBasedService.java index d2d2ee081b2..77c03618b30 100644 --- a/iot/iot-service-base/src/main/java/io/enmasse/iot/service/base/AbstractKubernetesBasedService.java +++ b/iot/iot-service-base/src/main/java/io/enmasse/iot/service/base/AbstractKubernetesBasedService.java @@ -63,14 +63,14 @@ protected void callBlocking(final Callable callable, final Handler startFuture) { + public void start(final Future startFuture) { doStart() .mapEmpty() .onComplete(startFuture); } @Override - public final void stop(final Future stopFuture) { + public void stop(final Future stopFuture) { doStop() .mapEmpty() .onComplete(stopFuture); diff --git a/iot/iot-service-base/src/main/java/io/enmasse/iot/service/base/AbstractProjectBasedService.java b/iot/iot-service-base/src/main/java/io/enmasse/iot/service/base/AbstractProjectBasedService.java index 7d11ce83e67..329c8b2b493 100644 --- a/iot/iot-service-base/src/main/java/io/enmasse/iot/service/base/AbstractProjectBasedService.java +++ b/iot/iot-service-base/src/main/java/io/enmasse/iot/service/base/AbstractProjectBasedService.java @@ -5,26 +5,26 @@ package io.enmasse.iot.service.base; -import static io.vertx.core.Future.succeededFuture; -import static java.util.Optional.ofNullable; - -import java.time.Duration; -import java.util.Map; -import java.util.Optional; -import java.util.concurrent.ConcurrentHashMap; - -import org.slf4j.Logger; -import org.springframework.boot.context.properties.ConfigurationProperties; - import io.enmasse.common.model.CustomResources; import io.enmasse.iot.model.v1.IoTCrd; import io.enmasse.iot.model.v1.IoTProject; import io.enmasse.iot.model.v1.IoTProjectBuilder; import io.enmasse.iot.model.v1.IoTProjectList; import io.enmasse.iot.utils.ConfigBase; + import io.fabric8.kubernetes.client.informers.ResourceEventHandler; import io.fabric8.kubernetes.client.informers.SharedInformerFactory; import io.vertx.core.Future; +import org.eclipse.microprofile.config.inject.ConfigProperty; +import org.slf4j.Logger; + +import java.time.Duration; +import java.util.Map; +import java.util.Optional; +import java.util.concurrent.ConcurrentHashMap; + +import static io.vertx.core.Future.succeededFuture; +import static java.util.Optional.ofNullable; public abstract class AbstractProjectBasedService extends AbstractKubernetesBasedService { @@ -41,7 +41,7 @@ public abstract class AbstractProjectBasedService extends AbstractKubernetesBase */ private Duration resyncPeriod = DEFAULT_RESYNC_PERIOD; - @ConfigurationProperties(ConfigBase.CONFIG_BASE + "kubernetes.informer.resyncPeriod") + @ConfigProperty(name = ConfigBase.CONFIG_BASE + "kubernetes.informer.resyncPeriod") public void setResyncPeriod(final Duration resyncPeriod) { this.resyncPeriod = resyncPeriod != null ? resyncPeriod : DEFAULT_RESYNC_PERIOD; } @@ -72,14 +72,17 @@ protected void startWatcher() { log.info("Starting project watcher"); + // CustomResourceDefinitions.registerAll(); + // create a new informer factory this.factory = getClient().inAnyNamespace().informers(); // setup informer - this.factory.sharedIndexInformerForCustomResource( + var informer = this.factory.sharedIndexInformerForCustomResource( CustomResources.toContext(IoTCrd.project()), - IoTProject.class, IoTProjectList.class, this.resyncPeriod.toMillis()) - .addEventHandler(new ResourceEventHandler() { + IoTProject.class, IoTProjectList.class, this.resyncPeriod.toMillis()); + + informer.addEventHandler(new ResourceEventHandler() { @Override public void onUpdate(final IoTProject oldObj, final IoTProject newObj) { diff --git a/iot/iot-sigfox-adapter/pom.xml b/iot/iot-sigfox-adapter/pom.xml index 697ab9e6f65..c03b9c75ce8 100644 --- a/iot/iot-sigfox-adapter/pom.xml +++ b/iot/iot-sigfox-adapter/pom.xml @@ -8,7 +8,7 @@ io.enmasse iot-application-base - 0.32-SNAPSHOT + 1.0-SNAPSHOT ../iot-application-base diff --git a/iot/iot-tenant-cleaner/pom.xml b/iot/iot-tenant-cleaner/pom.xml index 142a2bad873..945f3d2fdcc 100644 --- a/iot/iot-tenant-cleaner/pom.xml +++ b/iot/iot-tenant-cleaner/pom.xml @@ -8,7 +8,7 @@ io.enmasse iot-application-base - 0.32-SNAPSHOT + 1.0-SNAPSHOT ../iot-application-base diff --git a/iot/iot-tenant-service/.gitignore b/iot/iot-tenant-service/.gitignore new file mode 100644 index 00000000000..f10862a6559 --- /dev/null +++ b/iot/iot-tenant-service/.gitignore @@ -0,0 +1 @@ +/.env diff --git a/iot/iot-tenant-service/Dockerfile b/iot/iot-tenant-service/Dockerfile index ba1f128fee6..043a18be052 100644 --- a/iot/iot-tenant-service/Dockerfile +++ b/iot/iot-tenant-service/Dockerfile @@ -5,7 +5,8 @@ ARG maven_version ARG revision ENV VERSION=${version} REVISION=${revision} MAVEN_VERSION=${maven_version} -ADD target/iot-tenant-service-${maven_version}.jar /iot-tenant-service.jar +ADD target/iot-tenant-service-${maven_version}-runner.jar /iot-tenant-service.jar +ADD target/iot-tenant-service-${maven_version}-runner /iot-tenant-service ENV JAVA_LAUNCH_PROFILE=openjdk-11 CMD ["/opt/run-java/launch_java.sh", "-jar", "/iot-tenant-service.jar"] diff --git a/iot/iot-tenant-service/Makefile b/iot/iot-tenant-service/Makefile index 19a4914faa4..7d4e13886e8 100644 --- a/iot/iot-tenant-service/Makefile +++ b/iot/iot-tenant-service/Makefile @@ -1 +1,5 @@ include ../../Makefile.java.mk + +ifneq ($(SKIP_QUARKUS_NATIVE_IMAGE),true) +MAVEN_PACKAGE_ARGS += -Pnative -Dquarkus.native.container-runtime=$(QUARKUS_CONTAINER_RUNTIME) +endif diff --git a/iot/iot-tenant-service/pom.xml b/iot/iot-tenant-service/pom.xml index 18ad13ab336..cf09406ef0e 100644 --- a/iot/iot-tenant-service/pom.xml +++ b/iot/iot-tenant-service/pom.xml @@ -8,34 +8,82 @@ io.enmasse iot-application-base - 0.32-SNAPSHOT + 1.0-SNAPSHOT ../iot-application-base iot-tenant-service - EnMasse IoT Tenant Service + + true + quay.io/quarkus/ubi-quarkus-native-image:20.1.0-java11 + + io.enmasse iot-service-base + + + org.springframework + * + + + org.springframework.boot + * + + + + + + org.eclipse.hono + hono-core + ${hono.version} org.eclipse.hono hono-service-device-registry-base + + + org.springframework.boot + * + + + + + + io.quarkus + quarkus-vertx + + + + io.quarkus + quarkus-config-yaml - org.springframework.boot - spring-boot-starter-logging + io.quarkus + quarkus-hibernate-validator - org.springframework.boot - spring-boot-starter + io.quarkus + quarkus-kubernetes-client + + + + + org.bouncycastle + bcprov-jdk15on + + + + + org.bouncycastle + bcpkix-jdk15on @@ -53,16 +101,10 @@ kubernetes-client - - org.springframework.boot - spring-boot-starter-validation - ${spring.boot.version} - - - io.jaegertracing - jaeger-client + io.quarkus + quarkus-jaeger @@ -91,12 +133,54 @@ + + + + maven-surefire-plugin + + + org.jboss.logmanager.LogManager + + + + + - org.springframework.boot - spring-boot-maven-plugin + io.quarkus + quarkus-maven-plugin + + + + build + + + + + + native + + + native + + + + native + + -J-Djava.net.preferIPv4Stack=true, + --initialize-at-run-time=io.netty.internal.tcnative.SSL, + -H:ReflectionConfigurationFiles=reflection-config.json, + + true + true + true + true + + + + diff --git a/iot/iot-tenant-service/src/main/java/io/enmasse/iot/tenant/Application.java b/iot/iot-tenant-service/src/main/java/io/enmasse/iot/tenant/Application.java index 5c2cf214ffd..e0add10c5a6 100644 --- a/iot/iot-tenant-service/src/main/java/io/enmasse/iot/tenant/Application.java +++ b/iot/iot-tenant-service/src/main/java/io/enmasse/iot/tenant/Application.java @@ -1,90 +1,56 @@ /* - * Copyright 2018, 2019, EnMasse authors. + * Copyright 2018, 2019 EnMasse authors. * License: Apache License 2.0 (see the file LICENSE or http://apache.org/licenses/LICENSE-2.0.html). */ package io.enmasse.iot.tenant; -import java.util.LinkedList; -import java.util.List; +import java.util.concurrent.TimeUnit; -import org.eclipse.hono.service.AbstractBaseApplication; +import javax.enterprise.context.ApplicationScoped; +import javax.enterprise.event.Observes; +import javax.enterprise.inject.Instance; +import javax.inject.Inject; + +import io.vertx.core.Vertx; import org.eclipse.hono.service.HealthCheckProvider; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.boot.SpringApplication; -import org.springframework.boot.autoconfigure.EnableAutoConfiguration; -import org.springframework.context.annotation.ComponentScan; +import org.eclipse.hono.service.HealthCheckServer; +import io.enmasse.iot.utils.MoreFutures; import io.enmasse.model.CustomResourceDefinitions; -import io.vertx.core.CompositeFuture; -import io.vertx.core.Future; +import io.quarkus.runtime.ShutdownEvent; +import io.quarkus.runtime.Startup; +import io.quarkus.runtime.StartupEvent; +import io.vertx.core.AbstractVerticle; import io.vertx.core.Promise; -import io.vertx.core.Verticle; - -@ComponentScan("io.enmasse.iot.tenant") -@ComponentScan("org.eclipse.hono.service.metric") -@ComponentScan("org.eclipse.hono.deviceregistry.service.tenant") -@EnableAutoConfiguration -public class Application extends AbstractBaseApplication { - - /** - * All the verticles. - */ - private List verticles; - /** - * All the health check providers. - */ - private List healthCheckProviders; +@ApplicationScoped +@Startup(1) +public class Application { - @Autowired - public void setVerticles(final List verticles) { - this.verticles = verticles; - } + @Inject HealthCheckServer healthCheckServer; - @Autowired - public void setHealthCheckProviders(final List healthCheckProviders) { - this.healthCheckProviders = healthCheckProviders; + void onStart(@Observes final StartupEvent event) throws Exception { + CustomResourceDefinitions.registerAll(); + MoreFutures.map(this.healthCheckServer.start()).get(15, TimeUnit.SECONDS); } - @Override - protected final Future deployVerticles() { - - return super.deployVerticles().compose(ok -> { - - @SuppressWarnings("rawtypes") - final List futures = new LinkedList<>(); - - for (final Verticle verticle : this.verticles) { - log.info("Deploying: {}", verticle); - final Promise result = Promise.promise(); - getVertx().deployVerticle(verticle, result); - futures.add(result.future()); - } - - return CompositeFuture.all(futures); - - }); - + public void init(@Observes StartupEvent e, Vertx vertx, Instance verticles) throws Exception { + for (AbstractVerticle verticle : verticles) { + final Promise p = Promise.promise(); + vertx.deployVerticle(verticle, p); + MoreFutures.map(p.future()).get(); + } } - /** - * Registers any additional health checks that the service implementation components provide. - * - * @return A succeeded future. - */ - @Override - protected Future postRegisterServiceVerticles() { - return super.postRegisterServiceVerticles() - .compose(ok -> { - this.healthCheckProviders.forEach(this::registerHealthchecks); - return Future.succeededFuture(); - }); + public void initHealth(@Observes StartupEvent e, Vertx vertx, Instance providers) throws Exception { + for (HealthCheckProvider provider : providers) { + this.healthCheckServer.registerHealthCheckResources(provider); + } } - public static void main(final String[] args) { - CustomResourceDefinitions.registerAll(); - SpringApplication.run(Application.class, args); + void onStop(@Observes final ShutdownEvent event) throws Exception { + MoreFutures.map(this.healthCheckServer.stop()).get(15, TimeUnit.SECONDS); } } diff --git a/iot/iot-tenant-service/src/main/java/io/enmasse/iot/tenant/config/AmqpEndpointConfiguration.java b/iot/iot-tenant-service/src/main/java/io/enmasse/iot/tenant/config/AmqpEndpointConfiguration.java index d0ec8fed684..c508670cf64 100644 --- a/iot/iot-tenant-service/src/main/java/io/enmasse/iot/tenant/config/AmqpEndpointConfiguration.java +++ b/iot/iot-tenant-service/src/main/java/io/enmasse/iot/tenant/config/AmqpEndpointConfiguration.java @@ -5,23 +5,10 @@ package io.enmasse.iot.tenant.config; -import org.eclipse.hono.config.ServiceConfigProperties; -import org.eclipse.hono.util.Constants; -import org.springframework.beans.factory.annotation.Qualifier; -import org.springframework.boot.context.properties.ConfigurationProperties; -import org.springframework.context.annotation.Bean; -import org.springframework.context.annotation.Configuration; - +import io.enmasse.iot.tenant.config.compat.ServiceConfig; import io.enmasse.iot.utils.ConfigBase; +import io.quarkus.arc.config.ConfigProperties; -@Configuration -public class AmqpEndpointConfiguration { - - @Qualifier(Constants.QUALIFIER_AMQP) - @Bean - @ConfigurationProperties(ConfigBase.CONFIG_BASE + ".amqp") - public ServiceConfigProperties amqpEndpointProperties() { - return new ServiceConfigProperties(); - } - +@ConfigProperties(prefix = ConfigBase.CONFIG_BASE + ".amqp", namingStrategy = ConfigProperties.NamingStrategy.VERBATIM, failOnMismatchingMember = false) +public class AmqpEndpointConfiguration extends ServiceConfig { } diff --git a/iot/iot-tenant-service/src/main/java/io/enmasse/iot/tenant/config/ApplicationConfiguration.java b/iot/iot-tenant-service/src/main/java/io/enmasse/iot/tenant/config/ApplicationConfiguration.java deleted file mode 100644 index 572fa71cd75..00000000000 --- a/iot/iot-tenant-service/src/main/java/io/enmasse/iot/tenant/config/ApplicationConfiguration.java +++ /dev/null @@ -1,24 +0,0 @@ -/* - * Copyright 2018-2020, EnMasse authors. - * License: Apache License 2.0 (see the file LICENSE or http://apache.org/licenses/LICENSE-2.0.html). - */ - -package io.enmasse.iot.tenant.config; - -import org.eclipse.hono.config.ApplicationConfigProperties; -import org.springframework.boot.context.properties.ConfigurationProperties; -import org.springframework.context.annotation.Bean; -import org.springframework.context.annotation.Configuration; - -import io.enmasse.iot.utils.ConfigBase; - -@Configuration -public class ApplicationConfiguration { - - @Bean - @ConfigurationProperties(ConfigBase.CONFIG_BASE + ".app") - public ApplicationConfigProperties applicationConfigProperties() { - return new ApplicationConfigProperties(); - } - -} diff --git a/iot/iot-tenant-service/src/main/java/io/enmasse/iot/tenant/config/AuthenticationConfiguration.java b/iot/iot-tenant-service/src/main/java/io/enmasse/iot/tenant/config/AuthenticationConfiguration.java index fcf989be5cf..55223fa70e4 100644 --- a/iot/iot-tenant-service/src/main/java/io/enmasse/iot/tenant/config/AuthenticationConfiguration.java +++ b/iot/iot-tenant-service/src/main/java/io/enmasse/iot/tenant/config/AuthenticationConfiguration.java @@ -5,57 +5,10 @@ package io.enmasse.iot.tenant.config; -import static org.eclipse.hono.service.auth.AuthTokenHelperImpl.forValidating; - -import org.eclipse.hono.connection.ConnectionFactory; -import org.eclipse.hono.connection.impl.ConnectionFactoryImpl; -import org.eclipse.hono.service.auth.AuthTokenHelper; -import org.eclipse.hono.service.auth.HonoSaslAuthenticatorFactory; -import org.eclipse.hono.service.auth.delegating.AuthenticationServerClientConfigProperties; -import org.eclipse.hono.service.auth.delegating.DelegatingAuthenticationService; -import org.eclipse.hono.util.AuthenticationConstants; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.beans.factory.annotation.Qualifier; -import org.springframework.boot.context.properties.ConfigurationProperties; -import org.springframework.context.annotation.Bean; -import org.springframework.context.annotation.Configuration; - +import io.enmasse.iot.tenant.config.compat.AuthenticationServerClientConfig; import io.enmasse.iot.utils.ConfigBase; -import io.vertx.core.Vertx; - -@Configuration -public class AuthenticationConfiguration { - - @Bean - public DelegatingAuthenticationService authenticationService() { - return new DelegatingAuthenticationService(); - } - - @Bean - @ConfigurationProperties(ConfigBase.CONFIG_BASE + ".auth") - @Qualifier(AuthenticationConstants.QUALIFIER_AUTHENTICATION) - public AuthenticationServerClientConfigProperties authenticationServiceClientProperties() { - return new AuthenticationServerClientConfigProperties(); - } - - @Bean - @Qualifier(AuthenticationConstants.QUALIFIER_AUTHENTICATION) - public ConnectionFactory authenticationServiceConnectionFactory(final Vertx vertx) { - return new ConnectionFactoryImpl(vertx, authenticationServiceClientProperties()); - } - - @Bean - @Qualifier(AuthenticationConstants.QUALIFIER_AUTHENTICATION) - public AuthTokenHelper tokenValidator(final Vertx vertx) { - return forValidating(vertx, authenticationServiceClientProperties().getValidation()); - } - - @Bean - public HonoSaslAuthenticatorFactory authenticatorFactory( - @Autowired final Vertx vertx, - @Qualifier(AuthenticationConstants.QUALIFIER_AUTHENTICATION) final AuthTokenHelper validator) { - - return new HonoSaslAuthenticatorFactory(vertx, validator); +import io.quarkus.arc.config.ConfigProperties; - } +@ConfigProperties(prefix = ConfigBase.CONFIG_BASE + ".auth", namingStrategy = ConfigProperties.NamingStrategy.VERBATIM, failOnMismatchingMember = false) +public class AuthenticationConfiguration extends AuthenticationServerClientConfig { } diff --git a/iot/iot-tenant-service/src/main/java/io/enmasse/iot/tenant/config/HealthConfiguration.java b/iot/iot-tenant-service/src/main/java/io/enmasse/iot/tenant/config/HealthConfiguration.java index 23619b7f67f..5dd73c34461 100644 --- a/iot/iot-tenant-service/src/main/java/io/enmasse/iot/tenant/config/HealthConfiguration.java +++ b/iot/iot-tenant-service/src/main/java/io/enmasse/iot/tenant/config/HealthConfiguration.java @@ -5,31 +5,17 @@ package io.enmasse.iot.tenant.config; -import org.eclipse.hono.config.ServerConfig; -import org.eclipse.hono.service.VertxBasedHealthCheckServer; -import org.springframework.beans.factory.annotation.Qualifier; -import org.springframework.boot.context.properties.ConfigurationProperties; -import org.springframework.context.annotation.Bean; -import org.springframework.context.annotation.Configuration; - +import io.enmasse.iot.tenant.config.compat.ServerConfig; import io.enmasse.iot.utils.ConfigBase; -import io.vertx.core.Vertx; - -@Configuration -public class HealthConfiguration { +import io.quarkus.arc.config.ConfigProperties; - private static final String QUALIFIER = "health"; - - @Bean - @Qualifier(QUALIFIER) - @ConfigurationProperties(ConfigBase.CONFIG_BASE + ".health-check") - public ServerConfig healthCheckConfigProperties() { - return new ServerConfig(); - } +@ConfigProperties(prefix = ConfigBase.CONFIG_BASE + ".health-check", namingStrategy = ConfigProperties.NamingStrategy.VERBATIM, failOnMismatchingMember = false) +public class HealthConfiguration extends ServerConfig { - @Bean - public VertxBasedHealthCheckServer healthCheckServer (final Vertx vertx, @Qualifier(QUALIFIER) final ServerConfig config) { - return new VertxBasedHealthCheckServer(vertx, config); + public org.eclipse.hono.config.ServerConfig toHono() { + var result = new org.eclipse.hono.config.ServerConfig(); + super.applyTo(result); + return result; } } diff --git a/iot/iot-tenant-service/src/main/java/io/enmasse/iot/tenant/config/MetricsConfiguration.java b/iot/iot-tenant-service/src/main/java/io/enmasse/iot/tenant/config/MetricsConfiguration.java index 400717da8d0..e60abde0ca0 100644 --- a/iot/iot-tenant-service/src/main/java/io/enmasse/iot/tenant/config/MetricsConfiguration.java +++ b/iot/iot-tenant-service/src/main/java/io/enmasse/iot/tenant/config/MetricsConfiguration.java @@ -5,22 +5,30 @@ package io.enmasse.iot.tenant.config; +import javax.enterprise.context.ApplicationScoped; +import javax.inject.Singleton; + import org.eclipse.hono.service.metric.MetricsTags; -import org.springframework.context.annotation.Bean; -import org.springframework.context.annotation.Configuration; +import org.eclipse.hono.service.metric.PrometheusScrapingResource; -import io.micrometer.core.instrument.MeterRegistry; -import org.springframework.boot.actuate.autoconfigure.metrics.MeterRegistryCustomizer; +import io.micrometer.prometheus.PrometheusConfig; +import io.micrometer.prometheus.PrometheusMeterRegistry; -@Configuration +@ApplicationScoped public class MetricsConfiguration { - @Bean - public MeterRegistryCustomizer commonTags() { - - return r -> r.config().commonTags( - MetricsTags.forService("tenant-service")); + @Singleton + PrometheusMeterRegistry metricsRegistry() { + var result = new PrometheusMeterRegistry(PrometheusConfig.DEFAULT); + result + .config() + .commonTags(MetricsTags.forService("tenant-service")); + return result; + } + @Singleton + PrometheusScrapingResource scrapingResource(final PrometheusMeterRegistry registry) { + return new PrometheusScrapingResource(registry); } } diff --git a/iot/iot-tenant-service/src/main/java/io/enmasse/iot/tenant/config/ServiceConfiguration.java b/iot/iot-tenant-service/src/main/java/io/enmasse/iot/tenant/config/ServiceConfiguration.java deleted file mode 100644 index a74117cb316..00000000000 --- a/iot/iot-tenant-service/src/main/java/io/enmasse/iot/tenant/config/ServiceConfiguration.java +++ /dev/null @@ -1,23 +0,0 @@ -/* - * Copyright 2018-2020, EnMasse authors. - * License: Apache License 2.0 (see the file LICENSE or http://apache.org/licenses/LICENSE-2.0.html). - */ - -package io.enmasse.iot.tenant.config; - -import org.springframework.boot.context.properties.ConfigurationProperties; -import org.springframework.context.annotation.Bean; -import org.springframework.context.annotation.Configuration; - -import io.enmasse.iot.tenant.impl.TenantServiceConfigProperties; -import io.enmasse.iot.utils.ConfigBase; - -@Configuration -public class ServiceConfiguration { - - @Bean - @ConfigurationProperties(ConfigBase.CONFIG_BASE + ".tenant.service") - public TenantServiceConfigProperties tenantsProperties() { - return new TenantServiceConfigProperties(); - } -} diff --git a/iot/iot-tenant-service/src/main/java/io/enmasse/iot/tenant/config/TracerConfiguration.java b/iot/iot-tenant-service/src/main/java/io/enmasse/iot/tenant/config/TracerConfiguration.java deleted file mode 100644 index 31cfb42a389..00000000000 --- a/iot/iot-tenant-service/src/main/java/io/enmasse/iot/tenant/config/TracerConfiguration.java +++ /dev/null @@ -1,37 +0,0 @@ -/* - * Copyright 2019, EnMasse authors. - * License: Apache License 2.0 (see the file LICENSE or http://apache.org/licenses/LICENSE-2.0.html). - */ - -package io.enmasse.iot.tenant.config; - -import java.util.Optional; - -import org.springframework.context.annotation.Bean; -import org.springframework.context.annotation.Configuration; - -import io.opentracing.Tracer; -import io.opentracing.contrib.tracerresolver.TracerResolver; -import io.opentracing.noop.NoopTracerFactory; - -@Configuration -public class TracerConfiguration { - - /** - * Exposes an OpenTracing {@code Tracer} as a Spring Bean. - *

- * The Tracer will be resolved by means of a Java service lookup. - * If no tracer can be resolved this way, the {@code NoopTracer} is - * returned. - * - * @return The tracer. - */ - @Bean - public Tracer getTracer() { - - return Optional - .ofNullable(TracerResolver.resolveTracer()) - .orElse(NoopTracerFactory.create()); - } - -} diff --git a/iot/iot-tenant-service/src/main/java/io/enmasse/iot/tenant/config/VertxConfiguration.java b/iot/iot-tenant-service/src/main/java/io/enmasse/iot/tenant/config/VertxConfiguration.java deleted file mode 100644 index 577fb6f2c0d..00000000000 --- a/iot/iot-tenant-service/src/main/java/io/enmasse/iot/tenant/config/VertxConfiguration.java +++ /dev/null @@ -1,41 +0,0 @@ -/* - * Copyright 2018-2020, EnMasse authors. - * License: Apache License 2.0 (see the file LICENSE or http://apache.org/licenses/LICENSE-2.0.html). - */ - -package io.enmasse.iot.tenant.config; - -import org.eclipse.hono.config.VertxProperties; -import org.springframework.boot.context.properties.ConfigurationProperties; -import org.springframework.context.annotation.Bean; -import org.springframework.context.annotation.Configuration; - -import io.enmasse.iot.utils.ConfigBase; -import io.vertx.core.Vertx; -import io.vertx.core.VertxOptions; -import io.vertx.core.dns.AddressResolverOptions; - -@Configuration -public class VertxConfiguration { - - @Bean - public Vertx vertx() { - final VertxOptions options = new VertxOptions() - .setWarningExceptionTime(1500000000) - .setAddressResolverOptions(new AddressResolverOptions() - .setCacheNegativeTimeToLive(0) // discard failed DNS lookup results immediately - .setCacheMaxTimeToLive(0) // support DNS based service resolution - .setQueryTimeout(1000)); - - vertxProperties().configureVertx(options); - - return Vertx.vertx(options); - } - - @Bean - @ConfigurationProperties(ConfigBase.CONFIG_BASE + ".vertx") - public VertxProperties vertxProperties() { - return new VertxProperties(); - } - -} diff --git a/iot/iot-tenant-service/src/main/java/io/enmasse/iot/tenant/config/compat/AbstractConfig.java b/iot/iot-tenant-service/src/main/java/io/enmasse/iot/tenant/config/compat/AbstractConfig.java new file mode 100644 index 00000000000..c06a25372f1 --- /dev/null +++ b/iot/iot-tenant-service/src/main/java/io/enmasse/iot/tenant/config/compat/AbstractConfig.java @@ -0,0 +1,39 @@ +/* + * Copyright 2020, EnMasse authors. + * License: Apache License 2.0 (see the file LICENSE or http://apache.org/licenses/LICENSE-2.0.html). + */ + +package io.enmasse.iot.tenant.config.compat; + +import java.util.List; +import java.util.Optional; + +import org.eclipse.hono.config.FileFormat; + +public class AbstractConfig { + + public Optional trustStorePath; + public Optional trustStorePassword; + public Optional pathSeparator; + public Optional keyStorePath; + public Optional keyStorePassword; + public Optional certPath; + public Optional keyPath; + public Optional trustStoreFormat; + public Optional keyFormat; + public Optional> secureProtocols; + + public void applyTo(org.eclipse.hono.config.AbstractConfig result) { + this.trustStorePath.ifPresent(result::setTrustStorePath); + this.trustStorePassword.ifPresent(result::setTrustStorePassword); + this.pathSeparator.ifPresent(result::setPathSeparator); + this.keyStorePath.ifPresent(result::setKeyStorePath); + this.keyStorePassword.ifPresent(result::setKeyStorePassword); + this.certPath.ifPresent(result::setCertPath); + this.keyPath.ifPresent(result::setKeyPath); + this.trustStoreFormat.map(FileFormat::valueOf).ifPresent(result::setTrustStoreFormat); + this.keyFormat.map(FileFormat::valueOf).ifPresent(result::setKeyFormat); + this.secureProtocols.ifPresent(result::setSecureProtocols); + } + +} diff --git a/iot/iot-tenant-service/src/main/java/io/enmasse/iot/tenant/config/compat/AuthenticatingClientConfig.java b/iot/iot-tenant-service/src/main/java/io/enmasse/iot/tenant/config/compat/AuthenticatingClientConfig.java new file mode 100644 index 00000000000..756079e3afb --- /dev/null +++ b/iot/iot-tenant-service/src/main/java/io/enmasse/iot/tenant/config/compat/AuthenticatingClientConfig.java @@ -0,0 +1,36 @@ +/* + * Copyright 2020, EnMasse authors. + * License: Apache License 2.0 (see the file LICENSE or http://apache.org/licenses/LICENSE-2.0.html). + */ + +package io.enmasse.iot.tenant.config.compat; + +import java.util.Optional; + +import org.eclipse.hono.config.AuthenticatingClientConfigProperties; + +public class AuthenticatingClientConfig extends AbstractConfig { + + public Optional credentialsPath; + public Optional host; + public Optional hostnameVerificationRequired; + public Optional password; + public Optional port; + public Optional serverRole; + public Optional tlsEnabled; + public Optional username; + + public void applyTo(final AuthenticatingClientConfigProperties result) { + super.applyTo(result); + + this.credentialsPath.ifPresent(result::setCredentialsPath); + this.host.ifPresent(result::setHost); + this.hostnameVerificationRequired.ifPresent(result::setHostnameVerificationRequired); + this.password.ifPresent(result::setPassword); + this.port.ifPresent(result::setPort); + this.serverRole.ifPresent(result::setServerRole); + this.tlsEnabled.ifPresent(result::setTlsEnabled); + this.username.ifPresent(result::setUsername); + + } +} diff --git a/iot/iot-tenant-service/src/main/java/io/enmasse/iot/tenant/config/compat/AuthenticationServerClientConfig.java b/iot/iot-tenant-service/src/main/java/io/enmasse/iot/tenant/config/compat/AuthenticationServerClientConfig.java new file mode 100644 index 00000000000..190c1decb58 --- /dev/null +++ b/iot/iot-tenant-service/src/main/java/io/enmasse/iot/tenant/config/compat/AuthenticationServerClientConfig.java @@ -0,0 +1,30 @@ +/* + * Copyright 2020, EnMasse authors. + * License: Apache License 2.0 (see the file LICENSE or http://apache.org/licenses/LICENSE-2.0.html). + */ + +package io.enmasse.iot.tenant.config.compat; + +import java.util.List; +import java.util.Optional; + +import org.eclipse.hono.service.auth.delegating.AuthenticationServerClientConfigProperties; + +public class AuthenticationServerClientConfig extends ClientConfig { + + public Optional> supportedSaslMechanisms; + public SignatureSupportingConfig validation; + + public void applyTo(final AuthenticationServerClientConfigProperties result) { + super.applyTo(result); + + this.supportedSaslMechanisms.ifPresent(result::setSupportedSaslMechanisms); + this.validation.applyTo(result.getValidation()); + } + + public AuthenticationServerClientConfigProperties toHono() { + var result = new AuthenticationServerClientConfigProperties(); + applyTo(result); + return result; + } +} diff --git a/iot/iot-tenant-service/src/main/java/io/enmasse/iot/tenant/config/compat/ClientConfig.java b/iot/iot-tenant-service/src/main/java/io/enmasse/iot/tenant/config/compat/ClientConfig.java new file mode 100644 index 00000000000..37d4f5b7c45 --- /dev/null +++ b/iot/iot-tenant-service/src/main/java/io/enmasse/iot/tenant/config/compat/ClientConfig.java @@ -0,0 +1,46 @@ +/* + * Copyright 2020, EnMasse authors. + * License: Apache License 2.0 (see the file LICENSE or http://apache.org/licenses/LICENSE-2.0.html). + */ + +package io.enmasse.iot.tenant.config.compat; + +import java.util.Optional; + +import org.eclipse.hono.config.ClientConfigProperties; + +public class ClientConfig extends AuthenticatingClientConfig { + + public Optional amqpHostname; + public Optional connectTimeoutMillis; + public Optional flowLatency; + public Optional idleTimeoutMillis; + public Optional initialCredits; + public Optional linkEstablishmentTimeout; + public Optional name; + public Optional reconnectAttempts; + public Optional reconnectMinDelayMillis; + public Optional reconnectMaxDelayMillis; + public Optional reconnectDelayIncrementMillis; + public Optional requestTimeoutMillis; + public Optional sendMessageTimeoutMillis; + + public void applyTo(ClientConfigProperties result) { + super.applyTo(result); + + this.amqpHostname.ifPresent(result::setAmqpHostname); + this.connectTimeoutMillis.ifPresent(result::setConnectTimeout); + this.flowLatency.ifPresent(result::setFlowLatency); + this.idleTimeoutMillis.ifPresent(result::setIdleTimeout); + this.initialCredits.ifPresent(result::setInitialCredits); + this.linkEstablishmentTimeout.ifPresent(result::setLinkEstablishmentTimeout); + this.name.ifPresent(result::setName); + this.reconnectAttempts.ifPresent(result::setReconnectAttempts); + this.reconnectMinDelayMillis.ifPresent(result::setReconnectMinDelay); + this.reconnectMaxDelayMillis.ifPresent(result::setReconnectMaxDelay); + this.reconnectDelayIncrementMillis.ifPresent(result::setReconnectDelayIncrement); + this.requestTimeoutMillis.ifPresent(result::setRequestTimeout); + this.sendMessageTimeoutMillis.ifPresent(result::setSendMessageTimeout); + } + +} diff --git a/iot/iot-tenant-service/src/main/java/io/enmasse/iot/tenant/config/compat/ServerConfig.java b/iot/iot-tenant-service/src/main/java/io/enmasse/iot/tenant/config/compat/ServerConfig.java new file mode 100644 index 00000000000..501e9fd85fc --- /dev/null +++ b/iot/iot-tenant-service/src/main/java/io/enmasse/iot/tenant/config/compat/ServerConfig.java @@ -0,0 +1,32 @@ +/* + * Copyright 2020, EnMasse authors. + * License: Apache License 2.0 (see the file LICENSE or http://apache.org/licenses/LICENSE-2.0.html). + */ + +package io.enmasse.iot.tenant.config.compat; + +import java.util.Optional; + +public class ServerConfig extends AbstractConfig { + + public Optional port; + public Optional bindAddress; + public Optional nativeTlsRequired; + public Optional insecurePortEnabled; + public Optional insecurePortBindAddress; + public Optional insecurePort; + public Optional sni; + + public void applyTo(org.eclipse.hono.config.ServerConfig result) { + super.applyTo(result); + + this.port.ifPresent(result::setPort); + this.bindAddress.ifPresent(result::setBindAddress); + this.nativeTlsRequired.ifPresent(result::setNativeTlsRequired); + this.insecurePortEnabled.ifPresent(result::setInsecurePortEnabled); + this.insecurePortBindAddress.ifPresent(result::setInsecurePortBindAddress); + this.insecurePort.ifPresent(result::setInsecurePort); + this.sni.ifPresent(result::setSni); + } + +} diff --git a/iot/iot-tenant-service/src/main/java/io/enmasse/iot/tenant/config/compat/ServiceConfig.java b/iot/iot-tenant-service/src/main/java/io/enmasse/iot/tenant/config/compat/ServiceConfig.java new file mode 100644 index 00000000000..f14e94739be --- /dev/null +++ b/iot/iot-tenant-service/src/main/java/io/enmasse/iot/tenant/config/compat/ServiceConfig.java @@ -0,0 +1,40 @@ +/* + * Copyright 2020, EnMasse authors. + * License: Apache License 2.0 (see the file LICENSE or http://apache.org/licenses/LICENSE-2.0.html). + */ + +package io.enmasse.iot.tenant.config.compat; + +import java.util.Optional; + +import org.eclipse.hono.config.ServiceConfigProperties; + +public class ServiceConfig extends ServerConfig { + + public Optional singleTenant; + public Optional networkDebugLogging; + public Optional waitForDownstreamConnection; + public Optional maxPayloadSize; + public Optional receiverLinkCredit; + public Optional corsAllowedOrigin; + public Optional sendTimeOutInMs; + + public void applyTo(ServiceConfigProperties result) { + super.applyTo(result); + + this.singleTenant.ifPresent(result::setSingleTenant); + this.networkDebugLogging.ifPresent(result::setNetworkDebugLoggingEnabled); + this.waitForDownstreamConnection.ifPresent(result::setWaitForDownstreamConnectionEnabled); + this.maxPayloadSize.ifPresent(result::setMaxPayloadSize); + this.receiverLinkCredit.ifPresent(result::setReceiverLinkCredit); + this.corsAllowedOrigin.ifPresent(result::setCorsAllowedOrigin); + this.sendTimeOutInMs.ifPresent(result::setSendTimeOut); + } + + public ServiceConfigProperties toHono() { + var result = new ServiceConfigProperties(); + applyTo(result); + return result; + } + +} diff --git a/iot/iot-tenant-service/src/main/java/io/enmasse/iot/tenant/config/compat/SignatureSupportingConfig.java b/iot/iot-tenant-service/src/main/java/io/enmasse/iot/tenant/config/compat/SignatureSupportingConfig.java new file mode 100644 index 00000000000..0c814aa9228 --- /dev/null +++ b/iot/iot-tenant-service/src/main/java/io/enmasse/iot/tenant/config/compat/SignatureSupportingConfig.java @@ -0,0 +1,32 @@ +/* + * Copyright 2020, EnMasse authors. + * License: Apache License 2.0 (see the file LICENSE or http://apache.org/licenses/LICENSE-2.0.html). + */ + +package io.enmasse.iot.tenant.config.compat; + +import java.util.Optional; + +import org.eclipse.hono.config.SignatureSupportingConfigProperties; + +public class SignatureSupportingConfig { + + public Optional sharedSecret; + public Optional keyPath; + public Optional tokenExpirationSeconds; + public Optional certificatePath; + + public SignatureSupportingConfigProperties toHono() { + var result = new SignatureSupportingConfigProperties(); + applyTo(result); + return result; + } + + public void applyTo(SignatureSupportingConfigProperties result) { + this.sharedSecret.ifPresent(result::setSharedSecret); + this.keyPath.ifPresent(result::setKeyPath); + this.tokenExpirationSeconds.ifPresent(result::setTokenExpiration); + this.certificatePath.ifPresent(result::setCertPath); + } + +} diff --git a/iot/iot-tenant-service/src/main/java/io/enmasse/iot/tenant/factories/AuthenticationFactory.java b/iot/iot-tenant-service/src/main/java/io/enmasse/iot/tenant/factories/AuthenticationFactory.java new file mode 100644 index 00000000000..fb8aa55eeea --- /dev/null +++ b/iot/iot-tenant-service/src/main/java/io/enmasse/iot/tenant/factories/AuthenticationFactory.java @@ -0,0 +1,48 @@ +/* + * Copyright 2020 EnMasse authors. + * License: Apache License 2.0 (see the file LICENSE or http://apache.org/licenses/LICENSE-2.0.html). + */ + +package io.enmasse.iot.tenant.factories; + +import static org.eclipse.hono.service.auth.AuthTokenHelperImpl.forValidating; + +import javax.enterprise.context.Dependent; +import javax.inject.Inject; +import javax.inject.Singleton; + +import org.eclipse.hono.connection.ConnectionFactory; +import org.eclipse.hono.connection.impl.ConnectionFactoryImpl; +import org.eclipse.hono.service.auth.HonoSaslAuthenticatorFactory; +import org.eclipse.hono.service.auth.delegating.DelegatingAuthenticationService; + +import io.enmasse.iot.tenant.config.compat.AuthenticationServerClientConfig; +import io.vertx.core.Vertx; + +@Dependent +public class AuthenticationFactory { + + @Inject + AuthenticationServerClientConfig configuration; + + @Inject + Vertx vertx; + + @Singleton + public DelegatingAuthenticationService authenticationService() { + var result = new DelegatingAuthenticationService(); + result.setConfig(this.configuration.toHono()); + result.setConnectionFactory(authenticationServiceConnectionFactory()); + return result; + } + + private ConnectionFactory authenticationServiceConnectionFactory() { + return new ConnectionFactoryImpl(this.vertx, this.configuration.toHono()); + } + + @Singleton + public HonoSaslAuthenticatorFactory authenticatorFactory() { + return new HonoSaslAuthenticatorFactory(this.vertx, forValidating(this.vertx, this.configuration.toHono().getValidation())); + } + +} diff --git a/iot/iot-tenant-service/src/main/java/io/enmasse/iot/tenant/factories/HealthCheckServerFactory.java b/iot/iot-tenant-service/src/main/java/io/enmasse/iot/tenant/factories/HealthCheckServerFactory.java new file mode 100644 index 00000000000..672f635b18f --- /dev/null +++ b/iot/iot-tenant-service/src/main/java/io/enmasse/iot/tenant/factories/HealthCheckServerFactory.java @@ -0,0 +1,45 @@ +/* + * Copyright 2020 EnMasse authors. + * License: Apache License 2.0 (see the file LICENSE or http://apache.org/licenses/LICENSE-2.0.html). + */ + +package io.enmasse.iot.tenant.factories; + +import java.util.stream.Collectors; + +import javax.enterprise.context.Dependent; +import javax.enterprise.inject.Instance; +import javax.inject.Inject; +import javax.inject.Singleton; + +import org.eclipse.hono.service.HealthCheckServer; +import org.eclipse.hono.service.VertxBasedHealthCheckServer; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import io.enmasse.iot.tenant.config.HealthConfiguration; +import io.vertx.core.Handler; +import io.vertx.core.Vertx; +import io.vertx.ext.web.Router; + +@Dependent +public class HealthCheckServerFactory { + + private static final Logger log = LoggerFactory.getLogger(HealthCheckServer.class); + + @Inject + HealthConfiguration configugration; + + @Inject + Vertx vertx; + + @Singleton + public HealthCheckServer healthCheckServer(final Instance> additionalResources) { + var result = new VertxBasedHealthCheckServer(this.vertx, this.configugration.toHono()); + var resources = additionalResources.stream().collect(Collectors.toList()); + log.info("Adding additional resources: {}", resources); + result.setAdditionalResources(resources); + return result; + } + +} diff --git a/iot/iot-tenant-service/src/main/java/io/enmasse/iot/tenant/factories/TenantAmqpEndpointFactory.java b/iot/iot-tenant-service/src/main/java/io/enmasse/iot/tenant/factories/TenantAmqpEndpointFactory.java new file mode 100644 index 00000000000..2a0adbd810b --- /dev/null +++ b/iot/iot-tenant-service/src/main/java/io/enmasse/iot/tenant/factories/TenantAmqpEndpointFactory.java @@ -0,0 +1,46 @@ +/* + * Copyright 2020 EnMasse authors. + * License: Apache License 2.0 (see the file LICENSE or http://apache.org/licenses/LICENSE-2.0.html). + */ + +package io.enmasse.iot.tenant.factories; + +import javax.enterprise.context.ApplicationScoped; +import javax.inject.Inject; +import javax.inject.Singleton; + +import org.eclipse.hono.service.amqp.AmqpEndpoint; +import org.eclipse.hono.service.tenant.DelegatingTenantAmqpEndpoint; +import org.eclipse.hono.service.tenant.TenantService; + +import io.enmasse.iot.tenant.config.AmqpEndpointConfiguration; +import io.opentracing.Tracer; +import io.quarkus.runtime.Startup; +import io.vertx.core.Vertx; + +@ApplicationScoped +public class TenantAmqpEndpointFactory { + + @Inject + Tracer tracer; + + @Inject + AmqpEndpointConfiguration configuration; + + @Inject + TenantService service; + + @Inject + Vertx vertx; + + @Singleton + @Startup + public AmqpEndpoint tenantAmqpEndpoint() { + var result = new DelegatingTenantAmqpEndpoint<>(this.vertx, this.service); + result.setConfiguration(configuration.toHono()); + result.setTracer(tracer); + return result; + } + + +} diff --git a/iot/iot-tenant-service/src/main/java/io/enmasse/iot/tenant/factories/TenantAmqpServiceFactory.java b/iot/iot-tenant-service/src/main/java/io/enmasse/iot/tenant/factories/TenantAmqpServiceFactory.java new file mode 100644 index 00000000000..002356d5fcf --- /dev/null +++ b/iot/iot-tenant-service/src/main/java/io/enmasse/iot/tenant/factories/TenantAmqpServiceFactory.java @@ -0,0 +1,52 @@ +/* + * Copyright 2020 EnMasse authors. + * License: Apache License 2.0 (see the file LICENSE or http://apache.org/licenses/LICENSE-2.0.html). + */ + +package io.enmasse.iot.tenant.factories; + +import java.util.stream.Collectors; + +import javax.enterprise.context.Dependent; +import javax.enterprise.inject.Instance; +import javax.inject.Inject; +import javax.inject.Singleton; + +import org.eclipse.hono.service.HealthCheckServer; +import org.eclipse.hono.service.amqp.AmqpEndpoint; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import io.enmasse.iot.tenant.config.AmqpEndpointConfiguration; +import io.enmasse.iot.tenant.impl.TenantAmqpService; +import io.opentracing.Tracer; +import io.vertx.proton.sasl.ProtonSaslAuthenticatorFactory; + +@Dependent +public class TenantAmqpServiceFactory { + + private static final Logger log = LoggerFactory.getLogger(HealthCheckServer.class); + + @Inject + AmqpEndpointConfiguration configuration; + @Inject + HealthCheckServer healthCheckServer; + @Inject + ProtonSaslAuthenticatorFactory factory; + @Inject + Tracer tracer; + + @Singleton + public TenantAmqpService tenantAmqpService(final Instance endpoints) { + var result = new TenantAmqpService(); + result.setConfig(this.configuration.toHono()); + result.setHealthCheckServer(this.healthCheckServer); + result.setSaslAuthenticatorFactory(this.factory); + result.setTracer(this.tracer); + var endpointsList = endpoints.stream().collect(Collectors.toList()); + log.info("Adding endpoints: {}", endpointsList); + result.addEndpoints(endpointsList); + return result; + } + +} diff --git a/iot/iot-tenant-service/src/main/java/io/enmasse/iot/tenant/factories/TracerFactory.java b/iot/iot-tenant-service/src/main/java/io/enmasse/iot/tenant/factories/TracerFactory.java new file mode 100644 index 00000000000..06653204700 --- /dev/null +++ b/iot/iot-tenant-service/src/main/java/io/enmasse/iot/tenant/factories/TracerFactory.java @@ -0,0 +1,60 @@ +/* + * Copyright 2019-2020, EnMasse authors. + * License: Apache License 2.0 (see the file LICENSE or http://apache.org/licenses/LICENSE-2.0.html). + */ + +package io.enmasse.iot.tenant.factories; + +import io.jaegertracing.thrift.sampling_manager.PerOperationSamplingStrategies; +import io.jaegertracing.thrift.sampling_manager.ProbabilisticSamplingStrategy; +import io.jaegertracing.thrift.sampling_manager.RateLimitingSamplingStrategy; +import io.jaegertracing.thrift.sampling_manager.SamplingStrategyResponse; +import io.jaegertracing.thrift.sampling_manager.SamplingStrategyType; +import io.opentracing.Tracer; +import io.opentracing.contrib.tracerresolver.TracerResolver; +import io.opentracing.noop.NoopTracerFactory; +import io.quarkus.arc.DefaultBean; +import io.quarkus.arc.profile.IfBuildProfile; +import io.quarkus.runtime.annotations.RegisterForReflection; + +import javax.enterprise.context.Dependent; +import javax.inject.Singleton; +import java.util.Optional; + +@RegisterForReflection(targets = { + SamplingStrategyResponse.class, + SamplingStrategyType.class, + ProbabilisticSamplingStrategy.class, + RateLimitingSamplingStrategy.class, + PerOperationSamplingStrategies.class, +}) +class TracerFixup { +} + +@Dependent +public class TracerFactory { + + /** + * Exposes an OpenTracing {@code Tracer} as a bean. + *

+ * The Tracer will be resolved by means of a Java service lookup. + * If no tracer can be resolved this way, the {@code NoopTracer} is + * returned. + * + * @return The tracer. + */ + @Singleton + @DefaultBean + public Tracer getTracer() { + return Optional + .ofNullable(TracerResolver.resolveTracer()) + .orElse(NoopTracerFactory.create()); + } + + @Singleton + @IfBuildProfile("dev") + Tracer noopTracer() { + return NoopTracerFactory.create(); + } + +} diff --git a/iot/iot-tenant-service/src/main/java/io/enmasse/iot/tenant/impl/AbstractTenantService.java b/iot/iot-tenant-service/src/main/java/io/enmasse/iot/tenant/impl/AbstractTenantService.java index d3b37f689a9..13e7d9e9a57 100644 --- a/iot/iot-tenant-service/src/main/java/io/enmasse/iot/tenant/impl/AbstractTenantService.java +++ b/iot/iot-tenant-service/src/main/java/io/enmasse/iot/tenant/impl/AbstractTenantService.java @@ -12,6 +12,7 @@ import java.time.Instant; import java.util.Map; +import javax.inject.Inject; import javax.security.auth.x500.X500Principal; import org.eclipse.hono.service.tenant.TenantService; @@ -20,7 +21,6 @@ import org.eclipse.hono.util.TenantConstants; import org.eclipse.hono.util.TenantResult; import org.slf4j.Logger; -import org.springframework.beans.factory.annotation.Autowired; import io.enmasse.iot.model.v1.IoTProject; import io.enmasse.iot.service.base.AbstractProjectBasedService; @@ -43,13 +43,13 @@ public abstract class AbstractTenantService extends AbstractProjectBasedService protected Tracer tracer; - @Autowired + @Inject public void setConfig(final TenantServiceConfigProperties configuration) { this.configuration = configuration; } - @Autowired - public void setTracer(Tracer tracer) { + @Inject + public void setTracer(final Tracer tracer) { this.tracer = tracer; } diff --git a/iot/iot-tenant-service/src/main/java/io/enmasse/iot/tenant/impl/TenantAmqpService.java b/iot/iot-tenant-service/src/main/java/io/enmasse/iot/tenant/impl/TenantAmqpService.java index 85d46621830..b18a3bda960 100644 --- a/iot/iot-tenant-service/src/main/java/io/enmasse/iot/tenant/impl/TenantAmqpService.java +++ b/iot/iot-tenant-service/src/main/java/io/enmasse/iot/tenant/impl/TenantAmqpService.java @@ -1,5 +1,5 @@ /* - * Copyright 2018, EnMasse authors. + * Copyright 2018-2020, EnMasse authors. * License: Apache License 2.0 (see the file LICENSE or http://apache.org/licenses/LICENSE-2.0.html). */ @@ -7,10 +7,8 @@ import org.eclipse.hono.config.ServiceConfigProperties; import org.eclipse.hono.service.amqp.AmqpServiceBase; -import org.springframework.stereotype.Component; -@Component -public final class TenantAmqpService extends AmqpServiceBase { +public class TenantAmqpService extends AmqpServiceBase { @Override protected String getServiceName() { diff --git a/iot/iot-tenant-service/src/main/java/io/enmasse/iot/tenant/impl/TenantServiceConfigProperties.java b/iot/iot-tenant-service/src/main/java/io/enmasse/iot/tenant/impl/TenantServiceConfigProperties.java index 9143d0cfdde..d2ae4c3957f 100644 --- a/iot/iot-tenant-service/src/main/java/io/enmasse/iot/tenant/impl/TenantServiceConfigProperties.java +++ b/iot/iot-tenant-service/src/main/java/io/enmasse/iot/tenant/impl/TenantServiceConfigProperties.java @@ -7,6 +7,10 @@ import java.time.Duration; +import io.enmasse.iot.utils.ConfigBase; +import io.quarkus.arc.config.ConfigProperties; + +@ConfigProperties(prefix = ConfigBase.CONFIG_BASE + ".tenant.service", namingStrategy = ConfigProperties.NamingStrategy.VERBATIM, failOnMismatchingMember = false) public class TenantServiceConfigProperties { private static final Duration DEFAULT_CACHE_TIME_TO_LIVE = Duration.ofMinutes(5); diff --git a/iot/iot-tenant-service/src/main/java/io/enmasse/iot/tenant/impl/TenantServiceImpl.java b/iot/iot-tenant-service/src/main/java/io/enmasse/iot/tenant/impl/TenantServiceImpl.java index dddabfbbab7..a579d1be77e 100644 --- a/iot/iot-tenant-service/src/main/java/io/enmasse/iot/tenant/impl/TenantServiceImpl.java +++ b/iot/iot-tenant-service/src/main/java/io/enmasse/iot/tenant/impl/TenantServiceImpl.java @@ -18,6 +18,7 @@ import java.util.concurrent.locks.ReentrantReadWriteLock; import java.util.function.Supplier; +import javax.enterprise.context.ApplicationScoped; import javax.security.auth.x500.X500Principal; import org.eclipse.hono.util.Strings; @@ -25,19 +26,18 @@ import org.eclipse.hono.util.TenantResult; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import org.springframework.beans.factory.annotation.Qualifier; -import org.springframework.stereotype.Service; import com.google.common.collect.ImmutableMap; import io.enmasse.iot.model.v1.IoTProject; import io.opentracing.Span; +import io.quarkus.runtime.Startup; import io.vertx.core.Future; import io.vertx.core.json.JsonArray; import io.vertx.core.json.JsonObject; -@Service -@Qualifier("backend") +@ApplicationScoped +@Startup public class TenantServiceImpl extends AbstractTenantService { private static final Logger logger = LoggerFactory.getLogger(TenantServiceImpl.class); diff --git a/iot/iot-tenant-service/src/main/resources/config/application-prod.yml b/iot/iot-tenant-service/src/main/resources/application.yml similarity index 61% rename from iot/iot-tenant-service/src/main/resources/config/application-prod.yml rename to iot/iot-tenant-service/src/main/resources/application.yml index 15cf1ca6085..f028149805b 100644 --- a/iot/iot-tenant-service/src/main/resources/config/application-prod.yml +++ b/iot/iot-tenant-service/src/main/resources/application.yml @@ -1,3 +1,12 @@ +quarkus: + banner: + path: banner.txt + log: + console: + format: "%z{UTC}%d{yyyy-MM-dd'T'HH:mm:ss.SSS}Z %-5p [%c{1}] %m%n" + vertx: + prefer-native-transport: true + enmasse: iot: @@ -5,7 +14,6 @@ enmasse: name: 'EnMasse IoT Tenant Service' port: 5671 keyFormat: PEM - trustStorePath: /var/run/secrets/kubernetes.io/serviceaccount/service-ca.crt trustStoreFormat: PEM validation: @@ -22,3 +30,8 @@ enmasse: insecurePortBindAddress: 0.0.0.0 insecurePortEnabled: true insecurePort: 8080 + + health-check: + insecurePortBindAddress: 0.0.0.0 + insecurePortEnabled: true + insecurePort: 8088 diff --git a/iot/iot-tenant-service/src/main/resources/banner.txt b/iot/iot-tenant-service/src/main/resources/banner.txt index 666f3cced94..233c33e8798 100644 --- a/iot/iot-tenant-service/src/main/resources/banner.txt +++ b/iot/iot-tenant-service/src/main/resources/banner.txt @@ -5,9 +5,4 @@ | __| | '_ \| |\/| |/ _` / __/ __|/ _ \ | | / _ \| | | |____| | | | | | | (_| \__ \__ \ __/ _| || (_) | | |______|_| |_|_| |_|\__,_|___/___/\___| |_____\___/|_| - - -${application.title}${application.formatted-version} -using Spring Boot${spring-boot.formatted-version} - -Go to https://enmasse.io for more information. + \ No newline at end of file diff --git a/iot/iot-tenant-service/src/main/resources/config/application-dev.yml b/iot/iot-tenant-service/src/main/resources/config/application-dev.yml deleted file mode 100644 index c759caa339c..00000000000 --- a/iot/iot-tenant-service/src/main/resources/config/application-dev.yml +++ /dev/null @@ -1,14 +0,0 @@ -enmasse: - iot: - - auth: - validation: - sharedSecret: 1234567890123456789012345678901234567890123456789012345678901234 - - amqp: - insecurePortEnabled: true - insecurePort: 15672 - - http: - insecurePortEnabled: true - insecurePort: 18080 diff --git a/iot/iot-tenant-service/src/main/resources/config/application.yml b/iot/iot-tenant-service/src/main/resources/config/application.yml deleted file mode 100644 index abb4250ae68..00000000000 --- a/iot/iot-tenant-service/src/main/resources/config/application.yml +++ /dev/null @@ -1,14 +0,0 @@ -enmasse: - - iot: - - vertx: - preferNative: true - - healthCheck: - insecurePortBindAddress: 0.0.0.0 - insecurePortEnabled: true - insecurePort: 8088 - - app: - maxInstances: 1 diff --git a/iot/iot-tenant-service/src/main/resources/logback-spring.xml b/iot/iot-tenant-service/src/main/resources/logback-spring.xml deleted file mode 100644 index 40349a8c032..00000000000 --- a/iot/iot-tenant-service/src/main/resources/logback-spring.xml +++ /dev/null @@ -1,33 +0,0 @@ - - - - - - - - - %d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/iot/iot-tenant-service/src/main/resources/reflection-config.json b/iot/iot-tenant-service/src/main/resources/reflection-config.json new file mode 100644 index 00000000000..8c2c60cdc93 --- /dev/null +++ b/iot/iot-tenant-service/src/main/resources/reflection-config.json @@ -0,0 +1,26 @@ +[ + { + "name": "io.jsonwebtoken.impl.DefaultJwtParser", + "methods": [ + { "name": "", "parameterTypes": [] } + ] + }, + { + "name": "io.jsonwebtoken.impl.io.RuntimeClasspathDeserializerLocator", + "methods": [ + { "name": "", "parameterTypes": [] } + ] + }, + { + "name": "com.fasterxml.jackson.databind.ObjectMapper", + "methods": [ + { "name": "", "parameterTypes": [] } + ] + }, + { + "name": "io.jsonwebtoken.io.JacksonDeserializer", + "methods": [ + { "name": "", "parameterTypes": [] } + ] + } +] \ No newline at end of file diff --git a/iot/iot-utils/pom.xml b/iot/iot-utils/pom.xml index 39b573b4831..a72aceea52e 100644 --- a/iot/iot-utils/pom.xml +++ b/iot/iot-utils/pom.xml @@ -8,7 +8,7 @@ io.enmasse iot - 0.32-SNAPSHOT + 1.0-SNAPSHOT iot-utils diff --git a/iot/pom.xml b/iot/pom.xml index a28ef85ee86..cf53bb8d3cc 100644 --- a/iot/pom.xml +++ b/iot/pom.xml @@ -7,13 +7,13 @@ io.enmasse enmasse - 0.32-SNAPSHOT + 1.0-SNAPSHOT iot pom - 1.2.3 + 1.3.0-M3 9.4.16.Final 42.2.9 @@ -25,6 +25,7 @@ linux-x86_64-fedora 0.9.5.5 + 1.65 @@ -220,6 +221,19 @@ ${infinispan.version} test + + + org.bouncycastle + bcprov-jdk15on + ${bouncycastle.version} + + + + org.bouncycastle + bcpkix-jdk15on + ${bouncycastle.version} + + diff --git a/k8s-api-testutil/README.md b/k8s-api-testutil/README.md deleted file mode 100644 index cf52dfc19cd..00000000000 --- a/k8s-api-testutil/README.md +++ /dev/null @@ -1 +0,0 @@ -Test utilities for testing the address and address space java APIs diff --git a/k8s-api-testutil/pom.xml b/k8s-api-testutil/pom.xml deleted file mode 100644 index 6232b234d4f..00000000000 --- a/k8s-api-testutil/pom.xml +++ /dev/null @@ -1,19 +0,0 @@ - - - - io.enmasse - enmasse - 0.32-SNAPSHOT - - 4.0.0 - io.enmasse - k8s-api-testutil - - - io.enmasse - k8s-api - compile - - - diff --git a/k8s-api-testutil/src/main/java/io/enmasse/k8s/api/TestAddressApi.java b/k8s-api-testutil/src/main/java/io/enmasse/k8s/api/TestAddressApi.java deleted file mode 100644 index 0a0b85f6331..00000000000 --- a/k8s-api-testutil/src/main/java/io/enmasse/k8s/api/TestAddressApi.java +++ /dev/null @@ -1,94 +0,0 @@ -/* - * Copyright 2017-2018, EnMasse authors. - * License: Apache License 2.0 (see the file LICENSE or http://apache.org/licenses/LICENSE-2.0.html). - */ - -package io.enmasse.k8s.api; - -import io.enmasse.address.model.Address; -import io.enmasse.address.model.AddressBuilder; -import io.enmasse.k8s.api.cache.CacheWatcher; - -import java.time.Duration; -import java.util.LinkedHashSet; -import java.util.Map; -import java.util.Optional; -import java.util.Set; - -public class TestAddressApi implements AddressApi { - public boolean throwException = false; - - private final Set

addresses = new LinkedHashSet<>(); - - @Override - public void createAddress(Address destination) { - if (throwException) { - throw new RuntimeException("exception"); - } - addresses.add(destination); - } - - @Override - public boolean replaceAddress(Address destination) { - if (addresses.stream().noneMatch(d -> d.getMetadata().getName().equals(destination.getMetadata().getName()))) { - return false; - } - deleteAddress(destination); // necessary, because a simple set.add() doesn't replace the element - createAddress(destination); - return true; - } - - @Override - public boolean deleteAddress(Address destination) { - if (throwException) { - throw new RuntimeException("exception"); - } - return addresses.remove(destination); - } - - @Override - public Watch watchAddresses(CacheWatcher
watcher, Duration resyncInterval) throws Exception { - return null; - } - - @Override - public Optional
getAddressWithName(String namespace, String address) { - if (throwException) { - throw new RuntimeException("exception"); - } - return addresses.stream().filter(d -> d.getMetadata().getName().equals(address)).findAny(); - } - - @Override - public Set
listAddresses(String namespace) { - if (throwException) { - throw new RuntimeException("exception"); - } - Set
listed = new LinkedHashSet<>(); - for (Address address : addresses) { - if (namespace.equals(address.getMetadata().getNamespace())) { - listed.add(address); - } - } - return listed; - } - - @Override - public Set
listAddressesWithLabels(String namespace, Map labels) { - return listAddresses(namespace); - } - - @Override - public ContinuationResult
listAddresses(String namespace, Integer limit, ContinuationResult
continueValue, Map labels) { - return ContinuationResult.from(listAddresses(namespace), null); - } - - @Override - public void deleteAddresses(String namespace) { - addresses.removeIf(address -> namespace.equals(address.getMetadata().getNamespace())); - } - - public void setAllAddressesReady(boolean ready) { - addresses.stream().forEach(d -> replaceAddress(new AddressBuilder(d).withNewStatus(ready).build())); - } -} diff --git a/k8s-api-testutil/src/main/java/io/enmasse/k8s/api/TestAddressSpaceApi.java b/k8s-api-testutil/src/main/java/io/enmasse/k8s/api/TestAddressSpaceApi.java deleted file mode 100644 index fbc17f09555..00000000000 --- a/k8s-api-testutil/src/main/java/io/enmasse/k8s/api/TestAddressSpaceApi.java +++ /dev/null @@ -1,143 +0,0 @@ -/* - * Copyright 2017-2018, EnMasse authors. - * License: Apache License 2.0 (see the file LICENSE or http://apache.org/licenses/LICENSE-2.0.html). - */ -package io.enmasse.k8s.api; - -import io.enmasse.address.model.AddressSpace; -import io.enmasse.k8s.api.cache.CacheWatcher; - -import static java.util.Optional.ofNullable; - -import java.time.Duration; -import java.util.*; -import java.util.stream.Collectors; - -public class TestAddressSpaceApi implements AddressSpaceApi { - Map> addressSpaces = new HashMap<>(); - Map> addressApiMap = new LinkedHashMap<>(); - public boolean throwException = false; - - @Override - public Optional getAddressSpaceWithName(String namespace, String addressSpaceId) { - if (throwException) { - throw new RuntimeException("foo"); - } - return ofNullable(addressSpaces.get(namespace)) - .map(ns -> ns.get(addressSpaceId)); - } - - @Override - public void createAddressSpace(AddressSpace addressSpace) { - if (throwException) { - throw new RuntimeException("foo"); - } - addressSpaces - .computeIfAbsent(addressSpace.getMetadata().getNamespace(), ns -> new HashMap<>()) - .put(addressSpace.getMetadata().getName(), addressSpace); - } - - @Override - public boolean replaceAddressSpace(AddressSpace addressSpace) { - Map addressSpaces = this.addressSpaces.get(addressSpace.getMetadata().getNamespace()); - if ( addressSpaces == null ) { - return false; - } - if (!addressSpaces.containsKey(addressSpace.getMetadata().getName())) { - return false; - } - createAddressSpace(addressSpace); - return true; - } - - @Override - public boolean deleteAddressSpace(AddressSpace addressSpace) { - if (throwException) { - throw new RuntimeException("foo"); - } - return ofNullable(addressSpaces.get(addressSpace.getMetadata().getNamespace())) - .map(as -> as.remove(addressSpace.getMetadata().getName()).getMetadata() != null ) - .orElse(false); - } - - @Override - public Set listAddressSpaces(String namespace) { - if (throwException) { - throw new RuntimeException("foo"); - } - return ofNullable(addressSpaces.get(namespace)) - .map(as -> new LinkedHashSet<>(as.values())) - .orElseGet(LinkedHashSet::new); - } - - @Override - public Set listAddressSpacesWithLabels(String namespace, Map labels) { - return null; - } - - @Override - public Set listAllAddressSpaces() { - if (throwException) { - throw new RuntimeException("foo"); - } - - return addressSpaces.values().stream() - .flatMap(as -> as.values().stream()) - .collect(Collectors.toSet()); - } - - @Override - public Set listAllAddressSpacesWithLabels(Map labels) { - if (throwException) { - throw new RuntimeException("foo"); - } - return listAllAddressSpaces(); - } - - @Override - public void deleteAddressSpaces(String namespace) { - Map addressSpaces = this.addressSpaces.computeIfAbsent(namespace, x -> new HashMap<>()); - Map addressApiMap = this.addressApiMap.computeIfAbsent(namespace, x -> new HashMap<>()); - - for (AddressSpace addressSpace : new HashSet<>(addressSpaces.values())) { - if (namespace.equals(addressSpace.getMetadata().getNamespace())) { - addressSpaces.remove(addressSpace.getMetadata().getName()); - addressApiMap.remove(addressSpace.getMetadata().getName()); - } - } - } - - @Override - public Watch watchAddressSpaces(CacheWatcher watcher, Duration resyncInterval) throws Exception { - return null; - } - - @Override - public AddressApi withAddressSpace(AddressSpace addressSpace) { - final String addressSpaceNamespace = addressSpace.getMetadata().getNamespace(); - final String addressSpaceName = addressSpace.getMetadata().getName(); - - Map addressApiMap = this.addressApiMap.computeIfAbsent(addressSpaceNamespace, x -> new HashMap<>() ); - Map addressSpaces = this.addressSpaces.computeIfAbsent(addressSpaceNamespace, x -> new HashMap<>() ); - - if (!addressApiMap.containsKey(addressSpaceName)) { - addressSpaces.put(addressSpaceName, addressSpace); - addressApiMap.put(addressSpaceName, new TestAddressApi()); - } - - return addressApiMap.get(addressSpaceName); - } - - public Collection getAddressApis() { - return addressApiMap.values().stream() - .flatMap(as -> as.values().stream()) - .collect(Collectors.toList()); - } - - public void setAllInstancesReady(boolean ready) { - addressSpaces.values().stream() - .flatMap(as -> as.values().stream()) - .forEach(as -> as.getStatus().setReady(ready)); - - } -} diff --git a/k8s-api-testutil/src/main/java/io/enmasse/k8s/api/TestSchemaApi.java b/k8s-api-testutil/src/main/java/io/enmasse/k8s/api/TestSchemaApi.java deleted file mode 100644 index 07d988969ec..00000000000 --- a/k8s-api-testutil/src/main/java/io/enmasse/k8s/api/TestSchemaApi.java +++ /dev/null @@ -1,151 +0,0 @@ -/* - * Copyright 2018, EnMasse authors. - * License: Apache License 2.0 (see the file LICENSE or http://apache.org/licenses/LICENSE-2.0.html). - */ -package io.enmasse.k8s.api; - -import io.enmasse.address.model.AddressSpaceTypeBuilder; -import io.enmasse.address.model.AddressTypeBuilder; -import io.enmasse.address.model.EndpointSpecBuilder; -import io.enmasse.address.model.Schema; -import io.enmasse.address.model.SchemaBuilder; -import io.enmasse.admin.model.v1.AddressPlanBuilder; -import io.enmasse.admin.model.v1.AddressSpacePlanBuilder; -import io.enmasse.admin.model.v1.AuthenticationServiceBuilder; -import io.enmasse.admin.model.v1.AuthenticationServiceBuilder; -import io.enmasse.admin.model.v1.StandardInfraConfigBuilder; -import io.enmasse.admin.model.v1.StandardInfraConfigSpecBuilder; -import io.fabric8.kubernetes.api.model.ObjectMetaBuilder; - -import java.time.Duration; -import java.util.Arrays; -import java.util.Collections; -import java.util.Map; - -public class TestSchemaApi implements SchemaApi { - public Schema getSchema() { - return new SchemaBuilder() - .withAddressSpaceTypes(Arrays.asList( - new AddressSpaceTypeBuilder() - .withName("brokered") - .withDescription("Test Type") - .withAddressTypes(Collections.singletonList( - new AddressTypeBuilder() - .withName("queue") - .withPlans(Arrays.asList( - new AddressPlanBuilder() - .withMetadata(new ObjectMetaBuilder() - .withName("plan1") - .build()) - .editOrNewSpec() - .withAddressType("queue") - .withResources(Map.of("broker", 0.1)) - .endSpec() - .build() - )) - .build())) - .build(), - new AddressSpaceTypeBuilder() - .withName("type1") - .withDescription("Test Type") - .withAvailableEndpoints(Collections.singletonList(new EndpointSpecBuilder() - .withName("messaging") - .withService("messaging") - .build())) - .withAddressTypes(Arrays.asList( - new AddressTypeBuilder() - .withName("anycast") - .withDescription("Test direct") - .withPlans(Arrays.asList( - new AddressPlanBuilder() - .withMetadata(new ObjectMetaBuilder() - .withName("plan1") - .build()) - .editOrNewSpec() - .withAddressType("anycast") - .withResources(Map.of("router", 1.0)) - .endSpec() - .build() - )) - .build(), - new AddressTypeBuilder() - .withName("queue") - .withDescription("Test queue") - .withPlans(Arrays.asList( - new AddressPlanBuilder() - .withMetadata(new ObjectMetaBuilder() - .withName("pooled-inmemory") - .build()) - .editOrNewSpec() - .withAddressType("queue") - .withResources(Map.of("broker", 0.1)) - .endSpec() - .build(), - new AddressPlanBuilder() - .withMetadata(new ObjectMetaBuilder() - .withName("plan1") - .build()) - .editOrNewSpec() - .withAddressType("queue") - .withResources(Map.of("broker", 1.0)) - .endSpec() - .build()) - ).build() - )) - .withInfraConfigs(Arrays.asList(new StandardInfraConfigBuilder() - .withMetadata(new ObjectMetaBuilder() - .withName("infra") - .build()) - .withSpec(new StandardInfraConfigSpecBuilder() - .withVersion("1.0") - .build()) - .build())) - .withPlans(Arrays.asList( - new AddressSpacePlanBuilder() - .withMetadata(new ObjectMetaBuilder() - .withName("myplan") - .build()) - .editOrNewSpec() - .withInfraConfigRef("infra") - .withAddressSpaceType("brokered") - .withResourceLimits(Map.of("broker", 1.0)) - .withAddressPlans(Arrays.asList("plan1")) - .endSpec() - .build(), - new AddressSpacePlanBuilder() - .withMetadata(new ObjectMetaBuilder() - .withName("myplan") - .build()) - .editOrNewSpec() - .withInfraConfigRef("infra") - .withAddressSpaceType("type1") - .withResourceLimits(Map.of("broker", 1.0)) - .withAddressPlans(Arrays.asList("plan1")) - .endSpec() - .build() - )) - .build() - - )) - .withAuthenticationServices(new AuthenticationServiceBuilder() - .withNewMetadata() - .withName("standard") - .endMetadata() - .withNewSpec() - .withNewStandard() - .endStandard() - .endSpec() - .withNewStatus() - .withHost("example.com") - .withPort(5671) - .endStatus() - .build()) - .build(); - } - - @Override - public Watch watchSchema(Watcher schemaStore, Duration resyncInterval) throws Exception { - return null; - } - -} diff --git a/k8s-api/README.md b/k8s-api/README.md deleted file mode 100644 index 40219589f9f..00000000000 --- a/k8s-api/README.md +++ /dev/null @@ -1,2 +0,0 @@ -The k8s-api is a java API for creating, deleteing, updating and watching address spaces and addresses in -Kubernetes. diff --git a/k8s-api/pom.xml b/k8s-api/pom.xml deleted file mode 100644 index e2298fae70a..00000000000 --- a/k8s-api/pom.xml +++ /dev/null @@ -1,62 +0,0 @@ - - - - io.enmasse - enmasse - 0.32-SNAPSHOT - - 4.0.0 - io.enmasse - k8s-api - - - io.enmasse - api-model - - - io.fabric8 - openshift-client - compile - - - org.slf4j - slf4j-api - compile - - - org.slf4j - jul-to-slf4j - - - org.junit.jupiter - junit-jupiter-api - test - - - org.junit.jupiter - junit-jupiter-engine - test - - - org.hamcrest - hamcrest - test - - - org.mockito - mockito-core - test - - - io.fabric8 - openshift-server-mock - test - - - ch.qos.logback - logback-classic - test - - - diff --git a/k8s-api/src/main/java/io/enmasse/k8s/api/AddressApi.java b/k8s-api/src/main/java/io/enmasse/k8s/api/AddressApi.java deleted file mode 100644 index 29d8d8e0829..00000000000 --- a/k8s-api/src/main/java/io/enmasse/k8s/api/AddressApi.java +++ /dev/null @@ -1,39 +0,0 @@ -/* - * Copyright 2017-2018, EnMasse authors. - * License: Apache License 2.0 (see the file LICENSE or http://apache.org/licenses/LICENSE-2.0.html). - */ -package io.enmasse.k8s.api; - -import io.enmasse.address.model.Address; -import io.enmasse.k8s.api.cache.CacheWatcher; - -import java.time.Duration; -import java.util.Collection; -import java.util.Collections; -import java.util.Map; -import java.util.Optional; - -/** - * API for managing addresses in kubernetes. - */ -public interface AddressApi { - Optional
getAddressWithName(String namespace, String name); - - ContinuationResult
listAddresses(String namespace, Integer limit, ContinuationResult
continueValue, Map labels); - - default Collection
listAddresses(String namespace) { - return listAddressesWithLabels(namespace, Collections.emptyMap()); - } - - default Collection
listAddressesWithLabels(String namespace, Map labels) { - return listAddresses(namespace, null, null, labels).getItems(); - } - - void deleteAddresses(String namespace); - - void createAddress(Address address); - boolean replaceAddress(Address address); - boolean deleteAddress(Address address); - - Watch watchAddresses(CacheWatcher
watcher, Duration resyncInterval) throws Exception; -} diff --git a/k8s-api/src/main/java/io/enmasse/k8s/api/AddressSpaceApi.java b/k8s-api/src/main/java/io/enmasse/k8s/api/AddressSpaceApi.java deleted file mode 100644 index ec8fa2415b5..00000000000 --- a/k8s-api/src/main/java/io/enmasse/k8s/api/AddressSpaceApi.java +++ /dev/null @@ -1,46 +0,0 @@ -/* - * Copyright 2017-2018, EnMasse authors. - * License: Apache License 2.0 (see the file LICENSE or http://apache.org/licenses/LICENSE-2.0.html). - */ -package io.enmasse.k8s.api; - -import java.time.Duration; -import java.util.Map; -import java.util.Optional; -import java.util.Set; - -import io.enmasse.address.model.AddressSpace; -import io.enmasse.k8s.api.cache.CacheWatcher; - -/** - * API for managing address spaces. - */ -public interface AddressSpaceApi { - Optional getAddressSpaceWithName(String namespace, String id); - void createAddressSpace(AddressSpace addressSpace) throws Exception; - - /** - * Replace an existing address space instance with the newly provided one. - *
- * Note: Implementations of this interface must ensure - * that further changes to the address space instance don't have an impact - * on the internal state. - * - * @param addressSpace The address space to update. - * @return {@code true} is the address space as found and updated. {@code false} if the address space could not be found. - * @throws Exception In case of any error other than a non-existing instance. - */ - boolean replaceAddressSpace(AddressSpace addressSpace) throws Exception; - boolean deleteAddressSpace(AddressSpace addressSpace); - Set listAddressSpaces(String namespace); - Set listAddressSpacesWithLabels(String namespace, Map labels); - - Set listAllAddressSpaces(); - Set listAllAddressSpacesWithLabels(Map labels); - - void deleteAddressSpaces(String namespace); - - Watch watchAddressSpaces(CacheWatcher watcher, Duration resyncInterval) throws Exception; - - AddressApi withAddressSpace(AddressSpace addressSpace); -} diff --git a/k8s-api/src/main/java/io/enmasse/k8s/api/AuthenticationServiceRegistry.java b/k8s-api/src/main/java/io/enmasse/k8s/api/AuthenticationServiceRegistry.java deleted file mode 100644 index b10cad4d67d..00000000000 --- a/k8s-api/src/main/java/io/enmasse/k8s/api/AuthenticationServiceRegistry.java +++ /dev/null @@ -1,22 +0,0 @@ -/* - * Copyright 2019, EnMasse authors. - * License: Apache License 2.0 (see the file LICENSE or http://apache.org/licenses/LICENSE-2.0.html). - */ -package io.enmasse.k8s.api; - -import io.enmasse.address.model.AuthenticationService; -import io.enmasse.admin.model.v1.AuthenticationServiceType; - -import java.util.List; -import java.util.Optional; - -/** - * Interface for resolving different authentication service - */ -public interface AuthenticationServiceRegistry { - Optional findAuthenticationService(AuthenticationService authenticationService); - Optional findAuthenticationServiceByName(String name); - List findAuthenticationServiceByType(AuthenticationServiceType type); - Optional resolveDefaultAuthenticationService(); - List listAuthenticationServices(); -} diff --git a/k8s-api/src/main/java/io/enmasse/k8s/api/CachingSchemaProvider.java b/k8s-api/src/main/java/io/enmasse/k8s/api/CachingSchemaProvider.java deleted file mode 100644 index 115bea43aa4..00000000000 --- a/k8s-api/src/main/java/io/enmasse/k8s/api/CachingSchemaProvider.java +++ /dev/null @@ -1,41 +0,0 @@ -/* - * Copyright 2016-2018, EnMasse authors. - * License: Apache License 2.0 (see the file LICENSE or http://apache.org/licenses/LICENSE-2.0.html). - */ -package io.enmasse.k8s.api; - -import java.util.List; -import java.util.concurrent.CopyOnWriteArrayList; - -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import io.enmasse.address.model.Schema; -import io.enmasse.address.model.SchemaBuilder; - -public class CachingSchemaProvider implements SchemaProvider, Watcher { - private static final Logger log = LoggerFactory.getLogger(CachingSchemaProvider.class); - private volatile Schema schema = null; - private final List listeners = new CopyOnWriteArrayList<>(); - - @Override - public Schema getSchema() { - return schema; - } - - public void registerListener(SchemaListener listener) { - listeners.add(listener); - } - - @Override - public void onUpdate(List items) { - if (items == null || items.isEmpty()) { - return; - } - schema = new SchemaBuilder(items.get(0)).build(); - log.info("Schema updated: {}", schema.printSchema()); - for (SchemaListener listener : listeners) { - listener.onUpdate(schema); - } - } -} diff --git a/k8s-api/src/main/java/io/enmasse/k8s/api/ConfigMapAddressApi.java b/k8s-api/src/main/java/io/enmasse/k8s/api/ConfigMapAddressApi.java deleted file mode 100644 index a365eb9dc6a..00000000000 --- a/k8s-api/src/main/java/io/enmasse/k8s/api/ConfigMapAddressApi.java +++ /dev/null @@ -1,269 +0,0 @@ -/* - * Copyright 2017-2018, EnMasse authors. - * License: Apache License 2.0 (see the file LICENSE or http://apache.org/licenses/LICENSE-2.0.html). - */ -package io.enmasse.k8s.api; - -import java.io.IOException; -import java.io.UncheckedIOException; -import java.time.Clock; -import java.time.Duration; -import java.util.LinkedHashMap; -import java.util.LinkedHashSet; -import java.util.Map; -import java.util.Optional; -import java.util.Set; -import java.util.stream.Collectors; - -import io.fabric8.kubernetes.api.model.OwnerReference; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import com.fasterxml.jackson.databind.ObjectMapper; - -import io.enmasse.address.model.Address; -import io.enmasse.address.model.AddressBuilder; -import io.enmasse.common.model.AbstractHasMetadataFluent.MetadataNested; -import io.enmasse.config.AnnotationKeys; -import io.enmasse.config.LabelKeys; -import io.enmasse.k8s.api.cache.CacheWatcher; -import io.enmasse.k8s.api.cache.Controller; -import io.enmasse.k8s.api.cache.EventCache; -import io.enmasse.k8s.api.cache.HasMetadataFieldExtractor; -import io.enmasse.k8s.api.cache.ListOptions; -import io.enmasse.k8s.api.cache.ListerWatcher; -import io.enmasse.k8s.api.cache.Reflector; -import io.enmasse.k8s.api.cache.WorkQueue; -import io.fabric8.kubernetes.api.model.ConfigMap; -import io.fabric8.kubernetes.api.model.ConfigMapBuilder; -import io.fabric8.kubernetes.api.model.ConfigMapList; -import io.fabric8.kubernetes.client.KubernetesClientException; -import io.fabric8.kubernetes.client.NamespacedKubernetesClient; -import io.fabric8.kubernetes.client.RequestConfig; -import io.fabric8.kubernetes.client.RequestConfigBuilder; - -/** - * Implements the AddressApi using config maps. - */ -public class ConfigMapAddressApi implements AddressApi, ListerWatcher { - - private static final Logger log = LoggerFactory.getLogger(ConfigMapAddressApi.class); - private final NamespacedKubernetesClient client; - private final String infraUuid; - private final WorkQueue cache = new EventCache<>(new HasMetadataFieldExtractor<>()); - private ObjectMapper mapper = new ObjectMapper(); - private final OwnerReference ownerReference; - private final String version; - - public ConfigMapAddressApi(NamespacedKubernetesClient client, String infraUuid, OwnerReference ownerReference, String version) { - this.client = client; - this.infraUuid = infraUuid; - this.ownerReference = ownerReference; - this.version = version; - } - - @Override - public Optional
getAddressWithName(String namespace, String name) { - ConfigMap map = client.configMaps().withName(getConfigMapName(namespace, name)).get(); - if (map == null) { - return Optional.empty(); - } else { - return Optional.of(getAddressFromConfig(map)); - } - } - - private Address getAddressFromConfig(ConfigMap map) { - Map data = map.getData(); - - try { - Address address = mapper.readValue(data.get("config.json"), Address.class); - AddressBuilder builder = new AddressBuilder(address); - MetadataNested metadataBuilder = builder.editOrNewMetadata(); - - if (address.getMetadata().getUid() == null) { - metadataBuilder.withUid(map.getMetadata().getUid()); - } - - metadataBuilder.withResourceVersion(map.getMetadata().getResourceVersion()); - - if (address.getMetadata().getCreationTimestamp() == null) { - metadataBuilder.withCreationTimestamp(map.getMetadata().getCreationTimestamp()); - } - - if (address.getMetadata().getSelfLink() == null) { - metadataBuilder.withSelfLink("/apis/enmasse.io/v1beta1/namespaces/" + address.getMetadata().getNamespace() + "/addressspaces/" + Address.extractAddressSpace(address)); - } - - // commit changes to metadata - metadataBuilder.endMetadata(); - - return builder.build(); - } catch (IOException e) { - log.error("Error decoding address from configmap : {}", map, e); - throw new UncheckedIOException(e); - } - } - - @Override - public ContinuationResult
listAddresses(String namespace, Integer limit, ContinuationResult
continueValue, Map labelSelector) { - Map labels = labelSelector != null ? new LinkedHashMap<>(labelSelector) : new LinkedHashMap<>(2); - labels.put(LabelKeys.TYPE, "address-config"); - labels.put(LabelKeys.INFRA_UUID, infraUuid); - - Set
addresses = new LinkedHashSet<>(); - ConfigMapList list = client - .configMaps() - .withLabels(labels) - .list(limit, continueValue != null ? continueValue.getContinuation() : null); - - for (ConfigMap config : list.getItems()) { - Address address = getAddressFromConfig(config); - if (namespace.equals(address.getMetadata().getNamespace())) { - addresses.add(address); - } - } - return ContinuationResult.from(addresses, list.getMetadata().getContinue()); - } - - @Override - public void deleteAddresses(String namespace) { - Map labels = new LinkedHashMap<>(); - labels.put(LabelKeys.TYPE, "address-config"); - labels.put(LabelKeys.INFRA_UUID, infraUuid); - labels.put(LabelKeys.NAMESPACE, namespace); - - client.configMaps().withLabels(labels).withPropagationPolicy("Background").delete(); - } - - @Override - public void createAddress(Address address) { - String name = getConfigMapName(address.getMetadata().getNamespace(), address.getMetadata().getName()); - ConfigMap map = create(address); - client.configMaps().withName(name).create(map); - } - - @Override - public boolean replaceAddress(Address address) { - ConfigMap newMap = null; - try { - String name = getConfigMapName(address.getMetadata().getNamespace(), address.getMetadata().getName()); - newMap = create(address); - ConfigMap result; - if (address.getMetadata().getResourceVersion() != null) { - result = client.configMaps() - .withName(name) - .lockResourceVersion(address.getMetadata().getResourceVersion()) - .replace(newMap); - - } else { - result = client.configMaps() - .withName(name) - .replace(newMap); - } - cache.replace(newMap); - return result != null; - } catch (KubernetesClientException e) { - if (e.getStatus().getCode() == 404) { - return false; - } else { - throw e; - } - } - } - - private String getConfigMapName(String namespace, String name) { - return namespace + "." + name; - } - - private ConfigMap create(Address address) { - String addressSpaceName = Address.extractAddressSpace(address); - ConfigMapBuilder builder = new ConfigMapBuilder() - .editOrNewMetadata() - .withName(getConfigMapName(address.getMetadata().getNamespace(), address.getMetadata().getName())) - .addToLabels(address.getMetadata().getLabels()) - .addToLabels(LabelKeys.TYPE, "address-config") - .addToLabels(LabelKeys.INFRA_UUID, infraUuid) - - .addToAnnotations(address.getMetadata().getAnnotations()) - // TODO: Support other ways of doing this - .addToAnnotations(AnnotationKeys.ADDRESS_SPACE, addressSpaceName) - .endMetadata(); - - if (ownerReference != null) { - builder.editMetadata() - .addToOwnerReferences(ownerReference) - .endMetadata(); - } - - if (version != null) { - address.putAnnotation(AnnotationKeys.VERSION, version); - } - - if (address.getMetadata().getResourceVersion() != null) { - builder.editOrNewMetadata() - .withResourceVersion(address.getMetadata().getResourceVersion()) - .endMetadata(); - } - - try { - // Reset resource version to avoid unneeded extra writes - final Address newAddress = new AddressBuilder(address).editOrNewMetadata().withResourceVersion(null).endMetadata().build(); - builder.addToData("config.json", mapper.writeValueAsString(newAddress)); - return builder.build(); - } catch (IOException e) { - log.info("Error serializing address for {}", address, e); - throw new UncheckedIOException(e); - } - } - - @Override - public boolean deleteAddress(Address address) { - Boolean deleted = client.configMaps().withName(getConfigMapName(address.getMetadata().getNamespace(), address.getMetadata().getName())).cascading(true).delete(); - return deleted != null && deleted; - } - - @Override - public Watch watchAddresses(CacheWatcher
watcher, Duration resyncInterval) { - watcher.onInit(() -> cache.list().stream() - .map(ConfigMapAddressApi.this::getAddressFromConfig) - .collect(Collectors.toList())); - - Reflector.Config config = new Reflector.Config<>(); - config.setClock(Clock.systemUTC()); - config.setExpectedType(ConfigMap.class); - config.setListerWatcher(this); - config.setResyncInterval(resyncInterval); - config.setWorkQueue(cache); - config.setProcessor(map -> { - if (cache.hasSynced()) { - watcher.onUpdate(); - } - }); - - Reflector reflector = new Reflector<>(config); - Controller controller = new Controller(reflector); - controller.start(); - return controller; - } - - @Override - public ConfigMapList list(ListOptions listOptions) { - return client.configMaps() - .withLabel(LabelKeys.TYPE, "address-config") - .withLabel(LabelKeys.INFRA_UUID, infraUuid) - .list(); - } - - @Override - public io.fabric8.kubernetes.client.Watch watch(io.fabric8.kubernetes.client.Watcher watcher, ListOptions listOptions) { - RequestConfig requestConfig = new RequestConfigBuilder() - .withRequestTimeout(listOptions.getTimeoutSeconds()) - .build(); - return client.withRequestConfig(requestConfig).call(c -> - c.configMaps() - .withLabel(LabelKeys.TYPE, "address-config") - .withLabel(LabelKeys.INFRA_UUID, infraUuid) - .withResourceVersion(listOptions.getResourceVersion()) - .watch(watcher)); - } -} diff --git a/k8s-api/src/main/java/io/enmasse/k8s/api/ConfigMapAddressSpaceApi.java b/k8s-api/src/main/java/io/enmasse/k8s/api/ConfigMapAddressSpaceApi.java deleted file mode 100644 index 519231258dc..00000000000 --- a/k8s-api/src/main/java/io/enmasse/k8s/api/ConfigMapAddressSpaceApi.java +++ /dev/null @@ -1,288 +0,0 @@ -/* - * Copyright 2017-2018, EnMasse authors. - * License: Apache License 2.0 (see the file LICENSE or http://apache.org/licenses/LICENSE-2.0.html). - */ -package io.enmasse.k8s.api; - -import java.io.IOException; -import java.io.UncheckedIOException; -import java.time.Clock; -import java.time.Duration; -import java.util.Collections; -import java.util.HashMap; -import java.util.LinkedHashMap; -import java.util.LinkedHashSet; -import java.util.Map; -import java.util.Optional; -import java.util.Set; -import java.util.stream.Collectors; - -import io.fabric8.kubernetes.api.model.*; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import com.fasterxml.jackson.databind.ObjectMapper; - -import io.enmasse.address.model.AddressSpace; -import io.enmasse.address.model.AddressSpaceBuilder; -import io.enmasse.common.model.AbstractHasMetadataFluent.MetadataNested; -import io.enmasse.config.AnnotationKeys; -import io.enmasse.config.LabelKeys; -import io.enmasse.k8s.api.cache.CacheWatcher; -import io.enmasse.k8s.api.cache.Controller; -import io.enmasse.k8s.api.cache.EventCache; -import io.enmasse.k8s.api.cache.HasMetadataFieldExtractor; -import io.enmasse.k8s.api.cache.ListOptions; -import io.enmasse.k8s.api.cache.ListerWatcher; -import io.enmasse.k8s.api.cache.Reflector; -import io.enmasse.k8s.api.cache.WorkQueue; -import io.fabric8.kubernetes.client.KubernetesClientException; -import io.fabric8.kubernetes.client.NamespacedKubernetesClient; -import io.fabric8.kubernetes.client.RequestConfig; -import io.fabric8.kubernetes.client.RequestConfigBuilder; - -/** - * Implementation of the AddressSpace API towards Kubernetes - */ -public class ConfigMapAddressSpaceApi implements AddressSpaceApi, ListerWatcher { - protected final Logger log = LoggerFactory.getLogger(getClass().getName()); - private final NamespacedKubernetesClient client; - private final ObjectMapper mapper = new ObjectMapper(); - private final String version; - - public ConfigMapAddressSpaceApi(NamespacedKubernetesClient client, String version) { - this.client = client; - this.version = version; - } - - public static String getConfigMapName(String namespace, String name) { - return namespace + "." + name; - } - - private final WorkQueue cache = new EventCache<>(new HasMetadataFieldExtractor<>()); - - @Override - public Optional getAddressSpaceWithName(String namespace, String name) { - - ConfigMap map = client.configMaps().withName(getConfigMapName(namespace, name)).get(); - if (map == null) { - return Optional.empty(); - } else { - return Optional.of(getAddressSpaceFromConfig(map)); - } - } - - @Override - public void createAddressSpace(AddressSpace addressSpace) { - String name = getConfigMapName(addressSpace.getMetadata().getNamespace(), addressSpace.getMetadata().getName()); - ConfigMap map = create(addressSpace); - client.configMaps().withName(name).create(map); - } - - @Override - public boolean replaceAddressSpace(AddressSpace addressSpace) { - ConfigMap newMap = null; - try { - String name = getConfigMapName(addressSpace.getMetadata().getNamespace(), addressSpace.getMetadata().getName()); - newMap = create(addressSpace); - ConfigMap result; - if (addressSpace.getMetadata().getResourceVersion() != null) { - result = client.configMaps() - .withName(name) - .lockResourceVersion(addressSpace.getMetadata().getResourceVersion()) - .replace(newMap); - - } else { - result = client.configMaps() - .withName(name) - .replace(newMap); - } - cache.replace(newMap); - return result != null; - } catch (KubernetesClientException e) { - if (e.getStatus().getCode() == 404) { - return false; - } else { - throw e; - } - } - } - - private ConfigMap create(AddressSpace addressSpace) { - Map labels = addressSpace.getMetadata().getLabels(); - if ( labels == null ) { - labels = new HashMap<>(); - addressSpace.getMetadata().setLabels(labels); - } - String name = getConfigMapName(addressSpace.getMetadata().getNamespace(), addressSpace.getMetadata().getName()); - labels.put(LabelKeys.TYPE, "address-space"); - labels.put(LabelKeys.NAMESPACE, addressSpace.getMetadata().getNamespace()); - ConfigMapBuilder builder = new ConfigMapBuilder() - .editOrNewMetadata() - .withName(name) - .addToLabels(labels) - .addToAnnotations(addressSpace.getMetadata().getAnnotations()) - .endMetadata(); - - if (addressSpace.getMetadata().getResourceVersion() != null) { - builder.editOrNewMetadata() - .withResourceVersion(addressSpace.getMetadata().getResourceVersion()) - .endMetadata(); - } - - try { - // Reset resource version to avoid unneeded extra writes - AddressSpace newAddressSpace = new AddressSpaceBuilder(addressSpace).editOrNewMetadata().withResourceVersion(null).endMetadata().build(); - builder.addToData("config.json", mapper.writeValueAsString(newAddressSpace)); - return builder.build(); - } catch (IOException e) { - log.info("Error serializing addressspace for {}", addressSpace, e); - throw new UncheckedIOException(e); - } - } - - @Override - public boolean deleteAddressSpace(AddressSpace addressSpace) { - String name = getConfigMapName(addressSpace.getMetadata().getNamespace(), addressSpace.getMetadata().getName()); - Boolean deleted = client.configMaps().withName(name).cascading(true).delete(); - return deleted != null && deleted; - } - - @Override - public Set listAddressSpaces(String namespace) { - return listAddressSpacesWithLabels(namespace, Collections.emptyMap()); - } - - @Override - public Set listAddressSpacesWithLabels(String namespace, Map labels) { - labels = new LinkedHashMap<>(labels); - labels.put(LabelKeys.TYPE, "address-space"); - labels.put(LabelKeys.NAMESPACE, namespace); - return listAddressSpacesMatching(labels); - } - - @Override - public Set listAllAddressSpaces() { - return listAllAddressSpacesWithLabels(Collections.emptyMap()); - } - - @Override - public Set listAllAddressSpacesWithLabels(Map labels) { - labels = new LinkedHashMap<>(labels); - labels.put(LabelKeys.TYPE, "address-space"); - return listAddressSpacesMatching(labels); - } - - private Set listAddressSpacesMatching(Map labels) { - Set instances = new LinkedHashSet<>(); - ConfigMapList list = client.configMaps().withLabels(labels).list(); - for (ConfigMap map : list.getItems()) { - instances.add(getAddressSpaceFromConfig(map)); - } - return instances; - } - - @Override - public void deleteAddressSpaces(String namespace) { - Map labels = new LinkedHashMap<>(); - labels.put(LabelKeys.TYPE, "address-space"); - labels.put(LabelKeys.NAMESPACE, namespace); - client.configMaps().withLabels(labels).withPropagationPolicy("Background").delete(); - } - - private AddressSpace getAddressSpaceFromConfig(ConfigMap map) { - try { - AddressSpace addressSpace = mapper.readValue(map.getData().get("config.json"), AddressSpace.class); - AddressSpaceBuilder builder = new AddressSpaceBuilder(addressSpace); - MetadataNested metadataBuilder = builder.editOrNewMetadata(); - - if (addressSpace.getMetadata().getUid() == null) { - metadataBuilder.withUid(map.getMetadata().getUid()); - } - - metadataBuilder.withResourceVersion(map.getMetadata().getResourceVersion()); - - if (addressSpace.getMetadata().getCreationTimestamp() == null) { - metadataBuilder.withCreationTimestamp(map.getMetadata().getCreationTimestamp()); - } - - if (addressSpace.getMetadata().getSelfLink() == null) { - metadataBuilder.withSelfLink("/apis/enmasse.io/v1beta1/namespaces/" + addressSpace.getMetadata().getNamespace() + "/addressspaces/" + addressSpace.getMetadata().getName()); - } - - // commit changes - metadataBuilder.endMetadata(); - - return builder.build(); - } catch (IOException e) { - log.error("Error decoding address space from configmap : {}", map, e); - throw new UncheckedIOException(e); - } - } - - @Override - public Watch watchAddressSpaces(CacheWatcher watcher, Duration resyncInterval) { - Reflector.Config config = new Reflector.Config<>(); - watcher.onInit(() -> cache.list().stream() - .map(ConfigMapAddressSpaceApi.this::getAddressSpaceFromConfig) - .collect(Collectors.toList())); - config.setClock(Clock.systemUTC()); - config.setExpectedType(ConfigMap.class); - config.setListerWatcher(this); - config.setResyncInterval(resyncInterval); - config.setWorkQueue(cache); - config.setProcessor(map -> { - if (cache.hasSynced()) { - watcher.onUpdate(); - } - }); - - Reflector reflector = new Reflector<>(config); - Controller controller = new Controller(reflector); - controller.start(); - return controller; - } - - @Override - public AddressApi withAddressSpace(AddressSpace addressSpace) { - OwnerReference ownerReference = new OwnerReferenceBuilder() - .withApiVersion("v1") - .withKind("ConfigMap") - .withBlockOwnerDeletion(true) - .withController(true) - .withName(getConfigMapName(addressSpace.getMetadata().getNamespace(), addressSpace.getMetadata().getName())) - .withUid(addressSpace.getMetadata().getUid()) - .build(); - return new ConfigMapAddressApi(client, addressSpace.getAnnotation(AnnotationKeys.INFRA_UUID), ownerReference, version); - } - - @SuppressWarnings("unused") - private ConfigMapList list(String namespace) { - return client.configMaps() - .inNamespace(client.getNamespace()) - .withLabel(LabelKeys.TYPE, "address-space") - .withLabel(LabelKeys.NAMESPACE, namespace) - .list(); - } - - @Override - public ConfigMapList list(ListOptions listOptions) { - return client.configMaps() - .inNamespace(client.getNamespace()) - .withLabel(LabelKeys.TYPE, "address-space") - .list(); - } - - @Override - public io.fabric8.kubernetes.client.Watch watch(io.fabric8.kubernetes.client.Watcher watcher, ListOptions listOptions) { - RequestConfig requestConfig = new RequestConfigBuilder() - .withRequestTimeout(listOptions.getTimeoutSeconds()) - .build(); - return client.withRequestConfig(requestConfig).call(c -> - c.configMaps() - .inNamespace(client.getNamespace()) - .withLabel(LabelKeys.TYPE, "address-space") - .withResourceVersion(listOptions.getResourceVersion()) - .watch(watcher)); - } -} diff --git a/k8s-api/src/main/java/io/enmasse/k8s/api/ContinuationResult.java b/k8s-api/src/main/java/io/enmasse/k8s/api/ContinuationResult.java deleted file mode 100644 index abbd0109c6f..00000000000 --- a/k8s-api/src/main/java/io/enmasse/k8s/api/ContinuationResult.java +++ /dev/null @@ -1,50 +0,0 @@ -/* - * Copyright 2019, EnMasse authors. - * License: Apache License 2.0 (see the file LICENSE or http://apache.org/licenses/LICENSE-2.0.html). - */ -package io.enmasse.k8s.api; - -import java.util.Collection; -import java.util.Iterator; -import java.util.Objects; - -import io.fabric8.kubernetes.api.model.HasMetadata; -import io.fabric8.kubernetes.api.model.KubernetesResourceList; - -public class ContinuationResult implements Iterable { - private Collection items; - private String continuation; - - public static ContinuationResult from(final Collection items, final String continuation) { - Objects.requireNonNull(items); - return new ContinuationResult<>(items, continuation); - } - - public static ContinuationResult from(final KubernetesResourceList list) { - Objects.requireNonNull(list); - return new ContinuationResult<>(list.getItems(), list.getMetadata().getContinue()); - } - - private ContinuationResult(final Collection items, final String continuation) { - this.items = items; - this.continuation = continuation; - } - - public String getContinuation() { - return this.continuation; - } - - public Collection getItems() { - return this.items; - } - - public boolean canContinue() { - return this.continuation != null && !this.continuation.isBlank(); - } - - @Override - public Iterator iterator() { - return this.items.iterator(); - } - -} diff --git a/k8s-api/src/main/java/io/enmasse/k8s/api/CrdApi.java b/k8s-api/src/main/java/io/enmasse/k8s/api/CrdApi.java deleted file mode 100644 index 2321c327086..00000000000 --- a/k8s-api/src/main/java/io/enmasse/k8s/api/CrdApi.java +++ /dev/null @@ -1,12 +0,0 @@ -/* - * Copyright 2017-2018, EnMasse authors. - * License: Apache License 2.0 (see the file LICENSE or http://apache.org/licenses/LICENSE-2.0.html). - */ -package io.enmasse.k8s.api; - -import java.time.Duration; - -public interface CrdApi { - Watch watchResources(Watcher watcher, Duration resyncInterval); - void patch(T instance); -} diff --git a/k8s-api/src/main/java/io/enmasse/k8s/api/EventLogger.java b/k8s-api/src/main/java/io/enmasse/k8s/api/EventLogger.java deleted file mode 100644 index 8749912c5cd..00000000000 --- a/k8s-api/src/main/java/io/enmasse/k8s/api/EventLogger.java +++ /dev/null @@ -1,35 +0,0 @@ -/* - * Copyright 2017-2018, EnMasse authors. - * License: Apache License 2.0 (see the file LICENSE or http://apache.org/licenses/LICENSE-2.0.html). - */ -package io.enmasse.k8s.api; - -/** - * Interface for handling events - */ -public interface EventLogger { - - interface Reason { - String name(); - } - - enum Type { - Normal, - Warning - } - - interface ObjectKind { - String name(); - } - - /** - * Log an event. - * - * @param reason any of {@link Reason} - * @param message Descriptive message - * @param type any of {@link Type} - * @param objectKind any of {@link ObjectKind} - * @param objectName Name of object involved in event - */ - void log(Reason reason, String message, Type type, ObjectKind objectKind, String objectName); -} diff --git a/k8s-api/src/main/java/io/enmasse/k8s/api/KubeAddressApi.java b/k8s-api/src/main/java/io/enmasse/k8s/api/KubeAddressApi.java deleted file mode 100644 index 4036569a2da..00000000000 --- a/k8s-api/src/main/java/io/enmasse/k8s/api/KubeAddressApi.java +++ /dev/null @@ -1,172 +0,0 @@ -/* - * Copyright 2019, EnMasse authors. - * License: Apache License 2.0 (see the file LICENSE or http://apache.org/licenses/LICENSE-2.0.html). - */ -package io.enmasse.k8s.api; - -import io.enmasse.address.model.Address; -import io.enmasse.address.model.AddressBuilder; -import io.enmasse.address.model.AddressList; -import io.enmasse.address.model.CoreCrd; -import io.enmasse.address.model.DoneableAddress; -import io.enmasse.config.AnnotationKeys; -import io.enmasse.k8s.api.cache.*; -import io.fabric8.kubernetes.api.model.apiextensions.CustomResourceDefinition; -import io.fabric8.kubernetes.client.KubernetesClientException; -import io.fabric8.kubernetes.client.NamespacedKubernetesClient; -import io.fabric8.kubernetes.client.RequestConfig; -import io.fabric8.kubernetes.client.RequestConfigBuilder; -import io.fabric8.kubernetes.client.dsl.MixedOperation; -import io.fabric8.kubernetes.client.dsl.Resource; - -import java.time.Clock; -import java.time.Duration; -import java.util.Collections; -import java.util.Map; -import java.util.Optional; - -public class KubeAddressApi implements AddressApi, ListerWatcher { - private final NamespacedKubernetesClient kubernetesClient; - private final MixedOperation> client; - private final String namespace; - private final CustomResourceDefinition customResourceDefinition; - private final WorkQueue
cache = new EventCache<>(new HasMetadataFieldExtractor<>()); - private final String version; - - - private KubeAddressApi(NamespacedKubernetesClient kubeClient, String namespace, CustomResourceDefinition customResourceDefinition, String version) { - this.kubernetesClient = kubeClient; - this.namespace = namespace; - this.version = version; - this.client = kubeClient.customResources(customResourceDefinition, Address.class, AddressList.class, DoneableAddress.class); - this.customResourceDefinition = customResourceDefinition; - } - - public static AddressApi create(NamespacedKubernetesClient kubernetesClient, String namespace, String version) { - return new KubeAddressApi(kubernetesClient, namespace, CoreCrd.addresses(), version); - } - - @Override - public Optional
getAddressWithName(String namespace, String id) { - return Optional.ofNullable(client.inNamespace(namespace).withName(id).get()); - } - - @Override - public void createAddress(Address address) { - address = setDefaults(address); - client.inNamespace(address.getMetadata().getNamespace()).create(address); - } - - @Override - public boolean replaceAddress(Address address) { - boolean exists = client.inNamespace(address.getMetadata().getNamespace()).withName(address.getMetadata().getName()).get() != null; - if (!exists) { - return false; - } - - address = setDefaults(address); - try { - Address result = client - .inNamespace(address.getMetadata().getNamespace()) - .withName(address.getMetadata().getName()) - .lockResourceVersion(address.getMetadata().getResourceVersion()) - .replace(address); - - cache.replace(address); - return result != null; - } catch (KubernetesClientException e) { - if (e.getStatus().getCode() == 404) { - return false; - } else { - throw e; - } - } - } - - private Address setDefaults(Address address) { - String addressSpaceName = Address.extractAddressSpace(address); - AddressBuilder builder = new AddressBuilder(address) - .editOrNewMetadata() - .addToAnnotations(AnnotationKeys.ADDRESS_SPACE, addressSpaceName) - .endMetadata(); - - if (version != null) { - builder.editOrNewMetadata() - .addToAnnotations(AnnotationKeys.VERSION, version) - .endMetadata(); - } - - return builder.build(); - } - - @Override - public boolean deleteAddress(Address address) { - return client - .inNamespace(address.getMetadata().getNamespace()) - .delete(address); - } - - @Override - public ContinuationResult
listAddresses(final String namespace, final Integer limit, final ContinuationResult
continueValue, - final Map labels) { - - return ContinuationResult.from( - this.client.inNamespace(namespace) - .withLabels(labels != null ? labels : Collections.emptyMap()) - .list(limit, continueValue != null ? continueValue.getContinuation() : null)); - - - } - - public void deleteAddresses(String namespace) { - client.inNamespace(namespace).delete(); - } - - @Override - public Watch watchAddresses(CacheWatcher
watcher, Duration resyncInterval) throws Exception { - Reflector.Config config = new Reflector.Config<>(); - watcher.onInit(() -> cache.list()); - config.setClock(Clock.systemUTC()); - config.setExpectedType(Address.class); - config.setListerWatcher(this); - config.setResyncInterval(resyncInterval); - config.setWorkQueue(cache); - config.setProcessor(map -> { - if (cache.hasSynced()) { - watcher.onUpdate(); - } - }); - - Reflector reflector = new Reflector<>(config); - Controller controller = new Controller(reflector); - controller.start(); - return controller; - } - - @Override - public AddressList list(ListOptions listOptions) { - if (namespace != null) { - return client.inNamespace(namespace).list(); - } else { - return client.inAnyNamespace().list(); - } - } - - @Override - public io.fabric8.kubernetes.client.Watch watch(io.fabric8.kubernetes.client.Watcher
watcher, ListOptions listOptions) { - RequestConfig requestConfig = new RequestConfigBuilder() - .withRequestTimeout(listOptions.getTimeoutSeconds()) - .build(); - if (namespace != null) { - return kubernetesClient.withRequestConfig(requestConfig).call(c -> c.customResources(customResourceDefinition, Address.class, AddressList.class, DoneableAddress.class) - .inNamespace(namespace) - .withResourceVersion(listOptions.getResourceVersion()) - .watch(watcher)); - } else { - return kubernetesClient.withRequestConfig(requestConfig).call(c -> c.customResources(customResourceDefinition, Address.class, AddressList.class, DoneableAddress.class) - .inAnyNamespace() - .withResourceVersion(listOptions.getResourceVersion()) - .watch(watcher)); - } - } -} diff --git a/k8s-api/src/main/java/io/enmasse/k8s/api/KubeAddressSpaceApi.java b/k8s-api/src/main/java/io/enmasse/k8s/api/KubeAddressSpaceApi.java deleted file mode 100644 index 2f2b3f04583..00000000000 --- a/k8s-api/src/main/java/io/enmasse/k8s/api/KubeAddressSpaceApi.java +++ /dev/null @@ -1,181 +0,0 @@ -/* - * Copyright 2019, EnMasse authors. - * License: Apache License 2.0 (see the file LICENSE or http://apache.org/licenses/LICENSE-2.0.html). - */ -package io.enmasse.k8s.api; - -import java.time.Clock; -import java.time.Duration; -import java.util.HashSet; -import java.util.Map; -import java.util.Optional; -import java.util.Set; - -import io.enmasse.address.model.AddressSpace; -import io.enmasse.address.model.AddressSpaceBuilder; -import io.enmasse.address.model.AddressSpaceList; -import io.enmasse.address.model.CoreCrd; -import io.enmasse.address.model.DoneableAddressSpace; -import io.enmasse.k8s.api.cache.CacheWatcher; -import io.enmasse.k8s.api.cache.Controller; -import io.enmasse.k8s.api.cache.EventCache; -import io.enmasse.k8s.api.cache.HasMetadataFieldExtractor; -import io.enmasse.k8s.api.cache.ListOptions; -import io.enmasse.k8s.api.cache.ListerWatcher; -import io.enmasse.k8s.api.cache.Reflector; -import io.enmasse.k8s.api.cache.WorkQueue; -import io.fabric8.kubernetes.api.model.apiextensions.CustomResourceDefinition; -import io.fabric8.kubernetes.client.KubernetesClientException; -import io.fabric8.kubernetes.client.NamespacedKubernetesClient; -import io.fabric8.kubernetes.client.RequestConfig; -import io.fabric8.kubernetes.client.RequestConfigBuilder; -import io.fabric8.kubernetes.client.dsl.MixedOperation; -import io.fabric8.kubernetes.client.dsl.Resource; - -public class KubeAddressSpaceApi implements AddressSpaceApi, ListerWatcher { - private final NamespacedKubernetesClient kubernetesClient; - private final MixedOperation> client; - private final String namespace; - private final CustomResourceDefinition customResourceDefinition; - private final WorkQueue cache = new EventCache<>(new HasMetadataFieldExtractor<>()); - private final String version; - - private KubeAddressSpaceApi(NamespacedKubernetesClient kubeClient, String namespace, CustomResourceDefinition customResourceDefinition, String version) { - this.kubernetesClient = kubeClient; - this.namespace = namespace; - this.version = version; - this.client = kubeClient.customResources(customResourceDefinition, AddressSpace.class, AddressSpaceList.class, DoneableAddressSpace.class); - this.customResourceDefinition = customResourceDefinition; - } - - public static AddressSpaceApi create(NamespacedKubernetesClient kubernetesClient, String namespace, String version) { - return new KubeAddressSpaceApi(kubernetesClient, namespace, CoreCrd.addressSpaces(), version); - } - - @Override - public Optional getAddressSpaceWithName(String namespace, String id) { - return Optional.ofNullable(client.inNamespace(namespace).withName(id).get()); - } - - @Override - public void createAddressSpace(AddressSpace addressSpace) throws Exception { - client.inNamespace(addressSpace.getMetadata().getNamespace()).create(addressSpace); - } - - @Override - public boolean replaceAddressSpace(AddressSpace addressSpace) throws Exception { - boolean exists = client.inNamespace(addressSpace.getMetadata().getNamespace()).withName(addressSpace.getMetadata().getName()).get() != null; - if (!exists) { - return false; - } - - // Make a copy, so that no one else makes modifications to our instance. - // This is important as we do put this instance into your cache. - addressSpace = new AddressSpaceBuilder(addressSpace).build(); - - try { - AddressSpace result = client - .inNamespace(addressSpace.getMetadata().getNamespace()) - .withName(addressSpace.getMetadata().getName()) - .lockResourceVersion(addressSpace.getMetadata().getResourceVersion()) - .replace(addressSpace); - cache.replace(addressSpace); - return result != null; - } catch (KubernetesClientException e) { - if (e.getStatus().getCode() == 404) { - return false; - } else { - throw e; - } - } - } - - @Override - public boolean deleteAddressSpace(AddressSpace addressSpace) { - boolean exists = client.inNamespace(addressSpace.getMetadata().getNamespace()).withName(addressSpace.getMetadata().getName()).get() != null; - if (!exists) { - return false; - } - client.inNamespace(addressSpace.getMetadata().getNamespace()).delete(addressSpace); - return true; - } - - @Override - public Set listAddressSpaces(String namespace) { - return new HashSet<>(client.inNamespace(namespace).list().getItems()); - } - - @Override - public Set listAddressSpacesWithLabels(String namespace, Map labels) { - return new HashSet<>(client.inNamespace(namespace).withLabels(labels).list().getItems()); - } - - @Override - public Set listAllAddressSpaces() { - return new HashSet<>(client.inAnyNamespace().list().getItems()); - } - - @Override - public Set listAllAddressSpacesWithLabels(Map labels) { - return new HashSet<>(client.inAnyNamespace().withLabels(labels).list().getItems()); - } - - @Override - public void deleteAddressSpaces(String namespace) { - client.inNamespace(namespace).delete(); - } - - @Override - public Watch watchAddressSpaces(CacheWatcher watcher, Duration resyncInterval) throws Exception { - Reflector.Config config = new Reflector.Config<>(); - watcher.onInit(() -> cache.list()); - config.setClock(Clock.systemUTC()); - config.setExpectedType(AddressSpace.class); - config.setListerWatcher(this); - config.setResyncInterval(resyncInterval); - config.setWorkQueue(cache); - config.setProcessor(map -> { - if (cache.hasSynced()) { - watcher.onUpdate(); - } - }); - - Reflector reflector = new Reflector<>(config); - Controller controller = new Controller(reflector); - controller.start(); - return controller; - } - - @Override - public AddressApi withAddressSpace(AddressSpace addressSpace) { - return KubeAddressApi.create(kubernetesClient, namespace, version); - } - - @Override - public AddressSpaceList list(ListOptions listOptions) { - if (namespace != null) { - return client.inNamespace(namespace).list(); - } else { - return client.inAnyNamespace().list(); - } - } - - @Override - public io.fabric8.kubernetes.client.Watch watch(io.fabric8.kubernetes.client.Watcher watcher, ListOptions listOptions) { - RequestConfig requestConfig = new RequestConfigBuilder() - .withRequestTimeout(listOptions.getTimeoutSeconds()) - .build(); - if (namespace != null) { - return kubernetesClient.withRequestConfig(requestConfig).call(c -> c.customResources(customResourceDefinition, AddressSpace.class, AddressSpaceList.class, DoneableAddressSpace.class) - .inNamespace(namespace) - .withResourceVersion(listOptions.getResourceVersion()) - .watch(watcher)); - } else { - return kubernetesClient.withRequestConfig(requestConfig).call(c -> c.customResources(customResourceDefinition, AddressSpace.class, AddressSpaceList.class, DoneableAddressSpace.class) - .inAnyNamespace() - .withResourceVersion(listOptions.getResourceVersion()) - .watch(watcher)); - } - } - -} diff --git a/k8s-api/src/main/java/io/enmasse/k8s/api/KubeCrdApi.java b/k8s-api/src/main/java/io/enmasse/k8s/api/KubeCrdApi.java deleted file mode 100644 index 12d6c4040c9..00000000000 --- a/k8s-api/src/main/java/io/enmasse/k8s/api/KubeCrdApi.java +++ /dev/null @@ -1,95 +0,0 @@ -/* - * Copyright 2017-2018, EnMasse authors. - * License: Apache License 2.0 (see the file LICENSE or http://apache.org/licenses/LICENSE-2.0.html). - */ -package io.enmasse.k8s.api; - -import java.time.Clock; -import java.time.Duration; -import java.util.ArrayList; - -import io.enmasse.k8s.api.cache.Controller; -import io.enmasse.k8s.api.cache.EventCache; -import io.enmasse.k8s.api.cache.HasMetadataFieldExtractor; -import io.enmasse.k8s.api.cache.ListOptions; -import io.enmasse.k8s.api.cache.ListerWatcher; -import io.enmasse.k8s.api.cache.Reflector; -import io.enmasse.k8s.api.cache.WorkQueue; -import io.fabric8.kubernetes.api.model.Doneable; -import io.fabric8.kubernetes.api.model.HasMetadata; -import io.fabric8.kubernetes.api.model.KubernetesResourceList; -import io.fabric8.kubernetes.api.model.apiextensions.CustomResourceDefinition; -import io.fabric8.kubernetes.client.NamespacedKubernetesClient; -import io.fabric8.kubernetes.client.RequestConfig; -import io.fabric8.kubernetes.client.RequestConfigBuilder; - -public class KubeCrdApi, DT extends Doneable> implements CrdApi, ListerWatcher { - - private final NamespacedKubernetesClient client; - private final String namespace; - private final Class tClazz; - private final Class ltClazz; - private final Class
dtClazz; - private final CustomResourceDefinition customResourceDefinition; - - public KubeCrdApi(NamespacedKubernetesClient client, String namespace, CustomResourceDefinition customResourceDefinition, - Class tClazz, - Class ltClazz, - Class
dtClazz) { - this.client = client; - this.namespace = namespace; - this.tClazz = tClazz; - this.ltClazz = ltClazz; - this.dtClazz = dtClazz; - this.customResourceDefinition = customResourceDefinition; - } - - @Override - public LT list(ListOptions listOptions) { - if (namespace != null) { - return client.customResources(customResourceDefinition, tClazz, ltClazz, dtClazz).inNamespace(namespace).list(); - } else { - return client.customResources(customResourceDefinition, tClazz, ltClazz, dtClazz).inAnyNamespace().list(); - } - } - - @Override - public io.fabric8.kubernetes.client.Watch watch(io.fabric8.kubernetes.client.Watcher watcher, ListOptions listOptions) { - RequestConfig requestConfig = new RequestConfigBuilder() - .withRequestTimeout(listOptions.getTimeoutSeconds()) - .build(); - if (namespace != null) { - return client.withRequestConfig(requestConfig).call(c -> - c.customResources(customResourceDefinition, tClazz, ltClazz, dtClazz).inNamespace(namespace).withResourceVersion(listOptions.getResourceVersion()).watch(watcher)); - } else { - return client.withRequestConfig(requestConfig).call(c -> - c.customResources(customResourceDefinition, tClazz, ltClazz, dtClazz).inAnyNamespace().withResourceVersion(listOptions.getResourceVersion()).watch(watcher)); - } - } - - @Override - public Watch watchResources(Watcher watcher, Duration resyncInterval) { - WorkQueue queue = new EventCache<>(new HasMetadataFieldExtractor<>()); - Reflector.Config config = new Reflector.Config<>(); - config.setClock(Clock.systemUTC()); - config.setExpectedType(tClazz); - config.setListerWatcher(this); - config.setResyncInterval(resyncInterval); - config.setWorkQueue(queue); - config.setProcessor(map -> { - if (queue.hasSynced()) { - watcher.onUpdate(new ArrayList<>(queue.list())); - } - }); - - Reflector reflector = new Reflector<>(config); - Controller controller = new Controller(reflector); - controller.start(); - return controller; - } - - @Override - public void patch(T instance) { - client.customResources(customResourceDefinition, tClazz, ltClazz, dtClazz).inNamespace(namespace).withName(instance.getMetadata().getName()).patch(instance); - } -} diff --git a/k8s-api/src/main/java/io/enmasse/k8s/api/KubeEventLogger.java b/k8s-api/src/main/java/io/enmasse/k8s/api/KubeEventLogger.java deleted file mode 100644 index 291919d2159..00000000000 --- a/k8s-api/src/main/java/io/enmasse/k8s/api/KubeEventLogger.java +++ /dev/null @@ -1,69 +0,0 @@ -/* - * Copyright 2017-2018, EnMasse authors. - * License: Apache License 2.0 (see the file LICENSE or http://apache.org/licenses/LICENSE-2.0.html). - */ -package io.enmasse.k8s.api; - -import io.fabric8.kubernetes.api.model.Event; -import io.fabric8.kubernetes.api.model.EventBuilder; -import io.fabric8.kubernetes.client.KubernetesClient; -import io.fabric8.kubernetes.client.KubernetesClientException; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import java.time.Clock; -import java.time.Instant; - -public class KubeEventLogger implements EventLogger { - private static final Logger log = LoggerFactory.getLogger(KubeEventLogger.class); - private final KubernetesClient kubeClient; - private final String namespace; - private final Clock clock; - private final String componentName; - - public KubeEventLogger(KubernetesClient kubeClient, String namespace, Clock clock, String componentName) { - this.kubeClient = kubeClient; - this.namespace = namespace; - this.clock = clock; - this.componentName = componentName; - } - - @Override - public void log(Reason reason, String message, Type type, ObjectKind objectKind, String objectName) { - String eventName = componentName + "." + ((reason + message + type + objectKind + objectName).hashCode() & 0x7FFFFFFF); - log.info("Trying to create event with name {}", eventName); - Event existing = kubeClient.events().inNamespace(namespace).withName(eventName).get(); - String timestamp = Instant.now(clock).toString(); - try { - if (existing != null && existing.getType().equals(type.name()) && existing.getReason().equals(reason.name()) && existing.getInvolvedObject().getName().equals(objectName) && existing.getInvolvedObject().getKind().equals(objectKind.name())) { - existing.setCount(existing.getCount() + 1); - existing.setLastTimestamp(timestamp); - kubeClient.events().inNamespace(namespace).withName(eventName).replace(existing); - } else { - Event newEvent = new EventBuilder() - .withNewMetadata() - .withName(eventName) - .endMetadata() - .withCount(1) - .withReason(reason.name()) - .withMessage(message) - .withType(type.name()) - .withNewInvolvedObject() - .withNamespace(namespace) - .withKind(objectKind.name()) - .withName(objectName) - .endInvolvedObject() - .withFirstTimestamp(timestamp) - .withLastTimestamp(timestamp) - .withNewSource() - .withComponent(componentName) - .endSource() - .build(); - kubeClient.events().inNamespace(namespace).withName(eventName).create(newEvent); - } - LogEventLogger.log(log, reason, message, type, objectKind, objectName); - } catch (KubernetesClientException e) { - log.warn("Error reporting event: {}", e.getMessage()); - } - } -} diff --git a/k8s-api/src/main/java/io/enmasse/k8s/api/KubeResourceApplier.java b/k8s-api/src/main/java/io/enmasse/k8s/api/KubeResourceApplier.java deleted file mode 100644 index 64a44aac629..00000000000 --- a/k8s-api/src/main/java/io/enmasse/k8s/api/KubeResourceApplier.java +++ /dev/null @@ -1,82 +0,0 @@ -/* - * Copyright 2017-2018, EnMasse authors. - * License: Apache License 2.0 (see the file LICENSE or http://apache.org/licenses/LICENSE-2.0.html). - */ -package io.enmasse.k8s.api; - -import com.fasterxml.jackson.databind.ObjectMapper; -import com.fasterxml.jackson.dataformat.yaml.YAMLFactory; - -import io.fabric8.kubernetes.api.model.Doneable; -import io.fabric8.kubernetes.api.model.HasMetadata; -import io.fabric8.kubernetes.api.model.KubernetesResourceList; -import io.fabric8.kubernetes.client.dsl.NonNamespaceOperation; -import io.fabric8.kubernetes.client.dsl.Resource; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import java.io.File; -import java.util.Comparator; - -public class KubeResourceApplier { - private static final Logger log = LoggerFactory.getLogger(KubeResourceApplier.class); - private static final ObjectMapper mapper = new ObjectMapper(new YAMLFactory()); - - /** - * Apply new version of resources if considered changed - */ - public static , D extends Doneable> - void applyIfDifferent(File resourceDir, - NonNamespaceOperation> operation, - Class resourceClass, - Comparator comparator) { - if (resourceDir.isDirectory()) { - File[] files = resourceDir.listFiles(); - if (files != null) { - for (File file : files) { - T newResource = readAndParse(file, resourceClass); - if (newResource != null) { - T currentResource = operation.withName(newResource.getMetadata().getName()).get(); - if (currentResource == null) { - log.info("Creating {} {}", newResource.getKind(), newResource.getMetadata().getName()); - operation.create(newResource); - } else if (comparator.compare(currentResource, newResource) != 0) { - log.info("Updating {} {}", newResource.getKind(), newResource.getMetadata().getName()); - operation.createOrReplace(newResource); - } - } - } - } - } - } - - /** - * Creates resources in resourceDir if no resource of this type exists. - */ - public static , D extends Doneable> - void createIfNoneExists(File resourceDir, - NonNamespaceOperation> operation, - Class resourceClass) { - if (operation.list().getItems().isEmpty()) { - if (resourceDir.isDirectory()) { - File[] files = resourceDir.listFiles(); - if (files != null) { - for (File file : files) { - T newResource = readAndParse(file, resourceClass); - log.info("Creating {} {}", newResource.getKind(), newResource.getMetadata().getName()); - operation.create(newResource); - } - } - } - } - } - - private static T readAndParse(File file, Class resourceClass) { - try { - return mapper.readValue(file, resourceClass); - } catch (Exception e) { - log.error("Error reading and parsing file {} (skipped): {}", file.getAbsolutePath(), e.getMessage()); - return null; - } - } -} diff --git a/k8s-api/src/main/java/io/enmasse/k8s/api/KubeSchemaApi.java b/k8s-api/src/main/java/io/enmasse/k8s/api/KubeSchemaApi.java deleted file mode 100644 index ea59effdbd7..00000000000 --- a/k8s-api/src/main/java/io/enmasse/k8s/api/KubeSchemaApi.java +++ /dev/null @@ -1,511 +0,0 @@ -/* - * Copyright 2017-2018, EnMasse authors. - * License: Apache License 2.0 (see the file LICENSE or http://apache.org/licenses/LICENSE-2.0.html). - */ -package io.enmasse.k8s.api; - -import io.enmasse.address.model.*; -import io.enmasse.admin.model.AddressPlan; -import io.enmasse.admin.model.AddressSpacePlan; -import io.enmasse.admin.model.v1.AddressPlanStatus; -import io.enmasse.admin.model.v1.AddressPlanStatusBuilder; -import io.enmasse.admin.model.v1.AuthenticationService; -import io.enmasse.admin.model.v1.DoneableAuthenticationService; -import io.enmasse.admin.model.v1.*; -import io.fabric8.kubernetes.client.NamespacedKubernetesClient; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import java.time.Clock; -import java.time.Duration; -import java.time.ZoneId; -import java.time.format.DateTimeFormatter; -import java.util.*; -import java.util.stream.Collectors; - -public class KubeSchemaApi implements SchemaApi { - - private static final Logger log = LoggerFactory.getLogger(KubeSchemaApi.class); - private final CrdApi addressSpacePlanApi; - private final CrdApi addressPlanApi; - private final CrdApi brokeredInfraConfigApi; - private final CrdApi standardInfraConfigApi; - private final CrdApi authenticationServiceApi; - private final String defaultVersion; - - private final Clock clock; - private static final DateTimeFormatter formatter = DateTimeFormatter - .ofPattern("yyyy-MM-dd'T'HH:mm:ss.SSS'Z'") - .withZone(ZoneId.of("UTC")); - private final boolean isOpenShift; - private final boolean updateStatus; - - private volatile List currentAddressSpacePlans = Collections.emptyList(); - private volatile List currentAddressPlans = Collections.emptyList(); - private volatile List currentStandardInfraConfigs = Collections.emptyList(); - private volatile List currentBrokeredInfraConfigs = Collections.emptyList(); - private volatile List currentAuthenticationServices = Collections.emptyList(); - - public KubeSchemaApi(CrdApi addressSpacePlanApi, - CrdApi addressPlanApi, - CrdApi brokeredInfraConfigApi, - CrdApi standardInfraConfigApi, - CrdApi authenticationServiceApi, - String defaultVersion, Clock clock, - boolean isOpenShift, boolean updateStatus) { - this.addressSpacePlanApi = addressSpacePlanApi; - this.addressPlanApi = addressPlanApi; - this.brokeredInfraConfigApi = brokeredInfraConfigApi; - this.standardInfraConfigApi = standardInfraConfigApi; - this.authenticationServiceApi = authenticationServiceApi; - this.defaultVersion = defaultVersion; - this.clock = clock; - this.isOpenShift = isOpenShift; - this.updateStatus = updateStatus; - } - - public static KubeSchemaApi create(NamespacedKubernetesClient openShiftClient, String namespace, String defaultVersion, boolean isOpenShift, boolean updateStatus) { - CrdApi addressSpacePlanApi = new KubeCrdApi<>(openShiftClient, namespace, AdminCrd.addressSpacePlans(), - io.enmasse.admin.model.v1.AddressSpacePlan.class, - AddressSpacePlanList.class, - DoneableAddressSpacePlan.class); - - CrdApi addressPlanApi = new KubeCrdApi<>(openShiftClient, namespace, AdminCrd.addressPlans(), - io.enmasse.admin.model.v1.AddressPlan.class, - AddressPlanList.class, - DoneableAddressPlan.class); - - CrdApi brokeredInfraConfigApi = new KubeCrdApi<>(openShiftClient, namespace, AdminCrd.brokeredInfraConfigs(), - BrokeredInfraConfig.class, - BrokeredInfraConfigList.class, - DoneableBrokeredInfraConfig.class); - - CrdApi standardInfraConfigApi = new KubeCrdApi<>(openShiftClient, namespace, AdminCrd.standardInfraConfigs(), - StandardInfraConfig.class, - StandardInfraConfigList.class, - DoneableStandardInfraConfig.class); - - CrdApi authenticationServiceApi = new KubeCrdApi<>(openShiftClient, namespace, AdminCrd.authenticationServices(), - AuthenticationService.class, - AuthenticationServiceList.class, - DoneableAuthenticationService.class); - - Clock clock = Clock.systemUTC(); - - return new KubeSchemaApi(addressSpacePlanApi, addressPlanApi, brokeredInfraConfigApi, standardInfraConfigApi, authenticationServiceApi, defaultVersion, clock, isOpenShift, updateStatus); - } - - private void validateAddressSpacePlan(AddressSpacePlan addressSpacePlan, List addressPlans, List infraTemplateNames) { - String infraConfigRef = addressSpacePlan.getInfraConfigRef(); - if (!infraTemplateNames.contains(infraConfigRef)) { - String error = "Error validating address space plan " + addressSpacePlan.getMetadata().getName() + ": missing infra config definition " + infraConfigRef + ", found: " + infraTemplateNames; - log.warn(error); - throw new SchemaValidationException(error); - } - - Set addressPlanNames = addressPlans.stream().map(p -> p.getMetadata().getName()).collect(Collectors.toSet()); - if (!addressPlanNames.containsAll(addressSpacePlan.getAddressPlans())) { - Set missing = new HashSet<>(addressSpacePlan.getAddressPlans()); - missing.removeAll(addressPlanNames); - String error = "Error validating address space plan " + addressSpacePlan.getMetadata().getName() + ": missing " + missing; - log.warn(error); - throw new SchemaValidationException(error); - } - - Set resources = addressSpacePlan.getResourceLimits().keySet(); - List required = "brokered".equals(addressSpacePlan.getAddressSpaceType()) ? Arrays.asList("broker") : Arrays.asList("broker", "router", "aggregate"); - if (!resources.containsAll(required)) { - Set missing = new HashSet<>(required); - missing.removeAll(resources); - String error = "Error validating address space plan " + addressSpacePlan.getMetadata().getName() + ": missing resources " + missing; - log.warn(error); - throw new SchemaValidationException(error); - } - } - - private void validateAddressPlan(String addressSpaceType, AddressPlan addressPlan) { - - List supportedTypes = new ArrayList<>(); - List requiredResources = new ArrayList<>(); - if ("brokered".equals(addressSpaceType)) { - supportedTypes.addAll(Arrays.asList("queue", "topic")); - requiredResources.add("broker"); - } else if ("standard".equals(addressSpaceType)) { - supportedTypes.addAll(Arrays.asList("queue", "topic", "subscription", "anycast", "multicast")); - requiredResources.add("router"); - if (!Arrays.asList("anycast", "multicast").contains(addressPlan.getAddressType())) { - if (!"queue".equals(addressPlan.getAddressType()) && addressPlan.getPartitions() > 1) { - String error = "Error validating address plan " + addressPlan.getMetadata().getName() + ": address type " + addressPlan.getAddressType() + " does not support more than 1 partition"; - log.warn(error); - throw new SchemaValidationException(error); - } - requiredResources.add("broker"); - } - } - Set resourcesUsed = addressPlan.getResources().keySet(); - - if (!resourcesUsed.containsAll(requiredResources)) { - Set missing = new HashSet<>(requiredResources); - missing.removeAll(resourcesUsed); - String error = "Error validating address plan " + addressPlan.getMetadata().getName() + ": missing resources " + missing; - log.warn(error); - throw new SchemaValidationException(error); - } - - if (!supportedTypes.contains(addressPlan.getAddressType())) { - String error = String.format("Error validating address plan %s: address type %s not supported by address space type %s", addressPlan.getMetadata().getName(), addressPlan.getAddressType(), addressSpaceType); - log.warn(error); - throw new SchemaValidationException(error); - } - } - - private EndpointSpec createEndpointSpec(String name, String service, String port, TlsTermination tlsTermination) { - if (isOpenShift) { - return new EndpointSpecBuilder() - .withName(name) - .withService(service) - .withExpose(new ExposeSpecBuilder() - .withType(ExposeType.route) - .withRouteTlsTermination(tlsTermination) - .withRouteServicePort(port) - .build()) - .build(); - } else { - return new EndpointSpecBuilder() - .withName(name) - .withService(service) - .withExpose(new ExposeSpecBuilder() - .withType(ExposeType.loadbalancer) - .withLoadBalancerPorts(Collections.singletonList(port)) - .build()) - .build(); - } - } - - private AddressSpaceType createStandardType(List addressSpacePlans, Collection addressPlans, List standardInfraConfigs) { - AddressSpaceTypeBuilder builder = new AddressSpaceTypeBuilder(); - builder.withName("standard"); - builder.withDescription("A standard address space consists of an AMQP router network in combination with " + - "attachable 'storage units'. The implementation of a storage unit is hidden from the client " + - "and the routers with a well defined API."); - - builder.withAvailableEndpoints(Arrays.asList( - createEndpointSpec("messaging", "messaging", "amqps", TlsTermination.passthrough), - createEndpointSpec("messaging-wss", "messaging", "https", TlsTermination.reencrypt), - createEndpointSpec("mqtt", "mqtt", "secure-mqtt", TlsTermination.passthrough))); - - builder.withCertificateProviderTypes(createCertificateProviderTypeDescriptions()); - builder.withRouteServicePorts(createRouteServicePortDescriptions("standard")); - builder.withEndpointExposeTypes(createEndpointExposeTypeDescription()); - - List filteredAddressSpaceplans = addressSpacePlans.stream() - .filter(plan -> "standard".equals(plan.getAddressSpaceType())) - .collect(Collectors.toList()); - builder.withPlans(filteredAddressSpaceplans); - - List filteredAddressPlans = addressPlans.stream() - .filter(plan -> filteredAddressSpaceplans.stream() - .filter(aPlan -> aPlan.getAddressPlans().contains(plan.getMetadata().getName())) - .count() > 0) - .collect(Collectors.toList()); - - - builder.withInfraConfigs(standardInfraConfigs); - - builder.withAddressTypes(Arrays.asList( - createAddressType( - "anycast", - "A direct messaging address type. Messages sent to an anycast address are not " + - "stored but forwarded directly to a consumer.", - filteredAddressPlans), - createAddressType( - "multicast", - "A direct messaging address type. Messages sent to a multicast address are not " + - "stored but forwarded directly to multiple consumers.", - filteredAddressPlans), - createAddressType( - "queue", - "A store-and-forward queue. A queue may be sharded across multiple storage units, " + - "in which case message ordering is no longer guaranteed.", - filteredAddressPlans), - createAddressType( - "topic", - "A topic address for store-and-forward publish-subscribe messaging. Each message published " + - "to a topic address is forwarded to all subscribes on that address.", - filteredAddressPlans), - createAddressType( - "subscription", - "A subscription on a topic", - filteredAddressPlans))); - - return builder.build(); - } - - private AddressSpaceType createBrokeredType(List addressSpacePlans, Collection addressPlans, List brokeredInfraConfigs) { - AddressSpaceTypeBuilder builder = new AddressSpaceTypeBuilder(); - builder.withName("brokered"); - builder.withDescription("A brokered address space consists of a broker combined with a console for managing addresses."); - - builder.withAvailableEndpoints(Arrays.asList( - createEndpointSpec("messaging", "messaging", "amqps", TlsTermination.passthrough), - createEndpointSpec("messaging-wss", "messaging", "amqps", TlsTermination.reencrypt))); - - builder.withCertificateProviderTypes(createCertificateProviderTypeDescriptions()); - builder.withRouteServicePorts(createRouteServicePortDescriptions("brokered")); - builder.withEndpointExposeTypes(createEndpointExposeTypeDescription()); - - List filteredAddressSpaceplans = addressSpacePlans.stream() - .filter(plan -> "brokered".equals(plan.getAddressSpaceType())) - .collect(Collectors.toList()); - builder.withPlans(filteredAddressSpaceplans); - - List filteredAddressPlans = addressPlans.stream() - .filter(plan -> filteredAddressSpaceplans.stream() - .filter(aPlan -> aPlan.getAddressPlans().contains(plan.getMetadata().getName())) - .count() > 0) - .collect(Collectors.toList()); - - builder.withInfraConfigs(brokeredInfraConfigs); - - builder.withAddressTypes(Arrays.asList( - createAddressType( - "queue", - "A queue that supports selectors, message grouping and transactions", - filteredAddressPlans), - createAddressType( - "topic", - "A topic supports pub-sub semantics. Messages sent to a topic address is forwarded to all subscribes on that address.", - filteredAddressPlans))); - - return builder.build(); - } - - private AddressType createAddressType(String name, String description, List addressPlans) { - AddressTypeBuilder builder = new AddressTypeBuilder(); - builder.withPlans(addressPlans.stream() - .filter(plan -> plan.getAddressType().equals(name)) - .collect(Collectors.toList())); - builder.withName(name); - builder.withDescription(description); - return builder.build(); - } - - private List createRouteServicePortDescriptions(String addressSpaceType) { - List servicePortDescriptions = new ArrayList<>(); - - String displayName = "brokered".equals(addressSpaceType) ? "Messaging (AMQP, CORE, OpenWire, MQTT, STOMP)": "Messaging (AMQP)"; - servicePortDescriptions.add(new RouteServicePortDescriptionBuilder() - .withName("amqps") - .withDisplayName(displayName) - .withDescription(displayName) - .withRouteTlsTerminations(List.of(TlsTermination.passthrough)) - .build()); - - servicePortDescriptions.add(new RouteServicePortDescriptionBuilder() - .withName("https") - .withDisplayName("Websocket messaging (AMQP-WS)") - .withDescription("Websocket messaging (AMQP-WS)") - .withRouteTlsTerminations(List.of(TlsTermination.passthrough, TlsTermination.reencrypt)) - .build()); - - return servicePortDescriptions; - } - - private List createCertificateProviderTypeDescriptions() { - List certificateProviders = new ArrayList<>(); - certificateProviders.add(new CertificateProviderTypeDescriptionBuilder() - .withName("certBundle") - .withDisplayName("Certificate Bundle") - .withDescription("User provided TLS certificate bundle.").build()); - certificateProviders.add(new CertificateProviderTypeDescriptionBuilder() - .withName("selfsigned") - .withDisplayName("Self-Signed") - .withDescription("System generates self-signed TLS certificate.").build()); - if (isOpenShift) { - certificateProviders.add(new CertificateProviderTypeDescriptionBuilder() - .withName("openshift") - .withDisplayName("OpenShift") - .withDescription("OpenShift provides a TLS certificate.").build()); - } - return certificateProviders; - } - - private List createEndpointExposeTypeDescription() { - List endpointExposeTypeDescriptions = new ArrayList<>(); - - if (isOpenShift) { - endpointExposeTypeDescriptions.add(new EndpointExposeTypeDescriptionBuilder() - .withName(ExposeType.route) - .withDisplayName("OpenShift Route") - .withDescription("OpenShift Route") - .build()); - } - endpointExposeTypeDescriptions.add(new EndpointExposeTypeDescriptionBuilder() - .withName(ExposeType.loadbalancer) - .withDisplayName("LoadBalancer service") - .withDescription("LoadBalancer service") - .build()); - - return endpointExposeTypeDescriptions; - } - - @Override - public Watch watchSchema(Watcher watcher, Duration resyncInterval) { - List watches = new ArrayList<>(); - watches.add(addressSpacePlanApi.watchResources(items -> { - synchronized (KubeSchemaApi.this) { - currentAddressSpacePlans = items; - } - updateSchema(watcher); - }, resyncInterval)); - - watches.add(addressPlanApi.watchResources(items -> { - synchronized (KubeSchemaApi.this) { - currentAddressPlans = items; - } - updateSchema(watcher); - }, resyncInterval)); - - watches.add(brokeredInfraConfigApi.watchResources(items -> { - synchronized (KubeSchemaApi.this) { - currentBrokeredInfraConfigs = items; - } - updateSchema(watcher); - }, resyncInterval)); - - watches.add(standardInfraConfigApi.watchResources(items -> { - synchronized (KubeSchemaApi.this) { - currentStandardInfraConfigs = items; - } - updateSchema(watcher); - }, resyncInterval)); - - watches.add(authenticationServiceApi.watchResources(items -> { - synchronized (KubeSchemaApi.this) { - currentAuthenticationServices = items; - } - updateSchema(watcher); - }, resyncInterval)); - - return () -> { - Exception e = null; - for (Watch watch : watches) { - try { - watch.close(); - } catch (Exception ex) { - e = ex; - } - } - if (e != null) { - throw e; - } - }; - } - - private synchronized void updateSchema(Watcher watcher) throws Exception { - Schema schema = assembleSchema(currentAddressSpacePlans, currentAddressPlans, currentStandardInfraConfigs, currentBrokeredInfraConfigs, currentAuthenticationServices); - if (schema != null) { - watcher.onUpdate(Collections.singletonList(schema)); - } - } - - Schema assembleSchema(List addressSpacePlans, List addressPlans, List standardInfraConfigs, List brokeredInfraConfigs, List authenticationServices) { - - Set validAddressPlans = new HashSet<>(); - Map addressPlanByName = new HashMap<>(); - Map addressPlanStatusMap = new HashMap<>(); - Map addressSpacePlanStatusMap = new HashMap<>(); - - for (io.enmasse.admin.model.v1.AddressPlan addressPlan : addressPlans) { - addressPlanByName.put(addressPlan.getMetadata().getName(), addressPlan); - addressPlanStatusMap.put(addressPlan.getMetadata().getName(), new AddressPlanStatusBuilder() - .withPhase(Phase.Pending) - .build()); - - } - - for (StandardInfraConfig standardInfraConfig : standardInfraConfigs) { - if (standardInfraConfig.getSpec() != null) { - standardInfraConfig.getSpec().setVersion(defaultVersion); - } - } - - for (BrokeredInfraConfig brokeredInfraConfig : brokeredInfraConfigs) { - if (brokeredInfraConfig.getSpec() != null) { - brokeredInfraConfig.getSpec().setVersion(defaultVersion); - } - } - - List validAddressSpacePlans = new ArrayList<>(); - for (io.enmasse.admin.model.v1.AddressSpacePlan addressSpacePlan : addressSpacePlans) { - addressSpacePlanStatusMap.put(addressSpacePlan.getMetadata().getName(), new AddressSpacePlanStatusBuilder() - .withPhase(Phase.Pending) - .build()); - - List plansForAddressSpacePlan = new ArrayList<>(); - for (String addressPlanName : addressSpacePlan.getAddressPlans()) { - io.enmasse.admin.model.v1.AddressPlan addressPlan = null; - try { - addressPlan = addressPlanByName.get(addressPlanName); - if (addressPlan == null) { - String message = String.format("Error validating address plan %s: unable to find address plan definition", addressPlanName); - log.warn(message); - throw new SchemaValidationException(message); - } - validateAddressPlan(addressSpacePlan.getAddressSpaceType(), addressPlan); - plansForAddressSpacePlan.add(addressPlan); - addressPlanStatusMap.get(addressPlanName).setPhase(Phase.Active); - } catch (SchemaValidationException e) { - if (addressPlan == null) { - addressSpacePlanStatusMap.get(addressSpacePlan.getMetadata().getName()).setPhase(Phase.Failed); - addressSpacePlanStatusMap.get(addressSpacePlan.getMetadata().getName()).appendMessage(e.getMessage()); - } else { - addressPlanStatusMap.get(addressPlanName).setPhase(Phase.Failed); - addressPlanStatusMap.get(addressPlanName).appendMessage(e.getMessage()); - } - } - } - - try { - if (addressSpacePlan.getAddressSpaceType().equals("brokered")) { - validateAddressSpacePlan(addressSpacePlan, plansForAddressSpacePlan, brokeredInfraConfigs.stream().map(t -> t.getMetadata().getName()).collect(Collectors.toList())); - } else { - validateAddressSpacePlan(addressSpacePlan, plansForAddressSpacePlan, standardInfraConfigs.stream().map(t -> t.getMetadata().getName()).collect(Collectors.toList())); - } - validAddressSpacePlans.add(addressSpacePlan); - validAddressPlans.addAll(plansForAddressSpacePlan); - addressSpacePlanStatusMap.get(addressSpacePlan.getMetadata().getName()).setPhase(Phase.Active); - } catch (SchemaValidationException e) { - addressSpacePlanStatusMap.get(addressSpacePlan.getMetadata().getName()).setPhase(Phase.Failed); - addressSpacePlanStatusMap.get(addressSpacePlan.getMetadata().getName()).appendMessage(e.getMessage()); - } - } - - // Update status of plans to provide error messages and status - if (updateStatus) { - for (io.enmasse.admin.model.v1.AddressSpacePlan addressSpacePlan : addressSpacePlans) { - AddressSpacePlanStatus newStatus = addressSpacePlanStatusMap.get(addressSpacePlan.getMetadata().getName()); - if (!newStatus.equals(addressSpacePlan.getStatus())) { - addressSpacePlan.setStatus(newStatus); - addressSpacePlanApi.patch(addressSpacePlan); - } - } - for (io.enmasse.admin.model.v1.AddressPlan addressPlan : addressPlans) { - AddressPlanStatus newStatus = addressPlanStatusMap.get(addressPlan.getMetadata().getName()); - if (!newStatus.equals(addressPlan.getStatus())) { - addressPlan.setStatus(newStatus); - addressPlanApi.patch(addressPlan); - } - } - } - - List types = new ArrayList<>(); - types.add(createBrokeredType(validAddressSpacePlans, validAddressPlans, new ArrayList<>(brokeredInfraConfigs))); - types.add(createStandardType(validAddressSpacePlans, validAddressPlans, new ArrayList<>(standardInfraConfigs))); - return new SchemaBuilder() - .withAddressSpaceTypes(types) - .withAuthenticationServices(authenticationServices) - .withCreationTimestamp(formatter.format(clock.instant())) - .build(); - } -} diff --git a/k8s-api/src/main/java/io/enmasse/k8s/api/LogEventLogger.java b/k8s-api/src/main/java/io/enmasse/k8s/api/LogEventLogger.java deleted file mode 100644 index 239d763b843..00000000000 --- a/k8s-api/src/main/java/io/enmasse/k8s/api/LogEventLogger.java +++ /dev/null @@ -1,29 +0,0 @@ -/* - * Copyright 2018, EnMasse authors. - * License: Apache License 2.0 (see the file LICENSE or http://apache.org/licenses/LICENSE-2.0.html). - */ -package io.enmasse.k8s.api; - -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -public class LogEventLogger implements EventLogger { - private static final Logger log = LoggerFactory.getLogger(LogEventLogger.class); - - @Override - public void log(Reason reason, String message, Type type, ObjectKind objectKind, String objectName) { - log(log, reason, message, type, objectKind, objectName); - } - - public static void log(Logger logger, Reason reason, String message, Type type, ObjectKind objectKind, String objectName) { - String line = String.format("%s (kind=%s name=%s): %s", reason.name(), objectKind.name(), objectName, message); - switch (type) { - case Warning: - logger.warn(line); - break; - case Normal: - logger.info(line); - break; - } - } -} diff --git a/k8s-api/src/main/java/io/enmasse/k8s/api/ResourceCache.java b/k8s-api/src/main/java/io/enmasse/k8s/api/ResourceCache.java deleted file mode 100644 index b7e4cb86b17..00000000000 --- a/k8s-api/src/main/java/io/enmasse/k8s/api/ResourceCache.java +++ /dev/null @@ -1,11 +0,0 @@ -/* - * Copyright 2017-2018, EnMasse authors. - * License: Apache License 2.0 (see the file LICENSE or http://apache.org/licenses/LICENSE-2.0.html). - */ -package io.enmasse.k8s.api; - -import java.util.List; - -public interface ResourceCache { - List getItems(); -} diff --git a/k8s-api/src/main/java/io/enmasse/k8s/api/ResourceChecker.java b/k8s-api/src/main/java/io/enmasse/k8s/api/ResourceChecker.java deleted file mode 100644 index baa30a5964c..00000000000 --- a/k8s-api/src/main/java/io/enmasse/k8s/api/ResourceChecker.java +++ /dev/null @@ -1,85 +0,0 @@ -/* - * Copyright 2017-2018, EnMasse authors. - * License: Apache License 2.0 (see the file LICENSE or http://apache.org/licenses/LICENSE-2.0.html). - */ -package io.enmasse.k8s.api; - -import io.enmasse.k8s.api.cache.CacheWatcher; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import java.time.Duration; - -public class ResourceChecker implements CacheWatcher, Runnable { - private static final Logger log = LoggerFactory.getLogger(ResourceChecker.class.getName()); - private final Watcher watcher; - private volatile ResourceCache resourceCache; - private final Duration recheckInterval; - private final Object monitor = new Object(); - private volatile boolean synced = false; - private volatile boolean running = false; - - private Thread thread; - - public ResourceChecker(Watcher watcher, Duration recheckInterval) { - this.watcher = watcher; - this.recheckInterval = recheckInterval; - } - - public void start() { - running = true; - thread = new Thread(this); - thread.setName("resource-checker"); - thread.setDaemon(true); - thread.start(); - - } - - @Override - public void run() { - while (running) { - doWork(); - } - } - - void doWork() { - synchronized (monitor) { - try { - monitor.wait(recheckInterval.toMillis()); - log.debug("Woke up from monitor"); - if (synced) { - watcher.onUpdate(resourceCache.getItems()); - } - } catch (InterruptedException e) { - Thread.currentThread().interrupt(); - running = false; - log.warn("ResourceChecker interrupted, stopping."); - } catch (Exception e) { - log.warn("Exception in checker task", e); - } - } - } - - public void stop() { - try { - running = false; - thread.interrupt(); - thread.join(); - } catch (InterruptedException ignored) { - log.warn("Interrupted while stopping", ignored); - } - } - - @Override - public void onInit(ResourceCache cache) { - this.resourceCache = cache; - } - - @Override - public void onUpdate() { - synced = true; - synchronized (monitor) { - monitor.notifyAll(); - } - } -} diff --git a/k8s-api/src/main/java/io/enmasse/k8s/api/SchemaApi.java b/k8s-api/src/main/java/io/enmasse/k8s/api/SchemaApi.java deleted file mode 100644 index 4e686b7b5a4..00000000000 --- a/k8s-api/src/main/java/io/enmasse/k8s/api/SchemaApi.java +++ /dev/null @@ -1,19 +0,0 @@ -/* - * Copyright 2017-2018, EnMasse authors. - * License: Apache License 2.0 (see the file LICENSE or http://apache.org/licenses/LICENSE-2.0.html). - */ -package io.enmasse.k8s.api; - -import io.enmasse.address.model.Schema; - -import java.time.Duration; - -/** - * Interface for Schema of the address model - */ -public interface SchemaApi { - /** - * Watch changes to schema - */ - Watch watchSchema(Watcher watcher, Duration resyncInterval) throws Exception; -} diff --git a/k8s-api/src/main/java/io/enmasse/k8s/api/SchemaAuthenticationServiceRegistry.java b/k8s-api/src/main/java/io/enmasse/k8s/api/SchemaAuthenticationServiceRegistry.java deleted file mode 100644 index c641672dccc..00000000000 --- a/k8s-api/src/main/java/io/enmasse/k8s/api/SchemaAuthenticationServiceRegistry.java +++ /dev/null @@ -1,64 +0,0 @@ -/* - * Copyright 2019, EnMasse authors. - * License: Apache License 2.0 (see the file LICENSE or http://apache.org/licenses/LICENSE-2.0.html). - */ -package io.enmasse.k8s.api; - -import io.enmasse.address.model.Schema; -import io.enmasse.admin.model.v1.AuthenticationService; -import io.enmasse.admin.model.v1.AuthenticationServiceType; - -import java.util.List; -import java.util.Optional; - -/** - * Interface for resolving different authentication service - */ -public class SchemaAuthenticationServiceRegistry implements AuthenticationServiceRegistry { - - private final SchemaProvider schemaProvider; - - public SchemaAuthenticationServiceRegistry(SchemaProvider schemaProvider) { - this.schemaProvider = schemaProvider; - } - - @Override - public Optional findAuthenticationService(io.enmasse.address.model.AuthenticationService authenticationService) { - if (authenticationService == null) { - return resolveDefaultAuthenticationService(); - } - if (authenticationService.getName() == null) { - return findAuthenticationServiceByType(authenticationService.getType().toAdminType()).stream().findFirst(); - } else { - return findAuthenticationServiceByName(authenticationService.getName()); - } - } - - public Optional findAuthenticationServiceByName(String name) { - Schema schema = schemaProvider.getSchema(); - return schema.findAuthenticationService(name); - } - - @Override - public List findAuthenticationServiceByType(AuthenticationServiceType type) { - Schema schema = schemaProvider.getSchema(); - return schema.findAuthenticationServiceType(type); - } - - @Override - public Optional resolveDefaultAuthenticationService() { - Schema schema = schemaProvider.getSchema(); - List standards = schema.findAuthenticationServiceType(AuthenticationServiceType.standard); - if (standards.isEmpty()) { - return schema.findAuthenticationServiceType(AuthenticationServiceType.none).stream() - .findFirst(); - } else { - return standards.stream().findFirst(); - } - } - - @Override - public List listAuthenticationServices() { - return schemaProvider.getSchema().getAuthenticationServices(); - } -} diff --git a/k8s-api/src/main/java/io/enmasse/k8s/api/SchemaListener.java b/k8s-api/src/main/java/io/enmasse/k8s/api/SchemaListener.java deleted file mode 100644 index 5f9f6fcf75d..00000000000 --- a/k8s-api/src/main/java/io/enmasse/k8s/api/SchemaListener.java +++ /dev/null @@ -1,11 +0,0 @@ -/* - * Copyright 2018, EnMasse authors. - * License: Apache License 2.0 (see the file LICENSE or http://apache.org/licenses/LICENSE-2.0.html). - */ -package io.enmasse.k8s.api; - -import io.enmasse.address.model.Schema; - -public interface SchemaListener { - void onUpdate(Schema newSchema); -} diff --git a/k8s-api/src/main/java/io/enmasse/k8s/api/SchemaProvider.java b/k8s-api/src/main/java/io/enmasse/k8s/api/SchemaProvider.java deleted file mode 100644 index b99cff748d4..00000000000 --- a/k8s-api/src/main/java/io/enmasse/k8s/api/SchemaProvider.java +++ /dev/null @@ -1,11 +0,0 @@ -/* - * Copyright 2018, EnMasse authors. - * License: Apache License 2.0 (see the file LICENSE or http://apache.org/licenses/LICENSE-2.0.html). - */ -package io.enmasse.k8s.api; - -import io.enmasse.address.model.Schema; - -public interface SchemaProvider { - Schema getSchema(); -} diff --git a/k8s-api/src/main/java/io/enmasse/k8s/api/SchemaValidationException.java b/k8s-api/src/main/java/io/enmasse/k8s/api/SchemaValidationException.java deleted file mode 100644 index a5f05a33467..00000000000 --- a/k8s-api/src/main/java/io/enmasse/k8s/api/SchemaValidationException.java +++ /dev/null @@ -1,12 +0,0 @@ -/* - * Copyright 2018, EnMasse authors. - * License: Apache License 2.0 (see the file LICENSE or http://apache.org/licenses/LICENSE-2.0.html). - */ -package io.enmasse.k8s.api; - -@SuppressWarnings("serial") -public class SchemaValidationException extends RuntimeException { - public SchemaValidationException(String message) { - super(message); - } -} diff --git a/k8s-api/src/main/java/io/enmasse/k8s/api/Watch.java b/k8s-api/src/main/java/io/enmasse/k8s/api/Watch.java deleted file mode 100644 index 13062d5d684..00000000000 --- a/k8s-api/src/main/java/io/enmasse/k8s/api/Watch.java +++ /dev/null @@ -1,8 +0,0 @@ -/* - * Copyright 2017-2018, EnMasse authors. - * License: Apache License 2.0 (see the file LICENSE or http://apache.org/licenses/LICENSE-2.0.html). - */ -package io.enmasse.k8s.api; - -public interface Watch extends AutoCloseable { -} diff --git a/k8s-api/src/main/java/io/enmasse/k8s/api/Watcher.java b/k8s-api/src/main/java/io/enmasse/k8s/api/Watcher.java deleted file mode 100644 index fba37bb3c49..00000000000 --- a/k8s-api/src/main/java/io/enmasse/k8s/api/Watcher.java +++ /dev/null @@ -1,12 +0,0 @@ -/* - * Copyright 2018, EnMasse authors. - * License: Apache License 2.0 (see the file LICENSE or http://apache.org/licenses/LICENSE-2.0.html). - */ -package io.enmasse.k8s.api; - -import java.util.List; - -public interface Watcher { - void onUpdate(List items) throws Exception; - -} diff --git a/k8s-api/src/main/java/io/enmasse/k8s/api/cache/CacheWatcher.java b/k8s-api/src/main/java/io/enmasse/k8s/api/cache/CacheWatcher.java deleted file mode 100644 index 24a385282f3..00000000000 --- a/k8s-api/src/main/java/io/enmasse/k8s/api/cache/CacheWatcher.java +++ /dev/null @@ -1,12 +0,0 @@ -/* - * Copyright 2018, EnMasse authors. - * License: Apache License 2.0 (see the file LICENSE or http://apache.org/licenses/LICENSE-2.0.html). - */ -package io.enmasse.k8s.api.cache; - -import io.enmasse.k8s.api.ResourceCache; - -public interface CacheWatcher { - void onInit(ResourceCache cache); - void onUpdate() throws Exception; -} diff --git a/k8s-api/src/main/java/io/enmasse/k8s/api/cache/Controller.java b/k8s-api/src/main/java/io/enmasse/k8s/api/cache/Controller.java deleted file mode 100644 index ec03512f876..00000000000 --- a/k8s-api/src/main/java/io/enmasse/k8s/api/cache/Controller.java +++ /dev/null @@ -1,40 +0,0 @@ -/* - * Copyright 2018, EnMasse authors. - * License: Apache License 2.0 (see the file LICENSE or http://apache.org/licenses/LICENSE-2.0.html). - */ -package io.enmasse.k8s.api.cache; - -import io.enmasse.k8s.api.Watch; - -public class Controller implements Watch { - private final Reflector reflector; - private volatile boolean running; - private Thread thread; - - public Controller(Reflector reflector) { - this.reflector = reflector; - } - - public void start() { - running = true; - thread = new Thread(() -> { - while (running) { - reflector.run(); - } - }); - thread.setName(String.format("controller|%s", reflector.getExpectedType().getSimpleName())); - thread.start(); - } - - public void stop() throws InterruptedException { - running = false; - reflector.shutdown(); - thread.interrupt(); - thread.join(); - } - - @Override - public void close() throws Exception { - stop(); - } -} diff --git a/k8s-api/src/main/java/io/enmasse/k8s/api/cache/EventCache.java b/k8s-api/src/main/java/io/enmasse/k8s/api/cache/EventCache.java deleted file mode 100644 index ad822f72c4a..00000000000 --- a/k8s-api/src/main/java/io/enmasse/k8s/api/cache/EventCache.java +++ /dev/null @@ -1,184 +0,0 @@ -/* - * Copyright 2018, EnMasse authors. - * License: Apache License 2.0 (see the file LICENSE or http://apache.org/licenses/LICENSE-2.0.html). - */ -package io.enmasse.k8s.api.cache; - -import static io.enmasse.k8s.api.cache.EventCache.EventType.Added; -import static io.enmasse.k8s.api.cache.EventCache.EventType.Deleted; -import static io.enmasse.k8s.api.cache.EventCache.EventType.Sync; -import static io.enmasse.k8s.api.cache.EventCache.EventType.Updated; -import static io.enmasse.k8s.api.cache.EventCache.EventType.Wakeup; - -import java.util.ArrayList; -import java.util.HashMap; -import java.util.List; -import java.util.Map; -import java.util.concurrent.BlockingQueue; -import java.util.concurrent.LinkedBlockingDeque; -import java.util.concurrent.TimeUnit; -import java.util.concurrent.atomic.AtomicInteger; - -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -public class EventCache implements WorkQueue { - private static final Logger log = LoggerFactory.getLogger(EventCache.class); - private final FieldExtractor fieldExtractor; - private final Map store = new HashMap<>(); - private final BlockingQueue> queue = new LinkedBlockingDeque<>(); - private AtomicInteger initialPopulationCount = new AtomicInteger(0); - private volatile boolean populated = false; - - enum EventType { - Added, - Updated, - Deleted, - Sync, - Wakeup - } - - private static class Event { - final EventType eventType; - final T obj; - - private Event(EventType eventType, T obj) { - this.eventType = eventType; - this.obj = obj; - } - } - - public EventCache(FieldExtractor fieldExtractor) { - this.fieldExtractor = fieldExtractor; - } - - @Override - public void pop(Processor processor, long timeout, TimeUnit timeUnit) throws Exception { - Event firstEvent = queue.poll(timeout, timeUnit); - if (firstEvent == null || Wakeup == firstEvent.eventType) { - log.debug("Woke up but queue is empty"); - return; - } - - List> events = new ArrayList<>(); - events.add(firstEvent); - queue.drainTo(events); - - synchronized (this) { - for (Event event : events) { - String key = null; - if (event.obj != null) { - key = fieldExtractor.getKey(event.obj); - } - - switch (event.eventType) { - case Wakeup: - continue; - case Deleted: - store.remove(key); - break; - case Updated: - case Added: - store.put(key, event.obj); - break; - case Sync: - if (initialPopulationCount.get() > 0) { - initialPopulationCount.decrementAndGet(); - } - break; - } - } - } - String key = firstEvent.obj != null ? fieldExtractor.getKey(firstEvent.obj) : null; - log.debug("Processing event {} with key {}", firstEvent.eventType, key); - processor.process(firstEvent.obj); - } - - private boolean hasVersionChanged(T ths, T that) { - String thisVersion = fieldExtractor.getResourceVersion(ths); - String thatVersion = fieldExtractor.getResourceVersion(that); - if (thisVersion == null || thatVersion == null) { - return true; - } - return !thisVersion.equals(thatVersion); - } - - private void queueEvent(EventType eventType, T obj) throws InterruptedException { - populated = true; - queue.put(new Event<>(eventType, obj)); - } - - @Override - public boolean hasSynced() { - return populated && initialPopulationCount.get() == 0; - } - - @Override - public void add(T t) throws InterruptedException { - queueEvent(Added, t); - } - - @Override - public void update(T t) throws InterruptedException { - queueEvent(Updated, t); - } - - @Override - public void delete(T t) throws InterruptedException { - queueEvent(Deleted, t); - } - - @Override - public void wakeup() throws InterruptedException { - queue.put(new Event<>(Wakeup, null)); - } - - @Override - public synchronized List list() { - return new ArrayList<>(store.values()); - } - - @Override - public synchronized List listKeys() { - return new ArrayList<>(store.keySet()); - } - - @Override - public synchronized void replace(List list, String resourceVersion) throws InterruptedException { - - log.debug("Replacing queue with {} items. Populated {}.", list.size(), populated); - Map newItems = new HashMap<>(); - for (T item : list) { - String key = fieldExtractor.getKey(item); - newItems.put(key, item); - } - - log.debug("Current store size: {}", store.size()); - store.clear(); - store.putAll(newItems); - log.debug("New store size: {}", store.size()); - - if (!populated) { - initialPopulationCount.set(1); - } - queueEvent(Sync, null); - } - - @Override - public synchronized void replace(T item) { - String key = fieldExtractor.getKey(item); - T current = store.get(key); - if (current != null) { - String currentVersion = fieldExtractor.getResourceVersion(current); - String replaceVersion = fieldExtractor.getResourceVersion(item); - if (!hasVersionChanged(current, item)) { - log.debug("Replacing {} (old {}, new {})", key, currentVersion, replaceVersion); - store.put(key, item); - } else { - final Long currentGeneration = fieldExtractor.getGeneration(current); - final Long replaceGeneration = fieldExtractor.getGeneration(item); - log.debug("Not replacing {} (old {}/{}, new {}/{})", key, currentVersion, currentGeneration, replaceVersion, replaceGeneration); - } - } - } -} diff --git a/k8s-api/src/main/java/io/enmasse/k8s/api/cache/FieldExtractor.java b/k8s-api/src/main/java/io/enmasse/k8s/api/cache/FieldExtractor.java deleted file mode 100644 index 0b5071c83c8..00000000000 --- a/k8s-api/src/main/java/io/enmasse/k8s/api/cache/FieldExtractor.java +++ /dev/null @@ -1,11 +0,0 @@ -/* - * Copyright 2018, EnMasse authors. - * License: Apache License 2.0 (see the file LICENSE or http://apache.org/licenses/LICENSE-2.0.html). - */ -package io.enmasse.k8s.api.cache; - -public interface FieldExtractor { - String getKey(T obj); - String getResourceVersion(T obj); - Long getGeneration(T obj); -} diff --git a/k8s-api/src/main/java/io/enmasse/k8s/api/cache/HasMetadataFieldExtractor.java b/k8s-api/src/main/java/io/enmasse/k8s/api/cache/HasMetadataFieldExtractor.java deleted file mode 100644 index ecac63b6d37..00000000000 --- a/k8s-api/src/main/java/io/enmasse/k8s/api/cache/HasMetadataFieldExtractor.java +++ /dev/null @@ -1,25 +0,0 @@ -/* - * Copyright 2017-2018, EnMasse authors. - * License: Apache License 2.0 (see the file LICENSE or http://apache.org/licenses/LICENSE-2.0.html). - */ -package io.enmasse.k8s.api.cache; - -import io.fabric8.kubernetes.api.model.HasMetadata; - -public class HasMetadataFieldExtractor implements FieldExtractor { - - @Override - public String getKey(T item) { - return item.getMetadata().getNamespace() + "/" + item.getMetadata().getName(); - } - - @Override - public String getResourceVersion(T item) { - return item.getMetadata().getResourceVersion(); - } - - @Override - public Long getGeneration(T item) { - return item.getMetadata().getGeneration(); - } -} diff --git a/k8s-api/src/main/java/io/enmasse/k8s/api/cache/ListOptions.java b/k8s-api/src/main/java/io/enmasse/k8s/api/cache/ListOptions.java deleted file mode 100644 index 3891e856470..00000000000 --- a/k8s-api/src/main/java/io/enmasse/k8s/api/cache/ListOptions.java +++ /dev/null @@ -1,28 +0,0 @@ -/* - * Copyright 2018, EnMasse authors. - * License: Apache License 2.0 (see the file LICENSE or http://apache.org/licenses/LICENSE-2.0.html). - */ -package io.enmasse.k8s.api.cache; - -public class ListOptions { - private String resourceVersion; - private int timeoutSeconds; - - public ListOptions setResourceVersion(String resourceVersion) { - this.resourceVersion = resourceVersion; - return this; - } - - public ListOptions setTimeoutSeconds(int timeoutSeconds) { - this.timeoutSeconds = timeoutSeconds; - return this; - } - - public int getTimeoutSeconds() { - return timeoutSeconds; - } - - public String getResourceVersion() { - return resourceVersion; - } -} diff --git a/k8s-api/src/main/java/io/enmasse/k8s/api/cache/ListerWatcher.java b/k8s-api/src/main/java/io/enmasse/k8s/api/cache/ListerWatcher.java deleted file mode 100644 index 2d34c99f379..00000000000 --- a/k8s-api/src/main/java/io/enmasse/k8s/api/cache/ListerWatcher.java +++ /dev/null @@ -1,15 +0,0 @@ -/* - * Copyright 2018, EnMasse authors. - * License: Apache License 2.0 (see the file LICENSE or http://apache.org/licenses/LICENSE-2.0.html). - */ -package io.enmasse.k8s.api.cache; - -import io.fabric8.kubernetes.api.model.HasMetadata; -import io.fabric8.kubernetes.api.model.KubernetesResourceList; -import io.fabric8.kubernetes.client.Watch; -import io.fabric8.kubernetes.client.Watcher; - -public interface ListerWatcher> { - LT list(ListOptions listOptions); - Watch watch(Watcher watcher, ListOptions listOptions); -} diff --git a/k8s-api/src/main/java/io/enmasse/k8s/api/cache/Processor.java b/k8s-api/src/main/java/io/enmasse/k8s/api/cache/Processor.java deleted file mode 100644 index 2cd446832ab..00000000000 --- a/k8s-api/src/main/java/io/enmasse/k8s/api/cache/Processor.java +++ /dev/null @@ -1,9 +0,0 @@ -/* - * Copyright 2018, EnMasse authors. - * License: Apache License 2.0 (see the file LICENSE or http://apache.org/licenses/LICENSE-2.0.html). - */ -package io.enmasse.k8s.api.cache; - -public interface Processor { - void process(T t) throws Exception; -} diff --git a/k8s-api/src/main/java/io/enmasse/k8s/api/cache/Reflector.java b/k8s-api/src/main/java/io/enmasse/k8s/api/cache/Reflector.java deleted file mode 100644 index d68a8386627..00000000000 --- a/k8s-api/src/main/java/io/enmasse/k8s/api/cache/Reflector.java +++ /dev/null @@ -1,217 +0,0 @@ -/* - * Copyright 2018, EnMasse authors. - * License: Apache License 2.0 (see the file LICENSE or http://apache.org/licenses/LICENSE-2.0.html). - */ -package io.enmasse.k8s.api.cache; - -import java.time.Clock; -import java.time.Duration; -import java.time.Instant; -import java.util.List; -import java.util.Optional; -import java.util.concurrent.TimeUnit; -import java.util.concurrent.atomic.AtomicLong; - -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import io.fabric8.kubernetes.api.model.HasMetadata; -import io.fabric8.kubernetes.api.model.KubernetesResourceList; -import io.fabric8.kubernetes.api.model.ListMeta; -import io.fabric8.kubernetes.client.KubernetesClientException; -import io.fabric8.kubernetes.client.Watch; -import io.fabric8.kubernetes.client.Watcher; - -public class Reflector> implements Runnable { - private static final Logger log = LoggerFactory.getLogger(Reflector.class.getName()); - private static final Duration minWatchTimeout = Duration.ofMinutes(5); - - private final Duration resyncInterval; - private final ListerWatcher listerWatcher; - private final Processor processor; - private final Class expectedType; - private final WorkQueue queue; - private final Clock clock; - private volatile Watch watch; - private volatile Instant nextResync = Instant.MIN; - - private volatile String lastSyncResourceVersion; - - public Reflector(Config config) { - this.resyncInterval = config.resyncInterval; - this.expectedType = config.expectedType; - this.listerWatcher = config.listerWatcher; - this.processor = config.processor; - this.queue = config.queue; - this.clock = config.clock; - } - - public Class getExpectedType() { - return this.expectedType; - } - - @Override - public void run() { - try { - Instant now = Instant.now(clock); - if (now.isAfter(nextResync)) { - while (true) { - try { - resync(); - nextResync = Instant.now(clock).plus(resyncInterval); - break; - } catch (KubernetesClientException e) { - log.warn("KubernetesClientException when triggering resync. Pausing for a few seconds before retrying.", - e); - Thread.sleep(5000); - } - } - } - long sleepTime = Math.max(1, nextResync.toEpochMilli() - now.toEpochMilli()); - log.debug("Waiting on event queue for {} ms unless notified", sleepTime); - queue.pop(processor, sleepTime, TimeUnit.MILLISECONDS); - } catch (InterruptedException e) { - Thread.currentThread().interrupt(); - } catch (Exception e) { - log.warn("Exception doing resource update", e); - // TODO: We can attempt to requeue here - } - } - - public String getLastSyncResourceVersion() { - return lastSyncResourceVersion; - } - - private void resync() throws InterruptedException { - log.info("Resync: {}", this.expectedType); - if (watch != null) { - log.info("Closing existing watch"); - watch.close(); - } - Instant start = clock.instant(); - LT list = listerWatcher.list(new ListOptions().setResourceVersion(null)); - String resourceVersion = Optional.ofNullable(list) - .map(KubernetesResourceList::getMetadata) - .map(ListMeta::getResourceVersion).orElse(""); - - syncWith(list.getItems(), resourceVersion); - lastSyncResourceVersion = resourceVersion; - - ListOptions watchOptions = new ListOptions() - .setResourceVersion(resourceVersion) - .setTimeoutSeconds((int) minWatchTimeout.getSeconds()); - - AtomicLong eventCount = new AtomicLong(0); - - watch = listerWatcher.watch(new Watcher() { - @Override - public void eventReceived(Action action, T t) { - log.debug("Event received - action: {}, element: {}", action, t); - - if (!t.getClass().equals(expectedType)) { - log.warn("Got unexpected type {}", t.getClass()); - return; - } - - try { - String newResourceVersion = t.getMetadata().getResourceVersion(); - - long before = System.nanoTime(); - switch (action) { - case ADDED: - queue.add(t); - break; - case MODIFIED: - queue.update(t); - break; - case DELETED: - queue.delete(t); - break; - case ERROR: - log.warn("Got error event {}", action); - break; - } - if (log.isDebugEnabled()) { - long after = System.nanoTime(); - log.debug("{} {} version {} took {} ns", action, t.getMetadata().getName(), t.getMetadata().getResourceVersion(), after - before); - } - lastSyncResourceVersion = newResourceVersion; - eventCount.incrementAndGet(); - } catch (Exception e) { - log.error("Error handling watch event", e); - } - } - - @Override - public void onClose(KubernetesClientException e) { - Instant now = clock.instant(); - if (now.minusMillis(start.toEpochMilli()).toEpochMilli() < 1000 && eventCount.get() == 0) { - log.warn("Very short watch: Unexpected watch close - watch lasted less than a second and no items received"); - } else { - log.info("Watch closed"); - } - if (e != null) { - log.warn("Unexpected watch close. Code {}. Status: {}", e.getCode(), e.getStatus(), e); - if (e.getCode() == 410) { - nextResync = Instant.MIN; - try { - queue.wakeup(); - } catch (InterruptedException ex) { - log.warn("Interrupted when waking up queue processor", ex); - Thread.currentThread().interrupt(); - } - } - } - } - }, watchOptions); - } - - private void syncWith(List items, String resourceVersion) throws InterruptedException { - queue.replace(items, resourceVersion); - } - - public void shutdown() { - if (watch != null) { - watch.close(); - } - } - - public static class Config> { - private Clock clock; - private Duration resyncInterval; - private ListerWatcher listerWatcher; - private Processor processor; - private WorkQueue queue; - private Class expectedType; - - public Config setClock(Clock clock) { - this.clock = clock; - return this; - } - - public Config setResyncInterval(Duration resyncInterval) { - this.resyncInterval = resyncInterval; - return this; - } - - public Config setListerWatcher(ListerWatcher listerWatcher) { - this.listerWatcher = listerWatcher; - return this; - } - - public Config setProcessor(Processor processor) { - this.processor = processor; - return this; - } - - public Config setWorkQueue(WorkQueue queue) { - this.queue = queue; - return this; - } - - public Config setExpectedType(Class expectedType) { - this.expectedType = expectedType; - return this; - } - } -} diff --git a/k8s-api/src/main/java/io/enmasse/k8s/api/cache/Store.java b/k8s-api/src/main/java/io/enmasse/k8s/api/cache/Store.java deleted file mode 100644 index 9d75d86fc6c..00000000000 --- a/k8s-api/src/main/java/io/enmasse/k8s/api/cache/Store.java +++ /dev/null @@ -1,17 +0,0 @@ -/* - * Copyright 2018, EnMasse authors. - * License: Apache License 2.0 (see the file LICENSE or http://apache.org/licenses/LICENSE-2.0.html). - */ -package io.enmasse.k8s.api.cache; - -import java.util.List; - -public interface Store { - void add(T t) throws InterruptedException; - void update(T t) throws InterruptedException; - void delete(T t) throws InterruptedException; - List list(); - List listKeys(); - void replace(List list, String resourceVersion) throws InterruptedException; - void replace(T item); -} diff --git a/k8s-api/src/main/java/io/enmasse/k8s/api/cache/WorkQueue.java b/k8s-api/src/main/java/io/enmasse/k8s/api/cache/WorkQueue.java deleted file mode 100644 index 2fd50625b6f..00000000000 --- a/k8s-api/src/main/java/io/enmasse/k8s/api/cache/WorkQueue.java +++ /dev/null @@ -1,13 +0,0 @@ -/* - * Copyright 2018, EnMasse authors. - * License: Apache License 2.0 (see the file LICENSE or http://apache.org/licenses/LICENSE-2.0.html). - */ -package io.enmasse.k8s.api.cache; - -import java.util.concurrent.TimeUnit; - -public interface WorkQueue extends Store { - void pop(Processor processor, long timeout, TimeUnit timeUnit) throws Exception; - void wakeup() throws InterruptedException; - boolean hasSynced(); -} diff --git a/k8s-api/src/main/java/io/enmasse/k8s/util/JULInitializingTest.java b/k8s-api/src/main/java/io/enmasse/k8s/util/JULInitializingTest.java deleted file mode 100644 index 666ff5107d8..00000000000 --- a/k8s-api/src/main/java/io/enmasse/k8s/util/JULInitializingTest.java +++ /dev/null @@ -1,19 +0,0 @@ -/* - * Copyright 2017-2018, EnMasse authors. - * License: Apache License 2.0 (see the file LICENSE or http://apache.org/licenses/LICENSE-2.0.html). - */ -package io.enmasse.k8s.util; - -import org.slf4j.bridge.SLF4JBridgeHandler; - -/** - * Initializes the JUL logger bridge, required for test using OpenShiftServer, or other classes using JUL. - */ -public class JULInitializingTest { - - static { - SLF4JBridgeHandler.removeHandlersForRootLogger(); - SLF4JBridgeHandler.install(); - } - -} diff --git a/k8s-api/src/main/java/io/enmasse/k8s/util/Templates.java b/k8s-api/src/main/java/io/enmasse/k8s/util/Templates.java deleted file mode 100644 index 8244291b4c2..00000000000 --- a/k8s-api/src/main/java/io/enmasse/k8s/util/Templates.java +++ /dev/null @@ -1,182 +0,0 @@ -/* - * Copyright 2016-2018, EnMasse authors. - * License: Apache License 2.0 (see the file LICENSE or http://apache.org/licenses/LICENSE-2.0.html). - */ -package io.enmasse.k8s.util; - -import java.io.BufferedInputStream; -import java.io.File; -import java.io.FileInputStream; -import java.io.InputStream; -import java.util.Collections; -import java.util.HashMap; -import java.util.Map; -import java.util.Objects; -import java.util.regex.Matcher; -import java.util.regex.Pattern; - -import com.fasterxml.jackson.databind.JsonNode; -import com.fasterxml.jackson.databind.ObjectMapper; -import com.fasterxml.jackson.databind.node.TreeTraversingParser; -import com.fasterxml.jackson.dataformat.yaml.YAMLFactory; -import com.mifmif.common.regex.Generex; - -import io.fabric8.kubernetes.api.model.HasMetadata; -import io.fabric8.kubernetes.api.model.KubernetesList; -import io.fabric8.kubernetes.api.model.KubernetesListBuilder; - -public final class Templates { - private Templates() {} - - private static final ObjectMapper MAPPER = new ObjectMapper(new YAMLFactory()); - private static final Pattern VARIABLE_PATTERN = Pattern.compile("\\$\\{([^{}]*?)\\}"); - - public static String replace(final String string, final Map parameters) { - - if (string == null) { - return null; - } - - final Matcher m = VARIABLE_PATTERN.matcher(string); - - boolean result = m.find(); - if (result) { - final StringBuffer sb = new StringBuffer(); - do { - final String replacement = parameters.get(m.group(1)); - if (replacement == null) { - m.appendReplacement(sb, parameters.containsKey(m.group(1)) ? "" : escape(m.group())); - } else { - m.appendReplacement(sb, escape(replace(replacement, parameters))); - } - - result = m.find(); - } while (result); - m.appendTail(sb); - - return sb.toString(); - - } else { - return string; - } - } - - private static String escape(String text) { - if ( text == null ) { - return null; - } - return text.replace("\\", "\\\\").replace("$", "\\$"); - } - - /** - * Process a template. - * - * @param templateFile The file to process. - * @param parameters The parameters to apply, may be {@code null}. - * @return The list of processed resources. - */ - public static KubernetesList process(final File templateFile, final Map parameters) { - - try (InputStream input = new BufferedInputStream(new FileInputStream(templateFile))) { - return processLocally(input, parameters); - } catch (Exception e) { - throw new RuntimeException("Failed to process template", e); - } - - // FIXME: use processLocally when it is fixed - // return client.templates().load(templateFile).processLocally(parameters); - } - - public static KubernetesList process(final InputStream template, final Map parameters) { - try { - return processLocally(template, parameters); - } catch (Exception e) { - throw new RuntimeException("Failed to process template", e); - } - } - - private static KubernetesList processLocally(final InputStream input, final Map parameters) throws Exception { - - Objects.requireNonNull(parameters); - - final JsonNode tree = MAPPER.readTree(input); - - JsonNode objects = tree.get("objects"); - if (objects == null || !objects.isArray()) { - return new KubernetesList(); - } - - final Map finalParameters = makeParameters(parameters, tree.get("parameters")); - - KubernetesListBuilder result = new KubernetesListBuilder(); - - for (JsonNode node : objects) { - TreeTraversingParser parser = new TreeTraversingParser((JsonNode) node, MAPPER) { - @Override - public String getText() { - return replace(super.getText(), finalParameters); - } - }; - HasMetadata item = MAPPER.readValue(parser, HasMetadata.class); - result.addToItems(item); - } - - return result.build(); - } - - private static String fieldAsText(final JsonNode node, final String fieldName) { - final JsonNode field = node.get(fieldName); - if (field == null) { - return null; - } - return field.asText(); - } - - private static Map makeParameters(final Map parameters, final JsonNode parametersNode) { - - if (parametersNode == null || !parametersNode.isArray()) { - return Collections.emptyMap(); - } - - final Map result = new HashMap<>(); - - for (final JsonNode node : parametersNode) { - if (!node.isObject()) { - continue; - } - - final String name = fieldAsText(node, "name"); - if (name == null || name.isEmpty()) { - continue; - } - - final String value; - - if (parameters.containsKey(name)) { - value = parameters.get(name); - } else if ("expression".equals(fieldAsText(node, "generate"))) { - final String from = node.get("from").asText(); - final Generex generex = new Generex(from); - value = generex.random(); - } else { - value = fieldAsText(node, "value"); - } - - if (value == null) { - final JsonNode requireNode = node.get("required"); - if (requireNode != null && requireNode.asBoolean(true)) { - throw new IllegalArgumentException(String.format("Required parameter '%s' is missing and has no default value", name)); - } else { - result.put(name, ""); - } - } else { - result.put(name, value); - } - - } - - - return result; - } - -} diff --git a/k8s-api/src/main/java/io/enmasse/k8s/util/TimeUtil.java b/k8s-api/src/main/java/io/enmasse/k8s/util/TimeUtil.java deleted file mode 100644 index 5f797c59b0a..00000000000 --- a/k8s-api/src/main/java/io/enmasse/k8s/util/TimeUtil.java +++ /dev/null @@ -1,55 +0,0 @@ -/* - * Copyright 2016-2018, EnMasse authors. - * License: Apache License 2.0 (see the file LICENSE or http://apache.org/licenses/LICENSE-2.0.html). - */ -package io.enmasse.k8s.util; - -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import java.time.*; -import java.time.format.DateTimeFormatter; -import java.time.format.DateTimeParseException; - -public class TimeUtil { - private static final Logger log = LoggerFactory.getLogger(TimeUtil.class); - private static final DateTimeFormatter formatter = DateTimeFormatter - .ofPattern("yyyy-MM-dd'T'HH:mm:ss'Z'") - .withZone(ZoneId.of("UTC")); - - private static final DateTimeFormatter fallbackPattern = DateTimeFormatter - .ofPattern("yyyy-MM-dd'T'HH:mm:ss.SSS'Z'") - .withZone(ZoneId.of("UTC")); - - - public static String formatRfc3339(Instant instant) { - return formatter.format(instant); - } - - public static Instant parseRfc3339(String data) { - try { - return LocalDateTime.parse(data, formatter).toInstant(ZoneOffset.UTC); - } catch (DateTimeParseException e) { - log.info("Failed parsing {} using format '{}', falling back to format '{}'", data, formatter, fallbackPattern); - return LocalDateTime.parse(data, fallbackPattern).toInstant(ZoneOffset.UTC); - } - } - - public static String formatHumanReadable(Duration duration) { - if (duration.isNegative()) { - return ""; - } else if (duration.getSeconds() < 0) { - return "0s"; - } else if (duration.getSeconds() < 60) { - return String.format("%ds", duration.getSeconds()); - } else if (duration.toMinutes() < 60) { - return String.format("%dm", duration.toMinutes()); - } else if (duration.toHours() < 24) { - return String.format("%dh", duration.toHours()); - } else if (duration.toDays() < 365) { - return String.format("%dd", duration.toDays()); - } else { - return String.format("%dy", duration.toDays() / 365); - } - } -} diff --git a/k8s-api/src/test/java/io/enmasse/k8s/api/ConfigMapAddressApiTest.java b/k8s-api/src/test/java/io/enmasse/k8s/api/ConfigMapAddressApiTest.java deleted file mode 100644 index 430e17c3876..00000000000 --- a/k8s-api/src/test/java/io/enmasse/k8s/api/ConfigMapAddressApiTest.java +++ /dev/null @@ -1,120 +0,0 @@ -/* - * Copyright 2017-2018, EnMasse authors. - * License: Apache License 2.0 (see the file LICENSE or http://apache.org/licenses/LICENSE-2.0.html). - */ -package io.enmasse.k8s.api; - -import io.enmasse.address.model.Address; -import io.enmasse.address.model.AddressBuilder; -import io.enmasse.k8s.util.JULInitializingTest; -import io.fabric8.kubernetes.client.NamespacedKubernetesClient; -import io.fabric8.kubernetes.client.server.mock.KubernetesServer; -import org.junit.jupiter.api.AfterEach; -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Test; -import java.util.Optional; -import java.util.UUID; - -import static org.junit.jupiter.api.Assertions.*; - - -class ConfigMapAddressApiTest extends JULInitializingTest { - - private static final String ADDRESS = "myaddress"; - private static final String ADDRESS_TYPE = "mytype"; - private static final String ADDRESS_PLAN = "myplan"; - private static final String ADDRESS_SPACE_NAMESPACE = "myproject"; - private static final String ADDRESS_SPACE = "myspace"; - private static final String ADDRESS_NAME = String.format("%s.%s", ADDRESS_SPACE, ADDRESS); - - private KubernetesServer kubeServer = new KubernetesServer(false, true); - private AddressApi api; - - @BeforeEach - void setUp() { - kubeServer.before(); - NamespacedKubernetesClient client = kubeServer.getClient(); - api = new ConfigMapAddressApi(client, UUID.randomUUID().toString(), null, null); - } - - @AfterEach - void tearDown() { - kubeServer.after(); - } - - @Test - void create() { - Address address = createAddress(ADDRESS_SPACE_NAMESPACE, ADDRESS_NAME); - - api.createAddress(address); - Optional
readAddress = api.getAddressWithName(ADDRESS_SPACE_NAMESPACE, ADDRESS_NAME); - - assertTrue(readAddress.isPresent()); - Address read = readAddress.get(); - - assertEquals(ADDRESS, read.getSpec().getAddress()); - assertEquals(ADDRESS_SPACE, Address.extractAddressSpace(read)); - assertEquals(ADDRESS_NAME, read.getMetadata().getName()); - assertEquals(ADDRESS_TYPE, read.getSpec().getType()); - assertEquals(ADDRESS_PLAN, read.getSpec().getPlan()); - } - - @Test - void replace() { - Address address = createAddress(ADDRESS_SPACE_NAMESPACE, ADDRESS_NAME); - final String annotationKey = "myannotation"; - String annotationValue = "value"; - Address update = new AddressBuilder(address).editOrNewMetadata().addToAnnotations(annotationKey, annotationValue).endMetadata().build(); - - api.createAddress(address); - assertTrue(api.getAddressWithName(ADDRESS_SPACE_NAMESPACE, ADDRESS_NAME).isPresent()); - - boolean replaced = api.replaceAddress(update); - assertTrue(replaced); - - Address read = api.getAddressWithName(ADDRESS_SPACE_NAMESPACE, ADDRESS_NAME).get(); - - assertEquals(ADDRESS_NAME, read.getMetadata().getName()); - assertEquals(annotationValue, read.getAnnotation(annotationKey)); - } - - @Test - void replaceNotFound() { - Address address = createAddress(ADDRESS_SPACE_NAMESPACE, ADDRESS_NAME); - - boolean replaced = api.replaceAddress(address); - assertFalse(replaced); - } - - @Test - void delete() { - Address space = createAddress(ADDRESS_SPACE_NAMESPACE, ADDRESS_NAME); - - api.createAddress(space); - - boolean deleted = api.deleteAddress(space); - assertTrue(deleted); - - assertFalse(api.getAddressWithName(ADDRESS_SPACE_NAMESPACE, ADDRESS_NAME).isPresent()); - - boolean deletedAgain = api.deleteAddress(space); - assertFalse(deletedAgain); - } - - private Address createAddress(String namespace, String name) { - return new AddressBuilder() - .withNewMetadata() - .withName(name) - .withNamespace(namespace) - .endMetadata() - - .withNewSpec() - .withAddress(ADDRESS) - .withAddressSpace(ADDRESS_SPACE) - .withType(ADDRESS_TYPE) - .withPlan(ADDRESS_PLAN) - .endSpec() - - .build(); - } -} diff --git a/k8s-api/src/test/java/io/enmasse/k8s/api/ConfigMapAddressSpaceApiTest.java b/k8s-api/src/test/java/io/enmasse/k8s/api/ConfigMapAddressSpaceApiTest.java deleted file mode 100644 index bbb365f5084..00000000000 --- a/k8s-api/src/test/java/io/enmasse/k8s/api/ConfigMapAddressSpaceApiTest.java +++ /dev/null @@ -1,122 +0,0 @@ -/* - * Copyright 2017-2018, EnMasse authors. - * License: Apache License 2.0 (see the file LICENSE or http://apache.org/licenses/LICENSE-2.0.html). - */ -package io.enmasse.k8s.api; - -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertFalse; -import static org.junit.jupiter.api.Assertions.assertTrue; - -import java.util.Optional; - -import org.junit.jupiter.api.AfterEach; -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Test; - -import io.enmasse.address.model.AddressSpace; -import io.enmasse.address.model.AddressSpaceBuilder; -import io.enmasse.config.AnnotationKeys; -import io.enmasse.k8s.util.JULInitializingTest; -import io.fabric8.kubernetes.client.server.mock.KubernetesServer; - - -/** - * The mock server does not emulate behaviour with respect to resourceVersion. - */ -class ConfigMapAddressSpaceApiTest extends JULInitializingTest { - - private static final String ADDRESS_SPACE_NAME = "myspace"; - private static final String ADDRESS_SPACE_TYPE = "mytype"; - private static final String ADDRESS_SPACE_PLAN = "myplan"; - private static final String ADDRESS_SPACE_NAMESPACE = "myproject"; - private static final String TEST_UUID = "fd93dc62-197b-11e9-9e48-c85b762e5a2c"; - - private KubernetesServer kubeServer = new KubernetesServer(false, true); - private AddressSpaceApi api; - - @BeforeEach - void setUp() { - kubeServer.before(); - api = new ConfigMapAddressSpaceApi(kubeServer.getClient(), null); - } - - @AfterEach - void tearDown() { - kubeServer.after(); - } - - @Test - void create() throws Exception { - AddressSpace space = createAddressSpace(ADDRESS_SPACE_NAMESPACE, ADDRESS_SPACE_NAME); - - api.createAddressSpace(space); - Optional readAddressSpace = api.getAddressSpaceWithName(ADDRESS_SPACE_NAMESPACE, ADDRESS_SPACE_NAME); - - assertTrue(readAddressSpace.isPresent()); - AddressSpace read = readAddressSpace.get(); - - assertEquals(ADDRESS_SPACE_NAME, read.getMetadata().getName()); - assertEquals(ADDRESS_SPACE_TYPE, read.getSpec().getType()); - assertEquals(ADDRESS_SPACE_PLAN, read.getSpec().getPlan()); - assertEquals(TEST_UUID, read.getAnnotation(AnnotationKeys.INFRA_UUID)); - } - - @Test - void replace() throws Exception { - AddressSpace space = createAddressSpace(ADDRESS_SPACE_NAMESPACE, ADDRESS_SPACE_NAME); - final String annotationKey = "myannotation"; - String annotationValue = "value"; - AddressSpace update = new AddressSpaceBuilder(space).editOrNewMetadata().addToAnnotations(annotationKey, annotationValue).endMetadata().build(); - - api.createAddressSpace(space); - assertTrue(api.getAddressSpaceWithName(ADDRESS_SPACE_NAMESPACE, ADDRESS_SPACE_NAME).isPresent()); - - boolean replaced = api.replaceAddressSpace(update); - assertTrue(replaced); - - AddressSpace read = api.getAddressSpaceWithName(ADDRESS_SPACE_NAMESPACE, ADDRESS_SPACE_NAME).get(); - - assertEquals(ADDRESS_SPACE_NAME, read.getMetadata().getName()); - assertEquals(annotationValue, read.getAnnotation(annotationKey)); - } - - @Test - void replaceNotFound() throws Exception { - AddressSpace space = createAddressSpace(ADDRESS_SPACE_NAMESPACE, ADDRESS_SPACE_NAME); - - boolean replaced = api.replaceAddressSpace(space); - assertFalse(replaced); - } - - @Test - void delete() throws Exception { - AddressSpace space = createAddressSpace(ADDRESS_SPACE_NAMESPACE, ADDRESS_SPACE_NAME); - - api.createAddressSpace(space); - - boolean deleted = api.deleteAddressSpace(space); - assertTrue(deleted); - - assertFalse(api.getAddressSpaceWithName(ADDRESS_SPACE_NAMESPACE, ADDRESS_SPACE_NAME).isPresent()); - - boolean deletedAgain = api.deleteAddressSpace(space); - assertFalse(deletedAgain); - } - - private AddressSpace createAddressSpace(String namespace, String name) { - return new AddressSpaceBuilder() - .withNewMetadata() - .withName(name) - .withNamespace(namespace) - .addToAnnotations(AnnotationKeys.INFRA_UUID, TEST_UUID) - .endMetadata() - - .withNewSpec() - .withType(ADDRESS_SPACE_TYPE) - .withPlan(ADDRESS_SPACE_PLAN) - .endSpec() - - .build(); - } -} diff --git a/k8s-api/src/test/java/io/enmasse/k8s/api/KubeCrdApiTest.java b/k8s-api/src/test/java/io/enmasse/k8s/api/KubeCrdApiTest.java deleted file mode 100644 index 777f2e70764..00000000000 --- a/k8s-api/src/test/java/io/enmasse/k8s/api/KubeCrdApiTest.java +++ /dev/null @@ -1,108 +0,0 @@ -/* - * Copyright 2017-2018, EnMasse authors. - * License: Apache License 2.0 (see the file LICENSE or http://apache.org/licenses/LICENSE-2.0.html). - */ -package io.enmasse.k8s.api; - -import static org.junit.jupiter.api.Assertions.assertEquals; - -import java.time.Duration; -import java.util.Arrays; -import java.util.List; -import java.util.concurrent.CompletableFuture; -import java.util.concurrent.TimeUnit; - -import org.junit.jupiter.api.AfterEach; -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Test; - -import io.enmasse.admin.model.v1.AddressSpacePlan; -import io.enmasse.admin.model.v1.AddressSpacePlanList; -import io.enmasse.admin.model.v1.AdminCrd; -import io.enmasse.admin.model.v1.DoneableAddressSpacePlan; -import io.enmasse.k8s.util.JULInitializingTest; -import io.fabric8.kubernetes.api.model.apiextensions.CustomResourceDefinition; -import io.fabric8.kubernetes.client.NamespacedKubernetesClient; -import io.fabric8.kubernetes.client.server.mock.KubernetesServer; - -class KubeCrdApiTest extends JULInitializingTest { - - private KubernetesServer kubeServer = new KubernetesServer(false, true); - - @BeforeEach - void setUp() { - kubeServer.before(); - } - - @AfterEach - void tearDown() { - kubeServer.after(); - } - - @Test - void testNotifiesExisting() throws Exception { - NamespacedKubernetesClient client = kubeServer.getClient(); - CustomResourceDefinition crd = AdminCrd.addressSpacePlans(); - CrdApi addressSpacePlanApi = new KubeCrdApi<>(client, client.getNamespace(), crd, - AddressSpacePlan.class, - AddressSpacePlanList.class, - DoneableAddressSpacePlan.class); - - client.customResources(crd, AddressSpacePlan.class, AddressSpacePlanList.class, DoneableAddressSpacePlan.class) - .createNew() - .withNewMetadata() - .withName("plan1") - .withNamespace(client.getNamespace()) - .endMetadata() - .editOrNewSpec() - .withAddressSpaceType("standard") - .withAddressPlans(Arrays.asList("p1", "p2")) - .endSpec() - .done(); - - CompletableFuture> promise = new CompletableFuture<>(); - try (Watch watch = addressSpacePlanApi.watchResources(items -> { - if (!items.isEmpty()) { - promise.complete(items); - } - }, Duration.ofMinutes(1))) { - List list = promise.get(30, TimeUnit.SECONDS); - assertEquals(1, list.size()); - assertEquals("plan1", list.get(0).getMetadata().getName()); - } - } - - @Test - void testNotifiesCreated() throws Exception { - NamespacedKubernetesClient client = kubeServer.getClient(); - CustomResourceDefinition crd = AdminCrd.addressSpacePlans(); - CrdApi addressSpacePlanApi = new KubeCrdApi<>(client, client.getNamespace(), crd, - AddressSpacePlan.class, - AddressSpacePlanList.class, - DoneableAddressSpacePlan.class); - - CompletableFuture> promise = new CompletableFuture<>(); - try (Watch watch = addressSpacePlanApi.watchResources(items -> { - if (!items.isEmpty()) { - promise.complete(items); - } - - }, Duration.ofSeconds(2))) { - client.customResources(crd, AddressSpacePlan.class, AddressSpacePlanList.class, DoneableAddressSpacePlan.class) - .createNew() - .withNewMetadata() - .withName("plan1") - .withNamespace(client.getNamespace()) - .endMetadata() - .editOrNewSpec() - .withAddressSpaceType("standard") - .withAddressPlans(Arrays.asList("p1", "p2")) - .endSpec() - .done(); - - List list = promise.get(30, TimeUnit.SECONDS); - assertEquals(1, list.size()); - assertEquals("plan1", list.get(0).getMetadata().getName()); - } - } -} diff --git a/k8s-api/src/test/java/io/enmasse/k8s/api/KubeEventLoggerTest.java b/k8s-api/src/test/java/io/enmasse/k8s/api/KubeEventLoggerTest.java deleted file mode 100644 index 2d1e8dcb917..00000000000 --- a/k8s-api/src/test/java/io/enmasse/k8s/api/KubeEventLoggerTest.java +++ /dev/null @@ -1,89 +0,0 @@ -/* - * Copyright 2017-2018, EnMasse authors. - * License: Apache License 2.0 (see the file LICENSE or http://apache.org/licenses/LICENSE-2.0.html). - */ -package io.enmasse.k8s.api; - -import io.fabric8.kubernetes.api.model.DoneableEvent; -import io.fabric8.kubernetes.api.model.Event; -import io.fabric8.kubernetes.api.model.EventList; -import io.fabric8.kubernetes.client.KubernetesClient; -import io.fabric8.kubernetes.client.dsl.MixedOperation; -import io.fabric8.kubernetes.client.dsl.Resource; - -import org.junit.jupiter.api.Test; -import org.mockito.ArgumentCaptor; - -import java.time.Clock; -import java.time.Instant; -import java.time.ZoneId; - -import static io.enmasse.k8s.api.EventLogger.Type.Warning; -import static org.junit.jupiter.api.Assertions.assertNotNull; -import static org.hamcrest.CoreMatchers.is; -import static org.hamcrest.MatcherAssert.assertThat; -import static org.mockito.ArgumentMatchers.any; -import static org.mockito.ArgumentMatchers.startsWith; -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.verify; -import static org.mockito.Mockito.when; - -public class KubeEventLoggerTest { - - private enum TestReason implements EventLogger.Reason { - NONE - } - - private enum TestKind implements EventLogger.ObjectKind { - KIND - } - - @Test - public void testLogger() { - String ns = "myspace"; - String component = "me"; - Clock clock = Clock.fixed(Instant.ofEpochSecond(10), ZoneId.of("UTC")); - - String message = "it crashed"; - - KubernetesClient mockClient = mock(KubernetesClient.class); - MixedOperation> eventOperation = mock(MixedOperation.class); - Resource eventResource = mock(Resource.class); - - when(mockClient.events()).thenReturn(eventOperation); - when(eventOperation.inNamespace(any())).thenReturn(eventOperation); - when(eventOperation.withName(startsWith("me."))).thenReturn(eventResource); - when(eventResource.get()).thenReturn(null); - - EventLogger logger = new KubeEventLogger(mockClient, ns, clock, component); - logger.log(TestReason.NONE, "it crashed", Warning, TestKind.KIND, "myqueue"); - - ArgumentCaptor eventArgumentCaptor = ArgumentCaptor.forClass(Event.class); - verify(eventResource).create(eventArgumentCaptor.capture()); - Event newEvent = eventArgumentCaptor.getValue(); - assertNotNull(newEvent); - assertThat(newEvent.getMessage(), is(message)); - assertThat(newEvent.getReason(), is(TestReason.NONE.name())); - assertThat(newEvent.getType(), is(Warning.name())); - assertThat(newEvent.getFirstTimestamp(), is(clock.instant().toString())); - assertThat(newEvent.getLastTimestamp(), is(clock.instant().toString())); - assertThat(newEvent.getCount(), is(1)); - assertThat(newEvent.getInvolvedObject().getName(), is("myqueue")); - assertThat(newEvent.getInvolvedObject().getKind(), is(TestKind.KIND.name())); - - newEvent.setFirstTimestamp(Instant.ofEpochSecond(5).toString()); - when(eventResource.get()).thenReturn(newEvent); - logger.log(TestReason.NONE, "it crashed", Warning, TestKind.KIND, "myqueue"); - - eventArgumentCaptor = ArgumentCaptor.forClass(Event.class); - verify(eventResource).create(eventArgumentCaptor.capture()); - newEvent = eventArgumentCaptor.getValue(); - assertNotNull(newEvent); - assertThat(newEvent.getMessage(), is(message)); - assertThat(newEvent.getReason(), is(TestReason.NONE.name())); - assertThat(newEvent.getType(), is(Warning.name())); - assertThat(newEvent.getFirstTimestamp(), is(Instant.ofEpochSecond(5).toString())); - assertThat(newEvent.getLastTimestamp(), is(clock.instant().toString())); - assertThat(newEvent.getCount(), is(2)); - } -} diff --git a/k8s-api/src/test/java/io/enmasse/k8s/api/KubeSchemaApiTest.java b/k8s-api/src/test/java/io/enmasse/k8s/api/KubeSchemaApiTest.java deleted file mode 100644 index 3db62cf36d2..00000000000 --- a/k8s-api/src/test/java/io/enmasse/k8s/api/KubeSchemaApiTest.java +++ /dev/null @@ -1,229 +0,0 @@ -/* - * Copyright 2017-2018, EnMasse authors. - * License: Apache License 2.0 (see the file LICENSE or http://apache.org/licenses/LICENSE-2.0.html). - */ -package io.enmasse.k8s.api; - -import io.enmasse.address.model.AddressSpaceType; -import io.enmasse.address.model.Phase; -import io.enmasse.address.model.Schema; -import io.enmasse.admin.model.v1.*; -import io.enmasse.config.AnnotationKeys; -import io.fabric8.kubernetes.api.model.ObjectMetaBuilder; -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Test; - -import java.time.Clock; -import java.time.Duration; -import java.util.Arrays; -import java.util.List; -import java.util.Map; - -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertFalse; -import static org.junit.jupiter.api.Assertions.assertTrue; -import static org.mockito.ArgumentMatchers.any; -import static org.mockito.ArgumentMatchers.eq; -import static org.mockito.Mockito.*; - - -public class KubeSchemaApiTest { - - private CrdApi addressSpacePlanApi; - private CrdApi addressPlanApi; - private CrdApi standardInfraConfigApi; - private CrdApi brokeredInfraConfigApi; - private CrdApi authenticationServiceApi; - private CrdApi consoleServiceApi; - - @BeforeEach - public void setup() { - addressSpacePlanApi = mock(CrdApi.class); - addressPlanApi = mock(CrdApi.class); - standardInfraConfigApi = mock(CrdApi.class); - brokeredInfraConfigApi = mock(CrdApi.class); - authenticationServiceApi = mock(CrdApi.class); - consoleServiceApi = mock(CrdApi.class); - } - - @Test - public void testSchemaAssemble() { - KubeSchemaApi schemaApi = new KubeSchemaApi(addressSpacePlanApi, addressPlanApi, brokeredInfraConfigApi, standardInfraConfigApi, authenticationServiceApi, "1.0", Clock.systemUTC(), false, true); - - List addressSpacePlans = Arrays.asList( - new AddressSpacePlanBuilder() - .withNewMetadata() - .withName("spaceplan1") - .endMetadata() - .editOrNewSpec() - .withInfraConfigRef("infra1") - .withAddressSpaceType("standard") - .withAddressPlans(Arrays.asList("plan1", "plan2", "plan4")) - .withResourceLimits(Map.of("broker", 1.0, "router", 1.0, "aggregate", 1.0)) - .endSpec() - .build(), - new AddressSpacePlanBuilder() - .withNewMetadata() - .withName("spaceplan2") - .endMetadata() - .editOrNewSpec() - .withInfraConfigRef("infra1") - .withAddressSpaceType("brokered") - .withAddressPlans(Arrays.asList( "plan3")) - .withResourceLimits(Map.of("broker", 1.0)) - .endSpec() - .build(), - new AddressSpacePlanBuilder() - .withNewMetadata() - .withName("spaceplan3") - .endMetadata() - .editOrNewSpec() - .withInfraConfigRef("infra4") - .withAddressSpaceType("brokered") - .withAddressPlans(Arrays.asList( "unknown")) - .withResourceLimits(Map.of("broker", 1.0)) - .endSpec() - .build(), - new AddressSpacePlanBuilder() - .withNewMetadata() - .withName("spaceplan4") - .endMetadata() - .editOrNewSpec() - .withInfraConfigRef("infra1") - .withAddressSpaceType("brokered") - .withAddressPlans(Arrays.asList( "plan4")) - .withResourceLimits(Map.of("broker", 1.0)) - .endSpec() - .build()); - - List addressPlans = Arrays.asList( - new AddressPlanBuilder() - .withNewMetadata() - .withName("plan1") - .endMetadata() - .editOrNewSpec() - .withAddressType("queue") - .withResources(Map.of("broker", 0.1, "router", 0.01)) - .endSpec() - .build(), - new AddressPlanBuilder() - .withNewMetadata() - .withName("plan2") - .endMetadata() - .editOrNewSpec() - .withAddressType("topic") - .withResources(Map.of("broker", 0.1, "router", 0.01)) - .endSpec() - .build(), - new AddressPlanBuilder() - .withMetadata(new ObjectMetaBuilder() - .withName("plan4") - .build()) - .editOrNewSpec() - .withAddressType("anycast") - .withResources(Map.of("broker", 0.1, "router", 0.01)) - .endSpec() - .build(), - new AddressPlanBuilder() - .withNewMetadata() - .withName("plan3") - .endMetadata() - .editOrNewSpec() - .withAddressType("queue") - .withResources(Map.of("broker", 0.1)) - .endSpec() - .build()); - - List standardInfraConfigs = Arrays.asList( - new StandardInfraConfigBuilder() - .withNewMetadata() - .withName("infra1") - .endMetadata() - .build()); - - List brokeredInfraConfigs = Arrays.asList( - new BrokeredInfraConfigBuilder() - .withNewMetadata() - .withName("infra1") - .endMetadata() - .build()); - - List authenticationServices = Arrays.asList( - new AuthenticationServiceBuilder() - .withNewMetadata() - .withName("standard") - .endMetadata() - .build()); - - Schema schema = schemaApi.assembleSchema(addressSpacePlans, addressPlans, standardInfraConfigs, brokeredInfraConfigs, authenticationServices); - - assertTrue(schema.findAddressSpaceType("standard").isPresent()); - assertTrue(schema.findAddressSpaceType("brokered").isPresent()); - - { - AddressSpaceType type = schema.findAddressSpaceType("standard").get(); - assertTrue(type.findAddressSpacePlan("spaceplan1").isPresent()); - assertFalse(type.findAddressSpacePlan("spaceplan2").isPresent()); - assertTrue(type.findAddressSpacePlan("spaceplan1").get().getAddressPlans().contains("plan1")); - assertTrue(type.findAddressSpacePlan("spaceplan1").get().getAddressPlans().contains("plan2")); - assertTrue(type.findAddressSpacePlan("spaceplan1").get().getAddressPlans().contains("plan4")); - assertTrue(type.findInfraConfig("infra1").isPresent()); - - assertTrue(type.findAddressType("queue").get().findAddressPlan("plan1").isPresent()); - assertTrue(type.findAddressType("topic").get().findAddressPlan("plan2").isPresent()); - assertTrue(type.findAddressType("anycast").get().findAddressPlan("plan4").isPresent()); - assertEquals(Phase.Failed, ((AddressPlan)type.findAddressType("anycast").get().findAddressPlan("plan4").get()).getStatus().getPhase()); - assertEquals(Phase.Active, ((AddressPlan)type.findAddressType("queue").get().findAddressPlan("plan1").get()).getStatus().getPhase()); - } - { - AddressSpaceType type = schema.findAddressSpaceType("brokered").get(); - assertTrue(type.findAddressSpacePlan("spaceplan2").isPresent()); - assertFalse(type.findAddressSpacePlan("spaceplan1").isPresent()); - assertTrue(type.findAddressSpacePlan("spaceplan2").get().getAddressPlans().contains("plan3")); - assertFalse(type.findAddressSpacePlan("spaceplan2").get().getAddressPlans().contains("plan1")); - assertFalse(type.findAddressSpacePlan("spaceplan2").get().getAddressPlans().contains("plan2")); - assertTrue(type.findInfraConfig("infra1").isPresent()); - - assertTrue(type.findAddressType("queue").get().findAddressPlan("plan3").isPresent()); - } - { - assertEquals(Phase.Failed, addressSpacePlans.get(2).getStatus().getPhase()); - System.out.println(addressSpacePlans.get(2).getStatus().getMessage()); - assertTrue(addressSpacePlans.get(2).getStatus().getMessage().contains("missing infra config definition infra4")); - assertTrue(addressSpacePlans.get(2).getStatus().getMessage().contains("unable to find address plan definition")); - } - { - assertEquals(Phase.Failed, addressPlans.get(2).getStatus().getPhase()); - assertTrue(addressPlans.get(2).getStatus().getMessage().contains("address type anycast not supported by address space type brokered")); - } - - System.out.println(schema.printSchema()); - } - - @Test - public void testWatchCreated() throws Exception { - CrdApi addressSpacePlanApi = mock(CrdApi.class); - CrdApi addressPlanApi = mock(CrdApi.class); - CrdApi standardInfraConfigApi = mock(CrdApi.class); - CrdApi brokeredInfraConfigApi = mock(CrdApi.class); - CrdApi authenticationServiceApi = mock(CrdApi.class); - CrdApi consoleServiceApi = mock(CrdApi.class); - - Watch mockWatch = mock(Watch.class); - - when(addressSpacePlanApi.watchResources(any(), any())).thenReturn(mockWatch); - when(addressPlanApi.watchResources(any(), any())).thenReturn(mockWatch); - when(brokeredInfraConfigApi.watchResources(any(), any())).thenReturn(mockWatch); - when(standardInfraConfigApi.watchResources(any(), any())).thenReturn(mockWatch); - when(authenticationServiceApi.watchResources(any(), any())).thenReturn(mockWatch); - - SchemaApi schemaApi = new KubeSchemaApi(addressSpacePlanApi, addressPlanApi, brokeredInfraConfigApi, standardInfraConfigApi, authenticationServiceApi, "1.0", Clock.systemUTC(), true, false); - - schemaApi.watchSchema(items -> { }, Duration.ofSeconds(5)); - verify(addressSpacePlanApi).watchResources(any(), eq(Duration.ofSeconds(5))); - verify(addressPlanApi).watchResources(any(), eq(Duration.ofSeconds(5))); - verify(standardInfraConfigApi).watchResources(any(), eq(Duration.ofSeconds(5))); - verify(brokeredInfraConfigApi).watchResources(any(), eq(Duration.ofSeconds(5))); - verify(authenticationServiceApi).watchResources(any(), eq(Duration.ofSeconds(5))); - } -} diff --git a/k8s-api/src/test/java/io/enmasse/k8s/api/ResourceCheckerTest.java b/k8s-api/src/test/java/io/enmasse/k8s/api/ResourceCheckerTest.java deleted file mode 100644 index 65293959530..00000000000 --- a/k8s-api/src/test/java/io/enmasse/k8s/api/ResourceCheckerTest.java +++ /dev/null @@ -1,37 +0,0 @@ -/* - * Copyright 2017-2018, EnMasse authors. - * License: Apache License 2.0 (see the file LICENSE or http://apache.org/licenses/LICENSE-2.0.html). - */ -package io.enmasse.k8s.api; - -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Test; - -import java.time.Duration; -import java.util.Arrays; -import java.util.List; - -import static org.mockito.Mockito.*; - -public class ResourceCheckerTest { - ResourceChecker controller; - Watcher watcher; - - @BeforeEach - public void setup() { - watcher = mock(Watcher.class); - controller = new ResourceChecker<>(watcher, Duration.ofMillis(1)); - } - - @Test - public void testResourcesUpdated() throws Exception { - controller.doWork(); - verify(watcher, never()).onUpdate(any()); - - List items = Arrays.asList("hello", "there"); - controller.onInit(() -> items); - controller.onUpdate(); - controller.doWork(); - verify(watcher).onUpdate(eq(items)); - } -} diff --git a/k8s-api/src/test/java/io/enmasse/k8s/api/cache/EventCacheTest.java b/k8s-api/src/test/java/io/enmasse/k8s/api/cache/EventCacheTest.java deleted file mode 100644 index bf2bfc55c0a..00000000000 --- a/k8s-api/src/test/java/io/enmasse/k8s/api/cache/EventCacheTest.java +++ /dev/null @@ -1,134 +0,0 @@ -/* - * Copyright 2018-2019, EnMasse authors. - * License: Apache License 2.0 (see the file LICENSE or http://apache.org/licenses/LICENSE-2.0.html). - */ -package io.enmasse.k8s.api.cache; - -import io.fabric8.kubernetes.api.model.ConfigMap; -import io.fabric8.kubernetes.api.model.ConfigMapBuilder; -import org.junit.jupiter.api.Test; - -import java.util.Arrays; -import java.util.concurrent.TimeUnit; - -import static org.junit.jupiter.api.Assertions.assertFalse; -import static org.junit.jupiter.api.Assertions.assertTrue; -import static org.mockito.ArgumentMatchers.eq; -import static org.mockito.Mockito.*; - -public class EventCacheTest { - - @SuppressWarnings("unchecked") - private Processor mockProcessor() { - return mock(Processor.class); - } - - @Test - public void testAdd() throws Exception { - WorkQueue queue = new EventCache<>(new HasMetadataFieldExtractor<>()); - queue.add(map("ns", "k1")); - assertTrue(queue.hasSynced()); - assertFalse(queue.listKeys().contains("ns/k1")); - - Processor mockProc = mockProcessor(); - queue.pop(mockProc, 0, TimeUnit.SECONDS); - verify(mockProc).process(eq(map("ns", "k1"))); - assertTrue(queue.listKeys().contains("ns/k1")); - assertTrue(queue.list().contains(map("ns", "k1"))); - } - - @Test - public void testAddMultiple() throws Exception { - WorkQueue queue = new EventCache<>(new HasMetadataFieldExtractor<>()); - queue.add(map("ns1", "k1")); - queue.add(map("ns2", "k1")); - assertTrue(queue.hasSynced()); - assertFalse(queue.listKeys().contains("ns1/k1")); - assertFalse(queue.listKeys().contains("ns2/k1")); - - Processor mockProc = mockProcessor(); - queue.pop(mockProc, 0, TimeUnit.SECONDS); - // we only get the first event - verify(mockProc).process(eq(map("ns1", "k1"))); - - assertTrue(queue.listKeys().contains("ns1/k1")); - assertTrue(queue.listKeys().contains("ns2/k1")); - assertTrue(queue.list().contains(map("ns1", "k1"))); - assertTrue(queue.list().contains(map("ns2", "k1"))); - } - - @Test - public void testUpdate() throws Exception { - WorkQueue queue = new EventCache<>(new HasMetadataFieldExtractor<>()); - queue.update(map("ns", "k1")); - assertFalse(queue.listKeys().contains("ns/k1")); - assertFalse(queue.list().contains(map("ns", "k1"))); - - Processor mockProc = mockProcessor(); - queue.pop(mockProc, 0, TimeUnit.SECONDS); - verify(mockProc).process(eq(map("ns", "k1"))); - assertTrue(queue.listKeys().contains("ns/k1")); - assertTrue(queue.list().contains(map("ns", "k1"))); - } - - @Test - public void testRemove() throws Exception { - WorkQueue queue = new EventCache<>(new HasMetadataFieldExtractor<>()); - queue.add(map("ns", "k1")); - queue.delete(map("ns", "k1")); - assertTrue(queue.hasSynced()); - assertTrue(queue.listKeys().isEmpty()); - - Processor mockProc = mockProcessor(); - queue.pop(mockProc, 0, TimeUnit.SECONDS); - verify(mockProc).process(eq(map("ns", "k1"))); - assertTrue(queue.listKeys().isEmpty()); - assertTrue(queue.list().isEmpty()); - - queue.pop(mockProc, 0, TimeUnit.SECONDS); - verify(mockProc).process(eq(map("ns", "k1"))); - assertTrue(queue.listKeys().isEmpty()); - assertTrue(queue.list().isEmpty()); - } - - @Test - public void testEmpty() throws Exception { - WorkQueue queue = new EventCache<>(new HasMetadataFieldExtractor<>()); - Processor mockProc = mockProcessor(); - queue.pop(mockProc, 0, TimeUnit.SECONDS); - verifyZeroInteractions(mockProc); - assertFalse(queue.hasSynced()); - } - - @Test - public void testWakeup() throws Exception { - WorkQueue queue = new EventCache<>(new HasMetadataFieldExtractor<>()); - queue.wakeup(); - Processor mockProc = mockProcessor(); - queue.pop(mockProc, 1, TimeUnit.DAYS); - verifyZeroInteractions(mockProc); - assertFalse(queue.hasSynced()); - } - - @Test - public void testSync() throws Exception { - WorkQueue queue = new EventCache<>(new HasMetadataFieldExtractor<>()); - queue.replace(Arrays.asList(map("ns", "k1"), map("ns", "k2"), map("ns", "k3")), "33"); - assertFalse(queue.hasSynced()); - assertFalse(queue.list().isEmpty()); - - Processor mockProc = mockProcessor(); - queue.pop(mockProc, 0, TimeUnit.SECONDS); - verify(mockProc).process(null); - assertTrue(queue.hasSynced()); - } - - public static ConfigMap map(String namespace, String name) { - return new ConfigMapBuilder() - .editOrNewMetadata() - .withName(name) - .withNamespace(namespace) - .endMetadata() - .build(); - } -} diff --git a/k8s-api/src/test/java/io/enmasse/k8s/api/cache/ReflectorTest.java b/k8s-api/src/test/java/io/enmasse/k8s/api/cache/ReflectorTest.java deleted file mode 100644 index 3a56e66b8a0..00000000000 --- a/k8s-api/src/test/java/io/enmasse/k8s/api/cache/ReflectorTest.java +++ /dev/null @@ -1,130 +0,0 @@ -/* - * Copyright 2017-2018, EnMasse authors. - * License: Apache License 2.0 (see the file LICENSE or http://apache.org/licenses/LICENSE-2.0.html). - */ -package io.enmasse.k8s.api.cache; - -import io.fabric8.kubernetes.api.model.ConfigMap; -import io.fabric8.kubernetes.api.model.ConfigMapBuilder; -import io.fabric8.kubernetes.api.model.ConfigMapList; -import io.fabric8.kubernetes.api.model.ConfigMapListBuilder; -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Test; -import org.mockito.ArgumentCaptor; - -import java.time.Clock; -import java.time.Duration; -import java.time.Instant; -import java.time.ZoneId; -import java.util.Collections; -import java.util.List; - -import static org.hamcrest.CoreMatchers.is; -import static org.hamcrest.MatcherAssert.assertThat; -import static org.junit.jupiter.api.Assertions.assertNotNull; -import static org.mockito.Mockito.*; - -public class ReflectorTest { - Reflector reflector; - ListerWatcher testLister; - WorkQueue testStore; - Processor testProc; - - @BeforeEach - public void setup() { - testLister = mock(ListerWatcher.class); - testProc = mock(Processor.class); - testStore = new EventCache<>(new HasMetadataFieldExtractor<>()); - - Reflector.Config config = new Reflector.Config<>(); - config.setClock(Clock.fixed(Instant.now(), ZoneId.systemDefault())); - config.setListerWatcher(testLister); - config.setWorkQueue(testStore); - config.setExpectedType(ConfigMap.class); - config.setResyncInterval(Duration.ofSeconds(1)); - config.setProcessor(testProc); - reflector = new Reflector<>(config); - } - - @Test - public void testReflector() throws Exception { - when(testLister.list(any())).thenReturn(new ConfigMapListBuilder() - .editOrNewMetadata() - .withResourceVersion("3") - .endMetadata() - .addToItems(configMap("a1", "a2", "1")) - .addToItems(configMap("a1", "a3", "3")) - .addToItems(configMap("b1", "b2", "2")) - .build()); - - ArgumentCaptor> captor = ArgumentCaptor.forClass(io.fabric8.kubernetes.client.Watcher.class); - - reflector.run(); - verify(testProc, times(1)).process(any()); - reflector.run(); - verify(testProc, times(1)).process(any()); - assertStoreSize(2); - verify(testLister).watch(captor.capture(), any()); - verify(testLister).list(any()); - - assertConfigMap("a1", "a3"); - assertConfigMap("b1", "b2"); - - io.fabric8.kubernetes.client.Watcher watcher = captor.getValue(); - watcher.eventReceived(io.fabric8.kubernetes.client.Watcher.Action.MODIFIED, configMap("a1", "a4", "5")); - reflector.run(); - assertStoreSize(2); - assertConfigMap("a1", "a4"); - assertConfigMap("b1", "b2"); - watcher.eventReceived(io.fabric8.kubernetes.client.Watcher.Action.ADDED, configMap("c1", "c4", "5")); - reflector.run(); - assertStoreSize(3); - assertConfigMap("a1", "a4"); - assertConfigMap("b1", "b2"); - assertConfigMap("c1", "c4"); - watcher.eventReceived(io.fabric8.kubernetes.client.Watcher.Action.DELETED, configMap("b1", "b2", "6")); - reflector.run(); - assertStoreSize(2); - assertConfigMap("a1", "a4"); - assertConfigMap("c1", "c4"); - - reflector.run(); - reflector.run(); - reflector.run(); - reflector.run(); - verify(testProc, times(4)).process(any()); - reflector.run(); - verify(testProc, times(4)).process(any()); - } - - public void assertStoreSize(int expectedSize) throws InterruptedException { - assertThat("Store contains " + testStore.listKeys(), testStore.listKeys().size(), is(expectedSize)); - } - - private void assertConfigMap(String name, String expectedValue) throws InterruptedException { - String actual = findValue(testStore.list(), name); - assertNotNull(actual); - assertThat(actual, is(expectedValue)); - } - - private static String findValue(List list, String name) { - String found = null; - for (ConfigMap map : list) { - if (map.getMetadata().getName().equals(name)) { - found = map.getData().get("data"); - break; - } - } - return found; - } - - private static ConfigMap configMap(String name, String data, String resourceVersion) { - return new ConfigMapBuilder() - .editOrNewMetadata() - .withName(name) - .withResourceVersion(resourceVersion) - .endMetadata() - .withData(Collections.singletonMap("data", data)) - .build(); - } -} diff --git a/k8s-api/src/test/java/io/enmasse/k8s/util/TemplateTest.java b/k8s-api/src/test/java/io/enmasse/k8s/util/TemplateTest.java deleted file mode 100644 index de76463f37f..00000000000 --- a/k8s-api/src/test/java/io/enmasse/k8s/util/TemplateTest.java +++ /dev/null @@ -1,85 +0,0 @@ -/* - * Copyright 2016-2018, EnMasse authors. - * License: Apache License 2.0 (see the file LICENSE or http://apache.org/licenses/LICENSE-2.0.html). - */ -package io.enmasse.k8s.util; - -import static org.junit.Assert.assertThat; -import static org.junit.jupiter.api.Assertions.assertThrows; - -import java.io.InputStream; -import java.util.HashMap; -import java.util.Map; - -import org.hamcrest.CoreMatchers; -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Test; - -import com.fasterxml.jackson.databind.ObjectMapper; -import com.fasterxml.jackson.dataformat.yaml.YAMLFactory; - -import io.fabric8.kubernetes.api.model.KubernetesList; -import io.fabric8.kubernetes.api.model.KubernetesListBuilder; - -public class TemplateTest { - - private ObjectMapper mapper; - - @BeforeEach - public void setup() { - this.mapper = new ObjectMapper(new YAMLFactory()); - } - - @Test - public void testProcessLocally() throws Exception { - - final Map parameters = new HashMap<>(); - parameters.put("INFRA_UUID", "81932f70-20a9-11e9-b293-c85b762e5a2c"); - parameters.put("INFRA_NAMESPACE", "enmasse-infra"); - parameters.put("ADDRESS_SPACE_PLAN", "plan1"); - parameters.put("ADDRESS_SPACE", "myspace1"); - parameters.put("ADDRESS_SPACE_NAMESPACE", "ns"); - parameters.put("MESSAGING_SECRET", "secret2"); - parameters.put("AUTHENTICATION_SERVICE_HOST", "host1"); - parameters.put("AUTHENTICATION_SERVICE_PORT", "123"); - parameters.put("AUTHENTICATION_SERVICE_CA_CERT", "abc"); - parameters.put("STANDARD_INFRA_CONFIG_NAME", "configName1"); - - try ( - InputStream template = TemplateTest.class.getResourceAsStream("template_1.yaml"); - InputStream result = TemplateTest.class.getResourceAsStream("template_1.result.yaml");) { - - final KubernetesList list = normalize(Templates.process(template, parameters)); - final KubernetesList expected = normalize(mapper.readValue(result, KubernetesList.class)); - - assertThat(list, CoreMatchers.is(expected)); - // the next statements helps finding differences in the case of errors: - // assertEquals(mapper.writeValueAsString(expected), mapper.writeValueAsString(list)); - } - - } - - @Test - public void testMissingParameter() throws Exception { - - final Map parameters = new HashMap<>(); - - try (InputStream template = TemplateTest.class.getResourceAsStream("template_1.yaml")) { - assertThrows(RuntimeException.class, () -> { - Templates.process(template, parameters); - }); - } - - } - - - private KubernetesList normalize(KubernetesList list) throws Exception { - KubernetesListBuilder builder = new KubernetesListBuilder(list) - .withNewMetadata() - .endMetadata(); - - return mapper.readValue(mapper.writeValueAsBytes(builder.build()), KubernetesList.class); - } - - -} diff --git a/k8s-api/src/test/java/io/enmasse/k8s/util/TimeUtilTest.java b/k8s-api/src/test/java/io/enmasse/k8s/util/TimeUtilTest.java deleted file mode 100644 index ba73503b4c0..00000000000 --- a/k8s-api/src/test/java/io/enmasse/k8s/util/TimeUtilTest.java +++ /dev/null @@ -1,35 +0,0 @@ -/* - * Copyright 2016-2018, EnMasse authors. - * License: Apache License 2.0 (see the file LICENSE or http://apache.org/licenses/LICENSE-2.0.html). - */ -package io.enmasse.k8s.util; - -import org.junit.jupiter.api.Test; - -import java.time.Duration; -import java.time.Instant; - -import static org.junit.jupiter.api.Assertions.assertEquals; - - -public class TimeUtilTest { - @Test - public void testFormatter() { - assertEquals("1970-01-01T00:00:00Z", TimeUtil.formatRfc3339(Instant.ofEpochSecond(0))); - } - - @Test - public void testParser() { - assertEquals(1543313902, TimeUtil.parseRfc3339("2018-11-27T10:18:22Z").getEpochSecond()); - } - - @Test - public void testConvertToDuration() { - assertEquals("", TimeUtil.formatHumanReadable(Duration.ofSeconds(-1))); - assertEquals("10s", TimeUtil.formatHumanReadable(Duration.ofSeconds(10))); - assertEquals("10m", TimeUtil.formatHumanReadable(Duration.ofMinutes(10))); - assertEquals("10h", TimeUtil.formatHumanReadable(Duration.ofHours(10))); - assertEquals("10d", TimeUtil.formatHumanReadable(Duration.ofDays(10))); - assertEquals("1y", TimeUtil.formatHumanReadable(Duration.ofDays(400))); - } -} diff --git a/k8s-api/src/test/resources/io/enmasse/k8s/util/template_1.result.yaml b/k8s-api/src/test/resources/io/enmasse/k8s/util/template_1.result.yaml deleted file mode 100644 index 3f2639db3a3..00000000000 --- a/k8s-api/src/test/resources/io/enmasse/k8s/util/template_1.result.yaml +++ /dev/null @@ -1,526 +0,0 @@ -apiVersion: v1 -items: -- apiVersion: v1 - kind: Service - metadata: - annotations: - addressSpace: myspace1 - enmasse.io/service-port.amqp: 5672 - enmasse.io/service-port.amqp-wss: 443 - enmasse.io/service-port.amqps: 5671 - labels: - app: enmasse - infraType: standard - infraUuid: 81932f70-20a9-11e9-b293-c85b762e5a2c - name: messaging-81932f70-20a9-11e9-b293-c85b762e5a2c - spec: - ports: - - name: amqp - port: 5672 - protocol: TCP - targetPort: 5672 - - name: amqps - port: 5671 - protocol: TCP - targetPort: 5671 - - name: amqps-normal - port: 55671 - protocol: TCP - targetPort: amqps-normal - - name: amqps-broker - port: 56671 - protocol: TCP - targetPort: amqps-broker - - name: inter-router - port: 55672 - protocol: TCP - targetPort: 55672 - - name: https - port: 443 - protocol: TCP - targetPort: 8443 - selector: - capability: router - infraUuid: 81932f70-20a9-11e9-b293-c85b762e5a2c -- apiVersion: v1 - data: - qdrouterd.conf: "router {\n mode: interior\n id: ${HOSTNAME}\n defaultDistribution: - unavailable\n allowResumableLinkRoute: false\n}\n\nsslProfile {\n name: auth_service_ssl\n - \ certDb: /etc/qpid-dispatch/authservice-ca/tls.crt\n}\n\nauthServicePlugin - {\n name: auth_service\n authService: host1:123\n saslInitHostname: \n authSslProfile: - auth_service_ssl\n}\n\nlistener {\n host: 127.0.0.1\n port: 7777\n authenticatePeer: - no\n}\n\nlistener {\n host: 0.0.0.0\n port: 5672\n saslPlugin: auth_service\n - \ authenticatePeer: yes\n linkCapacity: 250\n}\n\nlistener {\n host: 0.0.0.0\n - \ port: 55672\n role: inter-router\n authenticatePeer: yes\n sslProfile: - inter_router_tls\n saslMechanisms: EXTERNAL\n linkCapacity: 250\n}\n\nlistener - {\n host: 0.0.0.0\n port: 8443\n saslPlugin: auth_service\n sslProfile: - ssl_details\n http: true\n authenticatePeer: yes\n linkCapacity: 250\n}\n\nsslProfile - {\n name: ssl_details\n certFile: /etc/qpid-dispatch/ssl/tls.crt\n keyFile: - /etc/qpid-dispatch/ssl/tls.key\n}\n\nlistener {\n host: 0.0.0.0\n port: 5671\n - \ saslPlugin: auth_service\n sslProfile: ssl_details\n requireSsl: true\n - \ authenticatePeer: yes\n linkCapacity: 250\n}\n\nsslProfile {\n name: inter_router_tls\n - \ certFile: /etc/enmasse-certs/tls.crt\n keyFile: /etc/enmasse-certs/tls.key\n - \ certDb: /etc/enmasse-certs/ca.crt\n}\n\nlistener {\n host: 0.0.0.0\n port: - 55671\n sslProfile: inter_router_tls\n saslMechanisms: EXTERNAL\n authenticatePeer: - yes\n linkCapacity: 250\n}\n\nlistener {\n host: 0.0.0.0\n port: 56671\n - \ sslProfile: inter_router_tls\n saslMechanisms: EXTERNAL\n role: route-container\n - \ authenticatePeer: yes\n linkCapacity: 250\n}\n\nconnector {\n host: ragent-81932f70-20a9-11e9-b293-c85b762e5a2c\n - \ port: 5671\n sslProfile: inter_router_tls\n verifyHostName: no\n}\n\nlinkRoute - {\n name: override.lwt_in\n prefix: $lwt\n direction: in\n containerId: - lwt-service\n}\n\nlinkRoute {\n name: override.lwt_out\n prefix: $lwt\n direction: - out\n containerId: lwt-service\n}\n\naddress {\n name: override.mqtt\n prefix: - $mqtt\n distribution: balanced\n}\n\naddress {\n name: override.subctrl\n - \ prefix: $subctrl\n distribution: balanced\n}\n\naddress {\n name: override.temp\n - \ prefix: $temp\n distribution: balanced\n}" - kind: ConfigMap - metadata: - labels: - app: enmasse - infraType: standard - infraUuid: 81932f70-20a9-11e9-b293-c85b762e5a2c - name: qdrouterd-config.81932f70-20a9-11e9-b293-c85b762e5a2c -- apiVersion: apps/v1 - kind: StatefulSet - metadata: - annotations: - addressSpace: myspace1 - enmasse.io/cert-cn: router.81932f70-20a9-11e9-b293-c85b762e5a2c - enmasse.io/cert-secret: router-internal-cert.81932f70-20a9-11e9-b293-c85b762e5a2c - prometheus.io/path: /metrics - prometheus.io/port: "8080" - prometheus.io/scrape: "true" - labels: - app: enmasse - infraType: standard - infraUuid: 81932f70-20a9-11e9-b293-c85b762e5a2c - name: qdrouterd - name: qdrouterd-81932f70-20a9-11e9-b293-c85b762e5a2c - spec: - affinity: - nodeAffinity: - preferredDuringSchedulingIgnoredDuringExecution: - - preference: - matchExpressions: - - key: node-role.enmasse.io/operator-infra - operator: In - values: - - "true" - weight: 1 - replicas: 1 - selector: - matchLabels: - app: enmasse - capability: router - infraType: standard - infraUuid: 81932f70-20a9-11e9-b293-c85b762e5a2c - name: qdrouterd - serviceName: qdrouterd-headless-81932f70-20a9-11e9-b293-c85b762e5a2c - template: - metadata: - annotations: - addressSpace: myspace1 - labels: - app: enmasse - capability: router - infraType: standard - infraUuid: 81932f70-20a9-11e9-b293-c85b762e5a2c - name: qdrouterd - spec: - containers: - - env: - - name: QDROUTERD_CONF - value: /etc/qpid-dispatch/config/qdrouterd.conf - - name: QDROUTERD_AUTO_MESH_DISCOVERY - value: INFER - - name: QDROUTERD_AUTO_MESH_SERVICE_NAME - value: qdrouterd-headless-81932f70-20a9-11e9-b293-c85b762e5a2c - image: docker.io/ctronenmassetesting/router:latest - imagePullPolicy: Always - livenessProbe: - initialDelaySeconds: 60 - tcpSocket: - port: amqp - name: router - ports: - - containerPort: 5672 - name: amqp - protocol: TCP - - containerPort: 5671 - name: amqps - protocol: TCP - - containerPort: 8443 - name: https - protocol: TCP - - containerPort: 55671 - name: amqps-normal - protocol: TCP - - containerPort: 56671 - name: amqps-broker - protocol: TCP - - containerPort: 56711 - name: amqps-probe - protocol: TCP - readinessProbe: - initialDelaySeconds: 60 - tcpSocket: - port: amqps-probe - resources: - limits: - memory: 512Mi - requests: - memory: 512Mi - volumeMounts: - - mountPath: /etc/qpid-dispatch/ssl - name: ssl-certs - readOnly: true - - mountPath: /etc/qpid-dispatch/authservice-ca - name: authservice-ca - readOnly: true - - mountPath: /etc/enmasse-certs - name: router-internal-cert - readOnly: true - - mountPath: /etc/qpid-dispatch/config - name: qdrouterd-config - - env: - - name: ROUTER_HOST - value: 127.0.0.1 - - name: ROUTER_PORT - value: "55671" - - name: CERT_DIR - value: /etc/enmasse-certs - image: docker.io/ctronenmassetesting/router-metrics:latest - imagePullPolicy: Always - livenessProbe: - initialDelaySeconds: 60 - tcpSocket: - port: routermetrics - name: metrics - ports: - - containerPort: 8080 - name: routermetrics - protocol: TCP - resources: - limits: - memory: 32Mi - requests: - memory: 32Mi - volumeMounts: - - mountPath: /etc/enmasse-certs - name: router-internal-cert - readOnly: true - volumes: - - name: ssl-certs - secret: - secretName: secret2 - - name: authservice-ca - secret: - secretName: authservice-ca.81932f70-20a9-11e9-b293-c85b762e5a2c - - name: router-internal-cert - secret: - secretName: router-internal-cert.81932f70-20a9-11e9-b293-c85b762e5a2c - - configMap: - name: qdrouterd-config.81932f70-20a9-11e9-b293-c85b762e5a2c - name: qdrouterd-config -- apiVersion: v1 - data: - tls.crt: abc - kind: Secret - metadata: - labels: - app: enmasse - infraType: standard - infraUuid: 81932f70-20a9-11e9-b293-c85b762e5a2c - name: authservice-ca.81932f70-20a9-11e9-b293-c85b762e5a2c -- apiVersion: apps/v1 - kind: Deployment - metadata: - annotations: - addressSpace: myspace1 - enmasse.io/cert-secret: admin-internal-cert.81932f70-20a9-11e9-b293-c85b762e5a2c - labels: - app: enmasse - infraType: standard - infraUuid: 81932f70-20a9-11e9-b293-c85b762e5a2c - name: admin - name: admin.81932f70-20a9-11e9-b293-c85b762e5a2c - spec: - affinity: - nodeAffinity: - preferredDuringSchedulingIgnoredDuringExecution: - - preference: - matchExpressions: - - key: node-role.enmasse.io/operator-infra - operator: In - values: - - "true" - weight: 1 - replicas: 1 - selector: - matchLabels: - app: enmasse - infraType: standard - infraUuid: 81932f70-20a9-11e9-b293-c85b762e5a2c - name: admin - strategy: - type: Recreate - template: - metadata: - annotations: - addressSpace: myspace1 - labels: - app: enmasse - infraType: standard - infraUuid: 81932f70-20a9-11e9-b293-c85b762e5a2c - name: admin - spec: - containers: - - env: - - name: MESSAGING_SERVICE_HOST - value: messaging-81932f70-20a9-11e9-b293-c85b762e5a2c - - name: MESSAGING_SERVICE_PORT_AMQPS_NORMAL - value: 55671 - - name: MESSAGING_SERVICE_PORT_AMQPS_BROKER - value: 56671 - - name: CERT_DIR - value: /etc/enmasse-certs - - name: TEMPLATE_DIR - value: /broker-templates - - name: STANDARD_INFRA_CONFIG_NAME - value: configName1 - - name: ADDRESS_SPACE - value: myspace1 - - name: ADDRESS_SPACE_NAMESPACE - value: ns - - name: INFRA_UUID - value: 81932f70-20a9-11e9-b293-c85b762e5a2c - - name: ADDRESS_SPACE_PLAN - value: plan1 - - name: RESYNC_INTERVAL - value: "600" - - name: CHECK_INTERVAL - value: "30" - - name: EVENT_QUEUE_SIZE - value: "10000" - - name: ENABLE_EVENT_LOGGER - value: "false" - - name: AUTHENTICATION_SERVICE_HOST - value: host1 - - name: AUTHENTICATION_SERVICE_PORT - value: "123" - - name: AUTHENTICATION_SERVICE_CA_SECRET - value: authservice-ca - - name: AUTHENTICATION_SERVICE_CLIENT_SECRET - value: "" - - name: JAVA_OPTS - value: -verbose:gc - - name: AUTHENTICATION_SERVICE_SASL_INIT_HOST - value: "" - - name: MESSAGING_SECRET - value: secret2 - image: docker.io/ctronenmassetesting/standard-controller:latest - imagePullPolicy: Always - livenessProbe: - httpGet: - path: /healthz - port: http - scheme: HTTP - initialDelaySeconds: 30 - name: standard-controller - ports: - - containerPort: 8889 - name: http - readinessProbe: - httpGet: - path: /healthz - port: http - scheme: HTTP - initialDelaySeconds: 30 - resources: - limits: - memory: 512Mi - requests: - memory: 512Mi - volumeMounts: - - mountPath: /etc/enmasse-certs - name: admin-internal-cert - readOnly: true - - mountPath: /broker-templates - name: broker-templates - readOnly: true - - env: - - name: MESSAGING_SERVICE_HOST - value: messaging-81932f70-20a9-11e9-b293-c85b762e5a2c - - name: MESSAGING_SERVICE_PORT_AMQPS_NORMAL - value: 55671 - - name: MESSAGING_SERVICE_PORT_AMQPS_BROKER - value: 56671 - - name: INFRA_UUID - value: 81932f70-20a9-11e9-b293-c85b762e5a2c - - name: ADDRESS_SPACE_PLAN - value: plan1 - - name: CERT_DIR - value: /etc/enmasse-certs - - name: AUTHENTICATION_SERVICE_HOST - value: host1 - - name: AUTHENTICATION_SERVICE_PORT - value: "123" - - name: AUTHENTICATION_SERVICE_CA_SECRET - value: authservice-ca - - name: AUTHENTICATION_SERVICE_CLIENT_SECRET - value: "" - - name: AUTHENTICATION_SERVICE_SASL_INIT_HOST - value: "" - - name: ADDRESS_SPACE - value: myspace1 - - name: ADDRESS_SPACE_NAMESPACE - value: ns - - name: MESSAGING_CERT - value: /opt/agent/messaging-cert/tls.crt - image: docker.io/ctronenmassetesting/agent:latest - imagePullPolicy: Always - livenessProbe: - httpGet: - path: /healthz - port: http - scheme: HTTP - periodSeconds: 30 - timeoutSeconds: 5 - name: agent - ports: - - containerPort: 8888 - name: http - - containerPort: 8080 - name: https - - containerPort: 56720 - name: amqp-ws - readinessProbe: - httpGet: - path: /healthz - port: http - scheme: HTTP - periodSeconds: 30 - timeoutSeconds: 5 - resources: - limits: - memory: 512Mi - requests: - memory: 512Mi - volumeMounts: - - mountPath: /opt/agent/authservice-ca - name: authservice-ca - readOnly: true - - mountPath: /etc/enmasse-certs - name: admin-internal-cert - readOnly: true - - mountPath: /opt/agent/messaging-cert - name: messaging-cert - readOnly: true - serviceAccountName: address-space-admin - volumes: - - name: authservice-ca - secret: - secretName: authservice-ca.81932f70-20a9-11e9-b293-c85b762e5a2c - - name: admin-internal-cert - secret: - secretName: admin-internal-cert.81932f70-20a9-11e9-b293-c85b762e5a2c - - name: messaging-cert - secret: - secretName: secret2 - - configMap: - name: standard-broker-definitions - name: broker-templates -- apiVersion: v1 - kind: Service - metadata: - annotations: - addressSpace: myspace1 - labels: - app: enmasse - infraType: standard - infraUuid: 81932f70-20a9-11e9-b293-c85b762e5a2c - name: qdrouterd-headless-81932f70-20a9-11e9-b293-c85b762e5a2c - spec: - clusterIP: None - ports: - - name: inter-router - port: 55672 - targetPort: 55672 - selector: - capability: router - infraUuid: 81932f70-20a9-11e9-b293-c85b762e5a2c -- apiVersion: v1 - kind: Service - metadata: - annotations: - addressSpace: myspace1 - labels: - app: enmasse - infraType: standard - infraUuid: 81932f70-20a9-11e9-b293-c85b762e5a2c - name: ragent-81932f70-20a9-11e9-b293-c85b762e5a2c - spec: - ports: - - name: amqp - port: 5671 - targetPort: 55671 - selector: - infraUuid: 81932f70-20a9-11e9-b293-c85b762e5a2c - name: admin -- apiVersion: v1 - kind: Service - metadata: - annotations: - addressSpace: myspace1 - labels: - app: enmasse - infraType: standard - infraUuid: 81932f70-20a9-11e9-b293-c85b762e5a2c - name: standard-controller-81932f70-20a9-11e9-b293-c85b762e5a2c - spec: - ports: - - name: health - port: 8080 - protocol: TCP - targetPort: 8889 - selector: - infraUuid: 81932f70-20a9-11e9-b293-c85b762e5a2c - name: admin -- apiVersion: v1 - kind: Service - metadata: - annotations: - addressSpace: myspace1 - labels: - app: enmasse - infraType: standard - infraUuid: 81932f70-20a9-11e9-b293-c85b762e5a2c - name: queue-scheduler-81932f70-20a9-11e9-b293-c85b762e5a2c - spec: - ports: - - name: amqp - port: 5672 - targetPort: 55671 - selector: - infraUuid: 81932f70-20a9-11e9-b293-c85b762e5a2c - name: admin -- apiVersion: v1 - kind: Service - metadata: - annotations: - addressSpace: myspace1 - enmasse.io/service-port.https: 8081 - labels: - app: enmasse - infraType: standard - infraUuid: 81932f70-20a9-11e9-b293-c85b762e5a2c - name: console-81932f70-20a9-11e9-b293-c85b762e5a2c - spec: - ports: - - name: https - port: 8081 - targetPort: 8080 - selector: - infraUuid: 81932f70-20a9-11e9-b293-c85b762e5a2c - name: admin -kind: List -metadata: {} diff --git a/k8s-api/src/test/resources/io/enmasse/k8s/util/template_1.yaml b/k8s-api/src/test/resources/io/enmasse/k8s/util/template_1.yaml deleted file mode 100644 index 31dbb1221cd..00000000000 --- a/k8s-api/src/test/resources/io/enmasse/k8s/util/template_1.yaml +++ /dev/null @@ -1,694 +0,0 @@ -apiVersion: v1 -kind: Template -metadata: - labels: - app: enmasse - name: standard-space-infra -objects: -- apiVersion: v1 - kind: Service - metadata: - annotations: - addressSpace: ${ADDRESS_SPACE} - enmasse.io/service-port.amqp: 5672 - enmasse.io/service-port.amqps: 5671 - enmasse.io/service-port.amqp-wss: 443 - labels: - app: enmasse - infraType: standard - infraUuid: ${INFRA_UUID} - name: messaging-${INFRA_UUID} - spec: - ports: - - name: amqp - port: 5672 - protocol: TCP - targetPort: 5672 - - name: amqps - port: 5671 - protocol: TCP - targetPort: 5671 - - name: amqps-normal - port: 55671 - protocol: TCP - targetPort: amqps-normal - - name: amqps-broker - port: 56671 - protocol: TCP - targetPort: amqps-broker - - name: inter-router - port: 55672 - protocol: TCP - targetPort: 55672 - - name: https - port: 443 - protocol: TCP - targetPort: 8443 - selector: - capability: router - infraUuid: ${INFRA_UUID} -- apiVersion: v1 - kind: ConfigMap - metadata: - name: qdrouterd-config.${INFRA_UUID} - labels: - app: enmasse - infraType: standard - infraUuid: ${INFRA_UUID} - data: - qdrouterd.conf: |- - router { - mode: interior - id: ${HOSTNAME} - defaultDistribution: unavailable - allowResumableLinkRoute: false - } - - sslProfile { - name: auth_service_ssl - certDb: /etc/qpid-dispatch/authservice-ca/tls.crt - } - - authServicePlugin { - name: auth_service - authService: ${AUTHENTICATION_SERVICE_HOST}:${AUTHENTICATION_SERVICE_PORT} - saslInitHostname: ${AUTHENTICATION_SERVICE_SASL_INIT_HOST} - authSslProfile: auth_service_ssl - } - - listener { - host: 127.0.0.1 - port: 7777 - authenticatePeer: no - } - - listener { - host: 0.0.0.0 - port: 5672 - saslPlugin: auth_service - authenticatePeer: yes - linkCapacity: ${ROUTER_LINK_CAPACITY} - } - - listener { - host: 0.0.0.0 - port: 55672 - role: inter-router - authenticatePeer: yes - sslProfile: inter_router_tls - saslMechanisms: EXTERNAL - linkCapacity: ${ROUTER_LINK_CAPACITY} - } - - listener { - host: 0.0.0.0 - port: 8443 - saslPlugin: auth_service - sslProfile: ssl_details - http: true - authenticatePeer: yes - linkCapacity: ${ROUTER_LINK_CAPACITY} - } - - sslProfile { - name: ssl_details - certFile: /etc/qpid-dispatch/ssl/tls.crt - keyFile: /etc/qpid-dispatch/ssl/tls.key - } - - listener { - host: 0.0.0.0 - port: 5671 - saslPlugin: auth_service - sslProfile: ssl_details - requireSsl: true - authenticatePeer: yes - linkCapacity: ${ROUTER_LINK_CAPACITY} - } - - sslProfile { - name: inter_router_tls - certFile: /etc/enmasse-certs/tls.crt - keyFile: /etc/enmasse-certs/tls.key - certDb: /etc/enmasse-certs/ca.crt - } - - listener { - host: 0.0.0.0 - port: 55671 - sslProfile: inter_router_tls - saslMechanisms: EXTERNAL - authenticatePeer: yes - linkCapacity: ${ROUTER_LINK_CAPACITY} - } - - listener { - host: 0.0.0.0 - port: 56671 - sslProfile: inter_router_tls - saslMechanisms: EXTERNAL - role: route-container - authenticatePeer: yes - linkCapacity: ${ROUTER_LINK_CAPACITY} - } - - connector { - host: ragent-${INFRA_UUID} - port: 5671 - sslProfile: inter_router_tls - verifyHostName: no - } - - linkRoute { - name: override.lwt_in - prefix: $lwt - direction: in - containerId: lwt-service - } - - linkRoute { - name: override.lwt_out - prefix: $lwt - direction: out - containerId: lwt-service - } - - address { - name: override.mqtt - prefix: $mqtt - distribution: balanced - } - - address { - name: override.subctrl - prefix: $subctrl - distribution: balanced - } - - address { - name: override.temp - prefix: $temp - distribution: balanced - } -- apiVersion: apps/v1 - kind: StatefulSet - metadata: - annotations: - addressSpace: ${ADDRESS_SPACE} - enmasse.io/cert-cn: router.${INFRA_UUID} - enmasse.io/cert-secret: router-internal-cert.${INFRA_UUID} - prometheus.io/path: /metrics - prometheus.io/port: '8080' - prometheus.io/scrape: 'true' - labels: - app: enmasse - name: qdrouterd - infraType: standard - infraUuid: ${INFRA_UUID} - name: qdrouterd-${INFRA_UUID} - spec: - affinity: - nodeAffinity: - preferredDuringSchedulingIgnoredDuringExecution: - - weight: 1 - preference: - matchExpressions: - - key: node-role.enmasse.io/operator-infra - operator: In - values: - - "true" - serviceName: qdrouterd-headless-${INFRA_UUID} - replicas: 1 - selector: - matchLabels: - app: enmasse - capability: router - name: qdrouterd - infraType: standard - infraUuid: ${INFRA_UUID} - template: - metadata: - annotations: - addressSpace: ${ADDRESS_SPACE} - labels: - app: enmasse - capability: router - name: qdrouterd - infraType: standard - infraUuid: ${INFRA_UUID} - spec: - containers: - - env: - - name: QDROUTERD_CONF - value: "/etc/qpid-dispatch/config/qdrouterd.conf" - - name: QDROUTERD_AUTO_MESH_DISCOVERY - value: "INFER" - - name: QDROUTERD_AUTO_MESH_SERVICE_NAME - value: "qdrouterd-headless-${INFRA_UUID}" - image: docker.io/ctronenmassetesting/router:latest - imagePullPolicy: Always - livenessProbe: - initialDelaySeconds: 60 - tcpSocket: - port: amqp - readinessProbe: - initialDelaySeconds: 60 - tcpSocket: - port: amqps-probe - name: router - resources: - limits: - memory: ${ROUTER_MEMORY_LIMIT} - requests: - memory: ${ROUTER_MEMORY_LIMIT} - ports: - - containerPort: 5672 - name: amqp - protocol: TCP - - containerPort: 5671 - name: amqps - protocol: TCP - - containerPort: 8443 - name: https - protocol: TCP - - containerPort: 55671 - name: amqps-normal - protocol: TCP - - containerPort: 56671 - name: amqps-broker - protocol: TCP - - containerPort: 56711 - name: amqps-probe - protocol: TCP - volumeMounts: - - mountPath: /etc/qpid-dispatch/ssl - name: ssl-certs - readOnly: true - - mountPath: /etc/qpid-dispatch/authservice-ca - name: authservice-ca - readOnly: true - - mountPath: /etc/enmasse-certs - name: router-internal-cert - readOnly: true - - mountPath: /etc/qpid-dispatch/config - name: qdrouterd-config - - env: - - name: ROUTER_HOST - value: 127.0.0.1 - - name: ROUTER_PORT - value: '55671' - - name: CERT_DIR - value: /etc/enmasse-certs - image: docker.io/ctronenmassetesting/router-metrics:latest - imagePullPolicy: Always - livenessProbe: - initialDelaySeconds: 60 - tcpSocket: - port: routermetrics - name: metrics - ports: - - containerPort: 8080 - name: routermetrics - protocol: TCP - resources: - limits: - memory: 32Mi - requests: - memory: 32Mi - volumeMounts: - - mountPath: /etc/enmasse-certs - name: router-internal-cert - readOnly: true - volumes: - - name: ssl-certs - secret: - secretName: ${MESSAGING_SECRET} - - name: authservice-ca - secret: - secretName: authservice-ca.${INFRA_UUID} - - name: router-internal-cert - secret: - secretName: router-internal-cert.${INFRA_UUID} - - name: qdrouterd-config - configMap: - name: qdrouterd-config.${INFRA_UUID} -- apiVersion: v1 - data: - tls.crt: ${AUTHENTICATION_SERVICE_CA_CERT} - kind: Secret - metadata: - name: authservice-ca.${INFRA_UUID} - labels: - app: enmasse - infraType: standard - infraUuid: ${INFRA_UUID} -- apiVersion: apps/v1 - kind: Deployment - metadata: - annotations: - addressSpace: ${ADDRESS_SPACE} - enmasse.io/cert-secret: admin-internal-cert.${INFRA_UUID} - labels: - app: enmasse - name: admin - infraType: standard - infraUuid: ${INFRA_UUID} - name: admin.${INFRA_UUID} - spec: - affinity: - nodeAffinity: - preferredDuringSchedulingIgnoredDuringExecution: - - weight: 1 - preference: - matchExpressions: - - key: node-role.enmasse.io/operator-infra - operator: In - values: - - "true" - replicas: 1 - strategy: - type: Recreate - selector: - matchLabels: - app: enmasse - name: admin - infraType: standard - infraUuid: ${INFRA_UUID} - template: - metadata: - annotations: - addressSpace: ${ADDRESS_SPACE} - labels: - app: enmasse - name: admin - infraType: standard - infraUuid: ${INFRA_UUID} - spec: - containers: - - env: - - name: MESSAGING_SERVICE_HOST - value: messaging-${INFRA_UUID} - - name: MESSAGING_SERVICE_PORT_AMQPS_NORMAL - value: 55671 - - name: MESSAGING_SERVICE_PORT_AMQPS_BROKER - value: 56671 - - name: CERT_DIR - value: /etc/enmasse-certs - - name: TEMPLATE_DIR - value: /broker-templates - - name: STANDARD_INFRA_CONFIG_NAME - value: ${STANDARD_INFRA_CONFIG_NAME} - - name: ADDRESS_SPACE - value: ${ADDRESS_SPACE} - - name: ADDRESS_SPACE_NAMESPACE - value: ${ADDRESS_SPACE_NAMESPACE} - - name: INFRA_UUID - value: ${INFRA_UUID} - - name: ADDRESS_SPACE_PLAN - value: ${ADDRESS_SPACE_PLAN} - - name: RESYNC_INTERVAL - value: ${CONTROLLER_RESYNC_INTERVAL} - - name: CHECK_INTERVAL - value: ${CONTROLLER_CHECK_INTERVAL} - - name: EVENT_QUEUE_SIZE - value: ${CONTROLLER_EVENT_QUEUE_SIZE} - - name: ENABLE_EVENT_LOGGER - value: ${ENABLE_EVENT_LOGGER} - - name: AUTHENTICATION_SERVICE_HOST - value: ${AUTHENTICATION_SERVICE_HOST} - - name: AUTHENTICATION_SERVICE_PORT - value: ${AUTHENTICATION_SERVICE_PORT} - - name: AUTHENTICATION_SERVICE_CA_SECRET - value: authservice-ca - - name: AUTHENTICATION_SERVICE_CLIENT_SECRET - value: ${AUTHENTICATION_SERVICE_CLIENT_SECRET} - - name: JAVA_OPTS - value: -verbose:gc - - name: AUTHENTICATION_SERVICE_SASL_INIT_HOST - value: ${AUTHENTICATION_SERVICE_SASL_INIT_HOST} - - name: MESSAGING_SECRET - value: ${MESSAGING_SECRET} - image: docker.io/ctronenmassetesting/standard-controller:latest - imagePullPolicy: Always - livenessProbe: - httpGet: - path: /healthz - port: http - scheme: HTTP - initialDelaySeconds: 30 - name: standard-controller - ports: - - containerPort: 8889 - name: http - readinessProbe: - httpGet: - path: /healthz - port: http - scheme: HTTP - initialDelaySeconds: 30 - resources: - limits: - memory: ${ADMIN_MEMORY_LIMIT} - requests: - memory: ${ADMIN_MEMORY_LIMIT} - volumeMounts: - - mountPath: /etc/enmasse-certs - name: admin-internal-cert - readOnly: true - - mountPath: /broker-templates - name: broker-templates - readOnly: true - - env: - - name: MESSAGING_SERVICE_HOST - value: messaging-${INFRA_UUID} - - name: MESSAGING_SERVICE_PORT_AMQPS_NORMAL - value: 55671 - - name: MESSAGING_SERVICE_PORT_AMQPS_BROKER - value: 56671 - - name: INFRA_UUID - value: ${INFRA_UUID} - - name: ADDRESS_SPACE_PLAN - value: ${ADDRESS_SPACE_PLAN} - - name: CERT_DIR - value: /etc/enmasse-certs - - name: AUTHENTICATION_SERVICE_HOST - value: ${AUTHENTICATION_SERVICE_HOST} - - name: AUTHENTICATION_SERVICE_PORT - value: ${AUTHENTICATION_SERVICE_PORT} - - name: AUTHENTICATION_SERVICE_CA_SECRET - value: authservice-ca - - name: AUTHENTICATION_SERVICE_CLIENT_SECRET - value: ${AUTHENTICATION_SERVICE_CLIENT_SECRET} - - name: AUTHENTICATION_SERVICE_SASL_INIT_HOST - value: ${AUTHENTICATION_SERVICE_SASL_INIT_HOST} - - name: ADDRESS_SPACE - value: ${ADDRESS_SPACE} - - name: ADDRESS_SPACE_NAMESPACE - value: ${ADDRESS_SPACE_NAMESPACE} - - name: MESSAGING_CERT - value: /opt/agent/messaging-cert/tls.crt - image: docker.io/ctronenmassetesting/agent:latest - imagePullPolicy: Always - livenessProbe: - httpGet: - path: /healthz - port: http - scheme: HTTP - periodSeconds: 30 - timeoutSeconds: 5 - name: agent - ports: - - containerPort: 8888 - name: http - - containerPort: 8080 - name: https - - containerPort: 56720 - name: amqp-ws - readinessProbe: - httpGet: - path: /healthz - port: http - scheme: HTTP - periodSeconds: 30 - timeoutSeconds: 5 - resources: - limits: - memory: ${ADMIN_MEMORY_LIMIT} - requests: - memory: ${ADMIN_MEMORY_LIMIT} - volumeMounts: - - mountPath: /opt/agent/authservice-ca - name: authservice-ca - readOnly: true - - mountPath: /etc/enmasse-certs - name: admin-internal-cert - readOnly: true - - mountPath: /opt/agent/messaging-cert - name: messaging-cert - readOnly: true - serviceAccountName: ${ADDRESS_SPACE_ADMIN_SA} - volumes: - - name: authservice-ca - secret: - secretName: authservice-ca.${INFRA_UUID} - - name: admin-internal-cert - secret: - secretName: admin-internal-cert.${INFRA_UUID} - - name: messaging-cert - secret: - secretName: ${MESSAGING_SECRET} - - name: broker-templates - configMap: - name: standard-broker-definitions -- apiVersion: v1 - kind: Service - metadata: - annotations: - addressSpace: ${ADDRESS_SPACE} - labels: - app: enmasse - infraType: standard - infraUuid: ${INFRA_UUID} - name: qdrouterd-headless-${INFRA_UUID} - spec: - clusterIP: None - ports: - - name: inter-router - port: 55672 - targetPort: 55672 - selector: - capability: router - infraUuid: ${INFRA_UUID} -- apiVersion: v1 - kind: Service - metadata: - annotations: - addressSpace: ${ADDRESS_SPACE} - labels: - app: enmasse - infraType: standard - infraUuid: ${INFRA_UUID} - name: ragent-${INFRA_UUID} - spec: - ports: - - name: amqp - port: 5671 - targetPort: 55671 - selector: - name: admin - infraUuid: ${INFRA_UUID} -- apiVersion: v1 - kind: Service - metadata: - annotations: - addressSpace: ${ADDRESS_SPACE} - labels: - app: enmasse - infraType: standard - infraUuid: ${INFRA_UUID} - name: standard-controller-${INFRA_UUID} - spec: - ports: - - name: health - port: 8080 - protocol: TCP - targetPort: 8889 - selector: - name: admin - infraUuid: ${INFRA_UUID} -- apiVersion: v1 - kind: Service - metadata: - annotations: - addressSpace: ${ADDRESS_SPACE} - labels: - app: enmasse - infraType: standard - infraUuid: ${INFRA_UUID} - name: queue-scheduler-${INFRA_UUID} - spec: - ports: - - name: amqp - port: 5672 - targetPort: 55671 - selector: - name: admin - infraUuid: ${INFRA_UUID} -- apiVersion: v1 - kind: Service - metadata: - annotations: - addressSpace: ${ADDRESS_SPACE} - enmasse.io/service-port.https: 8081 - labels: - app: enmasse - infraType: standard - infraUuid: ${INFRA_UUID} - name: console-${INFRA_UUID} - spec: - ports: - - name: https - port: 8081 - targetPort: 8080 - selector: - name: admin - infraUuid: ${INFRA_UUID} -parameters: -- name: INFRA_UUID - description: UUID to use for infrastructure - required: true -- name: INFRA_NAMESPACE - description: Namespace where infrastructure is created - required: true -- name: ADDRESS_SPACE_PLAN - description: Name of address space plan followed - required: true -- description: The link capacity setting for router - name: ROUTER_LINK_CAPACITY - value: '250' -- description: The secret with cert for the messaging service - name: MESSAGING_SECRET - required: true -- description: The name of our address space - name: ADDRESS_SPACE - required: true -- description: The namespace of our address space - name: ADDRESS_SPACE_NAMESPACE - required: true -- description: The hostname of the authentication service used by this address space - name: AUTHENTICATION_SERVICE_HOST - required: true -- description: The port of the authentication service used by this address space - name: AUTHENTICATION_SERVICE_PORT - required: true -- description: The CA cert to use for validating authentication service cert - name: AUTHENTICATION_SERVICE_CA_CERT - required: true -- description: The client cert to use as identity against authentication service - name: AUTHENTICATION_SERVICE_CLIENT_SECRET -- description: The hostname to use in sasl init - name: AUTHENTICATION_SERVICE_SASL_INIT_HOST -- description: Name standard infra config - name: STANDARD_INFRA_CONFIG_NAME - required: true -- description: Enable logging of kubernetes events - name: ENABLE_EVENT_LOGGER - value: 'false' -- description: Interval (in seconds) to use between controller resync - name: CONTROLLER_RESYNC_INTERVAL - value: '600' -- description: Interval (in seconds) to use between status checks - name: CONTROLLER_CHECK_INTERVAL - value: '30' -- description: Max number of events queued up for controller - name: CONTROLLER_EVENT_QUEUE_SIZE - value: '10000' -- description: The service account with address space admin privileges - name: ADDRESS_SPACE_ADMIN_SA - value: address-space-admin -- description: Memory limits for admin - name: ADMIN_MEMORY_LIMIT - value: 512Mi -- description: Memory limits for router - name: ROUTER_MEMORY_LIMIT - value: 512Mi diff --git a/k8s-api/src/test/resources/logback-test.xml b/k8s-api/src/test/resources/logback-test.xml deleted file mode 100644 index 1cecba0f6ef..00000000000 --- a/k8s-api/src/test/resources/logback-test.xml +++ /dev/null @@ -1,22 +0,0 @@ - - - - - - - true - - - - - %d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n - - - - - - - - - - \ No newline at end of file diff --git a/keycloak-plugin/Dockerfile b/keycloak-plugin/Dockerfile deleted file mode 100644 index 6cbfbdccd34..00000000000 --- a/keycloak-plugin/Dockerfile +++ /dev/null @@ -1,14 +0,0 @@ -FROM quay.io/enmasse/java-base:11-5 - -ARG version -ARG maven_version -ARG revision - -ENV VERSION=${version} REVISION=${revision} MAVEN_VERSION=${maven_version} -ENV KEYCLOAK_PLUGIN_DIR /keycloak-plugin - -ADD ./build/keycloak-plugin-${maven_version}.tar.gz ${KEYCLOAK_PLUGIN_DIR}/ - -USER 1000 - -ENTRYPOINT [ "sh", "-c", "${KEYCLOAK_PLUGIN_DIR}/bin/init-keycloak.sh" ] diff --git a/keycloak-plugin/LICENSE b/keycloak-plugin/LICENSE deleted file mode 100644 index 8dada3edaf5..00000000000 --- a/keycloak-plugin/LICENSE +++ /dev/null @@ -1,201 +0,0 @@ - Apache License - Version 2.0, January 2004 - http://www.apache.org/licenses/ - - TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION - - 1. Definitions. - - "License" shall mean the terms and conditions for use, reproduction, - and distribution as defined by Sections 1 through 9 of this document. - - "Licensor" shall mean the copyright owner or entity authorized by - the copyright owner that is granting the License. - - "Legal Entity" shall mean the union of the acting entity and all - other entities that control, are controlled by, or are under common - control with that entity. For the purposes of this definition, - "control" means (i) the power, direct or indirect, to cause the - direction or management of such entity, whether by contract or - otherwise, or (ii) ownership of fifty percent (50%) or more of the - outstanding shares, or (iii) beneficial ownership of such entity. - - "You" (or "Your") shall mean an individual or Legal Entity - exercising permissions granted by this License. - - "Source" form shall mean the preferred form for making modifications, - including but not limited to software source code, documentation - source, and configuration files. - - "Object" form shall mean any form resulting from mechanical - transformation or translation of a Source form, including but - not limited to compiled object code, generated documentation, - and conversions to other media types. - - "Work" shall mean the work of authorship, whether in Source or - Object form, made available under the License, as indicated by a - copyright notice that is included in or attached to the work - (an example is provided in the Appendix below). - - "Derivative Works" shall mean any work, whether in Source or Object - form, that is based on (or derived from) the Work and for which the - editorial revisions, annotations, elaborations, or other modifications - represent, as a whole, an original work of authorship. For the purposes - of this License, Derivative Works shall not include works that remain - separable from, or merely link (or bind by name) to the interfaces of, - the Work and Derivative Works thereof. - - "Contribution" shall mean any work of authorship, including - the original version of the Work and any modifications or additions - to that Work or Derivative Works thereof, that is intentionally - submitted to Licensor for inclusion in the Work by the copyright owner - or by an individual or Legal Entity authorized to submit on behalf of - the copyright owner. For the purposes of this definition, "submitted" - means any form of electronic, verbal, or written communication sent - to the Licensor or its representatives, including but not limited to - communication on electronic mailing lists, source code control systems, - and issue tracking systems that are managed by, or on behalf of, the - Licensor for the purpose of discussing and improving the Work, but - excluding communication that is conspicuously marked or otherwise - designated in writing by the copyright owner as "Not a Contribution." - - "Contributor" shall mean Licensor and any individual or Legal Entity - on behalf of whom a Contribution has been received by Licensor and - subsequently incorporated within the Work. - - 2. Grant of Copyright License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - copyright license to reproduce, prepare Derivative Works of, - publicly display, publicly perform, sublicense, and distribute the - Work and such Derivative Works in Source or Object form. - - 3. Grant of Patent License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - (except as stated in this section) patent license to make, have made, - use, offer to sell, sell, import, and otherwise transfer the Work, - where such license applies only to those patent claims licensable - by such Contributor that are necessarily infringed by their - Contribution(s) alone or by combination of their Contribution(s) - with the Work to which such Contribution(s) was submitted. If You - institute patent litigation against any entity (including a - cross-claim or counterclaim in a lawsuit) alleging that the Work - or a Contribution incorporated within the Work constitutes direct - or contributory patent infringement, then any patent licenses - granted to You under this License for that Work shall terminate - as of the date such litigation is filed. - - 4. Redistribution. You may reproduce and distribute copies of the - Work or Derivative Works thereof in any medium, with or without - modifications, and in Source or Object form, provided that You - meet the following conditions: - - (a) You must give any other recipients of the Work or - Derivative Works a copy of this License; and - - (b) You must cause any modified files to carry prominent notices - stating that You changed the files; and - - (c) You must retain, in the Source form of any Derivative Works - that You distribute, all copyright, patent, trademark, and - attribution notices from the Source form of the Work, - excluding those notices that do not pertain to any part of - the Derivative Works; and - - (d) If the Work includes a "NOTICE" text file as part of its - distribution, then any Derivative Works that You distribute must - include a readable copy of the attribution notices contained - within such NOTICE file, excluding those notices that do not - pertain to any part of the Derivative Works, in at least one - of the following places: within a NOTICE text file distributed - as part of the Derivative Works; within the Source form or - documentation, if provided along with the Derivative Works; or, - within a display generated by the Derivative Works, if and - wherever such third-party notices normally appear. The contents - of the NOTICE file are for informational purposes only and - do not modify the License. You may add Your own attribution - notices within Derivative Works that You distribute, alongside - or as an addendum to the NOTICE text from the Work, provided - that such additional attribution notices cannot be construed - as modifying the License. - - You may add Your own copyright statement to Your modifications and - may provide additional or different license terms and conditions - for use, reproduction, or distribution of Your modifications, or - for any such Derivative Works as a whole, provided Your use, - reproduction, and distribution of the Work otherwise complies with - the conditions stated in this License. - - 5. Submission of Contributions. Unless You explicitly state otherwise, - any Contribution intentionally submitted for inclusion in the Work - by You to the Licensor shall be under the terms and conditions of - this License, without any additional terms or conditions. - Notwithstanding the above, nothing herein shall supersede or modify - the terms of any separate license agreement you may have executed - with Licensor regarding such Contributions. - - 6. Trademarks. This License does not grant permission to use the trade - names, trademarks, service marks, or product names of the Licensor, - except as required for reasonable and customary use in describing the - origin of the Work and reproducing the content of the NOTICE file. - - 7. Disclaimer of Warranty. Unless required by applicable law or - agreed to in writing, Licensor provides the Work (and each - Contributor provides its Contributions) on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or - implied, including, without limitation, any warranties or conditions - of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A - PARTICULAR PURPOSE. You are solely responsible for determining the - appropriateness of using or redistributing the Work and assume any - risks associated with Your exercise of permissions under this License. - - 8. Limitation of Liability. In no event and under no legal theory, - whether in tort (including negligence), contract, or otherwise, - unless required by applicable law (such as deliberate and grossly - negligent acts) or agreed to in writing, shall any Contributor be - liable to You for damages, including any direct, indirect, special, - incidental, or consequential damages of any character arising as a - result of this License or out of the use or inability to use the - Work (including but not limited to damages for loss of goodwill, - work stoppage, computer failure or malfunction, or any and all - other commercial damages or losses), even if such Contributor - has been advised of the possibility of such damages. - - 9. Accepting Warranty or Additional Liability. While redistributing - the Work or Derivative Works thereof, You may choose to offer, - and charge a fee for, acceptance of support, warranty, indemnity, - or other liability obligations and/or rights consistent with this - License. However, in accepting such obligations, You may act only - on Your own behalf and on Your sole responsibility, not on behalf - of any other Contributor, and only if You agree to indemnify, - defend, and hold each Contributor harmless for any liability - incurred by, or claims asserted against, such Contributor by reason - of your accepting any such warranty or additional liability. - - END OF TERMS AND CONDITIONS - - APPENDIX: How to apply the Apache License to your work. - - To apply the Apache License to your work, attach the following - boilerplate notice, with the fields enclosed by brackets "{}" - replaced with your own identifying information. (Don't include - the brackets!) The text should be enclosed in the appropriate - comment syntax for the file format. We also recommend that a - file or class name and description of purpose be included on the - same "printed page" as the copyright notice for easier - identification within third-party archives. - - Copyright {yyyy} {name of copyright owner} - - 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. diff --git a/keycloak-plugin/Makefile b/keycloak-plugin/Makefile deleted file mode 100644 index e58bb7c8494..00000000000 --- a/keycloak-plugin/Makefile +++ /dev/null @@ -1,22 +0,0 @@ -include ../Makefile.java.mk -ARTIFACT_DIR=build/keycloak-plugin - -clean_plugin: - rm -rf sasl-plugin/target - -clean: clean_plugin - -build_tar: - mkdir -p $(ARTIFACT_DIR)/providers - mkdir -p $(ARTIFACT_DIR)/configuration - mkdir -p $(ARTIFACT_DIR)/bin - - cp -f sasl-plugin/target/sasl-plugin-$(MAVEN_VERSION).jar $(ARTIFACT_DIR)/providers - cp -f src/main/configuration/* $(ARTIFACT_DIR)/configuration - cp -f src/main/sh/*.sh $(ARTIFACT_DIR)/bin - - tar -czf build/keycloak-plugin-$(MAVEN_VERSION).tar.gz -C build/keycloak-plugin . - -package: build_tar - -.PHONY: build_tar diff --git a/keycloak-plugin/pom.xml b/keycloak-plugin/pom.xml deleted file mode 100644 index ead60c7a3af..00000000000 --- a/keycloak-plugin/pom.xml +++ /dev/null @@ -1,15 +0,0 @@ - - - - io.enmasse - enmasse - 0.32-SNAPSHOT - - pom - 4.0.0 - keycloak - - sasl-plugin - - diff --git a/keycloak-plugin/runlocal.sh b/keycloak-plugin/runlocal.sh deleted file mode 100755 index 940309491fd..00000000000 --- a/keycloak-plugin/runlocal.sh +++ /dev/null @@ -1,2 +0,0 @@ -#!/bin/sh -docker run -e KEYCLOAK_USER=admin -e KEYCLOAK_PASSWORD=admin -v $PWD/cert:/opt/enmasse/cert:z --net=host -ti enmasse-keycloak:latest diff --git a/keycloak-plugin/sasl-plugin/LICENSE b/keycloak-plugin/sasl-plugin/LICENSE deleted file mode 100644 index d6456956733..00000000000 --- a/keycloak-plugin/sasl-plugin/LICENSE +++ /dev/null @@ -1,202 +0,0 @@ - - Apache License - Version 2.0, January 2004 - http://www.apache.org/licenses/ - - TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION - - 1. Definitions. - - "License" shall mean the terms and conditions for use, reproduction, - and distribution as defined by Sections 1 through 9 of this document. - - "Licensor" shall mean the copyright owner or entity authorized by - the copyright owner that is granting the License. - - "Legal Entity" shall mean the union of the acting entity and all - other entities that control, are controlled by, or are under common - control with that entity. For the purposes of this definition, - "control" means (i) the power, direct or indirect, to cause the - direction or management of such entity, whether by contract or - otherwise, or (ii) ownership of fifty percent (50%) or more of the - outstanding shares, or (iii) beneficial ownership of such entity. - - "You" (or "Your") shall mean an individual or Legal Entity - exercising permissions granted by this License. - - "Source" form shall mean the preferred form for making modifications, - including but not limited to software source code, documentation - source, and configuration files. - - "Object" form shall mean any form resulting from mechanical - transformation or translation of a Source form, including but - not limited to compiled object code, generated documentation, - and conversions to other media types. - - "Work" shall mean the work of authorship, whether in Source or - Object form, made available under the License, as indicated by a - copyright notice that is included in or attached to the work - (an example is provided in the Appendix below). - - "Derivative Works" shall mean any work, whether in Source or Object - form, that is based on (or derived from) the Work and for which the - editorial revisions, annotations, elaborations, or other modifications - represent, as a whole, an original work of authorship. For the purposes - of this License, Derivative Works shall not include works that remain - separable from, or merely link (or bind by name) to the interfaces of, - the Work and Derivative Works thereof. - - "Contribution" shall mean any work of authorship, including - the original version of the Work and any modifications or additions - to that Work or Derivative Works thereof, that is intentionally - submitted to Licensor for inclusion in the Work by the copyright owner - or by an individual or Legal Entity authorized to submit on behalf of - the copyright owner. For the purposes of this definition, "submitted" - means any form of electronic, verbal, or written communication sent - to the Licensor or its representatives, including but not limited to - communication on electronic mailing lists, source code control systems, - and issue tracking systems that are managed by, or on behalf of, the - Licensor for the purpose of discussing and improving the Work, but - excluding communication that is conspicuously marked or otherwise - designated in writing by the copyright owner as "Not a Contribution." - - "Contributor" shall mean Licensor and any individual or Legal Entity - on behalf of whom a Contribution has been received by Licensor and - subsequently incorporated within the Work. - - 2. Grant of Copyright License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - copyright license to reproduce, prepare Derivative Works of, - publicly display, publicly perform, sublicense, and distribute the - Work and such Derivative Works in Source or Object form. - - 3. Grant of Patent License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - (except as stated in this section) patent license to make, have made, - use, offer to sell, sell, import, and otherwise transfer the Work, - where such license applies only to those patent claims licensable - by such Contributor that are necessarily infringed by their - Contribution(s) alone or by combination of their Contribution(s) - with the Work to which such Contribution(s) was submitted. If You - institute patent litigation against any entity (including a - cross-claim or counterclaim in a lawsuit) alleging that the Work - or a Contribution incorporated within the Work constitutes direct - or contributory patent infringement, then any patent licenses - granted to You under this License for that Work shall terminate - as of the date such litigation is filed. - - 4. Redistribution. You may reproduce and distribute copies of the - Work or Derivative Works thereof in any medium, with or without - modifications, and in Source or Object form, provided that You - meet the following conditions: - - (a) You must give any other recipients of the Work or - Derivative Works a copy of this License; and - - (b) You must cause any modified files to carry prominent notices - stating that You changed the files; and - - (c) You must retain, in the Source form of any Derivative Works - that You distribute, all copyright, patent, trademark, and - attribution notices from the Source form of the Work, - excluding those notices that do not pertain to any part of - the Derivative Works; and - - (d) If the Work includes a "NOTICE" text file as part of its - distribution, then any Derivative Works that You distribute must - include a readable copy of the attribution notices contained - within such NOTICE file, excluding those notices that do not - pertain to any part of the Derivative Works, in at least one - of the following places: within a NOTICE text file distributed - as part of the Derivative Works; within the Source form or - documentation, if provided along with the Derivative Works; or, - within a display generated by the Derivative Works, if and - wherever such third-party notices normally appear. The contents - of the NOTICE file are for informational purposes only and - do not modify the License. You may add Your own attribution - notices within Derivative Works that You distribute, alongside - or as an addendum to the NOTICE text from the Work, provided - that such additional attribution notices cannot be construed - as modifying the License. - - You may add Your own copyright statement to Your modifications and - may provide additional or different license terms and conditions - for use, reproduction, or distribution of Your modifications, or - for any such Derivative Works as a whole, provided Your use, - reproduction, and distribution of the Work otherwise complies with - the conditions stated in this License. - - 5. Submission of Contributions. Unless You explicitly state otherwise, - any Contribution intentionally submitted for inclusion in the Work - by You to the Licensor shall be under the terms and conditions of - this License, without any additional terms or conditions. - Notwithstanding the above, nothing herein shall supersede or modify - the terms of any separate license agreement you may have executed - with Licensor regarding such Contributions. - - 6. Trademarks. This License does not grant permission to use the trade - names, trademarks, service marks, or product names of the Licensor, - except as required for reasonable and customary use in describing the - origin of the Work and reproducing the content of the NOTICE file. - - 7. Disclaimer of Warranty. Unless required by applicable law or - agreed to in writing, Licensor provides the Work (and each - Contributor provides its Contributions) on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or - implied, including, without limitation, any warranties or conditions - of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A - PARTICULAR PURPOSE. You are solely responsible for determining the - appropriateness of using or redistributing the Work and assume any - risks associated with Your exercise of permissions under this License. - - 8. Limitation of Liability. In no event and under no legal theory, - whether in tort (including negligence), contract, or otherwise, - unless required by applicable law (such as deliberate and grossly - negligent acts) or agreed to in writing, shall any Contributor be - liable to You for damages, including any direct, indirect, special, - incidental, or consequential damages of any character arising as a - result of this License or out of the use or inability to use the - Work (including but not limited to damages for loss of goodwill, - work stoppage, computer failure or malfunction, or any and all - other commercial damages or losses), even if such Contributor - has been advised of the possibility of such damages. - - 9. Accepting Warranty or Additional Liability. While redistributing - the Work or Derivative Works thereof, You may choose to offer, - and charge a fee for, acceptance of support, warranty, indemnity, - or other liability obligations and/or rights consistent with this - License. However, in accepting such obligations, You may act only - on Your own behalf and on Your sole responsibility, not on behalf - of any other Contributor, and only if You agree to indemnify, - defend, and hold each Contributor harmless for any liability - incurred by, or claims asserted against, such Contributor by reason - of your accepting any such warranty or additional liability. - - END OF TERMS AND CONDITIONS - - APPENDIX: How to apply the Apache License to your work. - - To apply the Apache License to your work, attach the following - boilerplate notice, with the fields enclosed by brackets "[]" - replaced with your own identifying information. (Don't include - the brackets!) The text should be enclosed in the appropriate - comment syntax for the file format. We also recommend that a - file or class name and description of purpose be included on the - same "printed page" as the copyright notice for easier - identification within third-party archives. - - Copyright [yyyy] [name of copyright owner] - - 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. diff --git a/keycloak-plugin/sasl-plugin/pom.xml b/keycloak-plugin/sasl-plugin/pom.xml deleted file mode 100644 index bbcff21b27c..00000000000 --- a/keycloak-plugin/sasl-plugin/pom.xml +++ /dev/null @@ -1,173 +0,0 @@ - - - - io.enmasse - keycloak - 0.32-SNAPSHOT - - 4.0.0 - sasl-plugin - - 8 - 8 - - - - io.vertx - vertx-proton - compile - - - com.fasterxml.jackson.core - jackson-core - - - com.fasterxml.jackson.core - jackson-databind - - - com.fasterxml.jackson.core - jackson-annotations - - - - - - io.fabric8 - openshift-client - compile - - - com.fasterxml.jackson.core - jackson-core - - - com.fasterxml.jackson.core - jackson-databind - - - com.fasterxml.jackson.core - jackson-annotations - - - - - - org.keycloak - keycloak-server-spi - provided - - - - org.keycloak - keycloak-server-spi-private - provided - - - - org.keycloak - keycloak-services - provided - - - org.bouncycastle - * - - - javax.mail - javax.mail-api - - - org.glassfish - javax.json - - - org.jboss.spec.javax.servlet - jboss-servlet-api_3.0_spec - - - org.twitter4j - twitter4j-core - - - org.jboss.spec.javax.transaction - jboss-transaction-api_1.2_spec - - - org.jboss.resteasy - * - - - com.fasterxml.jackson.core - * - - - com.google.zxing - javase - - - com.openshift - openshift-restclient-java - - - - - - ch.qos.logback - logback-classic - test - - - org.junit.jupiter - junit-jupiter-api - test - - - org.junit.jupiter - junit-jupiter-engine - test - - - org.junit.platform - junit-platform-launcher - test - - - org.mockito - mockito-core - test - - - - - - org.apache.maven.plugins - maven-shade-plugin - - - - - - - io.netty:netty-handler-proxy - io.netty:netty-codec-socks - io.netty:netty-codec-http - io.netty:netty-codec-http2 - io.netty:netty-resolver-dns - io.netty:netty-codec-dns - - - - - - package - - shade - - - - - - - diff --git a/keycloak-plugin/sasl-plugin/src/main/assembly/assembly.xml b/keycloak-plugin/sasl-plugin/src/main/assembly/assembly.xml deleted file mode 100644 index 954cf70aa2e..00000000000 --- a/keycloak-plugin/sasl-plugin/src/main/assembly/assembly.xml +++ /dev/null @@ -1,26 +0,0 @@ - - bin - - zip - - false - - - ${project.basedir} - / - - README* - LICENSE* - NOTICE* - - - - - - / - true - runtime - - - diff --git a/keycloak-plugin/sasl-plugin/src/main/java/io/enmasse/keycloak/spi/AmqpServer.java b/keycloak-plugin/sasl-plugin/src/main/java/io/enmasse/keycloak/spi/AmqpServer.java deleted file mode 100644 index 594c687ff3d..00000000000 --- a/keycloak-plugin/sasl-plugin/src/main/java/io/enmasse/keycloak/spi/AmqpServer.java +++ /dev/null @@ -1,215 +0,0 @@ -/* - * Copyright 2018, EnMasse authors. - * License: Apache License 2.0 (see the file LICENSE or http://apache.org/licenses/LICENSE-2.0.html). - */ -/** - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You 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 io.enmasse.keycloak.spi; - -import io.fabric8.openshift.client.NamespacedOpenShiftClient; -import io.vertx.core.AbstractVerticle; -import io.vertx.core.Context; -import io.vertx.core.Vertx; -import io.vertx.core.net.JksOptions; -import io.vertx.core.net.PemKeyCertOptions; -import io.vertx.core.net.PfxOptions; -import io.vertx.proton.ProtonConnection; -import io.vertx.proton.ProtonServer; -import io.vertx.proton.ProtonServerOptions; -import okhttp3.OkHttpClient; -import org.apache.qpid.proton.amqp.Symbol; -import org.jboss.logging.Logger; -import org.keycloak.Config; -import org.keycloak.models.KeycloakSessionFactory; - -import java.io.UnsupportedEncodingException; -import java.net.URLDecoder; -import java.nio.charset.StandardCharsets; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.HashMap; -import java.util.HashSet; -import java.util.Map; -import java.util.Set; -import java.util.stream.Collectors; - -public class AmqpServer extends AbstractVerticle { - - private static final Logger LOG = Logger.getLogger(AmqpServer.class); - private static final Symbol ADDRESS_AUTHZ_CAPABILITY = Symbol.valueOf("ADDRESS-AUTHZ"); - private static final Symbol ADDRESS_AUTHZ_PROPERTY = Symbol.valueOf("address-authz"); - - private static final Map PERMISSIONS = new HashMap<>(); - static - { - PERMISSIONS.put("send", "send"); - PERMISSIONS.put("recv", "recv"); - // consume_ rather than recv_ was used as a prefix for Artemis plugin - PERMISSIONS.put("consume", "recv"); - PERMISSIONS.put("create", "create"); - PERMISSIONS.put("delete", "delete"); - PERMISSIONS.put("view", "view"); - PERMISSIONS.put("manage", "manage"); - - } - - private final String hostname; - private final int port; - private final Config.Scope config; - private final boolean useTls; - private final NamespacedOpenShiftClient client; - private final OkHttpClient httpClient; - private volatile ProtonServer server; - private KeycloakSessionFactory keycloakSessionFactory; - - public AmqpServer(String hostname, - int port, - final Config.Scope config, - final boolean useTls, - NamespacedOpenShiftClient client, - OkHttpClient httpClient) { - this.hostname = hostname; - this.port = port; - this.config = config; - this.useTls = useTls; - this.client = client; - this.httpClient = httpClient; - } - - private void connectHandler(ProtonConnection connection) { - String containerId = config.get("containerId", "keycloak-amqp"); - connection.setContainer(containerId); - connection.openHandler(conn -> { - UserData userData = connection.attachments().get(SaslAuthenticator.USER_ATTACHMENT, UserData.class); - if(userData != null) { - LOG.info("Responding with user data " + userData); - Map props = new HashMap<>(); - Map authUserMap = new HashMap<>(); - authUserMap.put("sub", userData.getId()); - authUserMap.put("preferred_username", userData.getUsername()); - props.put(Symbol.valueOf("authenticated-identity"), authUserMap); - props.put(Symbol.valueOf("groups"), new ArrayList<>(userData.getGroups())); - if(connection.getRemoteDesiredCapabilities() != null && Arrays.asList(connection.getRemoteDesiredCapabilities()).contains(ADDRESS_AUTHZ_CAPABILITY)) { - connection.setOfferedCapabilities(new Symbol[] { ADDRESS_AUTHZ_CAPABILITY }); - props.put(ADDRESS_AUTHZ_PROPERTY, getPermissionsFromGroups(userData.getGroups())); - } - connection.setProperties(props); - } - connection.open(); - connection.close(); - Context connectionContext = Vertx.currentContext(); - vertx.setTimer(10000, l -> { - connectionContext.runOnContext(x -> { - if(!connection.isDisconnected()) { - LOG.info("Closing connection which has not disconnected after 10s" + userData); - connection.disconnect(); - } - }); - }); - }).closeHandler(conn -> { - LOG.info("Received close - disconnecting"); - connection.close(); - connection.disconnect(); - }).disconnectHandler(protonConnection -> { - LOG.info("Disconnecting"); - connection.disconnect(); - }); - - } - - Map getPermissionsFromGroups(Set groups) { - Map> authMap = new HashMap<>(); - for(String group : groups) { - String[] parts = group.split("_", 2); - if(parts.length == 2) { - String permission = PERMISSIONS.get(parts[0]); - if(permission != null) { - try { - String address = URLDecoder.decode(parts[1], StandardCharsets.UTF_8.name()); - Set permissions = authMap.computeIfAbsent(address, a -> new HashSet<>()); - permissions.add(permission); - - } catch (UnsupportedEncodingException e) { - // Should never happen - } - } - } - } - return authMap.entrySet().stream().collect(Collectors.toMap(Map.Entry::getKey, e -> e.getValue().toArray(new String[e.getValue().size()]))); - } - - @Override - public void start() { - ProtonServerOptions options = new ProtonServerOptions(); - if(useTls) { - options.setSsl(true); - String path; - if((path = config.get("jksKeyStorePath")) != null) { - final JksOptions jksOptions = new JksOptions(); - jksOptions.setPath(path); - jksOptions.setPassword(config.get("keyStorePassword")); - options.setKeyStoreOptions(jksOptions); - } else if((path = config.get("pfxKeyStorePath")) != null) { - final PfxOptions pfxOptions = new PfxOptions(); - pfxOptions.setPath(path); - pfxOptions.setPassword(config.get("keyStorePassword")); - options.setPfxKeyCertOptions(pfxOptions); - } else if((path = config.get("pemCertificatePath")) != null) { - final PemKeyCertOptions pemKeyCertOptions = new PemKeyCertOptions(); - pemKeyCertOptions.setCertPath(path); - pemKeyCertOptions.setKeyPath(config.get("pemKeyPath")); - options.setPemKeyCertOptions(pemKeyCertOptions); - } else { - // use JDK settings? - } - - } - server = ProtonServer.create(vertx, options); - - server.saslAuthenticatorFactory(() -> new SaslAuthenticator(keycloakSessionFactory, config, useTls, this)); - server.connectHandler(this::connectHandler); - LOG.info("Starting server on "+hostname+":"+ port); - server.listen(port, hostname, event -> { - if(event.failed()) - { - LOG.error("Unable to listen for AMQP on "+hostname+":" + port, event.cause()); - } - - }); - } - - @Override - public void stop() { - if (server != null) { - server.close(); - } - } - - void setKeycloakSessionFactory(final KeycloakSessionFactory keycloakSessionFactory) - { - this.keycloakSessionFactory = keycloakSessionFactory; - } - - NamespacedOpenShiftClient getOpenShiftClient() { - return client; - } - - OkHttpClient getHttpClient() { - return httpClient; - } -} diff --git a/keycloak-plugin/sasl-plugin/src/main/java/io/enmasse/keycloak/spi/AmqpServerProviderFactory.java b/keycloak-plugin/sasl-plugin/src/main/java/io/enmasse/keycloak/spi/AmqpServerProviderFactory.java deleted file mode 100644 index 04c2ea22feb..00000000000 --- a/keycloak-plugin/sasl-plugin/src/main/java/io/enmasse/keycloak/spi/AmqpServerProviderFactory.java +++ /dev/null @@ -1,11 +0,0 @@ -/* - * Copyright 2018, EnMasse authors. - * License: Apache License 2.0 (see the file LICENSE or http://apache.org/licenses/LICENSE-2.0.html). - */ -package io.enmasse.keycloak.spi; - -import org.keycloak.provider.Provider; -import org.keycloak.provider.ProviderFactory; - -public interface AmqpServerProviderFactory extends Provider, ProviderFactory { -} diff --git a/keycloak-plugin/sasl-plugin/src/main/java/io/enmasse/keycloak/spi/AmqpServerProviderImpl.java b/keycloak-plugin/sasl-plugin/src/main/java/io/enmasse/keycloak/spi/AmqpServerProviderImpl.java deleted file mode 100644 index 364ef742fbf..00000000000 --- a/keycloak-plugin/sasl-plugin/src/main/java/io/enmasse/keycloak/spi/AmqpServerProviderImpl.java +++ /dev/null @@ -1,124 +0,0 @@ -/* - * Copyright 2018, EnMasse authors. - * License: Apache License 2.0 (see the file LICENSE or http://apache.org/licenses/LICENSE-2.0.html). - */ -/** - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You 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 io.enmasse.keycloak.spi; - -import io.fabric8.openshift.client.DefaultOpenShiftClient; -import io.fabric8.openshift.client.NamespacedOpenShiftClient; -import io.vertx.core.DeploymentOptions; -import io.vertx.core.Vertx; -import io.vertx.core.VertxOptions; -import okhttp3.OkHttpClient; -import org.jboss.logging.Logger; -import org.keycloak.Config; -import org.keycloak.models.KeycloakSession; -import org.keycloak.models.KeycloakSessionFactory; - -public class AmqpServerProviderImpl implements AmqpServerProviderFactory { - - private static final Logger LOG = Logger.getLogger(AmqpServerProviderImpl.class); - - private NamespacedOpenShiftClient client; - private Config.Scope config; - private OkHttpClient httpClient; - private volatile AmqpServer server; - private volatile AmqpServer tlsServer; - - @Override - public AmqpServerProviderFactory create(final KeycloakSession keycloakSession) { - return this; - } - - @Override - public void init(final Config.Scope config) { - client = new DefaultOpenShiftClient(); - httpClient = client.adapt(OkHttpClient.class); - this.config = config; - } - - @Override - public void postInit(final KeycloakSessionFactory keycloakSessionFactory) { - boolean enableNonTls = this.config.getBoolean("enableNonTls", true); - boolean enableTls = config.getBoolean("enableTls", true); - int numAmqpServerInstances = config.getInt("numAmqpServerInstances", Runtime.getRuntime().availableProcessors()); - int numAmqpsServerInstances = config.getInt("numAmqpsServerInstances", Runtime.getRuntime().availableProcessors()); - - VertxOptions vertxOptions = new VertxOptions(); - int requiredWorkers = (enableNonTls ? numAmqpServerInstances : 0) + (enableTls ? numAmqpsServerInstances : 0); - if (requiredWorkers > vertxOptions.getWorkerPoolSize()) { - vertxOptions.setWorkerPoolSize(requiredWorkers); - } - - Vertx vertx = Vertx.vertx(vertxOptions); - if (enableNonTls) { - try { - DeploymentOptions options = new DeploymentOptions().setWorker(true).setInstances(numAmqpServerInstances); - vertx.deployVerticle(() -> { - server = createAmqServer(config, config.get("host", "localhost"), config.getInt("port", 5672), false); - server.setKeycloakSessionFactory(keycloakSessionFactory); - return server; - }, options); - } catch (RuntimeException e) { - LOG.error("Unable to start AMQP Server using non-TLS", e); - } - } - - if(enableTls) { - try { - DeploymentOptions options = new DeploymentOptions().setWorker(true).setInstances(numAmqpsServerInstances); - vertx.deployVerticle(() -> { - tlsServer = createAmqServer(config, config.get("tlsHost", "0.0.0.0"), config.getInt("tlsPort", 5671), true); - tlsServer.setKeycloakSessionFactory(keycloakSessionFactory); - return tlsServer; - }, options); - } catch (RuntimeException e) { - LOG.error("Unable to start AMQP Server using TLS", e); - } - } - } - - @Override - public void close() { - if(server != null) { - server.stop(); - } - if(tlsServer != null) { - tlsServer.stop(); - } - if(client != null) { - client.close(); - } - - } - - @Override - public String getId() { - return "amqp-server"; - } - - private AmqpServer createAmqServer(Config.Scope config, String hostname, Integer port, final boolean useTls) { - try { - return new AmqpServer(hostname, port, config, useTls, client, httpClient); - } catch (RuntimeException e) { - throw new RuntimeException(String.format("Unable to create AMQP Server (hostname : %s, port: %s, TLS: %s)", hostname, port, useTls), e); - } - } -} diff --git a/keycloak-plugin/sasl-plugin/src/main/java/io/enmasse/keycloak/spi/AmqpServerSpi.java b/keycloak-plugin/sasl-plugin/src/main/java/io/enmasse/keycloak/spi/AmqpServerSpi.java deleted file mode 100644 index 3d31f2d370e..00000000000 --- a/keycloak-plugin/sasl-plugin/src/main/java/io/enmasse/keycloak/spi/AmqpServerSpi.java +++ /dev/null @@ -1,52 +0,0 @@ -/* - * Copyright 2018, EnMasse authors. - * License: Apache License 2.0 (see the file LICENSE or http://apache.org/licenses/LICENSE-2.0.html). - */ -/** - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You 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 io.enmasse.keycloak.spi; - -import org.keycloak.provider.Provider; -import org.keycloak.provider.ProviderFactory; -import org.keycloak.provider.Spi; - -public class AmqpServerSpi implements Spi { - @Override - public boolean isInternal() - { - return false; - } - - @Override - public String getName() - { - return "amqp"; - } - - @Override - public Class getProviderClass() - { - return AmqpServerProviderFactory.class; - } - - @Override - public Class> getProviderFactoryClass() - { - return AmqpServerProviderFactory.class; - } -} diff --git a/keycloak-plugin/sasl-plugin/src/main/java/io/enmasse/keycloak/spi/K8sServiceAccountCredentialProvider.java b/keycloak-plugin/sasl-plugin/src/main/java/io/enmasse/keycloak/spi/K8sServiceAccountCredentialProvider.java deleted file mode 100644 index 4b7c83a618e..00000000000 --- a/keycloak-plugin/sasl-plugin/src/main/java/io/enmasse/keycloak/spi/K8sServiceAccountCredentialProvider.java +++ /dev/null @@ -1,150 +0,0 @@ -/* - * Copyright 2018, EnMasse authors. - * License: Apache License 2.0 (see the file LICENSE or http://apache.org/licenses/LICENSE-2.0.html). - */ - -package io.enmasse.keycloak.spi; - -import io.fabric8.openshift.client.DefaultOpenShiftClient; -import io.fabric8.openshift.client.NamespacedOpenShiftClient; -import io.vertx.core.json.JsonObject; -import okhttp3.HttpUrl; -import okhttp3.MediaType; -import okhttp3.OkHttpClient; -import okhttp3.Request; -import okhttp3.RequestBody; -import okhttp3.Response; -import okhttp3.ResponseBody; -import org.jboss.logging.Logger; -import org.keycloak.credential.CredentialInput; -import org.keycloak.credential.CredentialInputUpdater; -import org.keycloak.credential.CredentialInputValidator; -import org.keycloak.credential.CredentialProvider; -import org.keycloak.models.KeycloakSession; -import org.keycloak.models.RealmModel; -import org.keycloak.models.UserCredentialModel; -import org.keycloak.models.UserModel; -import org.keycloak.models.cache.CachedUserModel; -import org.keycloak.models.cache.OnUserCache; - -import java.io.*; -import java.util.Collections; -import java.util.Objects; -import java.util.Set; - -public class K8sServiceAccountCredentialProvider implements CredentialProvider, CredentialInputValidator, OnUserCache, CredentialInputUpdater { - - private static final Logger LOG = Logger.getLogger(K8sServiceAccountCredentialProvider.class); - - public static final String ENMASSE_SERVICE_ACCOUNT_TYPE = "enmasse-service-account"; - - private final NamespacedOpenShiftClient client; - private final OkHttpClient httpClient; - - K8sServiceAccountCredentialProvider(KeycloakSession session) { - client = new DefaultOpenShiftClient(); - httpClient = client.adapt(OkHttpClient.class); - - } - - @Override - public boolean supportsCredentialType(String s) { - return ENMASSE_SERVICE_ACCOUNT_TYPE.equals(s); - } - - @Override - public boolean isConfiguredFor(RealmModel realmModel, UserModel userModel, String s) { - return "serviceaccount".equals(userModel.getFirstAttribute("authenticationType")) ; - } - - @Override - public boolean isValid(RealmModel realmModel, UserModel userModel, CredentialInput credentialInput) { - String token = ((UserCredentialModel) credentialInput).getValue(); - String userName = authenticateToken(token, client, httpClient); - - boolean authenticated = Objects.equals(userModel.getUsername(), userName); - if(userName == null) { - LOG.debug("User: " + userModel.getUsername() + " not authenticated for realm " + realmModel.getName()); - } else if(!authenticated) { - LOG.debug("Attempt to log in for user " + userModel.getUsername() + " in realm " + realmModel.getName() + " with token for " + userName); - } - - return authenticated; - } - - public static String authenticateToken(String token, NamespacedOpenShiftClient client, OkHttpClient httpClient) { - JsonObject body = new JsonObject(); - String userName = null; - body.put("kind", "TokenReview"); - body.put("apiVersion", "authentication.k8s.io/v1beta1"); - - JsonObject spec = new JsonObject(); - spec.put("token", token); - body.put("spec", spec); - - JsonObject result; - - HttpUrl url = HttpUrl.get(client.getOpenshiftUrl()).resolve("/apis/authentication.k8s.io/v1beta1/tokenreviews"); - Request.Builder requestBuilder = new Request.Builder() - .url(url) - .addHeader("Content-Type", "application/json") - .addHeader("Authorization", "Bearer " + client.getConfiguration().getOauthToken()) - .method("POST", RequestBody.create(MediaType.parse("application/json"), body.encode())); - - try (Response response = httpClient.newCall(requestBuilder.build()).execute()) { - try (ResponseBody responseBody = response.body()) { - String responseString = responseBody != null ? responseBody.string() : "{}"; - if (response.isSuccessful()) { - result = new JsonObject(responseString); - } else { - String errorMessage = String.format("Error performing POST on /apis/authentication.k8s.io/v1beta1/tokenreviews: %d, %s", response.code(), responseString); - throw new RuntimeException(errorMessage); - } - } - } catch (IOException e) { - throw new UncheckedIOException(e); - } - - JsonObject status = result.getJsonObject("status"); - - if (status != null) { - Boolean auth = status.getBoolean("authenticated"); - if(auth == null ? false : auth) { - JsonObject user = status.getJsonObject("user"); - if (user != null) { - userName = user.getString("username"); - } - } else { - LOG.debug("Token was not authenticated"); - } - } - return userName; - } - - @Override - public void onCache(RealmModel realmModel, CachedUserModel cachedUserModel, UserModel userModel) { - } - - @Override - public boolean updateCredential(RealmModel realmModel, UserModel userModel, CredentialInput credentialInput) { - return false; - } - - @Override - public void disableCredentialType(RealmModel realmModel, UserModel userModel, String s) { - } - - @Override - public Set getDisableableCredentialTypes(RealmModel realmModel, UserModel userModel) { - if(!"serviceaccount".equals(userModel.getFirstAttribute("authenticationType"))) { - return Collections.singleton(ENMASSE_SERVICE_ACCOUNT_TYPE); - } else { - return Collections.emptySet(); - } - } - - @Override - public void close() { - client.close(); - } -} diff --git a/keycloak-plugin/sasl-plugin/src/main/java/io/enmasse/keycloak/spi/K8sServiceAccountCredentialProviderFactory.java b/keycloak-plugin/sasl-plugin/src/main/java/io/enmasse/keycloak/spi/K8sServiceAccountCredentialProviderFactory.java deleted file mode 100644 index 3c68cd7c220..00000000000 --- a/keycloak-plugin/sasl-plugin/src/main/java/io/enmasse/keycloak/spi/K8sServiceAccountCredentialProviderFactory.java +++ /dev/null @@ -1,28 +0,0 @@ -/* - * Copyright 2018, EnMasse authors. - * License: Apache License 2.0 (see the file LICENSE or http://apache.org/licenses/LICENSE-2.0.html). - */ - -package io.enmasse.keycloak.spi; - -import org.keycloak.credential.CredentialProviderFactory; -import org.keycloak.credential.PasswordCredentialProvider; -import org.keycloak.models.KeycloakSession; - -public class K8sServiceAccountCredentialProviderFactory implements CredentialProviderFactory { - - - public static final String PROVIDER_ID="k8s-service-account"; - @Override - public K8sServiceAccountCredentialProvider create(KeycloakSession session) { - return new K8sServiceAccountCredentialProvider(session); - } - - @Override - public String getId() { - return PROVIDER_ID; - } - - - -} diff --git a/keycloak-plugin/sasl-plugin/src/main/java/io/enmasse/keycloak/spi/PlainSaslServerMechanism.java b/keycloak-plugin/sasl-plugin/src/main/java/io/enmasse/keycloak/spi/PlainSaslServerMechanism.java deleted file mode 100644 index 6913de7daf7..00000000000 --- a/keycloak-plugin/sasl-plugin/src/main/java/io/enmasse/keycloak/spi/PlainSaslServerMechanism.java +++ /dev/null @@ -1,204 +0,0 @@ -/* - * Copyright 2018, EnMasse authors. - * License: Apache License 2.0 (see the file LICENSE or http://apache.org/licenses/LICENSE-2.0.html). - */ -/** - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You 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 io.enmasse.keycloak.spi; - -import org.jboss.logging.Logger; -import org.keycloak.Config; -import org.keycloak.models.GroupModel; -import org.keycloak.models.KeycloakSession; -import org.keycloak.models.KeycloakSessionFactory; -import org.keycloak.models.KeycloakTransactionManager; -import org.keycloak.models.RealmModel; -import org.keycloak.models.UserCredentialModel; -import org.keycloak.models.UserModel; - -import java.nio.charset.StandardCharsets; -import java.util.stream.Collectors; - -import static io.enmasse.keycloak.spi.K8sServiceAccountCredentialProvider.ENMASSE_SERVICE_ACCOUNT_TYPE; - -public class PlainSaslServerMechanism implements SaslServerMechanism { - - private static final Logger LOG = Logger.getLogger(PlainSaslServerMechanism.class); - private static final int MAX_RETRIES = 10; - - @Override - public String getName() { - return "PLAIN"; - } - - @Override - public int priority() { - return 10; - } - - @Override - public boolean isSupported(String passwordHashAlgo) { - return true; - } - - @Override - public Instance newInstance(final KeycloakSessionFactory keycloakSessionFactory, - final String hostname, - final Config.Scope config, - AmqpServer amqpServer) - { - return new Instance() - { - public UserData authenticatedUser; - private boolean complete; - private boolean authenticated; - private RuntimeException error; - - @Override - public byte[] processResponse(byte[] response) throws IllegalArgumentException, InterruptedException { - if(error != null) { - throw error; - } - - int authzidNullPosition = findNullPosition(response, 0); - if (authzidNullPosition < 0) { - error = new IllegalArgumentException("Invalid PLAIN encoding, authzid null terminator not found"); - throw error; - } - - int authcidNullPosition = findNullPosition(response, authzidNullPosition + 1); - if (authcidNullPosition < 0) { - error = new IllegalArgumentException("Invalid PLAIN encoding, authcid null terminator not found"); - throw error; - } - - String username = new String(response, authzidNullPosition + 1, authcidNullPosition - authzidNullPosition - 1, StandardCharsets.UTF_8); - int passwordLen = response.length - authcidNullPosition - 1; - String password = new String(response, authcidNullPosition + 1, passwordLen, StandardCharsets.UTF_8); - - LOG.debug("SASL hostname: " + hostname); - int retry = 0; - while (true) { - KeycloakSession keycloakSession = keycloakSessionFactory.create(); - KeycloakTransactionManager transactionManager = keycloakSession.getTransactionManager(); - transactionManager.begin(); - try { - try { - final RealmModel realm = keycloakSession.realms().getRealmByName(hostname); - if (realm == null) { - LOG.info("Realm " + hostname + " not found"); - authenticated = false; - complete = true; - return null; - } - if ("@@serviceaccount@@".equals(username)) { - String tokenUser = K8sServiceAccountCredentialProvider.authenticateToken(password, amqpServer.getOpenShiftClient(), amqpServer.getHttpClient()); - if (tokenUser != null) { - final UserModel user = keycloakSession.userStorageManager().getUserByUsername(tokenUser, realm); - if (user != null) { - if ("serviceaccount".equals(user.getFirstAttribute("authenticationType"))) { - authenticatedUser = new UserDataImpl(user.getId(), user.getUsername(), user.getGroups().stream().map(GroupModel::getName).collect(Collectors.toSet())); - authenticated = true; - complete = true; - return null; - } else { - LOG.debug("User " + tokenUser + " in realm " + hostname + " is not a serviceaccount user"); - } - } else { - LOG.debug("No such user " + tokenUser + " in realm " + hostname); - } - } - } else { - final UserModel user = keycloakSession.userStorageManager().getUserByUsername(username, realm); - if (user != null) { - UserCredentialModel credentialModel = "serviceaccount".equals(user.getFirstAttribute("authenticationType")) ? createServiceAccountUserCredential(password) : UserCredentialModel.password(password); - if (keycloakSession.userCredentialManager().isValid(realm, user, credentialModel)) { - authenticatedUser = new UserDataImpl(user.getId(), user.getUsername(), user.getGroups().stream().map(GroupModel::getName).collect(Collectors.toSet())); - authenticated = true; - complete = true; - return null; - } - LOG.debug("Invalid password for " + username + " in realm " + hostname); - } else { - LOG.debug("user not found: " + username); - } - - } - authenticated = false; - complete = true; - return null; - - } finally { - transactionManager.commit(); - keycloakSession.close(); - } - } catch (Exception e) { - if (retry < MAX_RETRIES) { - LOG.warn("Exception when authenticating " + username, e); - Thread.sleep(1000); - retry++; - LOG.info("Retry attempt " + retry + " for authenticating " + username); - } else { - LOG.warn("Exception when authenticating " + username + ". Aborting.", e); - authenticated = false; - complete = true; - return null; - } - } - } - } - - @Override - public boolean isComplete() - { - return complete; - } - - @Override - public boolean isAuthenticated() - { - return authenticated; - } - - @Override - public UserData getAuthenticatedUser() { - return authenticatedUser; - } - - private int findNullPosition(byte[] response, int startPosition) { - int position = startPosition; - while (position < response.length) { - if (response[position] == (byte) 0) { - return position; - } - position++; - } - return -1; - } - - }; - } - - private UserCredentialModel createServiceAccountUserCredential(String token) { - UserCredentialModel userCredentialModel = new UserCredentialModel(); - userCredentialModel.setType(ENMASSE_SERVICE_ACCOUNT_TYPE); - userCredentialModel.setValue(token); - return userCredentialModel; - } - -} diff --git a/keycloak-plugin/sasl-plugin/src/main/java/io/enmasse/keycloak/spi/SaslAuthenticator.java b/keycloak-plugin/sasl-plugin/src/main/java/io/enmasse/keycloak/spi/SaslAuthenticator.java deleted file mode 100644 index 92934c87f41..00000000000 --- a/keycloak-plugin/sasl-plugin/src/main/java/io/enmasse/keycloak/spi/SaslAuthenticator.java +++ /dev/null @@ -1,207 +0,0 @@ -/* - * Copyright 2018, EnMasse authors. - * License: Apache License 2.0 (see the file LICENSE or http://apache.org/licenses/LICENSE-2.0.html). - */ -/** - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You 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 io.enmasse.keycloak.spi; - -import io.vertx.core.Handler; -import io.vertx.core.net.NetSocket; -import io.vertx.proton.ProtonConnection; -import io.vertx.proton.sasl.ProtonSaslAuthenticator; -import org.apache.qpid.proton.engine.Sasl; -import org.apache.qpid.proton.engine.Transport; -import org.jboss.logging.Logger; -import org.keycloak.Config; -import org.keycloak.credential.hash.Pbkdf2PasswordHashProviderFactory; -import org.keycloak.credential.hash.Pbkdf2Sha256PasswordHashProviderFactory; -import org.keycloak.credential.hash.Pbkdf2Sha512PasswordHashProviderFactory; -import org.keycloak.models.KeycloakSession; -import org.keycloak.models.KeycloakSessionFactory; -import org.keycloak.models.KeycloakTransactionManager; -import org.keycloak.models.RealmModel; - -import java.util.Arrays; -import java.util.Collections; -import java.util.Comparator; -import java.util.HashSet; -import java.util.List; -import java.util.Optional; -import java.util.Set; -import java.util.TreeSet; - -class SaslAuthenticator implements ProtonSaslAuthenticator -{ - - static final Object USER_ATTACHMENT = new Object(); - private static final Logger LOG = Logger.getLogger(SaslAuthenticator.class); - - - // TODO - load these dynamically - private static final List ALL_MECHANISMS = - Collections.unmodifiableList(Arrays.asList( - new PlainSaslServerMechanism(), - new ScramSaslServerMechanism("HmacSHA1", "SCRAM-SHA-1", "SHA-1", ScramSha1PasswordHashProviderFactory.ID, Pbkdf2PasswordHashProviderFactory.ID, 50), - new ScramSaslServerMechanism("HmacSHA256", "SCRAM-SHA-256", "SHA-256", ScramSha256PasswordHashProviderFactory.ID, Pbkdf2Sha256PasswordHashProviderFactory.ID, 60), - new ScramSaslServerMechanism("HmacSHA512", "SCRAM-SHA-512", "SHA-512", ScramSha512PasswordHashProviderFactory.ID, Pbkdf2Sha512PasswordHashProviderFactory.ID, 70), - new XOAUTH2SaslServerMechanism())); - - private final Config.Scope config; - private final AmqpServer amqpServer; - - private KeycloakSessionFactory keycloakSessionFactory; - private Sasl sasl; - private boolean succeeded; - private SaslServerMechanism.Instance saslMechanism; - private ProtonConnection connection; - - SaslAuthenticator(final KeycloakSessionFactory sessionFactory, - final Config.Scope config, - final boolean secure, - AmqpServer amqpServer) { - this.keycloakSessionFactory = sessionFactory; - this.config = config; - this.amqpServer = amqpServer; - } - - @Override - public void init(final NetSocket socket, - final ProtonConnection protonConnection, - final Transport transport) { - // allow for frames bigger than 512 bytes to support mechanisms that send (for instance) tokens - transport.setInitialRemoteMaxFrameSize(1024*1024); - this.sasl = transport.sasl(); - sasl.server(); - sasl.allowSkip(false); - sasl.setMechanisms(getValidMechanisms(getPasswordHashAlgorithms())); - connection = protonConnection; - } - - private String[] getValidMechanisms(Set hashAlgos) { - TreeSet mechanisms = new TreeSet<>(Comparator.comparingInt(SaslServerMechanism::priority)); - for(SaslServerMechanism mech : ALL_MECHANISMS) { - if(hashAlgos.isEmpty()) { - if (mech.isSupported("")) { - mechanisms.add(mech); - } - } else { - for (String hashAlgo : hashAlgos) { - if (mech.isSupported(hashAlgo)) { - mechanisms.add(mech); - break; - } - } - } - } - return mechanisms.stream().map(SaslServerMechanism::getName).toArray(String[]::new); - } - - private Set getPasswordHashAlgorithms() { - Set hashAlgos = new HashSet<>(); - boolean enmasseRealmsFound = false; - KeycloakSession keycloakSession = keycloakSessionFactory.create(); - KeycloakTransactionManager transactionManager = keycloakSession.getTransactionManager(); - transactionManager.begin(); - try { - List realms = keycloakSession.realms().getRealms(); - for(RealmModel realm : realms) { - if(realm.getAttribute("enmasse-realm",Boolean.FALSE)) { - enmasseRealmsFound = true; - hashAlgos.add(realm.getPasswordPolicy().getHashAlgorithm()); - } - } - } finally { - transactionManager.commit(); - keycloakSession.close(); - } - if(!enmasseRealmsFound) { - LOG.warn("No realms with attribute \"enmasse-realm\" found, only universally accepted SASL mechanisms will be offered"); - } - return hashAlgos; - } - - @Override - public void process(final Handler completionHandler) { - String[] remoteMechanisms = sasl.getRemoteMechanisms(); - boolean done = false; - - try { - if(saslMechanism == null) { - if (remoteMechanisms.length > 0) { - String chosen = remoteMechanisms[0]; - Optional mechanismImpl = ALL_MECHANISMS.stream().filter(m -> m.getName().equals(chosen)).findFirst(); - if (mechanismImpl.isPresent()) { - String saslHostname = sasl.getHostname(); - if(saslHostname == null) { - saslHostname = config.get("defaultDomain",""); - } - try { - saslMechanism = mechanismImpl.get().newInstance(keycloakSessionFactory, saslHostname, config, amqpServer); - } catch (Exception e) { - sasl.done(Sasl.SaslOutcome.PN_SASL_SYS); - done = true; - } - } else { - sasl.done(Sasl.SaslOutcome.PN_SASL_SYS); - done = true; - } - } - } - if(!done && saslMechanism != null) { - byte[] response; - if(sasl.pending()>0) { - response = new byte[sasl.pending()]; - sasl.recv(response, 0, response.length); - } else { - response = new byte[0]; - } - try { - byte[] challenge = saslMechanism.processResponse(response); - if (!saslMechanism.isComplete() || (challenge != null && challenge.length > 0)) { - sasl.send(challenge, 0, challenge.length); - } else { - succeeded = saslMechanism.isAuthenticated(); - done = true; - if (succeeded) { - connection.attachments().set(USER_ATTACHMENT, UserData.class, saslMechanism.getAuthenticatedUser()); - sasl.done(Sasl.SaslOutcome.PN_SASL_OK); - } else { - sasl.done(Sasl.SaslOutcome.PN_SASL_AUTH); - } - } - } catch (InterruptedException e) { - Thread.currentThread().interrupt(); - done = true; - sasl.done(Sasl.SaslOutcome.PN_SASL_SYS); - } catch (Exception e) { - LOG.warn("Unrecoverable exception during SASL negotiation", e); - done = true; - sasl.done(Sasl.SaslOutcome.PN_SASL_SYS); - } - } - } finally { - completionHandler.handle(done); - } - } - - @Override - public boolean succeeded() { - return succeeded; - } -} diff --git a/keycloak-plugin/sasl-plugin/src/main/java/io/enmasse/keycloak/spi/SaslServerMechanism.java b/keycloak-plugin/sasl-plugin/src/main/java/io/enmasse/keycloak/spi/SaslServerMechanism.java deleted file mode 100644 index 55ad8f9a0bd..00000000000 --- a/keycloak-plugin/sasl-plugin/src/main/java/io/enmasse/keycloak/spi/SaslServerMechanism.java +++ /dev/null @@ -1,43 +0,0 @@ -/* - * Copyright 2018, EnMasse authors. - * License: Apache License 2.0 (see the file LICENSE or http://apache.org/licenses/LICENSE-2.0.html). - */ -/** - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You 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 io.enmasse.keycloak.spi; - -import org.keycloak.Config; -import org.keycloak.models.KeycloakSessionFactory; - -public interface SaslServerMechanism { - - boolean isSupported(String passwordHashAlgo); - int priority(); - String getName(); - - Instance newInstance(KeycloakSessionFactory session, - String hostname, - final Config.Scope config, - AmqpServer amqpServer); - interface Instance { - byte[] processResponse(byte[] response) throws IllegalArgumentException, InterruptedException; - boolean isComplete(); - boolean isAuthenticated(); - UserData getAuthenticatedUser(); - } -} diff --git a/keycloak-plugin/sasl-plugin/src/main/java/io/enmasse/keycloak/spi/ScramPasswordHashProvider.java b/keycloak-plugin/sasl-plugin/src/main/java/io/enmasse/keycloak/spi/ScramPasswordHashProvider.java deleted file mode 100644 index 9a5975584d8..00000000000 --- a/keycloak-plugin/sasl-plugin/src/main/java/io/enmasse/keycloak/spi/ScramPasswordHashProvider.java +++ /dev/null @@ -1,144 +0,0 @@ -/* - * Copyright 2016-2018, EnMasse authors. - * License: Apache License 2.0 (see the file LICENSE or http://apache.org/licenses/LICENSE-2.0.html). - */ - -package io.enmasse.keycloak.spi; - -import java.nio.charset.StandardCharsets; -import java.security.InvalidKeyException; -import java.security.MessageDigest; -import java.security.NoSuchAlgorithmException; -import java.security.SecureRandom; - -import javax.crypto.Mac; -import javax.crypto.spec.SecretKeySpec; - -import org.keycloak.common.util.Base64; -import org.keycloak.credential.CredentialModel; -import org.keycloak.credential.hash.PasswordHashProvider; -import org.keycloak.models.PasswordPolicy; -import org.keycloak.models.UserCredentialModel; - -public class ScramPasswordHashProvider implements PasswordHashProvider -{ - - private final String providerId; - private final String hmacName; - private final String digestName; - private final int defaultIterations; - - private static final byte[] INT_1 = new byte[]{0, 0, 0, 1}; - - public ScramPasswordHashProvider(String providerId, - int defaultIterations, - final String hmacName, - final String digestName) { - this.providerId = providerId; - this.defaultIterations = defaultIterations; - this.hmacName = hmacName; - this.digestName = digestName; - } - - @Override - public boolean policyCheck(PasswordPolicy policy, CredentialModel credential) { - return credential.getHashIterations() == policy.getHashIterations() && providerId.equals(credential.getAlgorithm()); - } - - @Override - public void encode(String rawPassword, int iterations, CredentialModel credential) { - if (iterations == -1) { - iterations = defaultIterations; - } - - byte[] salt = getSalt(); - String encodedPassword = encode(rawPassword, iterations, salt); - - credential.setAlgorithm(providerId); - credential.setType(UserCredentialModel.PASSWORD); - credential.setSalt(salt); - credential.setHashIterations(iterations); - credential.setValue(encodedPassword); - } - - @Override - public boolean verify(String rawPassword, CredentialModel credential) { - - String encodedPassword = encode(rawPassword, credential.getHashIterations(), credential.getSalt()); - - String storedCredential = credential.getValue(); - return encodedPassword.equals(storedCredential); - } - - @Override - public void close() { - } - - private byte[] createSaltedPassword(byte[] salt, String password, int iterationCount) { - Mac mac = createShaHmac(password.getBytes(StandardCharsets.US_ASCII)); - - mac.update(salt); - mac.update(INT_1); - byte[] result = mac.doFinal(); - - byte[] previous = null; - for(int i = 1; i < iterationCount; i++) { - mac.update(previous != null? previous: result); - previous = mac.doFinal(); - for(int x = 0; x < result.length; x++) { - result[x] ^= previous[x]; - } - } - - return result; - - } - - private byte[] computeHmac(final byte[] key, final String string) { - Mac mac = createShaHmac(key); - mac.update(string.getBytes(StandardCharsets.US_ASCII)); - return mac.doFinal(); - } - - - private Mac createShaHmac(final byte[] keyBytes) { - try { - SecretKeySpec key = new SecretKeySpec(keyBytes, getHmacName()); - Mac mac = Mac.getInstance(getHmacName()); - mac.init(key); - return mac; - } catch (NoSuchAlgorithmException | InvalidKeyException e) { - throw new IllegalArgumentException(e.getMessage(), e); - } - } - - private String getHmacName() { - return hmacName; - } - - private String getDigestName() { - return digestName; - } - - private String encode(String rawPassword, int iterations, byte[] salt) { - try { - byte[] saltedPassword = createSaltedPassword(salt, rawPassword, iterations); - byte[] clientKey = computeHmac(saltedPassword, "Client Key"); - - byte[] storedKey = MessageDigest.getInstance(getDigestName()).digest(clientKey); - byte[] serverKey = computeHmac(saltedPassword, "Server Key"); - - return Base64.encodeBytes(storedKey) + "|" + Base64.encodeBytes(serverKey); - } catch (NoSuchAlgorithmException e) { - throw new RuntimeException(e); - } - } - - private byte[] getSalt() { - byte[] buffer = new byte[16]; - SecureRandom secureRandom = new SecureRandom(); - secureRandom.nextBytes(buffer); - return buffer; - } - -} diff --git a/keycloak-plugin/sasl-plugin/src/main/java/io/enmasse/keycloak/spi/ScramSaslAuthenticator.java b/keycloak-plugin/sasl-plugin/src/main/java/io/enmasse/keycloak/spi/ScramSaslAuthenticator.java deleted file mode 100644 index 90df22fef53..00000000000 --- a/keycloak-plugin/sasl-plugin/src/main/java/io/enmasse/keycloak/spi/ScramSaslAuthenticator.java +++ /dev/null @@ -1,423 +0,0 @@ -/* - * Copyright 2018, EnMasse authors. - * License: Apache License 2.0 (see the file LICENSE or http://apache.org/licenses/LICENSE-2.0.html). - */ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You 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 io.enmasse.keycloak.spi; - -import org.keycloak.common.util.Base64; -import org.keycloak.credential.CredentialInputValidator; -import org.keycloak.credential.CredentialModel; -import org.keycloak.credential.PasswordCredentialProvider; -import org.keycloak.credential.UserCredentialStoreManager; -import org.keycloak.models.GroupModel; -import org.keycloak.models.KeycloakSession; -import org.keycloak.models.KeycloakSessionFactory; -import org.keycloak.models.RealmModel; -import org.keycloak.models.UserModel; -import org.keycloak.storage.StorageId; -import org.keycloak.storage.UserStorageManager; -import org.keycloak.storage.UserStorageProvider; - -import java.io.IOException; -import java.nio.charset.StandardCharsets; -import java.security.GeneralSecurityException; -import java.security.InvalidKeyException; -import java.security.MessageDigest; -import java.security.NoSuchAlgorithmException; -import java.security.SecureRandom; -import java.util.Arrays; -import java.util.HashMap; -import java.util.List; -import java.util.Map; -import java.util.UUID; -import java.util.function.Function; -import java.util.stream.Collectors; -import javax.crypto.Mac; -import javax.crypto.spec.SecretKeySpec; - -class ScramSaslAuthenticator implements SaslServerMechanism.Instance -{ - private final KeycloakSession keycloakSession; - private final String hostname; - private boolean authenticated; - private State state = State.INITIAL; - private byte[] gs2Header; - private String clientFirstMessageBare; - private String username; - private String nonce; - private String serverFirstMessage; - private UserData user; - private final byte[] randomBytes; - private final String digestName; - private final String hmacName; - private Map> keyRetrievalFunctions; - private CredentialModel credentialModel; - - interface StoredAndServerKey { - byte[] getStoredKey(); - byte[] getServerKey(); - } - - public ScramSaslAuthenticator(final KeycloakSessionFactory keycloakSessionFactory, - final String hostname, - String digestName, - String hmacName, - Map> keyRetrievalFunctions) { - this.keycloakSession = keycloakSessionFactory.create(); - this.hostname = hostname; - this.randomBytes = new byte[32]; - (new SecureRandom()).nextBytes(this.randomBytes); - this.digestName = digestName; - this.hmacName = hmacName; - this.keyRetrievalFunctions = new HashMap<>(keyRetrievalFunctions); - } - - - enum State - { - INITIAL, - SERVER_FIRST_MESSAGE_SENT, - COMPLETE - } - - - private byte[] generateServerFirstMessage(final byte[] response) { - String clientFirstMessage = new String(response, StandardCharsets.US_ASCII); - if(!clientFirstMessage.startsWith("n")) { - throw new IllegalArgumentException("Cannot parse gs2-header"); - } - String[] parts = clientFirstMessage.split(","); - if(parts.length < 4) { - throw new IllegalArgumentException("Cannot parse client first message"); - } - gs2Header = ("n,"+parts[1]+",").getBytes(StandardCharsets.US_ASCII); - clientFirstMessageBare = clientFirstMessage.substring(gs2Header.length); - if(!parts[2].startsWith("n=")) { - throw new IllegalArgumentException("Cannot parse client first message"); - } - username = decodeUsername(parts[2].substring(2)); - - keycloakSession.getTransactionManager().begin(); - try { - RealmModel realm = keycloakSession.realms().getRealmByName(hostname); - if (realm != null) { - UserModel userModel = keycloakSession.userStorageManager().getUserByUsername(username, realm); - if (userModel != null) { - PasswordCredentialProvider passwordCredentialProvider = getPasswordCredentialProvider(realm, userModel); - credentialModel = passwordCredentialProvider.getPassword(realm, userModel); - - user = new UserDataImpl(userModel.getId(), userModel.getUsername(), userModel.getGroups().stream().map(GroupModel::getName).collect(Collectors.toSet())); - } - } - } finally { - keycloakSession.getTransactionManager().commit(); - } - - - if(!parts[3].startsWith("r=")) { - throw new IllegalArgumentException("Cannot parse client first message"); - } - nonce = parts[3].substring(2) + UUID.randomUUID().toString(); - - serverFirstMessage = "r=" + nonce + ",s=" + Base64.encodeBytes(getSalt()) + ",i=" + getIterations(); - return serverFirstMessage.getBytes(StandardCharsets.US_ASCII); - } - - private byte[] getSalt() { - if(credentialModel == null) { - byte[] tmpSalt = new byte[16]; - byte[] hmac = computeHmac(this.hmacName, this.randomBytes, username); - if(hmac.length >= tmpSalt.length) { - System.arraycopy(hmac, 0, tmpSalt, 0, tmpSalt.length); - } else { - int offset = 0; - String key = username; - while(offset < tmpSalt.length) { - System.arraycopy(hmac, 0, tmpSalt, offset, Math.min(hmac.length, tmpSalt.length-offset)); - key = key+"1"; - hmac = computeHmac(this.hmacName, this.randomBytes, key); - offset+=hmac.length; - } - } - return tmpSalt; - } else { - return credentialModel.getSalt(); - } - } - - private int getIterations() { - if (credentialModel == null) { - // TODO - should come from the policy - return 20000; - } else { - return credentialModel.getHashIterations(); - } - } - - private String decodeUsername(String username) { - if(username.contains("=")) { - String check = username; - while (check.contains("=")) { - check = check.substring(check.indexOf('=') + 1); - if (!(check.startsWith("2C") || check.startsWith("3D"))) { - throw new IllegalArgumentException("Invalid username"); - } - } - username = username.replace("=2C", ","); - username = username.replace("=3D","="); - } - return username; - } - - private static byte[] decodeBase64(String base64String) { - base64String = base64String.replaceAll("\\s",""); - if(!base64String.matches("^(?:[A-Za-z0-9+/]{4})*(?:[A-Za-z0-9+/]{2}==|[A-Za-z0-9+/]{3}=)?$")) { - throw new IllegalArgumentException("Cannot convert string '"+ base64String+ "'to a byte[] - it does not appear to be base64 data"); - } - - try { - return Base64.decode(base64String); - } catch (IOException e) { - throw new IllegalArgumentException(e); - } - } - - private byte[] generateServerFinalMessage(final byte[] response) { - try { - String clientFinalMessage = new String(response, StandardCharsets.US_ASCII); - String[] parts = clientFinalMessage.split(","); - if (!parts[0].startsWith("c=")) { - throw new IllegalArgumentException("Cannot parse client final message"); - } - if (!Arrays.equals(gs2Header, decodeBase64(parts[0].substring(2)))) { - throw new IllegalArgumentException("Client final message channel bind data invalid"); - } - if (!parts[1].startsWith("r=")) { - throw new IllegalArgumentException("Cannot parse client final message"); - } - if (!parts[1].substring(2).equals(nonce)) { - throw new IllegalArgumentException("Client final message has incorrect nonce value"); - } - if (!parts[parts.length - 1].startsWith("p=")) { - throw new IllegalArgumentException("Client final message does not have proof"); - } - - String clientFinalMessageWithoutProof = - clientFinalMessage.substring(0, clientFinalMessage.length() - (1 + parts[parts.length- 1].length())); - byte[] proofBytes = decodeBase64(parts[parts.length - 1].substring(2)); - - String authMessage = - clientFirstMessageBare + "," + serverFirstMessage + "," + clientFinalMessageWithoutProof; - - StoredAndServerKey keys; - - if (credentialModel == null) { - keys = getStoredAndServerKeyFromPbkdf2Hash(null, hmacName, digestName); - } else { - Function keyRetrieverFunction = this.keyRetrievalFunctions.get(credentialModel.getAlgorithm()); - if( keyRetrieverFunction != null) { - keys = keyRetrieverFunction.apply(credentialModel); - } else { - throw new IllegalArgumentException("Unsupported algorithm: " + credentialModel.getAlgorithm()); - } - } - - byte[] clientSignature = computeHmac(hmacName, keys.getStoredKey(), authMessage); - - for(int i = 0 ; i < proofBytes.length; i++) { - proofBytes[i] ^= clientSignature[i]; - } - - - final byte[] storedKeyFromClient = MessageDigest.getInstance(this.digestName).digest(proofBytes); - - if(user == null || !Arrays.equals(storedKeyFromClient, keys.getStoredKey())) { - authenticated = false; - state = State.COMPLETE; - return null; - } else { - authenticated = true; - } - - - String finalResponse = "v=" + Base64.encodeBytes(computeHmac(this.hmacName, keys.getServerKey(), authMessage)); - - return finalResponse.getBytes(StandardCharsets.US_ASCII); - } catch (GeneralSecurityException e) { - throw new IllegalArgumentException(e.getMessage(), e); - } - } - - static StoredAndServerKey getStoredAndServerKeyFromScramHash(CredentialModel credentialModel, String hmacName, String digestName) { - try { - StoredAndServerKey keys; - if (credentialModel == null) { - return getStoredAndServerKeyFromPbkdf2Hash(credentialModel, hmacName, digestName); - } - String[] storedAndServerKeys = credentialModel.getValue().split("\\|", 2); - byte[] storedKey = Base64.decode(storedAndServerKeys[0]); - byte[] serverKey = Base64.decode(storedAndServerKeys[1]); - keys = new StoredAndServerKey() { - @Override - public byte[] getStoredKey() { - return storedKey; - } - - @Override - public byte[] getServerKey() { - return serverKey; - } - }; - return keys; - } catch (IOException e) { - throw new IllegalArgumentException(e.getMessage(), e); - } - } - - static StoredAndServerKey getStoredAndServerKeyFromPbkdf2Hash(CredentialModel credentialModel, String hamcName, String digestName) { - try { - StoredAndServerKey keys; - byte[] saltedPassword = getSaltedPassword(credentialModel); - byte[] clientKey = computeHmac(hamcName, saltedPassword, "Client Key"); - - byte[] storedKey = MessageDigest.getInstance(digestName).digest(clientKey); - byte[] serverKey = computeHmac(hamcName, saltedPassword, "Server Key"); - keys = new StoredAndServerKey() { - @Override - public byte[] getStoredKey() { - return storedKey; - } - - @Override - public byte[] getServerKey() { - return serverKey; - } - }; - return keys; - } catch (NoSuchAlgorithmException e) { - throw new IllegalArgumentException(e.getMessage(), e); - } - } - - private static byte[] getSaltedPassword(CredentialModel credentialModel) { - if(credentialModel == null) { - byte[] password = new byte[20]; - (new SecureRandom()).nextBytes(password); - return password; - } else { - byte[] storedValue = decodeBase64(credentialModel.getValue()); - byte[] saltedPassword = new byte[20]; - System.arraycopy(storedValue, 0, saltedPassword, 0, 20); - return saltedPassword; - } - - } - - private PasswordCredentialProvider getPasswordCredentialProvider(final RealmModel realm, final UserModel user) { - PasswordCredentialProvider passwordCredentialProvider = null; - - if (!StorageId.isLocalStorage(user)) { - String providerId = StorageId.resolveProviderId(user); - UserStorageProvider provider = UserStorageManager.getStorageProvider(keycloakSession, realm, providerId); - if (provider instanceof PasswordCredentialProvider) { - passwordCredentialProvider = (PasswordCredentialProvider)provider; - } - } else { - if (user.getFederationLink() != null) { - UserStorageProvider provider = UserStorageManager.getStorageProvider(keycloakSession, realm, user.getFederationLink()); - if (provider != null && provider instanceof PasswordCredentialProvider) { - passwordCredentialProvider = (PasswordCredentialProvider)provider; - } - } - } - if(passwordCredentialProvider == null) { - List - credentialProviders = UserCredentialStoreManager.getCredentialProviders(keycloakSession, realm, CredentialInputValidator.class); - for (CredentialInputValidator validator : credentialProviders) { - if (validator != null && validator instanceof PasswordCredentialProvider) { - passwordCredentialProvider = (PasswordCredentialProvider)validator; - break; - } - } - } - return passwordCredentialProvider; - } - - - @Override - public boolean isComplete() { - return state == State.COMPLETE; - } - - - @Override - public byte[] processResponse(byte[] response) throws IllegalArgumentException { - - byte[] challenge; - switch (state) { - case INITIAL: - challenge = generateServerFirstMessage(response); - state = State.SERVER_FIRST_MESSAGE_SENT; - break; - case SERVER_FIRST_MESSAGE_SENT: - challenge = generateServerFinalMessage(response); - state = State.COMPLETE; - break; - case COMPLETE: - if(response == null || response.length == 0) { - challenge = new byte[0]; - break; - } - //$FALL-THROUGH$ - default: - throw new IllegalArgumentException("No response expected in state " + state); - - } - return challenge; - } - - @Override - public boolean isAuthenticated() { - return authenticated; - } - - @Override - public UserData getAuthenticatedUser() { - return user; - } - - private static byte[] computeHmac(String hmacName, final byte[] key, final String string) { - Mac mac = createShaHmac(hmacName, key); - mac.update(string.getBytes(StandardCharsets.US_ASCII)); - return mac.doFinal(); - } - - - private static Mac createShaHmac(String hmacName, final byte[] keyBytes) { - try { - SecretKeySpec key = new SecretKeySpec(keyBytes, hmacName); - Mac mac = Mac.getInstance(hmacName); - mac.init(key); - return mac; - } catch (NoSuchAlgorithmException | InvalidKeyException e) { - throw new IllegalArgumentException(e.getMessage(), e); - } - } -} diff --git a/keycloak-plugin/sasl-plugin/src/main/java/io/enmasse/keycloak/spi/ScramSaslServerMechanism.java b/keycloak-plugin/sasl-plugin/src/main/java/io/enmasse/keycloak/spi/ScramSaslServerMechanism.java deleted file mode 100644 index c818bfd2755..00000000000 --- a/keycloak-plugin/sasl-plugin/src/main/java/io/enmasse/keycloak/spi/ScramSaslServerMechanism.java +++ /dev/null @@ -1,103 +0,0 @@ -/* - * Copyright 2018, EnMasse authors. - * License: Apache License 2.0 (see the file LICENSE or http://apache.org/licenses/LICENSE-2.0.html). - */ -/** - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You 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 io.enmasse.keycloak.spi; - -import org.keycloak.Config; -import org.keycloak.credential.CredentialModel; -import org.keycloak.models.KeycloakSessionFactory; - -import java.util.HashMap; -import java.util.Map; -import java.util.function.Function; - -import static io.enmasse.keycloak.spi.ScramSaslAuthenticator.StoredAndServerKey; - -class ScramSaslServerMechanism implements SaslServerMechanism { - - private final String hmacName; - - private final String mechanism; - private final String digestName; - private final Map> keyRetrievalFunctions; - private final int priority; - - ScramSaslServerMechanism(String hmacName, - String mechanism, - String digestName, - String scramCredAlgo, - String pbkdf2CredAlgo, - int priority) { - this(hmacName, mechanism, digestName, createKeyRetrievalFunctionMap(scramCredAlgo, pbkdf2CredAlgo, hmacName, digestName),priority); - } - - private static Map> createKeyRetrievalFunctionMap(String scramCredAlgo, - String pbkdf2CredAlgo, - String hmacName, - String digestName) { - - Map> functions = new HashMap<>(); - if(scramCredAlgo != null) { - functions.put(scramCredAlgo, m -> ScramSaslAuthenticator.getStoredAndServerKeyFromScramHash(m, hmacName, digestName)); - } - if(pbkdf2CredAlgo != null) { - functions.put(pbkdf2CredAlgo, m -> ScramSaslAuthenticator.getStoredAndServerKeyFromPbkdf2Hash(m, hmacName, digestName)); - } - return functions; - } - - private ScramSaslServerMechanism(String hmacName, - String mechanism, - String digestName, - Map> keyRetrievalFunctions, - int priority) { - this.hmacName = hmacName; - this.mechanism = mechanism; - this.digestName = digestName; - this.keyRetrievalFunctions = new HashMap<>(keyRetrievalFunctions); - this.priority = priority; - } - - @Override - public boolean isSupported(String passwordHashAlgo) - { - return this.keyRetrievalFunctions.containsKey(passwordHashAlgo); - } - - @Override - public final String getName() { - return mechanism; - } - - @Override - public int priority() { - return priority; - } - - @Override - public final Instance newInstance(KeycloakSessionFactory keycloakSessionFactory, - String hostname, - Config.Scope config, - AmqpServer amqpServer) { - return new ScramSaslAuthenticator(keycloakSessionFactory, hostname, digestName, hmacName, keyRetrievalFunctions); - } - -} diff --git a/keycloak-plugin/sasl-plugin/src/main/java/io/enmasse/keycloak/spi/ScramSha1PasswordHashProviderFactory.java b/keycloak-plugin/sasl-plugin/src/main/java/io/enmasse/keycloak/spi/ScramSha1PasswordHashProviderFactory.java deleted file mode 100644 index 5151d6e5eb0..00000000000 --- a/keycloak-plugin/sasl-plugin/src/main/java/io/enmasse/keycloak/spi/ScramSha1PasswordHashProviderFactory.java +++ /dev/null @@ -1,41 +0,0 @@ -/* - * Copyright 2017-2018, EnMasse authors. - * License: Apache License 2.0 (see the file LICENSE or http://apache.org/licenses/LICENSE-2.0.html). - */ - -package io.enmasse.keycloak.spi; - -import org.keycloak.Config; -import org.keycloak.credential.hash.PasswordHashProvider; -import org.keycloak.credential.hash.PasswordHashProviderFactory; -import org.keycloak.models.KeycloakSession; -import org.keycloak.models.KeycloakSessionFactory; - -public class ScramSha1PasswordHashProviderFactory implements PasswordHashProviderFactory { - - public static final String ID = "scramsha1"; - - public static final int DEFAULT_ITERATIONS = 20000; - - @Override - public PasswordHashProvider create(KeycloakSession session) { - return new ScramPasswordHashProvider(ID, DEFAULT_ITERATIONS, "HmacSHA1", "SHA-1"); - } - - @Override - public void init(Config.Scope config) { - } - - @Override - public void postInit(KeycloakSessionFactory factory) { - } - - @Override - public String getId() { - return ID; - } - - @Override - public void close() { - } -} diff --git a/keycloak-plugin/sasl-plugin/src/main/java/io/enmasse/keycloak/spi/ScramSha256PasswordHashProviderFactory.java b/keycloak-plugin/sasl-plugin/src/main/java/io/enmasse/keycloak/spi/ScramSha256PasswordHashProviderFactory.java deleted file mode 100644 index b7dc28e3199..00000000000 --- a/keycloak-plugin/sasl-plugin/src/main/java/io/enmasse/keycloak/spi/ScramSha256PasswordHashProviderFactory.java +++ /dev/null @@ -1,41 +0,0 @@ -/* - * Copyright 2017-2018, EnMasse authors. - * License: Apache License 2.0 (see the file LICENSE or http://apache.org/licenses/LICENSE-2.0.html). - */ - -package io.enmasse.keycloak.spi; - -import org.keycloak.Config; -import org.keycloak.credential.hash.PasswordHashProvider; -import org.keycloak.credential.hash.PasswordHashProviderFactory; -import org.keycloak.models.KeycloakSession; -import org.keycloak.models.KeycloakSessionFactory; - -public class ScramSha256PasswordHashProviderFactory implements PasswordHashProviderFactory { - - public static final String ID = "scramsha256"; - - public static final int DEFAULT_ITERATIONS = 20000; - - @Override - public PasswordHashProvider create(KeycloakSession session) { - return new ScramPasswordHashProvider(ID, DEFAULT_ITERATIONS, "HmacSHA256", "SHA-256"); - } - - @Override - public void init(Config.Scope config) { - } - - @Override - public void postInit(KeycloakSessionFactory factory) { - } - - @Override - public String getId() { - return ID; - } - - @Override - public void close() { - } -} diff --git a/keycloak-plugin/sasl-plugin/src/main/java/io/enmasse/keycloak/spi/ScramSha512PasswordHashProviderFactory.java b/keycloak-plugin/sasl-plugin/src/main/java/io/enmasse/keycloak/spi/ScramSha512PasswordHashProviderFactory.java deleted file mode 100644 index c9e953a785e..00000000000 --- a/keycloak-plugin/sasl-plugin/src/main/java/io/enmasse/keycloak/spi/ScramSha512PasswordHashProviderFactory.java +++ /dev/null @@ -1,41 +0,0 @@ -/* - * Copyright 2017-2018, EnMasse authors. - * License: Apache License 2.0 (see the file LICENSE or http://apache.org/licenses/LICENSE-2.0.html). - */ - -package io.enmasse.keycloak.spi; - -import org.keycloak.Config; -import org.keycloak.credential.hash.PasswordHashProvider; -import org.keycloak.credential.hash.PasswordHashProviderFactory; -import org.keycloak.models.KeycloakSession; -import org.keycloak.models.KeycloakSessionFactory; - -public class ScramSha512PasswordHashProviderFactory implements PasswordHashProviderFactory { - - public static final String ID = "scramsha512"; - - public static final int DEFAULT_ITERATIONS = 20000; - - @Override - public PasswordHashProvider create(KeycloakSession session) { - return new ScramPasswordHashProvider(ID, DEFAULT_ITERATIONS, "HmacSHA512", "SHA-512"); - } - - @Override - public void init(Config.Scope config) { - } - - @Override - public void postInit(KeycloakSessionFactory factory) { - } - - @Override - public String getId() { - return ID; - } - - @Override - public void close() { - } -} diff --git a/keycloak-plugin/sasl-plugin/src/main/java/io/enmasse/keycloak/spi/UserData.java b/keycloak-plugin/sasl-plugin/src/main/java/io/enmasse/keycloak/spi/UserData.java deleted file mode 100644 index 03275db4328..00000000000 --- a/keycloak-plugin/sasl-plugin/src/main/java/io/enmasse/keycloak/spi/UserData.java +++ /dev/null @@ -1,15 +0,0 @@ -/* - * Copyright 2018, EnMasse authors. - * License: Apache License 2.0 (see the file LICENSE or http://apache.org/licenses/LICENSE-2.0.html). - */ - -package io.enmasse.keycloak.spi; - -import java.util.Set; - -public interface UserData { - String getId(); - String getUsername(); - Set getGroups(); - -} diff --git a/keycloak-plugin/sasl-plugin/src/main/java/io/enmasse/keycloak/spi/UserDataImpl.java b/keycloak-plugin/sasl-plugin/src/main/java/io/enmasse/keycloak/spi/UserDataImpl.java deleted file mode 100644 index 127e21e7793..00000000000 --- a/keycloak-plugin/sasl-plugin/src/main/java/io/enmasse/keycloak/spi/UserDataImpl.java +++ /dev/null @@ -1,41 +0,0 @@ -/* - * Copyright 2018, EnMasse authors. - * License: Apache License 2.0 (see the file LICENSE or http://apache.org/licenses/LICENSE-2.0.html). - */ - -package io.enmasse.keycloak.spi; - -import java.util.Set; - -class UserDataImpl implements UserData { - - private final String userId; - private final String userName; - private final Set groups; - - public UserDataImpl(String userId, String userName, Set groups) { - this.userId = userId; - this.userName = userName; - this.groups = groups; - } - - @Override - public String getId() { - return userId; - } - - @Override - public String getUsername() { - return userName; - } - - @Override - public Set getGroups() { - return groups; - } - - @Override - public String toString() { - return "{userId=" + userId + ", userName=" + userName + ", groups=" + groups + "}"; - } -} diff --git a/keycloak-plugin/sasl-plugin/src/main/java/io/enmasse/keycloak/spi/XOAUTH2SaslServerMechanism.java b/keycloak-plugin/sasl-plugin/src/main/java/io/enmasse/keycloak/spi/XOAUTH2SaslServerMechanism.java deleted file mode 100644 index b273792d673..00000000000 --- a/keycloak-plugin/sasl-plugin/src/main/java/io/enmasse/keycloak/spi/XOAUTH2SaslServerMechanism.java +++ /dev/null @@ -1,230 +0,0 @@ -/* - * Copyright 2018, EnMasse authors. - * License: Apache License 2.0 (see the file LICENSE or http://apache.org/licenses/LICENSE-2.0.html). - */ - -package io.enmasse.keycloak.spi; - -import org.jboss.logging.Logger; -import org.keycloak.Config; -import org.keycloak.RSATokenVerifier; -import org.keycloak.common.VerificationException; -import org.keycloak.models.*; -import org.keycloak.representations.AccessToken; -import org.keycloak.services.Urls; -import org.keycloak.services.managers.AuthenticationManager; -import org.keycloak.services.managers.UserSessionCrossDCManager; - -import java.net.URI; -import java.net.URISyntaxException; -import java.nio.charset.StandardCharsets; -import java.util.stream.Collectors; - -public class XOAUTH2SaslServerMechanism implements SaslServerMechanism { - - private static final String BEARER_PREFIX = "Bearer "; - private static final Logger LOG = Logger.getLogger(XOAUTH2SaslServerMechanism.class); - - private final TokenVerifier tokenVerifier; - - XOAUTH2SaslServerMechanism() { - tokenVerifier = this::verifyRSAToken; - } - - // Used only for testing purposes - XOAUTH2SaslServerMechanism(TokenVerifier tokenVerifier) { - this.tokenVerifier = tokenVerifier; - } - - @Override - public String getName() { - return "XOAUTH2"; - } - - @Override - public int priority() { - return 100; - } - - @Override - public boolean isSupported(String passwordHashAlgo) { - return true; - } - - @Override - public Instance newInstance(final KeycloakSessionFactory keycloakSessionFactory, - final String hostname, - final Config.Scope config, AmqpServer amqpServer) - { - return new Instance() - { - private UserDataImpl authenticatedUser; - private boolean complete; - private boolean authenticated; - private RuntimeException error; - - @Override - public byte[] processResponse(byte[] response) throws IllegalArgumentException - { - if (error != null) { - throw error; - } - - KeycloakSession keycloakSession = keycloakSessionFactory.create(); - keycloakSession.getTransactionManager().begin(); - try { - - final RealmModel realm = keycloakSession.realms().getRealmByName(hostname); - if (realm == null) { - LOG.info("Realm " + hostname + " not found"); - authenticated = false; - complete = true; - return null; - } - - String tokenString = getTokenFromInitialResponse(response); - - AccessToken token = null; - try { - String defaultUri = System.getenv("KEYCLOAK_SASL_XOAUTH_BASE_URI"); - if (defaultUri == null) { - String host = System.getenv("STANDARD_AUTHSERVICE_SERVICE_HOST"); - if (host == null) { - host = "localhost"; - } - defaultUri = "https://" + host + ":8443/auth"; - } - URI baseUri = new URI(config.get("baseUri", defaultUri)); - - token = tokenVerifier.verifyTokenString(realm, tokenString, baseUri, keycloakSession); - } catch (VerificationException e) { - LOG.debug("Token invalid: " + e.getMessage()); - authenticated = false; - complete = true; - return null; - } catch (URISyntaxException e) { - error = new IllegalArgumentException("Invalid URI from SASL XOAUTH2 config: " + e.getMessage()); - throw error; - } - - ClientModel clientModel = realm.getClientByClientId(token.getIssuedFor()); - - if (clientModel == null || !clientModel.isEnabled()) { - authenticated = false; - } else { - UserSessionModel userSession = findValidSession(token, clientModel, realm, keycloakSession); - if (userSession != null) { - UserModel user = userSession.getUser(); - if (user == null || !AuthenticationManager.isSessionValid(realm, userSession)) { - authenticated = false; - } else { - authenticated = true; - authenticatedUser = new UserDataImpl(user.getId(), user.getUsername(), user.getGroups().stream().map(GroupModel::getName).collect(Collectors.toSet())); - } - } else { - authenticated = false; - } - } - } finally { - keycloakSession.getTransactionManager().commit(); - keycloakSession.close(); - } - - complete = true; - - return null; - } - - private String getTokenFromInitialResponse(byte[] response) { - String[] splitResponse = new String(response, StandardCharsets.US_ASCII).split("\1"); - String invalidFormatMessage = "Invalid XOAUTH2 encoding, the format must be of the form user={User}^Aauth=Bearer{Access Token}^A^A"; - String specifiedUser; - String auth; - - if(splitResponse.length != 2) { - LOG.info(invalidFormatMessage); - error = new IllegalArgumentException(invalidFormatMessage); - throw error; - } - - String[] userValue = splitResponse[0].split("=", 2); - if (userValue.length == 2 && userValue[0].trim().equals("user")) { - specifiedUser = userValue[1].trim(); - } else { - LOG.info(invalidFormatMessage); - error = new IllegalArgumentException(invalidFormatMessage); - throw error; - } - - String[] authValue = splitResponse[1].split("=", 2); - if (authValue.length == 2 && authValue[0].trim().equals("auth")) { - auth = authValue[1].trim(); - } else { - LOG.info(invalidFormatMessage); - error = new IllegalArgumentException(invalidFormatMessage); - throw error; - } - - if (!auth.startsWith(BEARER_PREFIX)) { - String msg = "Invalid XOAUTH2 encoding, the 'auth' part of response does not not begin with the expected prefix"; - LOG.info(msg); - error = new IllegalArgumentException(msg); - throw error; - } - return auth.substring(BEARER_PREFIX.length()).trim(); - } - - @Override - public boolean isComplete() { - return complete; - } - - @Override - public boolean isAuthenticated() { - return authenticated; - } - - @Override - public UserData getAuthenticatedUser() { - return authenticatedUser; - } - - private UserSessionModel findValidSession(AccessToken token, ClientModel client, RealmModel realm, KeycloakSession session) { - UserSessionModel userSession = new UserSessionCrossDCManager(session).getUserSessionWithClient(realm, token.getSessionState(), false, client.getId()); - UserSessionModel offlineUserSession = null; - if (AuthenticationManager.isSessionValid(realm, userSession)) { - return userSession; - } else { - offlineUserSession = new UserSessionCrossDCManager(session).getUserSessionWithClient(realm, token.getSessionState(), true, client.getId()); - if (AuthenticationManager.isOfflineSessionValid(realm, offlineUserSession)) { - return offlineUserSession; - } - } - - if (userSession == null && offlineUserSession == null) { - LOG.debug("User session not found or doesn't have client attached on it"); - } else { - LOG.debug("Session expired"); - } - return null; - } - - }; - } - - interface TokenVerifier - { - AccessToken verifyTokenString(RealmModel realm, String tokenString, URI baseUri, KeycloakSession keycloakSession) throws VerificationException; - } - - private AccessToken verifyRSAToken(RealmModel realm, String tokenString, URI baseUri, KeycloakSession keycloakSession) throws VerificationException { - AccessToken token; - RSATokenVerifier verifier = RSATokenVerifier.create(tokenString) - .realmUrl(Urls.realmIssuer(baseUri, - realm.getName())); - String kid = verifier.getHeader().getKeyId(); - verifier.publicKey(keycloakSession.keys().getRsaPublicKey(realm, kid)); - token = verifier.verify().getToken(); - return token; - } -} diff --git a/keycloak-plugin/sasl-plugin/src/main/resources/META-INF/services/io.enmasse.keycloak.spi.AmqpServerProviderFactory b/keycloak-plugin/sasl-plugin/src/main/resources/META-INF/services/io.enmasse.keycloak.spi.AmqpServerProviderFactory deleted file mode 100644 index f80a71005da..00000000000 --- a/keycloak-plugin/sasl-plugin/src/main/resources/META-INF/services/io.enmasse.keycloak.spi.AmqpServerProviderFactory +++ /dev/null @@ -1,4 +0,0 @@ -# Copyright 2018, EnMasse authors. -# License: Apache License 2.0 (see the file LICENSE or http://apache.org/licenses/LICENSE-2.0.html). - -io.enmasse.keycloak.spi.AmqpServerProviderImpl diff --git a/keycloak-plugin/sasl-plugin/src/main/resources/META-INF/services/org.keycloak.credential.CredentialProviderFactory b/keycloak-plugin/sasl-plugin/src/main/resources/META-INF/services/org.keycloak.credential.CredentialProviderFactory deleted file mode 100644 index b7612e25437..00000000000 --- a/keycloak-plugin/sasl-plugin/src/main/resources/META-INF/services/org.keycloak.credential.CredentialProviderFactory +++ /dev/null @@ -1,4 +0,0 @@ -# Copyright 2018, EnMasse authors. -# License: Apache License 2.0 (see the file LICENSE or http://apache.org/licenses/LICENSE-2.0.html). - -io.enmasse.keycloak.spi.K8sServiceAccountCredentialProviderFactory diff --git a/keycloak-plugin/sasl-plugin/src/main/resources/META-INF/services/org.keycloak.credential.hash.PasswordHashProviderFactory b/keycloak-plugin/sasl-plugin/src/main/resources/META-INF/services/org.keycloak.credential.hash.PasswordHashProviderFactory deleted file mode 100644 index e5612eaa17d..00000000000 --- a/keycloak-plugin/sasl-plugin/src/main/resources/META-INF/services/org.keycloak.credential.hash.PasswordHashProviderFactory +++ /dev/null @@ -1,6 +0,0 @@ -# Copyright 2018, EnMasse authors. -# License: Apache License 2.0 (see the file LICENSE or http://apache.org/licenses/LICENSE-2.0.html). - -io.enmasse.keycloak.spi.ScramSha1PasswordHashProviderFactory -io.enmasse.keycloak.spi.ScramSha256PasswordHashProviderFactory -io.enmasse.keycloak.spi.ScramSha512PasswordHashProviderFactory diff --git a/keycloak-plugin/sasl-plugin/src/main/resources/META-INF/services/org.keycloak.provider.Spi b/keycloak-plugin/sasl-plugin/src/main/resources/META-INF/services/org.keycloak.provider.Spi deleted file mode 100644 index 7b52598d5fb..00000000000 --- a/keycloak-plugin/sasl-plugin/src/main/resources/META-INF/services/org.keycloak.provider.Spi +++ /dev/null @@ -1,4 +0,0 @@ -# Copyright 2018, EnMasse authors. -# License: Apache License 2.0 (see the file LICENSE or http://apache.org/licenses/LICENSE-2.0.html). - -io.enmasse.keycloak.spi.AmqpServerSpi diff --git a/keycloak-plugin/sasl-plugin/src/test/java/io/enmasse/keycloak/spi/AmqpServerTest.java b/keycloak-plugin/sasl-plugin/src/test/java/io/enmasse/keycloak/spi/AmqpServerTest.java deleted file mode 100644 index 8f8d1542da4..00000000000 --- a/keycloak-plugin/sasl-plugin/src/test/java/io/enmasse/keycloak/spi/AmqpServerTest.java +++ /dev/null @@ -1,89 +0,0 @@ -/* - * Copyright 2018, EnMasse authors. - * License: Apache License 2.0 (see the file LICENSE or http://apache.org/licenses/LICENSE-2.0.html). - */ -/** - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You 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 io.enmasse.keycloak.spi; - - -import io.fabric8.openshift.client.NamespacedOpenShiftClient; -import okhttp3.OkHttpClient; -import org.junit.jupiter.api.Test; - -import java.util.Arrays; -import java.util.Collections; -import java.util.HashSet; -import java.util.Map; - -import static org.junit.jupiter.api.Assertions.*; -import static org.mockito.Mockito.mock; - -public class AmqpServerTest { - - @Test - public void testEmptyGroupsGeneratesEmptyPermissions() { - AmqpServer amqpServer = new AmqpServer(null, 0, null, false, mock(NamespacedOpenShiftClient.class), mock(OkHttpClient.class)); - Map props = amqpServer.getPermissionsFromGroups(Collections.emptySet()); - assertNotNull(props); - assertTrue(props.isEmpty()); - - props = amqpServer.getPermissionsFromGroups(Collections.singleton("foo")); - assertNotNull(props); - assertTrue(props.isEmpty()); - - props = amqpServer.getPermissionsFromGroups(Collections.singleton("foo_bar")); - assertNotNull(props); - assertTrue(props.isEmpty()); - - props = amqpServer.getPermissionsFromGroups(Collections.singleton("send")); - assertNotNull(props); - assertTrue(props.isEmpty()); - - } - - @Test - public void testMultiplePermissionsForSameAddress() { - AmqpServer amqpServer = new AmqpServer(null, 0, null, false, mock(NamespacedOpenShiftClient.class), mock(OkHttpClient.class)); - Map props = amqpServer.getPermissionsFromGroups(new HashSet<>(Arrays.asList("send_foo", "consume_foo"))); - assertNotNull(props); - assertEquals(1, props.size()); - assertNotNull(props.get("foo")); - assertEquals(2, props.get("foo").length); - assertTrue(Arrays.stream(props.get("foo")).anyMatch(e -> e.equals("send"))); - assertTrue(Arrays.stream(props.get("foo")).anyMatch(e -> e.equals("recv"))); - } - - - @Test - public void testMultiplePermissionsWithUrlEncoding() { - AmqpServer amqpServer = new AmqpServer(null, 0, null, false, mock(NamespacedOpenShiftClient.class), mock(OkHttpClient.class)); - Map props = amqpServer.getPermissionsFromGroups(new HashSet<>(Arrays.asList("send_foo%2Fbar", "view_%66oo%2Fbar", "recv_foo"))); - assertNotNull(props); - assertEquals(2, props.size()); - assertNotNull(props.get("foo/bar")); - assertEquals(2, props.get("foo/bar").length); - assertTrue(Arrays.stream(props.get("foo/bar")).anyMatch(e -> e.equals("send"))); - assertEquals(2, props.get("foo/bar").length); - assertTrue(Arrays.stream(props.get("foo/bar")).anyMatch(e -> e.equals("view"))); - - assertNotNull(props.get("foo")); - assertEquals(1, props.get("foo").length); - assertTrue(Arrays.stream(props.get("foo")).anyMatch(e -> e.equals("recv"))); - } -} diff --git a/keycloak-plugin/sasl-plugin/src/test/java/io/enmasse/keycloak/spi/PlainSaslServerMechanismTest.java b/keycloak-plugin/sasl-plugin/src/test/java/io/enmasse/keycloak/spi/PlainSaslServerMechanismTest.java deleted file mode 100644 index 302a4b41ba8..00000000000 --- a/keycloak-plugin/sasl-plugin/src/test/java/io/enmasse/keycloak/spi/PlainSaslServerMechanismTest.java +++ /dev/null @@ -1,130 +0,0 @@ -/* - * Copyright 2017-2018, EnMasse authors. - * License: Apache License 2.0 (see the file LICENSE or http://apache.org/licenses/LICENSE-2.0.html). - */ - -package io.enmasse.keycloak.spi; - -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Test; -import org.keycloak.Config; -import org.keycloak.models.*; -import org.keycloak.models.credential.PasswordUserCredentialModel; -import org.mockito.ArgumentMatcher; - -import java.nio.charset.StandardCharsets; - -import static org.junit.jupiter.api.Assertions.*; -import static org.mockito.ArgumentMatchers.argThat; -import static org.mockito.ArgumentMatchers.eq; -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.when; - -public class PlainSaslServerMechanismTest { - private KeycloakSessionFactory keycloakSessionFactory; - private KeycloakSession keycloakSession; - private Config.Scope config; - - @BeforeEach - public void setup() { - keycloakSessionFactory = mock(KeycloakSessionFactory.class); - keycloakSession = mock(KeycloakSession.class); - when(keycloakSessionFactory.create()).thenReturn(keycloakSession); - KeycloakTransactionManager txnManager = mock(KeycloakTransactionManager.class); - when(keycloakSession.getTransactionManager()).thenReturn(txnManager); - RealmProvider realms = mock(RealmProvider.class); - when(keycloakSession.realms()).thenReturn(realms); - RealmModel realm = mock(RealmModel.class); - when(realms.getRealmByName(eq("realm"))).thenReturn(realm); - UserProvider userProvider = mock(UserProvider.class); - UserModel user = mock(UserModel.class); - when(userProvider.getUserByUsername(eq("user"), eq(realm))).thenReturn(user); - when(keycloakSession.userStorageManager()).thenReturn(userProvider); - UserCredentialManager userCredentialManager = mock(UserCredentialManager.class); - when(keycloakSession.userCredentialManager()).thenReturn(userCredentialManager); - when(userCredentialManager.isValid(eq(realm), eq(user), argThat(new PasswordCredentialMatcher("password")))).thenReturn(true); - - config = mock(Config.Scope.class); - } - - private byte[] createInitialResponse(final String user, final String password) { - byte[] userBytes = user.getBytes(StandardCharsets.UTF_8); - byte[] passwordBytes = password.getBytes(StandardCharsets.UTF_8); - byte[] response = new byte[2 + userBytes.length + passwordBytes.length]; - System.arraycopy(userBytes, 0, response, 1, userBytes.length); - System.arraycopy(passwordBytes, 0, response, 2 + userBytes.length, passwordBytes.length); - - return response; - } - - private static final class PasswordCredentialMatcher implements ArgumentMatcher { - - private final String password; - - private PasswordCredentialMatcher(final String password) { - this.password = password; - } - - @Override - public boolean matches(final PasswordUserCredentialModel item) { - return item.getValue().equals(password); - } - } - - // unknown realm - @Test - public void testUnknownRealm() throws InterruptedException { - final SaslServerMechanism.Instance instance = - (new PlainSaslServerMechanism()).newInstance(keycloakSessionFactory, "unknownRealm", config, null); - byte[] response = instance.processResponse(createInitialResponse("user", "password")); - assertTrue(response == null || response.length == 0); - assertTrue(instance.isComplete()); - assertFalse(instance.isAuthenticated()); - - } - - // known realm, unknown user - @Test - public void testUnknownUser() throws InterruptedException { - final SaslServerMechanism.Instance instance = - (new PlainSaslServerMechanism()).newInstance(keycloakSessionFactory, "realm", config, null); - byte[] response = instance.processResponse(createInitialResponse("unknown", "password")); - assertTrue(response == null || response.length == 0); - assertTrue(instance.isComplete()); - assertFalse(instance.isAuthenticated()); - - } - - // known user, wrong password - @Test - public void testWrongPassword() throws InterruptedException { - final SaslServerMechanism.Instance instance = - (new PlainSaslServerMechanism()).newInstance(keycloakSessionFactory, "realm", config, null); - byte[] response = instance.processResponse(createInitialResponse("user", "wrong")); - assertTrue(response == null || response.length == 0); - assertTrue(instance.isComplete()); - assertFalse(instance.isAuthenticated()); - - } - - // known user, correct password - @Test - public void testCorrectPassword() throws InterruptedException { - final SaslServerMechanism.Instance instance = - (new PlainSaslServerMechanism()).newInstance(keycloakSessionFactory, "realm", config, null); - byte[] response = instance.processResponse(createInitialResponse("user", "password")); - assertTrue(response == null || response.length == 0); - assertTrue(instance.isComplete()); - assertTrue(instance.isAuthenticated()); - } - - // incorrect sasl format - @Test - public void testBadInitialResponse() { - final SaslServerMechanism.Instance instance = - (new PlainSaslServerMechanism()).newInstance(keycloakSessionFactory, "realm", config, null); - assertThrows(IllegalArgumentException.class, () -> instance.processResponse("potato".getBytes(StandardCharsets.UTF_8))); - } - - -} diff --git a/keycloak-plugin/sasl-plugin/src/test/java/io/enmasse/keycloak/spi/ScramPasswordHashProviderTest.java b/keycloak-plugin/sasl-plugin/src/test/java/io/enmasse/keycloak/spi/ScramPasswordHashProviderTest.java deleted file mode 100644 index a723874e07c..00000000000 --- a/keycloak-plugin/sasl-plugin/src/test/java/io/enmasse/keycloak/spi/ScramPasswordHashProviderTest.java +++ /dev/null @@ -1,72 +0,0 @@ -/* - * Copyright 2017-2018, EnMasse authors. - * License: Apache License 2.0 (see the file LICENSE or http://apache.org/licenses/LICENSE-2.0.html). - */ - -package io.enmasse.keycloak.spi; - -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Test; -import org.keycloak.credential.CredentialModel; -import org.mockito.ArgumentCaptor; - -import java.util.HashSet; -import java.util.Set; - -import static org.junit.jupiter.api.Assertions.*; -import static org.mockito.Mockito.*; - -public class ScramPasswordHashProviderTest { - private ScramPasswordHashProvider hashprovider; - private CredentialModel credentialModel; - - @BeforeEach - public void setup() { - hashprovider = new ScramPasswordHashProvider("scramsha1", 10000, "HmacSHA1", "SHA-1"); - credentialModel = mock(CredentialModel.class); - - ArgumentCaptor saltCaptor = ArgumentCaptor.forClass(byte[].class); - doAnswer(invocation -> { - when(credentialModel.getSalt()).thenReturn(saltCaptor.getValue()); - return null; - }).when(credentialModel).setSalt(saltCaptor.capture()); - - ArgumentCaptor passwordCaptor = ArgumentCaptor.forClass(String.class); - doAnswer(invocation -> { - when(credentialModel.getValue()).thenReturn(passwordCaptor.getValue()); - return null; - }).when(credentialModel).setValue(passwordCaptor.capture()); - - - ArgumentCaptor iterationsCaptor = ArgumentCaptor.forClass(Integer.class); - doAnswer(invocation -> { - when(credentialModel.getHashIterations()).thenReturn(iterationsCaptor.getValue()); - return null; - }).when(credentialModel).setHashIterations(iterationsCaptor.capture()); - } - - @Test - public void testProviderVerifiesCorrect() { - hashprovider.encode("testpassword", 25000, credentialModel); - assertTrue(hashprovider.verify("testpassword", credentialModel)); - } - - @Test - public void testProviderFailsIncorrect() { - hashprovider.encode("testpassword", 25000, credentialModel); - assertFalse(hashprovider.verify("wrongpassword", credentialModel)); - } - - @Test - public void testSameValueEncodedDifferently() { - Set encoded = new HashSet<>(); - for (int i = 0; i < 100; i++) { - hashprovider.encode("testpassword", 25000, credentialModel); - if (!encoded.add(credentialModel.getValue())) { - fail("Duplicate encoding of the password"); - } - } - - } - -} diff --git a/keycloak-plugin/sasl-plugin/src/test/java/io/enmasse/keycloak/spi/XOAUTH2SaslServerMechanismTest.java b/keycloak-plugin/sasl-plugin/src/test/java/io/enmasse/keycloak/spi/XOAUTH2SaslServerMechanismTest.java deleted file mode 100644 index 05d9d1778af..00000000000 --- a/keycloak-plugin/sasl-plugin/src/test/java/io/enmasse/keycloak/spi/XOAUTH2SaslServerMechanismTest.java +++ /dev/null @@ -1,127 +0,0 @@ -/* - * Copyright 2017-2018, EnMasse authors. - * License: Apache License 2.0 (see the file LICENSE or http://apache.org/licenses/LICENSE-2.0.html). - */ - -package io.enmasse.keycloak.spi; - -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Test; -import org.keycloak.Config; -import org.keycloak.common.VerificationException; -import org.keycloak.common.util.Time; -import org.keycloak.models.*; -import org.keycloak.representations.AccessToken; - -import java.net.URI; -import java.nio.charset.StandardCharsets; - -import static org.junit.jupiter.api.Assertions.*; -import static org.mockito.ArgumentMatchers.*; -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.when; - -public class XOAUTH2SaslServerMechanismTest implements XOAUTH2SaslServerMechanism.TokenVerifier { - private KeycloakSession keycloakSession; - private Config.Scope config; - private boolean verifyToken; - private KeycloakSessionFactory keycloakSessionFactory; - - @BeforeEach - public void setup() { - keycloakSession = mock(KeycloakSession.class); - keycloakSessionFactory = mock(KeycloakSessionFactory.class); - when(keycloakSessionFactory.create()).thenReturn(keycloakSession); - KeycloakTransactionManager txnManager = mock(KeycloakTransactionManager.class); - when(keycloakSession.getTransactionManager()).thenReturn(txnManager); - RealmProvider realms = mock(RealmProvider.class); - when(keycloakSession.realms()).thenReturn(realms); - RealmModel realm = mock(RealmModel.class); - when(realm.getName()).thenReturn("realm"); - when(realms.getRealmByName(eq("realm"))).thenReturn(realm); - ClientModel clientModel = mock(ClientModel.class); - when(clientModel.isEnabled()).thenReturn(true); - when(realm.getClientByClientId("client")).thenReturn(clientModel); - UserProvider userProvider = mock(UserProvider.class); - UserModel user = mock(UserModel.class); - when(userProvider.getUserByUsername(eq("user"), eq(realm))).thenReturn(user); - when(keycloakSession.userStorageManager()).thenReturn(userProvider); - UserCredentialManager userCredentialManager = mock(UserCredentialManager.class); - when(keycloakSession.userCredentialManager()).thenReturn(userCredentialManager); - - UserSessionProvider userSessionProvider = mock(UserSessionProvider.class); - when(keycloakSession.sessions()).thenReturn(userSessionProvider); - UserSessionModel userSessionModel = mock(UserSessionModel.class); - when(userSessionProvider.getUserSessionWithPredicate(any(), any(), anyBoolean(), any())).thenReturn(userSessionModel); - when(userSessionModel.getStarted()).thenReturn(Time.currentTime()); - when(userSessionModel.getLastSessionRefresh()).thenReturn(Time.currentTime()); - when(realm.getSsoSessionMaxLifespan()).thenReturn(36000); - when(realm.getSsoSessionIdleTimeout()).thenReturn(36000); - when(userSessionModel.getUser()).thenReturn(user); - - config = mock(Config.Scope.class); - when(config.get(eq("baseUri"), anyString())).thenReturn("https://localhost:8443/auth"); - } - - private byte[] createInitialResponse(final String user, final String token) { - String initialResponseString="user="+user+"\1auth=Bearer "+token+"\1\1"; - return initialResponseString.getBytes(StandardCharsets.US_ASCII); - } - - // unknown realm - @Test - public void testUnknownRealm() throws InterruptedException { - final SaslServerMechanism.Instance instance = - (new XOAUTH2SaslServerMechanism(this)).newInstance(keycloakSessionFactory, "unknownRealm", config, null); - byte[] response = instance.processResponse(createInitialResponse("user", "token")); - assertTrue(response == null || response.length == 0); - assertTrue(instance.isComplete()); - assertFalse(instance.isAuthenticated()); - - } - - - // Invalid token - @Test - public void testWrongPassword() throws InterruptedException { - final SaslServerMechanism.Instance instance = - (new XOAUTH2SaslServerMechanism(this)).newInstance(keycloakSessionFactory, "realm", config, null); - this.verifyToken = false; - byte[] response = instance.processResponse(createInitialResponse("user", "eyJhbGciOiJSUzI1NiIsInR5cCIgOiAiSldUIiwia2lkIiA6ICJ3N0xUZ3BvbFJmU1NqQkNEeTljU3VEeFpkM2hmMTEyN3R5ZzNweU52LVRBIn0.eyJqdGkiOiJmM2E3ZjFhNS0zZGM2LTQ1YjEtOWIyYi1hZjIzYmYxYTc5NTUiLCJleHAiOjE1MjAwODI2NTMsIm5iZiI6MCwiaWF0IjoxNTIwMDgyMzUzLCJpc3MiOiJodHRwOi8vbG9jYWxob3N0OjgwODAvYXV0aC9yZWFsbXMvYW1xcCIsImF1ZCI6ImFtcXAiLCJzdWIiOiI4NjA2NDJiMy0wZmE1LTRiYjctOGI0YS0xZGNiOTdlZjhmZDgiLCJ0eXAiOiJCZWFyZXIiLCJhenAiOiJhbXFwIiwiYXV0aF90aW1lIjowLCJzZXNzaW9uX3N0YXRlIjoiY2Y5Njc0YTAtN2FiNy00YmZiLWI4ZTktZjk3MzM5NTY4NzYwIiwiYWNyIjoiMSIsImFsbG93ZWQtb3JpZ2lucyI6W10sInJlYWxtX2FjY2VzcyI6eyJyb2xlcyI6WyJ1bWFfYXV0aG9yaXphdGlvbiJdfSwicmVzb3VyY2VfYWNjZXNzIjp7ImFjY291bnQiOnsicm9sZXMiOlsibWFuYWdlLWFjY291bnQiLCJtYW5hZ2UtYWNjb3VudC1saW5rcyIsInZpZXctcHJvZmlsZSJdfX0sInByZWZlcnJlZF91c2VybmFtZSI6Imd1ZXN0In0.Ul33UaC4EXdxtMqv6fLyOHRvHNkA3U1F2FDKxo4Rs4gIvmrbyjK_RN_AciVZjtphYM4xXDn3E9acchyLcQB690NCneDVwqUUj5c2ZU5LcZsAARtBC8MPk8ekDhfmm3ppsRnnSYzucDC1Qe-iLtmhj-v3NzdkgxIwzbgL2E7QzUuf8KFSj2Ue322r27tPhKLm2ay3lcauKe_u3LziA6S1sgxdABWzTBP8UhSeKtqY0j6JT50LA7mvVgmEZvdzqgt6EVYmU0ALzbdjQuOJhmlTDH68cPqQI1-MLAreHt7BDLTN0YuthzoFKheZBaIpBvdDuSI_iV0iAe_AlT16ka4rUg")); - assertTrue(response == null || response.length == 0); - assertTrue(instance.isComplete()); - assertFalse(instance.isAuthenticated()); - } - - // Validated token - @Test - public void testValidatedToken() throws InterruptedException { - final SaslServerMechanism.Instance instance = - (new XOAUTH2SaslServerMechanism(this)).newInstance(keycloakSessionFactory, "realm", config, null); - this.verifyToken = true; - byte[] response = instance.processResponse(createInitialResponse("user", "eyJhbGciOiJSUzI1NiIsInR5cCIgOiAiSldUIiwia2lkIiA6ICJ3N0xUZ3BvbFJmU1NqQkNEeTljU3VEeFpkM2hmMTEyN3R5ZzNweU52LVRBIn0.eyJqdGkiOiJmM2E3ZjFhNS0zZGM2LTQ1YjEtOWIyYi1hZjIzYmYxYTc5NTUiLCJleHAiOjE1MjAwODI2NTMsIm5iZiI6MCwiaWF0IjoxNTIwMDgyMzUzLCJpc3MiOiJodHRwOi8vbG9jYWxob3N0OjgwODAvYXV0aC9yZWFsbXMvYW1xcCIsImF1ZCI6ImFtcXAiLCJzdWIiOiI4NjA2NDJiMy0wZmE1LTRiYjctOGI0YS0xZGNiOTdlZjhmZDgiLCJ0eXAiOiJCZWFyZXIiLCJhenAiOiJhbXFwIiwiYXV0aF90aW1lIjowLCJzZXNzaW9uX3N0YXRlIjoiY2Y5Njc0YTAtN2FiNy00YmZiLWI4ZTktZjk3MzM5NTY4NzYwIiwiYWNyIjoiMSIsImFsbG93ZWQtb3JpZ2lucyI6W10sInJlYWxtX2FjY2VzcyI6eyJyb2xlcyI6WyJ1bWFfYXV0aG9yaXphdGlvbiJdfSwicmVzb3VyY2VfYWNjZXNzIjp7ImFjY291bnQiOnsicm9sZXMiOlsibWFuYWdlLWFjY291bnQiLCJtYW5hZ2UtYWNjb3VudC1saW5rcyIsInZpZXctcHJvZmlsZSJdfX0sInByZWZlcnJlZF91c2VybmFtZSI6Imd1ZXN0In0.Ul33UaC4EXdxtMqv6fLyOHRvHNkA3U1F2FDKxo4Rs4gIvmrbyjK_RN_AciVZjtphYM4xXDn3E9acchyLcQB690NCneDVwqUUj5c2ZU5LcZsAARtBC8MPk8ekDhfmm3ppsRnnSYzucDC1Qe-iLtmhj-v3NzdkgxIwzbgL2E7QzUuf8KFSj2Ue322r27tPhKLm2ay3lcauKe_u3LziA6S1sgxdABWzTBP8UhSeKtqY0j6JT50LA7mvVgmEZvdzqgt6EVYmU0ALzbdjQuOJhmlTDH68cPqQI1-MLAreHt7BDLTN0YuthzoFKheZBaIpBvdDuSI_iV0iAe_AlT16ka4rUg")); - assertTrue(response == null || response.length == 0); - assertTrue(instance.isComplete()); - assertTrue(instance.isAuthenticated()); - } - - // incorrect sasl format - @Test - public void testBadInitialResponse() { - final SaslServerMechanism.Instance instance = - (new XOAUTH2SaslServerMechanism(this)).newInstance(keycloakSessionFactory, "realm", config, null); - assertThrows(IllegalArgumentException.class, () -> instance.processResponse("potato".getBytes(StandardCharsets.UTF_8))); - } - - - @Override - public AccessToken verifyTokenString(RealmModel realm, String tokenString, URI baseUri, KeycloakSession keycloakSession) throws VerificationException { - if(verifyToken) { - AccessToken accessToken = new AccessToken(); - accessToken.issuedFor("client"); - return accessToken; - } else { - throw new VerificationException(); - } - } -} diff --git a/keycloak-plugin/sasl-plugin/src/test/resources/logback-test.xml b/keycloak-plugin/sasl-plugin/src/test/resources/logback-test.xml deleted file mode 100644 index 0037f119770..00000000000 --- a/keycloak-plugin/sasl-plugin/src/test/resources/logback-test.xml +++ /dev/null @@ -1,17 +0,0 @@ - - - - - - - %d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n - - - - - - - - - - \ No newline at end of file diff --git a/keycloak-plugin/src/main/configuration/application-roles.properties b/keycloak-plugin/src/main/configuration/application-roles.properties deleted file mode 100644 index e69de29bb2d..00000000000 diff --git a/keycloak-plugin/src/main/configuration/application-users.properties b/keycloak-plugin/src/main/configuration/application-users.properties deleted file mode 100644 index e69de29bb2d..00000000000 diff --git a/keycloak-plugin/src/main/configuration/logging.properties b/keycloak-plugin/src/main/configuration/logging.properties deleted file mode 100644 index 03b30911784..00000000000 --- a/keycloak-plugin/src/main/configuration/logging.properties +++ /dev/null @@ -1,41 +0,0 @@ -# Note this file has been generated and will be overwritten if a -# logging subsystem has been defined in the XML configuration. - - -# Additional loggers to configure (the root logger is always configured) -loggers=org.keycloack,sun.rmi,org.jboss.as.config,com.arjuna - -logger.level=INFO -logger.handlers=CONSOLE - -logger.org.keycloack.level=INFO -logger.org.keycloack.useParentHandlers=true - -logger.sun.rmi.level=WARN -logger.sun.rmi.useParentHandlers=true - -logger.org.jboss.as.config.level=DEBUG -logger.org.jboss.as.config.useParentHandlers=true - -logger.com.arjuna.level=WARN -logger.com.arjuna.useParentHandlers=true - -handler.CONSOLE=org.jboss.logmanager.handlers.ConsoleHandler -handler.CONSOLE.level=INFO -handler.CONSOLE.formatter=COLOR-PATTERN -handler.CONSOLE.properties=enabled,autoFlush,target -handler.CONSOLE.enabled=true -handler.CONSOLE.autoFlush=true -handler.CONSOLE.target=SYSTEM_OUT - -# Additional formatters to configure -formatters=PATTERN - - -formatter.PATTERN=org.jboss.logmanager.formatters.PatternFormatter -formatter.PATTERN.properties=pattern -formatter.PATTERN.pattern=%d{yyyy-MM-dd}T%d{HH:mm:ss.SSS}Z %-5p [%c{1}] %m%n - -formatter.COLOR-PATTERN=org.jboss.logmanager.formatters.PatternFormatter -formatter.COLOR-PATTERN.properties=pattern -formatter.COLOR-PATTERN.pattern=%K%d{yyyy-MM-dd}T%d{HH:mm:ss.SSS}Z %-5p [%c{1}] %m%n diff --git a/keycloak-plugin/src/main/configuration/mgmt-groups.properties b/keycloak-plugin/src/main/configuration/mgmt-groups.properties deleted file mode 100644 index e69de29bb2d..00000000000 diff --git a/keycloak-plugin/src/main/configuration/mgmt-users.properties b/keycloak-plugin/src/main/configuration/mgmt-users.properties deleted file mode 100644 index e69de29bb2d..00000000000 diff --git a/keycloak-plugin/src/main/configuration/standalone-h2.xml b/keycloak-plugin/src/main/configuration/standalone-h2.xml deleted file mode 100644 index 84ad1d3e1af..00000000000 --- a/keycloak-plugin/src/main/configuration/standalone-h2.xml +++ /dev/null @@ -1,608 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - jdbc:h2:mem:test;DB_CLOSE_DELAY=-1;DB_CLOSE_ON_EXIT=FALSE - h2 - - sa - sa - - - - jdbc:h2:${jboss.server.data.dir}/keycloak;AUTO_SERVER=TRUE - h2 - - sa - sa - - - - - org.h2.jdbcx.JdbcDataSource - - - - - - - - - false - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - auth - - - classpath:${jboss.home.dir}/providers/* - - - master - 900 - - 2592000 - true - true -

${jboss.home.dir}/themes - - - - - - - - - - - - - - - - - - - - - - jpa - - - basic - - - - - - - - - - - - - - - - - - - default - - - - - - - - ${keycloak.jta.lookup.provider:jboss} - - - - - - - - - - - - - - - - - - - - - - - - - - ${keycloak.x509cert.lookup.provider:default} - - - - ${keycloak.hostname.provider:request} - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/keycloak-plugin/src/main/configuration/standalone-postgresql.xml b/keycloak-plugin/src/main/configuration/standalone-postgresql.xml deleted file mode 100644 index 4d111fa12e3..00000000000 --- a/keycloak-plugin/src/main/configuration/standalone-postgresql.xml +++ /dev/null @@ -1,608 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - ${env.DB_DATABASE} - ${env.DB_PORT} - ${env.DB_HOST} - postgresql - - ${env.DB_USERNAME} - ${env.DB_PASSWORD} - - - true - false - - - - - - - org.postgresql.xa.PGXADataSource - - - - - - - - - false - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - auth - - - classpath:${jboss.home.dir}/providers/* - - - master - 900 - - 2592000 - true - true - ${jboss.home.dir}/themes - - - - - - - - - - - - - - - - - - - - - - jpa - - - basic - - - - - - - - - - - - - - - - - - - default - - - - - - - - ${keycloak.jta.lookup.provider:jboss} - - - - - - - - - - - - - - - - - - - - - - - - - - ${keycloak.x509cert.lookup.provider:default} - - - - ${keycloak.hostname.provider:request} - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/keycloak-plugin/src/main/sh/init-keycloak.sh b/keycloak-plugin/src/main/sh/init-keycloak.sh deleted file mode 100755 index 2145f8b5caa..00000000000 --- a/keycloak-plugin/src/main/sh/init-keycloak.sh +++ /dev/null @@ -1,27 +0,0 @@ -#!/bin/sh -set -e -KEYCLOAK_CONFIG=${KEYCLOAK_DIR}/standalone/configuration/ -OPENSHIFT_CA=${OPENSHIFT_CA:-/var/run/secrets/kubernetes.io/serviceaccount/ca.crt} -SERVICE_CA=${OPENSHIFT_CA:-/var/run/secrets/kubernetes.io/serviceaccount/service-ca.crt} - -cp ${KEYCLOAK_PLUGIN_DIR}/configuration/* ${KEYCLOAK_CONFIG}/ -cp ${KEYCLOAK_PLUGIN_DIR}/configuration/${KEYCLOAK_CONFIG_FILE} ${KEYCLOAK_CONFIG}/standalone-openshift.xml -cp ${KEYCLOAK_PLUGIN_DIR}/providers/* ${KEYCLOAK_DIR}/providers/ - -KEYSTORE_PATH=${KEYCLOAK_DIR}/standalone/configuration/certificates.keystore -CERT_PATH=/opt/enmasse/cert -TRUSTSTORE_PATH=${KEYCLOAK_DIR}/standalone/configuration/truststore.jks - -rm -f ${KEYSTORE_PATH} -openssl pkcs12 -export -passout pass:enmasse -in ${CERT_PATH}/tls.crt -inkey ${CERT_PATH}/tls.key -name "server" -out /tmp/certificates-keystore.p12 -keytool -importkeystore -srcstorepass enmasse -deststorepass enmasse -destkeystore ${KEYSTORE_PATH} -srckeystore /tmp/certificates-keystore.p12 -srcstoretype PKCS12 -echo "Keystore ${KEYSTORE_PATH} created" - -rm -rf ${TRUSTSTORE_PATH} -cp /etc/pki/java/cacerts ${TRUSTSTORE_PATH} -chmod 644 ${TRUSTSTORE_PATH} -echo "Copied system trust store. Importing OpenShift CA" -keytool -import -noprompt -file ${OPENSHIFT_CA} -alias firstCA -deststorepass changeit -keystore $TRUSTSTORE_PATH -echo "Importing OpenShift Service CA" -keytool -import -noprompt -file ${SERVICE_CA} -alias secondCA -deststorepass changeit -keystore $TRUSTSTORE_PATH -echo "Truststore ${TRUSTSTORE_PATH} created" diff --git a/keycloak-user-api/pom.xml b/keycloak-user-api/pom.xml deleted file mode 100644 index 5153b17df80..00000000000 --- a/keycloak-user-api/pom.xml +++ /dev/null @@ -1,73 +0,0 @@ - - - - io.enmasse - enmasse - 0.32-SNAPSHOT - - 4.0.0 - io.enmasse - keycloak-user-api - - - io.enmasse - api-model - - - io.enmasse - k8s-api - compile - - - org.keycloak - keycloak-admin-client - compile - - - io.fabric8 - openshift-client - compile - - - org.jboss.resteasy - resteasy-client - compile - - - org.jboss.resteasy - resteasy-jackson2-provider - compile - - - org.slf4j - slf4j-api - compile - - - org.junit.jupiter - junit-jupiter-api - test - - - org.junit.jupiter - junit-jupiter-engine - test - - - org.junit.platform - junit-platform-launcher - test - - - org.mockito - mockito-core - test - - - ch.qos.logback - logback-classic - test - - - diff --git a/keycloak-user-api/src/main/java/io/enmasse/user/keycloak/KeycloakFactory.java b/keycloak-user-api/src/main/java/io/enmasse/user/keycloak/KeycloakFactory.java deleted file mode 100644 index e6813836f3a..00000000000 --- a/keycloak-user-api/src/main/java/io/enmasse/user/keycloak/KeycloakFactory.java +++ /dev/null @@ -1,12 +0,0 @@ -/* - * Copyright 2017-2018, EnMasse authors. - * License: Apache License 2.0 (see the file LICENSE or http://apache.org/licenses/LICENSE-2.0.html). - */ -package io.enmasse.user.keycloak; - -import io.enmasse.admin.model.v1.AuthenticationService; -import org.keycloak.admin.client.Keycloak; - -public interface KeycloakFactory { - Keycloak createInstance(AuthenticationService authenticationService); -} diff --git a/keycloak-user-api/src/main/java/io/enmasse/user/keycloak/KeycloakRealmApi.java b/keycloak-user-api/src/main/java/io/enmasse/user/keycloak/KeycloakRealmApi.java deleted file mode 100644 index b8b32a4f6f7..00000000000 --- a/keycloak-user-api/src/main/java/io/enmasse/user/keycloak/KeycloakRealmApi.java +++ /dev/null @@ -1,148 +0,0 @@ -/* - * Copyright 2017-2018, EnMasse authors. - * License: Apache License 2.0 (see the file LICENSE or http://apache.org/licenses/LICENSE-2.0.html). - */ -package io.enmasse.user.keycloak; - - -import io.enmasse.admin.model.v1.AuthenticationService; -import io.enmasse.user.api.RealmApi; -import org.keycloak.admin.client.Keycloak; -import org.keycloak.admin.client.resource.RealmResource; -import org.keycloak.representations.idm.RealmRepresentation; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import javax.ws.rs.WebApplicationException; -import java.time.Clock; -import java.time.Duration; -import java.time.Instant; -import java.util.HashMap; -import java.util.HashSet; -import java.util.List; -import java.util.Map; -import java.util.Set; -import java.util.stream.Collectors; - -public class KeycloakRealmApi implements RealmApi { - - private static final Logger log = LoggerFactory.getLogger(KeycloakRealmApi.class); - - private final Clock clock; - private final KeycloakFactory keycloakFactory; - private final Duration apiTimeout; - private final Map keycloakMap = new HashMap<>(); - - public KeycloakRealmApi(KeycloakFactory keycloakFactory, Clock clock, Duration apiTimeout) { - this.keycloakFactory = keycloakFactory; - this.clock = clock; - this.apiTimeout = apiTimeout; - } - - public synchronized void retainAuthenticationServices(List items) { - Set desired = items.stream().map(a -> a.getMetadata().getName()).collect(Collectors.toSet()); - Set toRemove = new HashSet<>(keycloakMap.keySet()); - toRemove.removeAll(desired); - for (String authService : toRemove) { - Keycloak keycloak = keycloakMap.remove(authService); - if (keycloak != null && !keycloak.isClosed()) { - keycloak.close(); - } - } - } - - interface KeycloakHandler { - T handle(Keycloak keycloak) throws Exception; - } - - private synchronized T withKeycloak(AuthenticationService authenticationService, KeycloakHandler consumer) throws Exception { - if (keycloakMap.get(authenticationService.getMetadata().getName()) == null) { - keycloakMap.put(authenticationService.getMetadata().getName(), keycloakFactory.createInstance(authenticationService)); - } - Keycloak keycloak = keycloakMap.get(authenticationService.getMetadata().getName()); - try { - return consumer.handle(keycloak); - } catch (Exception e) { - keycloakMap.remove(authenticationService.getMetadata().getName()); - keycloak.close(); - throw e; - } - } - - interface RealmHandler { - T handle(RealmResource realm); - } - - private synchronized T withRealm(AuthenticationService authenticationService, String realmName, RealmHandler consumer) throws Exception { - return withKeycloak(authenticationService, keycloak -> { - RealmResource realmResource = waitForRealm(keycloak, realmName, apiTimeout); - return consumer.handle(realmResource); - }); - } - - private RealmResource waitForRealm(Keycloak keycloak, String realmName, Duration timeout) throws Exception { - Instant now = clock.instant(); - Instant endTime = now.plus(timeout); - RealmResource realmResource = null; - while (now.isBefore(endTime)) { - realmResource = getRealmResource(keycloak, realmName); - if (realmResource != null) { - break; - } - log.info("Waiting 1 second for realm {} to exist", realmName); - Thread.sleep(1000); - now = clock.instant(); - } - - if (realmResource == null) { - realmResource = getRealmResource(keycloak, realmName); - } - - if (realmResource != null) { - return realmResource; - } - - throw new WebApplicationException("Timed out waiting for realm " + realmName + " to exist", 503); - } - - private RealmResource getRealmResource(Keycloak keycloak, String realmName) { - List realms = keycloak.realms().findAll(); - for (RealmRepresentation realm : realms) { - if (realm.getRealm().equals(realmName)) { - return keycloak.realm(realmName); - } - } - return null; - } - - @Override - public Set getRealmNames(AuthenticationService authenticationService) throws Exception { - return withKeycloak(authenticationService, kc -> kc.realms().findAll().stream() - .map(RealmRepresentation::getRealm) - .collect(Collectors.toSet())); - } - - @Override - public void createRealm(AuthenticationService authenticationService, String namespace, String realmName) throws Exception { - final RealmRepresentation newRealm = new RealmRepresentation(); - newRealm.setRealm(realmName); - newRealm.setEnabled(true); - newRealm.setPasswordPolicy("hashAlgorithm(scramsha1)"); - newRealm.setAttributes(new HashMap<>()); - newRealm.getAttributes().put("namespace", namespace); - newRealm.getAttributes().put("enmasse-realm", "true"); - - withKeycloak(authenticationService, kc -> { - kc.realms().create(newRealm); - return true; - }); - } - - @Override - public void deleteRealm(AuthenticationService authenticationService, String realmName) throws Exception { - withKeycloak(authenticationService, kc -> { - kc.realm(realmName).remove(); - return true; - }); - } -} diff --git a/keycloak-user-api/src/main/java/io/enmasse/user/keycloak/KeycloakUnavailableException.java b/keycloak-user-api/src/main/java/io/enmasse/user/keycloak/KeycloakUnavailableException.java deleted file mode 100644 index a9d84343854..00000000000 --- a/keycloak-user-api/src/main/java/io/enmasse/user/keycloak/KeycloakUnavailableException.java +++ /dev/null @@ -1,12 +0,0 @@ -/* - * Copyright 2019, EnMasse authors. - * License: Apache License 2.0 (see the file LICENSE or http://apache.org/licenses/LICENSE-2.0.html). - */ -package io.enmasse.user.keycloak; - -@SuppressWarnings("serial") -public class KeycloakUnavailableException extends RuntimeException { - public KeycloakUnavailableException(String message) { - super(message); - } -} diff --git a/keycloak-user-api/src/main/java/io/enmasse/user/keycloak/KubeKeycloakFactory.java b/keycloak-user-api/src/main/java/io/enmasse/user/keycloak/KubeKeycloakFactory.java deleted file mode 100644 index 60b57378b30..00000000000 --- a/keycloak-user-api/src/main/java/io/enmasse/user/keycloak/KubeKeycloakFactory.java +++ /dev/null @@ -1,129 +0,0 @@ -/* - * Copyright 2017-2018, EnMasse authors. - * License: Apache License 2.0 (see the file LICENSE or http://apache.org/licenses/LICENSE-2.0.html). - */ -package io.enmasse.user.keycloak; - -import com.fasterxml.jackson.annotation.JsonInclude; -import com.fasterxml.jackson.databind.ObjectMapper; -import io.enmasse.admin.model.v1.AuthenticationService; -import io.enmasse.admin.model.v1.AuthenticationServiceSpecStandard; -import io.enmasse.admin.model.v1.AuthenticationServiceType; -import io.fabric8.kubernetes.api.model.Secret; -import io.fabric8.kubernetes.client.NamespacedKubernetesClient; -import org.jboss.resteasy.client.jaxrs.ResteasyClient; -import org.jboss.resteasy.client.jaxrs.ResteasyClientBuilder; -import org.jboss.resteasy.plugins.providers.jackson.ResteasyJackson2Provider; -import org.keycloak.admin.client.Keycloak; -import org.keycloak.admin.client.KeycloakBuilder; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import javax.ws.rs.core.MediaType; -import javax.ws.rs.core.MultivaluedMap; -import java.io.ByteArrayInputStream; -import java.io.File; -import java.io.IOException; -import java.io.OutputStream; -import java.lang.annotation.Annotation; -import java.lang.reflect.Type; -import java.nio.charset.StandardCharsets; -import java.nio.file.Files; -import java.security.KeyStore; -import java.security.cert.CertificateFactory; -import java.util.Base64; -import java.util.concurrent.ExecutorService; -import java.util.concurrent.Executors; -import java.util.concurrent.TimeUnit; - -public class KubeKeycloakFactory implements KeycloakFactory { - private static final Logger log = LoggerFactory.getLogger(KubeKeycloakFactory.class.getName()); - - private final ExecutorService executorService = Executors.newSingleThreadExecutor(); - private final NamespacedKubernetesClient kubeClient; - private static final File serviceCaFile = new File("/var/run/secrets/kubernetes.io/serviceaccount/service-ca.crt"); - - public KubeKeycloakFactory(NamespacedKubernetesClient kubeClient) { - this.kubeClient = kubeClient; - } - - @Override - public Keycloak createInstance(AuthenticationService authenticationService) { - if (!authenticationService.getSpec().getType().equals(AuthenticationServiceType.standard) || authenticationService.getStatus() == null) { - throw new KeycloakUnavailableException("Standard authentication service " + authenticationService.getMetadata().getName() + " is not available"); - } - log.info("Using standard authentication service '{}'", authenticationService.getMetadata().getName()); - - AuthenticationServiceSpecStandard standard = authenticationService.getSpec().getStandard(); - String credentialsSecretNamespace = standard.getCredentialsSecret().getNamespace(); - if (credentialsSecretNamespace == null || credentialsSecretNamespace.isEmpty()) { - credentialsSecretNamespace = kubeClient.getNamespace(); - } - Secret credentials = kubeClient.secrets().inNamespace(credentialsSecretNamespace).withName(standard.getCredentialsSecret().getName()).get(); - - String keycloakUri = String.format("https://%s:8443/auth", authenticationService.getStatus().getHost()); - Base64.Decoder b64dec = Base64.getDecoder(); - String adminUser = new String(b64dec.decode(credentials.getData().get("admin.username")), StandardCharsets.UTF_8); - String adminPassword = new String(b64dec.decode(credentials.getData().get("admin.password")), StandardCharsets.UTF_8); - log.info("User keycloak URI {}", keycloakUri); - - String certificateSecretNamespace = standard.getCredentialsSecret().getNamespace(); - if (certificateSecretNamespace == null || certificateSecretNamespace.isEmpty()) { - certificateSecretNamespace = kubeClient.getNamespace(); - } - Secret certificate = kubeClient.secrets().inNamespace(certificateSecretNamespace).withName(standard.getCertificateSecret().getName()).get(); - - byte []serviceCa = null; - try { - serviceCa = Files.readAllBytes(serviceCaFile.toPath()); - } catch (IOException e) { - log.warn("Unable to load service CA, ignoring", e); - } - KeyStore trustStore = createKeyStore(b64dec.decode(certificate.getData().get("tls.crt")), serviceCa); - ResteasyJackson2Provider provider = new ResteasyJackson2Provider() { - @Override - public void writeTo(Object value, Class type, Type genericType, Annotation[] annotations, MediaType mediaType, MultivaluedMap httpHeaders, OutputStream entityStream) throws IOException { - ObjectMapper mapper = locateMapper(type, mediaType); - mapper.setSerializationInclusion(JsonInclude.Include.NON_NULL); - super.writeTo(value, type, genericType, annotations, mediaType, httpHeaders, entityStream); - } - }; - ResteasyClient resteasyClient = new ResteasyClientBuilder() - .connectTimeout(30, TimeUnit.SECONDS) - .connectionPoolSize(1) - .asyncExecutor(executorService) // executorService is the replacement but returns the wrong type - .trustStore(trustStore) - .hostnameVerification(ResteasyClientBuilder.HostnameVerificationPolicy.ANY) - .register(provider) - .build(); - return KeycloakBuilder.builder() - .serverUrl(keycloakUri) - .realm("master") - .username(adminUser) - .password(adminPassword) - .clientId("admin-cli") - .resteasyClient(resteasyClient) - .build(); - } - - private static KeyStore createKeyStore(byte [] ca, byte [] serviceCa) { - try { - KeyStore keyStore = KeyStore.getInstance("JKS"); - keyStore.load(null); - CertificateFactory cf = CertificateFactory.getInstance("X.509"); - if (ca != null) { - keyStore.setCertificateEntry("keycloak", - cf.generateCertificate(new ByteArrayInputStream(ca))); - } - if (serviceCa != null) { - keyStore.setCertificateEntry("service-ca", - cf.generateCertificate(new ByteArrayInputStream(serviceCa))); - } - return keyStore; - } catch (Exception ignored) { - log.warn("Error creating keystore. Ignoring", ignored); - return null; - } - } - -} diff --git a/keycloak-user-api/src/test/java/io/enmasse/user/keycloak/Helpers.java b/keycloak-user-api/src/test/java/io/enmasse/user/keycloak/Helpers.java deleted file mode 100644 index 1e14becaee8..00000000000 --- a/keycloak-user-api/src/test/java/io/enmasse/user/keycloak/Helpers.java +++ /dev/null @@ -1,84 +0,0 @@ -/* - * Copyright 2017-2018, EnMasse authors. - * License: Apache License 2.0 (see the file LICENSE or http://apache.org/licenses/LICENSE-2.0.html). - */ - -package io.enmasse.user.keycloak; - -import static org.junit.jupiter.api.Assertions.assertArrayEquals; - -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Collection; -import java.util.List; - -import io.enmasse.user.model.v1.UserAuthorization; - -public final class Helpers { - private Helpers() { - } - - public static > int compareLists(List o1, List o2) { - - if (o1 == o2) { - return 0; - } - - // ensure lists are non-null and mutable - - if (o1 == null) { - o1 = new ArrayList<>(); - } else { - o1 = new ArrayList<>(o1); - } - - if (o2 == null) { - o2 = new ArrayList<>(); - } else { - o2 = new ArrayList<>(o2); - } - - // compare by size - - int rc = Integer.compare(o1.size(), o2.size()); - - if (rc != 0) { - return rc; - } - - // compare by content - - for (int i = 0; i < o1.size(); i++) { - rc = o1.get(i).compareTo(o2.get(i)); - if (rc != 0) { - return rc; - } - } - - // seem equal - - return 0; - } - - /** - * Compare two user authorizations. - */ - public static int compareUserAuthorization(final UserAuthorization o1, final UserAuthorization o2) { - final int rc = compareLists(o1.getOperations(), o2.getOperations()); - if (rc != 0) { - return rc; - } - return compareLists(o1.getAddresses(), o2.getAddresses()); - } - - public static void assertSorted(final Object[] expected, final Object[] actual) { - Arrays.sort(expected); - Arrays.sort(actual); - assertArrayEquals(expected, actual); - } - - public static > void assertSorted(final Collection expected, - final Collection actual) { - assertSorted(expected.toArray(), actual.toArray()); - } -} diff --git a/keycloak-user-api/src/test/resources/logback-test.xml b/keycloak-user-api/src/test/resources/logback-test.xml deleted file mode 100644 index 0037f119770..00000000000 --- a/keycloak-user-api/src/test/resources/logback-test.xml +++ /dev/null @@ -1,17 +0,0 @@ - - - - - - - %d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n - - - - - - - - - - \ No newline at end of file diff --git a/metrics-api/pom.xml b/metrics-api/pom.xml deleted file mode 100644 index c8d093c9e70..00000000000 --- a/metrics-api/pom.xml +++ /dev/null @@ -1,43 +0,0 @@ - - - - io.enmasse - enmasse - 0.32-SNAPSHOT - - 4.0.0 - io.enmasse - metrics-api - - - org.junit.jupiter - junit-jupiter-api - test - - - org.junit.jupiter - junit-jupiter-engine - test - - - org.junit.platform - junit-platform-launcher - test - - - org.slf4j - slf4j-api - compile - - - - - - - src/main/resources - true - - - - diff --git a/metrics-api/src/main/java/io/enmasse/metrics/api/HistogramMetric.java b/metrics-api/src/main/java/io/enmasse/metrics/api/HistogramMetric.java deleted file mode 100644 index 044576cc737..00000000000 --- a/metrics-api/src/main/java/io/enmasse/metrics/api/HistogramMetric.java +++ /dev/null @@ -1,47 +0,0 @@ -/* - * Copyright 2016-2018, EnMasse authors. - * License: Apache License 2.0 (see the file LICENSE or http://apache.org/licenses/LICENSE-2.0.html). - */ -package io.enmasse.metrics.api; - -import java.util.ArrayList; -import java.util.List; - -public class HistogramMetric implements Metric { - private final String name; - private final String description; - private final MetricType type; - private final MetricValueSupplier sumSupplier; - private final MetricValueSupplier countSupplier; - private final List bucketSupplier; - - public HistogramMetric(String name, String description, MetricType type, MetricValueSupplier sumSupplier, MetricValueSupplier countSupplier, List bucketSupplier) { - this.name = name; - this.description = description; - this.type = type; - this.sumSupplier = sumSupplier; - this.countSupplier = countSupplier; - this.bucketSupplier = bucketSupplier; - } - - public String getName() { - return name; - } - - public String getDescription() { - return description; - } - - public MetricType getType() { - return type; - } - - @Override - public MetricSnapshot getSnapshot() { - List values = new ArrayList<>(); - for (MetricValueSupplier bucket : bucketSupplier) { - values.addAll(bucket.get()); - } - return MetricSnapshot.histogram(values, sumSupplier.get(), countSupplier.get()); - } -} diff --git a/metrics-api/src/main/java/io/enmasse/metrics/api/Metric.java b/metrics-api/src/main/java/io/enmasse/metrics/api/Metric.java deleted file mode 100644 index 19e113827ae..00000000000 --- a/metrics-api/src/main/java/io/enmasse/metrics/api/Metric.java +++ /dev/null @@ -1,12 +0,0 @@ -/* - * Copyright 2019, EnMasse authors. - * License: Apache License 2.0 (see the file LICENSE or http://apache.org/licenses/LICENSE-2.0.html). - */ -package io.enmasse.metrics.api; - -public interface Metric { - String getName(); - String getDescription(); - MetricType getType(); - MetricSnapshot getSnapshot(); -} diff --git a/metrics-api/src/main/java/io/enmasse/metrics/api/MetricLabel.java b/metrics-api/src/main/java/io/enmasse/metrics/api/MetricLabel.java deleted file mode 100644 index 8eb8263f284..00000000000 --- a/metrics-api/src/main/java/io/enmasse/metrics/api/MetricLabel.java +++ /dev/null @@ -1,39 +0,0 @@ -/* - * Copyright 2016-2018, EnMasse authors. - * License: Apache License 2.0 (see the file LICENSE or http://apache.org/licenses/LICENSE-2.0.html). - */ -package io.enmasse.metrics.api; - -import java.util.Objects; - -public class MetricLabel { - private final String key; - private final String value; - - public MetricLabel(String key, String value) { - this.key = key; - this.value = value; - } - - public String getKey() { - return key; - } - - public String getValue() { - return value; - } - - @Override - public boolean equals(Object o) { - if (this == o) return true; - if (o == null || getClass() != o.getClass()) return false; - MetricLabel that = (MetricLabel) o; - return Objects.equals(key, that.key) && - Objects.equals(value, that.value); - } - - @Override - public int hashCode() { - return Objects.hash(key, value); - } -} diff --git a/metrics-api/src/main/java/io/enmasse/metrics/api/MetricSnapshot.java b/metrics-api/src/main/java/io/enmasse/metrics/api/MetricSnapshot.java deleted file mode 100644 index 6c94b5da6f6..00000000000 --- a/metrics-api/src/main/java/io/enmasse/metrics/api/MetricSnapshot.java +++ /dev/null @@ -1,42 +0,0 @@ -/* - * Copyright 2019, EnMasse authors. - * License: Apache License 2.0 (see the file LICENSE or http://apache.org/licenses/LICENSE-2.0.html). - */ -package io.enmasse.metrics.api; - -import java.util.ArrayList; -import java.util.Collections; -import java.util.List; - -public class MetricSnapshot { - private final List values; - private final List sumValues; - private final List countValues; - - private MetricSnapshot(List values, List sumValues, List countValues) { - this.values = new ArrayList<>(values); - this.sumValues = sumValues; - this.countValues = countValues; - } - - public List getValues() { - return values; - } - - public List getSumValues() { - return sumValues; - } - - public List getCountValues() { - return countValues; - } - - public static MetricSnapshot histogram(List bucketValues, List sumValues, List countValues) { - return new MetricSnapshot(bucketValues, sumValues, countValues); - } - - public static MetricSnapshot scalar(List values) { - return new MetricSnapshot(values, Collections.emptyList(), Collections.emptyList()); - } - -} diff --git a/metrics-api/src/main/java/io/enmasse/metrics/api/MetricType.java b/metrics-api/src/main/java/io/enmasse/metrics/api/MetricType.java deleted file mode 100644 index 5ca1028e2fb..00000000000 --- a/metrics-api/src/main/java/io/enmasse/metrics/api/MetricType.java +++ /dev/null @@ -1,12 +0,0 @@ -/* - * Copyright 2016-2018, EnMasse authors. - * License: Apache License 2.0 (see the file LICENSE or http://apache.org/licenses/LICENSE-2.0.html). - */ -package io.enmasse.metrics.api; - -public enum MetricType { - up, - gauge, - counter, - histogram -} diff --git a/metrics-api/src/main/java/io/enmasse/metrics/api/MetricValue.java b/metrics-api/src/main/java/io/enmasse/metrics/api/MetricValue.java deleted file mode 100644 index 1a7e40efcb4..00000000000 --- a/metrics-api/src/main/java/io/enmasse/metrics/api/MetricValue.java +++ /dev/null @@ -1,33 +0,0 @@ -/* - * Copyright 2016-2018, EnMasse authors. - * License: Apache License 2.0 (see the file LICENSE or http://apache.org/licenses/LICENSE-2.0.html). - */ -package io.enmasse.metrics.api; - -import java.util.List; - -public class MetricValue { - private Number value; - private final List labels; - - public MetricValue(Number value, MetricLabel ... labels) { - this(value, List.of(labels)); - } - - public MetricValue(Number value, List labels) { - if (value == null) { - this.value = Float.NaN; - } else { - this.value = value; - } - this.labels = labels; - } - - public List getLabels() { - return labels; - } - - public Number getValue() { - return value; - } -} diff --git a/metrics-api/src/main/java/io/enmasse/metrics/api/MetricValueSupplier.java b/metrics-api/src/main/java/io/enmasse/metrics/api/MetricValueSupplier.java deleted file mode 100644 index 258762c262c..00000000000 --- a/metrics-api/src/main/java/io/enmasse/metrics/api/MetricValueSupplier.java +++ /dev/null @@ -1,11 +0,0 @@ -/* - * Copyright 2019, EnMasse authors. - * License: Apache License 2.0 (see the file LICENSE or http://apache.org/licenses/LICENSE-2.0.html). - */ -package io.enmasse.metrics.api; - -import java.util.List; -import java.util.function.Supplier; - -public interface MetricValueSupplier extends Supplier> { -} diff --git a/metrics-api/src/main/java/io/enmasse/metrics/api/Metrics.java b/metrics-api/src/main/java/io/enmasse/metrics/api/Metrics.java deleted file mode 100644 index cf3a140b3c5..00000000000 --- a/metrics-api/src/main/java/io/enmasse/metrics/api/Metrics.java +++ /dev/null @@ -1,26 +0,0 @@ -/* - * Copyright 2016-2018, EnMasse authors. - * License: Apache License 2.0 (see the file LICENSE or http://apache.org/licenses/LICENSE-2.0.html). - */ -package io.enmasse.metrics.api; - -import java.util.ArrayList; -import java.util.LinkedHashMap; -import java.util.List; -import java.util.Map; - -public class Metrics { - private final Map metrics = new LinkedHashMap<>(); - - public synchronized List getMetrics() { - return new ArrayList<>(metrics.values()); - } - - public synchronized void registerMetric(Metric metric) { - Metric existing = metrics.get(metric.getName()); - if (existing != null) { - throw new IllegalArgumentException("Metric " + metric.getName() + " already registered"); - } - metrics.put(metric.getName(), metric); - } -} diff --git a/metrics-api/src/main/java/io/enmasse/metrics/api/MetricsFormatter.java b/metrics-api/src/main/java/io/enmasse/metrics/api/MetricsFormatter.java deleted file mode 100644 index 791f2720d74..00000000000 --- a/metrics-api/src/main/java/io/enmasse/metrics/api/MetricsFormatter.java +++ /dev/null @@ -1,11 +0,0 @@ -/* - * Copyright 2016-2018, EnMasse authors. - * License: Apache License 2.0 (see the file LICENSE or http://apache.org/licenses/LICENSE-2.0.html). - */ -package io.enmasse.metrics.api; - -import java.util.Collection; - -public interface MetricsFormatter { - String format(Collection metricList, long timestamp); -} diff --git a/metrics-api/src/main/java/io/enmasse/metrics/api/PrometheusMetricsFormatter.java b/metrics-api/src/main/java/io/enmasse/metrics/api/PrometheusMetricsFormatter.java deleted file mode 100644 index a516dda91f7..00000000000 --- a/metrics-api/src/main/java/io/enmasse/metrics/api/PrometheusMetricsFormatter.java +++ /dev/null @@ -1,60 +0,0 @@ -/* - * Copyright 2016-2018, EnMasse authors. - * License: Apache License 2.0 (see the file LICENSE or http://apache.org/licenses/LICENSE-2.0.html). - */ -package io.enmasse.metrics.api; - -import java.util.Collection; -import java.util.stream.Collectors; - -public class PrometheusMetricsFormatter implements MetricsFormatter { - public String format(Collection metricList, long timestamp) { - StringBuilder sb = new StringBuilder(); - - for (Metric metric : metricList) { - if (!MetricType.up.equals(metric.getType())) { - sb.append("# HELP ").append(metric.getName()).append(" ").append(metric.getDescription()).append("\n"); - sb.append("# TYPE ").append(metric.getName()).append(" ").append(metric.getType().name()).append("\n"); - } - String suffix = metric.getType().equals(MetricType.histogram) ? "_bucket" : ""; - MetricSnapshot snapshot = metric.getSnapshot(); - for (MetricValue metricValue : snapshot.getValues()) { - sb.append(formatMetricValue(metric.getName() + suffix, metricValue, timestamp)); - } - - for (MetricValue metricValue : snapshot.getSumValues()) { - sb.append(formatMetricValue(metric.getName() + "_sum", metricValue, timestamp)); - } - - for (MetricValue metricValue : snapshot.getCountValues()) { - sb.append(formatMetricValue(metric.getName() + "_count", metricValue, timestamp)); - } - } - return sb.toString(); - } - - private static String formatMetricValue(String name, MetricValue metricValue, long timestamp) { - StringBuilder sb = new StringBuilder(); - sb.append(name); - if (!metricValue.getLabels().isEmpty()) { - sb.append("{"); - sb.append(metricValue.getLabels().stream() - .map(label -> label.getKey() + "=\"" + escapeLabelValue(label.getValue()) + "\"") - .collect(Collectors.joining(","))); - sb.append("}"); - } - sb.append(" ").append(metricValue.getValue()); - if (timestamp != 0) { - sb.append(" ").append(timestamp); - } - sb.append("\n"); - return sb.toString(); - } - - private static String escapeLabelValue(String value) { - return value.replace("\\", "\\\\") - .replace("\"", "\\\"") - .replace("\n", "\\n"); - } - -} diff --git a/metrics-api/src/main/java/io/enmasse/metrics/api/ScalarMetric.java b/metrics-api/src/main/java/io/enmasse/metrics/api/ScalarMetric.java deleted file mode 100644 index 4df070df79d..00000000000 --- a/metrics-api/src/main/java/io/enmasse/metrics/api/ScalarMetric.java +++ /dev/null @@ -1,36 +0,0 @@ -/* - * Copyright 2016-2018, EnMasse authors. - * License: Apache License 2.0 (see the file LICENSE or http://apache.org/licenses/LICENSE-2.0.html). - */ -package io.enmasse.metrics.api; - -public class ScalarMetric implements Metric { - private final String name; - private final String description; - private final MetricType type; - private final MetricValueSupplier metricValueSupplier; - - public ScalarMetric(String name, String description, MetricType type, MetricValueSupplier metricValueSupplier) { - this.name = name; - this.description = description; - this.type = type; - this.metricValueSupplier = metricValueSupplier; - } - - public String getName() { - return name; - } - - public String getDescription() { - return description; - } - - public MetricType getType() { - return type; - } - - @Override - public MetricSnapshot getSnapshot() { - return MetricSnapshot.scalar(metricValueSupplier.get()); - } -} diff --git a/metrics-api/src/test/java/io/enmasse/metrics/api/MetricsTest.java b/metrics-api/src/test/java/io/enmasse/metrics/api/MetricsTest.java deleted file mode 100644 index 7754ede9d9a..00000000000 --- a/metrics-api/src/test/java/io/enmasse/metrics/api/MetricsTest.java +++ /dev/null @@ -1,82 +0,0 @@ -/* - * Copyright 2016-2018, EnMasse authors. - * License: Apache License 2.0 (see the file LICENSE or http://apache.org/licenses/LICENSE-2.0.html). - */ -package io.enmasse.metrics.api; - -import org.junit.jupiter.api.Test; - -import java.util.Collection; -import java.util.Collections; -import java.util.Iterator; -import java.util.List; -import java.util.concurrent.atomic.AtomicInteger; - -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertNotNull; - -public class MetricsTest { - @Test - public void testMetrics() { - Metrics metrics = new Metrics(); - metrics.registerMetric(new ScalarMetric( - "m1", - "m1", - MetricType.gauge, - () -> Collections.singletonList(new MetricValue(2)))); - - metrics.registerMetric(new ScalarMetric( - "m2", - "m2", - MetricType.gauge, - () -> List.of( - new MetricValue(2, new MetricLabel("k1", "v1")), - new MetricValue(3, new MetricLabel("k1", "v2"))))); - - metrics.registerMetric(new ScalarMetric( - "m3", - "m3", - MetricType.gauge, - () -> Collections.singletonList( - new MetricValue(5, new MetricLabel("k1", "v1"))))); - - Collection metricList = metrics.getMetrics(); - assertEquals(3, metricList.size()); - } - @Test - public void testMetricsUpdate() { - Metrics metrics = new Metrics(); - AtomicInteger val = new AtomicInteger(2); - - metrics.registerMetric(new ScalarMetric( - "m2", - "m2", - MetricType.gauge, - () -> List.of( - new MetricValue(val.get(), new MetricLabel("k1", "v1"))))); - - Metric m = metrics.getMetrics().iterator().next(); - assertNotNull(m); - - MetricSnapshot snapshot = m.getSnapshot(); - assertContainsValue(snapshot, new MetricValue(2, new MetricLabel("k1", "v1"))); - val.incrementAndGet(); - assertContainsValue(snapshot, new MetricValue(2, new MetricLabel("k1", "v1"))); - - assertContainsValue(m.getSnapshot(), new MetricValue(3, new MetricLabel("k1", "v1"))); - } - - private void assertContainsValue(MetricSnapshot snapshot, MetricValue value) { - Iterator it = snapshot.getValues().iterator(); - MetricValue v = null; - while (it.hasNext()) { - v = it.next(); - if (v.getLabels().equals(value.getLabels())) { - break; - } - } - assertNotNull(v); - assertEquals(value.getLabels(), v.getLabels()); - assertEquals(value.getValue(), v.getValue()); - } -} diff --git a/metrics-api/src/test/java/io/enmasse/metrics/api/PrometheusMetricsFormatterTest.java b/metrics-api/src/test/java/io/enmasse/metrics/api/PrometheusMetricsFormatterTest.java deleted file mode 100644 index 2599ff6181f..00000000000 --- a/metrics-api/src/test/java/io/enmasse/metrics/api/PrometheusMetricsFormatterTest.java +++ /dev/null @@ -1,52 +0,0 @@ -/* - * Copyright 2016-2018, EnMasse authors. - * License: Apache License 2.0 (see the file LICENSE or http://apache.org/licenses/LICENSE-2.0.html). - */ -package io.enmasse.metrics.api; - -import org.junit.jupiter.api.Test; - -import java.util.Collections; -import java.util.List; - -import static org.junit.jupiter.api.Assertions.assertEquals; - - -public class PrometheusMetricsFormatterTest { - - @Test - public void testEscaping() { - PrometheusMetricsFormatter formatter = new PrometheusMetricsFormatter(); - - ScalarMetric metric = new ScalarMetric("m1", "mdesc", MetricType.gauge, - () -> List.of(new MetricValue(2, new MetricLabel("key1", "value1"), new MetricLabel("key2", "\\this\"is\nescaped\\")))); - - String value = formatter.format(Collections.singletonList(metric), 3); - System.out.println(value); - String expected = "# HELP m1 mdesc\n# TYPE m1 gauge\nm1{key1=\"value1\",key2=\"\\\\this\\\"is\\nescaped\\\\\"} 2 3\n"; - assertEquals(expected, value); - } - - @Test - public void testEmpty() { - Metrics metrics = new Metrics(); - metrics.registerMetric(new ScalarMetric( - "address_spaces_ready_total", - "Total number of address spaces in ready state", - MetricType.gauge, - Collections::emptyList)); - metrics.registerMetric(new ScalarMetric( - "address_spaces_not_ready_total", - "Total number of address spaces in a not ready state", - MetricType.gauge, - Collections::emptyList)); - metrics.registerMetric(new ScalarMetric( - "address_spaces_total", - "Total number of address spaces", - MetricType.gauge, - () -> Collections.singletonList(new MetricValue(0)))); - - PrometheusMetricsFormatter formatter = new PrometheusMetricsFormatter(); - System.out.print(formatter.format(metrics.getMetrics(), 0)); - } -} diff --git a/mqtt-gateway/.gitignore b/mqtt-gateway/.gitignore deleted file mode 100644 index 408ec85fc20..00000000000 --- a/mqtt-gateway/.gitignore +++ /dev/null @@ -1,14 +0,0 @@ -# Gradle stuff -.gradle -/build/ - -# Eclipse stuff -**/.project -**/.settings/* -**/.prefs -**/.classpath -/target/ - -# IntelliJ IDEA specific -.idea/ -*.iml diff --git a/mqtt-gateway/Dockerfile b/mqtt-gateway/Dockerfile deleted file mode 100644 index 83cb76569cd..00000000000 --- a/mqtt-gateway/Dockerfile +++ /dev/null @@ -1,9 +0,0 @@ -FROM quay.io/enmasse/java-base:11-5 - -ARG version -ARG maven_version -ARG revision -ENV VERSION=${version} REVISION=${revision} MAVEN_VERSION=${maven_version} -ADD target/mqtt-gateway-${maven_version}.jar /mqtt-gateway.jar - -CMD ["/opt/run-java/launch_java.sh", "-jar", "/mqtt-gateway.jar"] diff --git a/mqtt-gateway/LICENSE b/mqtt-gateway/LICENSE deleted file mode 100644 index 8dada3edaf5..00000000000 --- a/mqtt-gateway/LICENSE +++ /dev/null @@ -1,201 +0,0 @@ - Apache License - Version 2.0, January 2004 - http://www.apache.org/licenses/ - - TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION - - 1. Definitions. - - "License" shall mean the terms and conditions for use, reproduction, - and distribution as defined by Sections 1 through 9 of this document. - - "Licensor" shall mean the copyright owner or entity authorized by - the copyright owner that is granting the License. - - "Legal Entity" shall mean the union of the acting entity and all - other entities that control, are controlled by, or are under common - control with that entity. For the purposes of this definition, - "control" means (i) the power, direct or indirect, to cause the - direction or management of such entity, whether by contract or - otherwise, or (ii) ownership of fifty percent (50%) or more of the - outstanding shares, or (iii) beneficial ownership of such entity. - - "You" (or "Your") shall mean an individual or Legal Entity - exercising permissions granted by this License. - - "Source" form shall mean the preferred form for making modifications, - including but not limited to software source code, documentation - source, and configuration files. - - "Object" form shall mean any form resulting from mechanical - transformation or translation of a Source form, including but - not limited to compiled object code, generated documentation, - and conversions to other media types. - - "Work" shall mean the work of authorship, whether in Source or - Object form, made available under the License, as indicated by a - copyright notice that is included in or attached to the work - (an example is provided in the Appendix below). - - "Derivative Works" shall mean any work, whether in Source or Object - form, that is based on (or derived from) the Work and for which the - editorial revisions, annotations, elaborations, or other modifications - represent, as a whole, an original work of authorship. For the purposes - of this License, Derivative Works shall not include works that remain - separable from, or merely link (or bind by name) to the interfaces of, - the Work and Derivative Works thereof. - - "Contribution" shall mean any work of authorship, including - the original version of the Work and any modifications or additions - to that Work or Derivative Works thereof, that is intentionally - submitted to Licensor for inclusion in the Work by the copyright owner - or by an individual or Legal Entity authorized to submit on behalf of - the copyright owner. For the purposes of this definition, "submitted" - means any form of electronic, verbal, or written communication sent - to the Licensor or its representatives, including but not limited to - communication on electronic mailing lists, source code control systems, - and issue tracking systems that are managed by, or on behalf of, the - Licensor for the purpose of discussing and improving the Work, but - excluding communication that is conspicuously marked or otherwise - designated in writing by the copyright owner as "Not a Contribution." - - "Contributor" shall mean Licensor and any individual or Legal Entity - on behalf of whom a Contribution has been received by Licensor and - subsequently incorporated within the Work. - - 2. Grant of Copyright License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - copyright license to reproduce, prepare Derivative Works of, - publicly display, publicly perform, sublicense, and distribute the - Work and such Derivative Works in Source or Object form. - - 3. Grant of Patent License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - (except as stated in this section) patent license to make, have made, - use, offer to sell, sell, import, and otherwise transfer the Work, - where such license applies only to those patent claims licensable - by such Contributor that are necessarily infringed by their - Contribution(s) alone or by combination of their Contribution(s) - with the Work to which such Contribution(s) was submitted. If You - institute patent litigation against any entity (including a - cross-claim or counterclaim in a lawsuit) alleging that the Work - or a Contribution incorporated within the Work constitutes direct - or contributory patent infringement, then any patent licenses - granted to You under this License for that Work shall terminate - as of the date such litigation is filed. - - 4. Redistribution. You may reproduce and distribute copies of the - Work or Derivative Works thereof in any medium, with or without - modifications, and in Source or Object form, provided that You - meet the following conditions: - - (a) You must give any other recipients of the Work or - Derivative Works a copy of this License; and - - (b) You must cause any modified files to carry prominent notices - stating that You changed the files; and - - (c) You must retain, in the Source form of any Derivative Works - that You distribute, all copyright, patent, trademark, and - attribution notices from the Source form of the Work, - excluding those notices that do not pertain to any part of - the Derivative Works; and - - (d) If the Work includes a "NOTICE" text file as part of its - distribution, then any Derivative Works that You distribute must - include a readable copy of the attribution notices contained - within such NOTICE file, excluding those notices that do not - pertain to any part of the Derivative Works, in at least one - of the following places: within a NOTICE text file distributed - as part of the Derivative Works; within the Source form or - documentation, if provided along with the Derivative Works; or, - within a display generated by the Derivative Works, if and - wherever such third-party notices normally appear. The contents - of the NOTICE file are for informational purposes only and - do not modify the License. You may add Your own attribution - notices within Derivative Works that You distribute, alongside - or as an addendum to the NOTICE text from the Work, provided - that such additional attribution notices cannot be construed - as modifying the License. - - You may add Your own copyright statement to Your modifications and - may provide additional or different license terms and conditions - for use, reproduction, or distribution of Your modifications, or - for any such Derivative Works as a whole, provided Your use, - reproduction, and distribution of the Work otherwise complies with - the conditions stated in this License. - - 5. Submission of Contributions. Unless You explicitly state otherwise, - any Contribution intentionally submitted for inclusion in the Work - by You to the Licensor shall be under the terms and conditions of - this License, without any additional terms or conditions. - Notwithstanding the above, nothing herein shall supersede or modify - the terms of any separate license agreement you may have executed - with Licensor regarding such Contributions. - - 6. Trademarks. This License does not grant permission to use the trade - names, trademarks, service marks, or product names of the Licensor, - except as required for reasonable and customary use in describing the - origin of the Work and reproducing the content of the NOTICE file. - - 7. Disclaimer of Warranty. Unless required by applicable law or - agreed to in writing, Licensor provides the Work (and each - Contributor provides its Contributions) on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or - implied, including, without limitation, any warranties or conditions - of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A - PARTICULAR PURPOSE. You are solely responsible for determining the - appropriateness of using or redistributing the Work and assume any - risks associated with Your exercise of permissions under this License. - - 8. Limitation of Liability. In no event and under no legal theory, - whether in tort (including negligence), contract, or otherwise, - unless required by applicable law (such as deliberate and grossly - negligent acts) or agreed to in writing, shall any Contributor be - liable to You for damages, including any direct, indirect, special, - incidental, or consequential damages of any character arising as a - result of this License or out of the use or inability to use the - Work (including but not limited to damages for loss of goodwill, - work stoppage, computer failure or malfunction, or any and all - other commercial damages or losses), even if such Contributor - has been advised of the possibility of such damages. - - 9. Accepting Warranty or Additional Liability. While redistributing - the Work or Derivative Works thereof, You may choose to offer, - and charge a fee for, acceptance of support, warranty, indemnity, - or other liability obligations and/or rights consistent with this - License. However, in accepting such obligations, You may act only - on Your own behalf and on Your sole responsibility, not on behalf - of any other Contributor, and only if You agree to indemnify, - defend, and hold each Contributor harmless for any liability - incurred by, or claims asserted against, such Contributor by reason - of your accepting any such warranty or additional liability. - - END OF TERMS AND CONDITIONS - - APPENDIX: How to apply the Apache License to your work. - - To apply the Apache License to your work, attach the following - boilerplate notice, with the fields enclosed by brackets "{}" - replaced with your own identifying information. (Don't include - the brackets!) The text should be enclosed in the appropriate - comment syntax for the file format. We also recommend that a - file or class name and description of purpose be included on the - same "printed page" as the copyright notice for easier - identification within third-party archives. - - Copyright {yyyy} {name of copyright owner} - - 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. diff --git a/mqtt-gateway/Makefile b/mqtt-gateway/Makefile deleted file mode 100644 index 27e8bca799a..00000000000 --- a/mqtt-gateway/Makefile +++ /dev/null @@ -1 +0,0 @@ -include ../Makefile.java.mk diff --git a/mqtt-gateway/README.md b/mqtt-gateway/README.md deleted file mode 100644 index d1e81543fec..00000000000 --- a/mqtt-gateway/README.md +++ /dev/null @@ -1,4 +0,0 @@ -# MQTT gateway -MQTT gateway component for EnMasse which provides connectivity to remote MQTT clients. Through this components, MQTT clients can connect to EnMasse in order to subscribe to topics for receiving published messages; at same time they can publish messages to topics as well. - -This implementation follow the specification defined by the following documentation for bringing [MQTT over AMQP](../documentation/design_docs/mqtt-over-amqp) diff --git a/mqtt-gateway/pom.xml b/mqtt-gateway/pom.xml deleted file mode 100644 index 39da5bff799..00000000000 --- a/mqtt-gateway/pom.xml +++ /dev/null @@ -1,92 +0,0 @@ - - - - io.enmasse - enmasse - 0.32-SNAPSHOT - - 4.0.0 - mqtt-gateway - - - io.vertx - vertx-mqtt - compile - - - io.vertx - vertx-proton - compile - - - org.slf4j - slf4j-api - compile - - - ch.qos.logback - logback-classic - runtime - - - org.junit.jupiter - junit-jupiter-api - test - - - org.junit.jupiter - junit-jupiter-engine - test - - - org.junit.platform - junit-platform-launcher - test - - - io.enmasse - amqp-utils - test - - - org.eclipse.paho - org.eclipse.paho.client.mqttv3 - test - - - - - - - maven-shade-plugin - - - package - - shade - - - - - - - enmasse.mqtt.Application - - - - - *:* - - META-INF/*.SF - META-INF/*.DSA - META-INF/*.RSA - - - - - - - - - - diff --git a/mqtt-gateway/src/main/java/enmasse/mqtt/AmqpBridge.java b/mqtt-gateway/src/main/java/enmasse/mqtt/AmqpBridge.java deleted file mode 100644 index 834b79d71e3..00000000000 --- a/mqtt-gateway/src/main/java/enmasse/mqtt/AmqpBridge.java +++ /dev/null @@ -1,772 +0,0 @@ -/* - * Copyright 2016-2018, EnMasse authors. - * License: Apache License 2.0 (see the file LICENSE or http://apache.org/licenses/LICENSE-2.0.html). - */ - -package enmasse.mqtt; - -import static io.netty.handler.codec.mqtt.MqttConnectReturnCode.CONNECTION_ACCEPTED; -import static io.netty.handler.codec.mqtt.MqttConnectReturnCode.CONNECTION_REFUSED_NOT_AUTHORIZED; -import static io.netty.handler.codec.mqtt.MqttConnectReturnCode.CONNECTION_REFUSED_SERVER_UNAVAILABLE; - -import enmasse.mqtt.endpoints.AmqpPublishData; -import enmasse.mqtt.endpoints.AmqpPublishEndpoint; -import enmasse.mqtt.endpoints.AmqpPublisher; -import enmasse.mqtt.endpoints.AmqpReceiver; -import enmasse.mqtt.endpoints.AmqpReceiverEndpoint; -import enmasse.mqtt.endpoints.AmqpSubscriptionServiceEndpoint; -import enmasse.mqtt.endpoints.AmqpLwtServiceEndpoint; -import enmasse.mqtt.messages.AmqpCloseMessage; -import enmasse.mqtt.messages.AmqpListMessage; -import enmasse.mqtt.messages.AmqpPublishMessage; -import enmasse.mqtt.messages.AmqpPubrelMessage; -import enmasse.mqtt.messages.AmqpSubscribeMessage; -import enmasse.mqtt.messages.AmqpSubscriptionsMessage; -import enmasse.mqtt.messages.AmqpTopicSubscription; -import enmasse.mqtt.messages.AmqpUnsubscribeMessage; -import enmasse.mqtt.messages.AmqpWillMessage; -import io.netty.handler.codec.mqtt.MqttConnectReturnCode; -import io.netty.handler.codec.mqtt.MqttQoS; -import io.vertx.core.AsyncResult; -import io.vertx.core.Future; -import io.vertx.core.Handler; -import io.vertx.core.Vertx; -import io.vertx.core.buffer.Buffer; -import io.vertx.core.net.SocketAddress; -import io.vertx.mqtt.MqttEndpoint; -import io.vertx.mqtt.MqttWill; -import io.vertx.mqtt.messages.MqttPublishMessage; -import io.vertx.mqtt.messages.MqttSubscribeMessage; -import io.vertx.mqtt.messages.MqttUnsubscribeMessage; -import io.vertx.proton.ProtonClient; -import io.vertx.proton.ProtonClientOptions; -import io.vertx.proton.ProtonConnection; -import io.vertx.proton.ProtonDelivery; -import io.vertx.proton.ProtonLinkOptions; -import io.vertx.proton.ProtonReceiver; -import io.vertx.proton.ProtonSender; -import org.apache.qpid.proton.amqp.messaging.Accepted; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import java.util.ArrayList; -import java.util.Collections; -import java.util.HashMap; -import java.util.List; -import java.util.Map; -import java.util.concurrent.atomic.AtomicBoolean; -import java.util.stream.Collectors; - -/** - * AMQP bridging class from/to the MQTT endpoint to/from the AMQP related endpoints - */ -public class AmqpBridge { - - private static final int AMQP_SERVICES_CONNECTION_TIMEOUT = 60_000; // in ms - - private static final Logger LOG = LoggerFactory.getLogger(AmqpBridge.class); - - // Completed once a bridge is opened, or fails to open - private final Future openedFuture = Future.future(); - - // Future that completes when the bridge closes. - private final Future closedFuture = Future.future(); - - // local endpoint for handling remote connected MQTT client - private final MqttEndpoint mqttEndpoint; - - private final SocketAddress remoteAddress; - - private final Vertx vertx; - - private final AtomicBoolean closed = new AtomicBoolean(); - - private ProtonClient client; - private ProtonConnection connection; - - - // endpoint for handling communication with Last Will and Testament Service (LWTS) - private AmqpLwtServiceEndpoint lwtEndpoint; - // endpoint for handling communication with Subscription Service (SS) - private AmqpSubscriptionServiceEndpoint ssEndpoint; - // endpoint for handling incoming messages on the unique client address - private AmqpReceiverEndpoint rcvEndpoint; - // endpoint for publishing message on topic (via AMQP) - private AmqpPublishEndpoint pubEndpoint; - - // callback called when the MQTT client closes connection - private Handler mqttEndpointCloseHandler; - - // topic subscriptions with granted QoS levels - private Map grantedQoSLevels; - - private boolean detachForced = true; - - /** - * Constructor - * - * @param vertx Vert.x instance - * @param mqttEndpoint MQTT local endpoint - */ - public AmqpBridge(Vertx vertx, MqttEndpoint mqttEndpoint) { - this.vertx = vertx; - this.mqttEndpoint = mqttEndpoint; - this.remoteAddress = mqttEndpoint.remoteAddress(); - } - - /** - * Open the bridge and connect to the AMQP service provider - * - * @param address AMQP service provider address - * @param port AMQP service provider port - * @param openHandler handler called when the open is completed (with success or not) - */ - public void open(String address, int port, Handler> openHandler) { - - this.client = ProtonClient.create(this.vertx); - - // TODO: check correlation between MQTT and AMQP keep alive - ProtonClientOptions clientOptions = new ProtonClientOptions(); - clientOptions.setHeartbeat(this.mqttEndpoint.keepAliveTimeSeconds() * 1000); - - String userName = (this.mqttEndpoint.auth() != null) ? this.mqttEndpoint.auth().getUsername() : null; - String password = (this.mqttEndpoint.auth() != null) ? this.mqttEndpoint.auth().getPassword() : null; - // NOTE : if username/password are null then Vert.x Proton just provides SASL ANONYMOUS as supported mechanism - // otherwise it provides PLAIN with username/password provided here - this.client.connect(clientOptions, address, port, userName, password, done -> { - - String clientIdentifier = this.mqttEndpoint.clientIdentifier(); - if (done.succeeded()) { - - this.connection = done.result(); - this.connection - .closeHandler(remoteClose -> handleRemoteConnectionClose(this.connection, remoteClose)) - .disconnectHandler(this::handleRemoteDisconnect) - .open(); - - // setup MQTT endpoint handlers and AMQP endpoints - this.setupMqttEndpoint(); - this.setupAmqpEndpoits(); - - // setup a Future for completed connection steps with all services - // with AMQP_WILL and AMQP_LIST/AMQP_SUBSCRIPTIONS or AMQP_CLOSE handled - Future connectionFuture = Future.future(); - connectionFuture.setHandler(ar -> { - - try { - if (ar.succeeded()) { - - this.rcvEndpoint.publishHandler(this::publishHandler); - this.rcvEndpoint.pubrelHandler(this::pubrelHandler); - - AmqpSubscriptionsMessage amqpSubscriptionsMessage = ar.result(); - - if (amqpSubscriptionsMessage != null) { - this.mqttEndpoint.accept(!amqpSubscriptionsMessage.topicSubscriptions().isEmpty()); - // added topic subscriptions of a previous session in the local collection - this.grantedQoSLevels = amqpSubscriptionsMessage.topicSubscriptions() - .stream() - .collect(Collectors.toMap(amqpTopicSubscription -> amqpTopicSubscription.topic(), - amqpTopicSubscription -> amqpTopicSubscription.qos())); - - } else { - this.mqttEndpoint.accept(false); - this.grantedQoSLevels = new HashMap<>(); - } - LOG.info("CONNACK [{}] to MQTT client {} at {}", CONNECTION_ACCEPTED.ordinal(), - clientIdentifier, this.remoteAddress); - - // open unique client publish address receiver - this.rcvEndpoint.openPublish(); - - openHandler.handle(Future.succeededFuture(AmqpBridge.this)); - - } else { - - this.mqttEndpoint.reject(CONNECTION_REFUSED_SERVER_UNAVAILABLE); - LOG.error("CONNACK [{}] to MQTT client {} at {}", CONNECTION_REFUSED_SERVER_UNAVAILABLE.ordinal(), - clientIdentifier, this.remoteAddress); - - openHandler.handle(Future.failedFuture(ar.cause())); - } - } - finally - { - openedFuture.complete(); - } - }); - - // step 1 : send AMQP_WILL to Last Will and Testament Service - Future willFuture = Future.future(); - // if remote MQTT has specified the will - if (this.mqttEndpoint.will().isWillFlag()) { - - // sending AMQP_WILL - MqttWill will = this.mqttEndpoint.will(); - - AmqpWillMessage amqpWillMessage = - new AmqpWillMessage(will.isWillRetain(), - will.getWillTopic(), - MqttQoS.valueOf(will.getWillQos()), - Buffer.buffer(will.getWillMessageBytes())); - - // specified link name for the Last Will and Testament Service as MQTT clientid - ProtonLinkOptions linkOptions = new ProtonLinkOptions(); - linkOptions.setLinkName(clientIdentifier); - - // setup and open AMQP endpoints to Last Will and Testament Service - ProtonSender wsSender = this.connection.createSender(AmqpLwtServiceEndpoint.LWT_SERVICE_ENDPOINT, linkOptions); - this.lwtEndpoint = new AmqpLwtServiceEndpoint(wsSender); - - this.lwtEndpoint.open(); - this.lwtEndpoint.sendWill(amqpWillMessage, willFuture); - - } else { - - // otherwise just complete the Future - willFuture.complete(); - } - - willFuture.compose(v -> { - - // handling AMQP_SUBSCRIPTIONS reply from Subscription Service - this.rcvEndpoint.subscriptionsHandler(amqpSubscriptionsMessage -> { - - LOG.info("Session present: {}", !amqpSubscriptionsMessage.topicSubscriptions().isEmpty()); - LOG.info(amqpSubscriptionsMessage.toString()); - - connectionFuture.complete(amqpSubscriptionsMessage); - }); - - // step 2 : send AMQP_CLOSE or AMQP_LIST (based on "clean session" flag) to Subscription Service - Future sessionFuture = Future.future(); - - if (this.mqttEndpoint.isCleanSession()) { - - // sending AMQP_CLOSE - AmqpCloseMessage amqpCloseMessage = - new AmqpCloseMessage(clientIdentifier); - - this.ssEndpoint.sendClose(amqpCloseMessage, closeAsyncResult -> { - - // in case of AMQP_CLOSE, the connection completes on its disposition - // no other AMQP message will be delivered by Subscription Service (i.e. AMQP_SUBSCRIPTIONS) - if (closeAsyncResult.succeeded()) { - connectionFuture.complete(); - } else { - connectionFuture.fail(closeAsyncResult.cause()); - } - }); - - } else { - - // sending AMQP_LIST - AmqpListMessage amqpListMessage = - new AmqpListMessage(clientIdentifier); - - this.ssEndpoint.sendList(amqpListMessage, sessionFuture); - } - - return sessionFuture; - - }).compose(v -> { - // nothing here !?? - }, connectionFuture); - - // timeout for the overall connection process - vertx.setTimer(AMQP_SERVICES_CONNECTION_TIMEOUT, timer -> { - if (!connectionFuture.isComplete()) { - connectionFuture.fail("Timeout on connecting to AMQP services"); - } - }); - - } else { - - LOG.error("Error connecting to AMQP services ...", done.cause()); - final MqttConnectReturnCode code; - if (done.cause() instanceof SecurityException) { - // error on the SASL mechanism side - code = CONNECTION_REFUSED_NOT_AUTHORIZED; - } else { - code = CONNECTION_REFUSED_SERVER_UNAVAILABLE; - - } - this.mqttEndpoint.reject(code); - - openHandler.handle(Future.failedFuture(done.cause())); - - LOG.info("CONNACK [{}] to MQTT client {} at {}", code.ordinal(), - clientIdentifier, this.remoteAddress); - } - - }); - - } - - /** - * Close the bridge with all related attached links and connection to AMQP services - */ - public Future close() { - if (this.closed.compareAndSet(false, true)) { - openedFuture.setHandler(unused -> { - LOG.info("Closing session for MQTT client : {} at {}", this.mqttEndpoint.clientIdentifier(), this.remoteAddress); - Future cleanSessionFuture = Future.future(); - cleanSessionFuture.setHandler(unused1 -> { - AsyncResult result = Future.failedFuture((Throwable) null); - try { - if (this.lwtEndpoint != null) { - this.lwtEndpoint.close(this.detachForced); - } - if (this.ssEndpoint != null) { - this.ssEndpoint.close(); - } - if (this.rcvEndpoint != null) { - this.rcvEndpoint.close(); - } - if (this.pubEndpoint != null) { - this.pubEndpoint.close(); - } - if (this.connection != null) { - this.connection.close(); - } - if (this.grantedQoSLevels != null) { - this.grantedQoSLevels.clear(); - } - - try { - this.mqttEndpoint.close(); - } catch (IllegalStateException e) { - } - result = Future.succeededFuture(); - } catch (Throwable e) { - result = Future.failedFuture(e); - } finally { - this.handleMqttEndpointClose(); - this.closedFuture.handle(result); - } - }); - - if (this.mqttEndpoint.isCleanSession()) { - AmqpCloseMessage value = new AmqpCloseMessage(this.mqttEndpoint.clientIdentifier()); - this.ssEndpoint.sendClose(value, event -> { - if (event.failed()) { - LOG.warn("Failed to close session for MQTT client : {}", - this.mqttEndpoint.clientIdentifier(), - event.cause()); - cleanSessionFuture.fail(event.cause()); - } else { - LOG.trace("Closed session for MQTT client : {} at {}", this.mqttEndpoint.clientIdentifier(), this.remoteAddress); - cleanSessionFuture.complete(); - } - }); - } else { - LOG.trace("Closed session for MQTT client : {} at {}", this.mqttEndpoint.clientIdentifier(), this.remoteAddress); - cleanSessionFuture.complete(); - } - }); - } - return this.closedFuture; - } - - /** - * Handler for incoming MQTT PUBLISH message - * - * @param publish PUBLISH message - */ - private void publishHandler(MqttPublishMessage publish) { - - final int mqttPacketId = publish.messageId(); - LOG.info("PUBLISH [{}] from MQTT client {}", mqttPacketId, this.mqttEndpoint.clientIdentifier()); - - // TODO: simple way, without considering wildcards - - // check if a publisher already exists for the requested topic - if (!this.pubEndpoint.isPublisher(publish.topicName())) { - - // create two sender for publishing QoS 0/1 and QoS 2 messages - ProtonSender senderQoS01 = this.connection.createSender(publish.topicName()); - ProtonSender senderQoS2 = this.connection.createSender(publish.topicName()); - - this.pubEndpoint.addPublisher(publish.topicName(), new AmqpPublisher(senderQoS01, senderQoS2)); - } - - // sending AMQP_PUBLISH - AmqpPublishMessage amqpPublishMessage = - new AmqpPublishMessage(null, - publish.qosLevel(), - publish.isDup(), - publish.isRetain(), - publish.topicName(), - publish.payload()); - - pubEndpoint.publish(amqpPublishMessage, mqttPacketId, done -> { - - if (done.succeeded()) { - - ProtonDelivery delivery = done.result(); - if (delivery != null) { - - if (publish.qosLevel() == MqttQoS.AT_LEAST_ONCE) { - this.mqttEndpoint.publishAcknowledge(mqttPacketId); - LOG.info("PUBACK [{}] to MQTT client {}", mqttPacketId, this.mqttEndpoint.clientIdentifier()); - } else { - - this.mqttEndpoint.publishReceived(mqttPacketId); - LOG.info("PUBREC [{}] to MQTT client {}", mqttPacketId, this.mqttEndpoint.clientIdentifier()); - } - - } - } - - }); - } - - /** - * Handler for incoming AMQP_PUBLISH message - * - * @param amqpPublishData object with the AMQP_PUBLISH message - */ - private void publishHandler(AmqpPublishData amqpPublishData) { - - AmqpPublishMessage publish = amqpPublishData.amqpPublishMessage(); - - // defensive ... check that current bridge has information about subscriptions and related granted QoS - // see https://github.com/EnMasseProject/subserv/issues/8 - - // try to get subscribed topic (that could have wildcards) that matches the publish topic - String topic = (this.grantedQoSLevels.size() == 0) ? null : - TopicMatcher.match(this.grantedQoSLevels.keySet().stream().collect(Collectors.toList()), publish.topic()); - - if (topic != null) { - - // MQTT 3.1.1 spec : The QoS of Payload Messages sent in response to a Subscription MUST be - // the minimum of the QoS of the originally published message and the maximum QoS granted by the Server - MqttQoS qos = (publish.qos().value() < this.grantedQoSLevels.get(topic).value()) ? - publish.qos() : - this.grantedQoSLevels.get(topic); - - this.mqttEndpoint.publish(publish.topic(), publish.payload(), qos, publish.isDup(), publish.isRetain()); - // the the message identifier assigned to the published message - amqpPublishData.setMessageId(this.mqttEndpoint.lastMessageId()); - - LOG.info("PUBLISH [{}] to MQTT client {}", this.mqttEndpoint.lastMessageId(), this.mqttEndpoint.clientIdentifier()); - - // for QoS 0, message settled immediately - if (qos == MqttQoS.AT_MOST_ONCE) { - this.rcvEndpoint.settle(amqpPublishData.messageId()); - } - - } else { - - LOG.error("Published message : MQTT client {} is not subscribed to {} !!", this.mqttEndpoint.clientIdentifier(), publish.topic()); - } - } - - /** - * Handler for incoming AMQP_PUBREL message - * - * @param pubrel AMQP_PUBREL message - */ - private void pubrelHandler(AmqpPubrelMessage pubrel) { - - this.mqttEndpoint.publishRelease((int) pubrel.messageId()); - - LOG.info("PUBREL [{}] to MQTT client {}", pubrel.messageId(), this.mqttEndpoint.clientIdentifier()); - } - - /** - * Handler for incoming MQTT SUBSCRIBE message - * - * @param subscribe SUBSCRIBE message - */ - private void subscribeHandler(MqttSubscribeMessage subscribe) { - - final int messageId = subscribe.messageId(); - LOG.info("SUBSCRIBE [{}] from MQTT client {}", messageId, this.mqttEndpoint.clientIdentifier()); - - // sending AMQP_SUBSCRIBE - - List topicSubscriptions = - subscribe.topicSubscriptions().stream().map(topicSubscription -> { - return new AmqpTopicSubscription(topicSubscription.topicName(), topicSubscription.qualityOfService()); - }).collect(Collectors.toList()); - - AmqpSubscribeMessage amqpSubscribeMessage = - new AmqpSubscribeMessage(this.mqttEndpoint.clientIdentifier(), - topicSubscriptions); - - this.ssEndpoint.sendSubscribe(amqpSubscribeMessage, done -> { - - if (done.succeeded()) { - - ProtonDelivery delivery = done.result(); - - List grantedQoSLevels = null; - if (delivery.getRemoteState() == Accepted.getInstance()) { - - // QoS levels requested are granted - grantedQoSLevels = amqpSubscribeMessage.topicSubscriptions().stream().map(topicSubscription -> { - return topicSubscription.qos(); - }).collect(Collectors.toList()); - - // add accepted topic subscriptions to the local collection - amqpSubscribeMessage.topicSubscriptions().stream().forEach(amqpTopicSubscription -> { - this.grantedQoSLevels.put(amqpTopicSubscription.topic(), amqpTopicSubscription.qos()); - }); - - } else { - - // failure for all QoS levels requested - grantedQoSLevels = new ArrayList<>(Collections.nCopies(amqpSubscribeMessage.topicSubscriptions().size(), MqttQoS.FAILURE)); - } - - this.mqttEndpoint.subscribeAcknowledge(messageId, grantedQoSLevels); - - LOG.info("SUBACK [{}] to MQTT client {}", messageId, this.mqttEndpoint.clientIdentifier()); - } - }); - } - - /** - * Handler for incoming MQTT UNSUBSCRIBE message - * - * @param unsubscribe UNSUBSCRIBE message - */ - private void unsubscribeHandler(MqttUnsubscribeMessage unsubscribe) { - - final int messageId = unsubscribe.messageId(); - LOG.info("UNSUBSCRIBE [{}] from MQTT client {}", messageId, this.mqttEndpoint.clientIdentifier()); - - // sending AMQP_UNSUBSCRIBE - - AmqpUnsubscribeMessage amqpUnsubscribeMessage = - new AmqpUnsubscribeMessage(this.mqttEndpoint.clientIdentifier(), unsubscribe.topics()); - - this.ssEndpoint.sendUnsubscribe(amqpUnsubscribeMessage, done -> { - - if (done.succeeded()) { - - this.mqttEndpoint.unsubscribeAcknowledge(messageId); - - // removing topics from local collection - unsubscribe.topics().stream().forEach(topic -> { - - this.grantedQoSLevels.remove(topic); - }); - - LOG.info("UNSUBACK [{}] to MQTT client {}", messageId, this.mqttEndpoint.clientIdentifier()); - } - }); - } - - /** - * Handler for incoming MQTT DISCONNECT message - * - * @param v - */ - private void disconnectHandler(Void v) { - - LOG.info("DISCONNECT from MQTT client {}", this.mqttEndpoint.clientIdentifier()); - this.detachForced = false; - } - - /** - * Handler for connection closed by remote MQTT client - * - * @param v - */ - private void closeHandler(Void v) { - LOG.info("Close from MQTT client {} at {}", this.mqttEndpoint.clientIdentifier(), this.remoteAddress); - close(); - } - - /** - * Handler for incoming MQTT PUBACK message - * - * @param messageId message identifier - */ - private void pubackHandler(int messageId) { - - LOG.info("PUBACK [{}] from MQTT client {}", messageId, this.mqttEndpoint.clientIdentifier()); - - // a PUBLISH message with QoS 1 was sent to remote MQTT client (not settled yet at source) - // now PUBACK is received so it's time to settle - this.rcvEndpoint.settle(messageId); - } - - /** - * Handler for incoming MQTT PUBREL message - * - * @param messageId message identifier - */ - private void pubrelHandler(int messageId) { - - LOG.info("PUBREL [{}] from MQTT client {}", messageId, this.mqttEndpoint.clientIdentifier()); - - // a PUBLISH message with QoS 2 was received from remote MQTT client, PUBREC was already sent - // as reply, now that PUBREL is coming it's time to settle and reply with PUBCOMP - this.pubEndpoint.settle(messageId); - - this.mqttEndpoint.publishComplete(messageId); - - LOG.info("PUBCOMP [{}] to MQTT client {}", messageId, this.mqttEndpoint.clientIdentifier()); - } - - /** - * Handler for incoming MQTT PUBREC message - * - * @param messageId message identifier - */ - private void pubrecHandler(int messageId) { - - LOG.info("PUBREC [{}] from MQTT client {}", messageId, this.mqttEndpoint.clientIdentifier()); - - AmqpPubrelMessage amqpPubrelMessage = new AmqpPubrelMessage(messageId); - - this.pubEndpoint.publish(amqpPubrelMessage, done -> { - - if (done.succeeded()) { - - this.rcvEndpoint.settle(messageId); - } - }); - } - - /** - * Handler for incoming MQTT PUBCOMP message - * - * @param messageId message identifier - */ - private void pubcompHandler(int messageId) { - - LOG.info("PUBCOMP [{}] from MQTT client {}", messageId, this.mqttEndpoint.clientIdentifier()); - - // a PUBLISH message with QoS 2 was sent to remote MQTT client (not settled yet at source) - // then PUBREC was received. The corresponding PUBREL was sent (after PUBLISH settlement at source) - // and now the PUBCOMP was received so it's time to settle - this.rcvEndpoint.settle(messageId); - } - - /** - * Setup handlers for MQTT endpoint - */ - private void setupMqttEndpoint() { - - this.mqttEndpoint - .publishHandler(this::publishHandler) - .publishAcknowledgeHandler(this::pubackHandler) - .publishReleaseHandler(this::pubrelHandler) - .publishReceivedHandler(this::pubrecHandler) - .publishCompletionHandler(this::pubcompHandler) - .subscribeHandler(this::subscribeHandler) - .unsubscribeHandler(this::unsubscribeHandler) - .disconnectHandler(this::disconnectHandler) - .closeHandler(this::closeHandler); - } - - /** - * Setup all AMQP endpoints - */ - private void setupAmqpEndpoits() { - - // NOTE : Last Will and Testament Service endpoint is opened only if MQTT client provides will information - // The receiver on the unique client publish address will be opened only after - // connection is established (and CONNACK sent to the MQTT client) - - // setup and open AMQP endpoint for receiving on unique client control/publish addresses - ProtonReceiver receiverControl = this.connection.createReceiver(String.format(AmqpReceiverEndpoint.CLIENT_CONTROL_ENDPOINT_TEMPLATE, this.mqttEndpoint.clientIdentifier())); - ProtonReceiver receiverPublish = this.connection.createReceiver(String.format(AmqpReceiverEndpoint.CLIENT_PUBLISH_ENDPOINT_TEMPLATE, this.mqttEndpoint.clientIdentifier())); - this.rcvEndpoint = new AmqpReceiverEndpoint(new AmqpReceiver(receiverControl, receiverPublish)); - - // setup and open AMQP endpoint to Subscription Service - ProtonSender ssSender = this.connection.createSender(AmqpSubscriptionServiceEndpoint.SUBSCRIPTION_SERVICE_ENDPOINT); - this.ssEndpoint = new AmqpSubscriptionServiceEndpoint(ssSender); - - // setup and open AMQP endpoint for publishing - ProtonSender senderPubrel = this.connection.createSender(String.format(AmqpPublishEndpoint.AMQP_CLIENT_PUBREL_ENDPOINT_TEMPLATE, this.mqttEndpoint.clientIdentifier())); - this.pubEndpoint = new AmqpPublishEndpoint(senderPubrel); - - this.rcvEndpoint.openControl(); - this.ssEndpoint.open(); - this.pubEndpoint.open(); - } - - /** - * Set the session handler called when MQTT client closes connection - * - * @param handler the handler - * @return the current AmqpBridge instance - */ - public AmqpBridge mqttEndpointCloseHandler(Handler handler) { - - this.mqttEndpointCloseHandler = handler; - return this; - } - - /** - * Used for calling the close handler when MQTT client closes connection - * - */ - private void handleMqttEndpointClose() { - - if (this.mqttEndpointCloseHandler != null) { - this.mqttEndpointCloseHandler.handle(this); - } - } - - /** - * Handle connection closed with remote AMQP container - * - * @param connection current ProtonConnection instance - * @param result result of remote connection closing - */ - private void handleRemoteConnectionClose(ProtonConnection connection, AsyncResult result) { - - // NOTE : the connection parameter is needed because Vert.x doesn't provide the ProtonConnection - // instance when the operation ends with errors (so exception). We need the instance for closing. - if (result.succeeded()) { - LOG.info("AMQP connection closed with {}", connection.getRemoteContainer()); - } else { - LOG.info("AMQP connection closed with {} with error", connection.getRemoteContainer(), result.cause()); - } - connection.close(); - - try { - this.mqttEndpoint.close(); - } catch (IllegalStateException e) { - LOG.warn("MQTT endpoint for client {} already closed", this.mqttEndpoint.clientIdentifier()); - } - } - - /** - * Handler disconnection with remote AMQP container - * - * @param connection current ProtonConnection instance - */ - private void handleRemoteDisconnect(ProtonConnection connection) { - - LOG.info("AMQP disconnection with {}", connection.getRemoteContainer()); - connection.disconnect(); - - try { - this.mqttEndpoint.close(); - } catch (IllegalStateException e) { - LOG.warn("MQTT endpoint for client {} already closed", this.mqttEndpoint.clientIdentifier()); - } - } - - /** - * AMQP bridge identifier - * - * @return - */ - public String id() { - // just the MQTT client identifier - return this.mqttEndpoint.clientIdentifier(); - } - - public SocketAddress remoteAddress() - { - return remoteAddress; - } -} diff --git a/mqtt-gateway/src/main/java/enmasse/mqtt/Application.java b/mqtt-gateway/src/main/java/enmasse/mqtt/Application.java deleted file mode 100644 index 795fc2ce357..00000000000 --- a/mqtt-gateway/src/main/java/enmasse/mqtt/Application.java +++ /dev/null @@ -1,176 +0,0 @@ -/* - * Copyright 2016-2018, EnMasse authors. - * License: Apache License 2.0 (see the file LICENSE or http://apache.org/licenses/LICENSE-2.0.html). - */ - -package enmasse.mqtt; - -import io.vertx.core.Future; -import io.vertx.core.Handler; -import io.vertx.core.Vertx; -import io.vertx.core.logging.SLF4JLogDelegateFactory; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import java.util.Map; -import java.util.concurrent.CountDownLatch; -import java.util.concurrent.TimeUnit; -import java.util.concurrent.atomic.AtomicBoolean; - -import static io.vertx.core.logging.LoggerFactory.LOGGER_DELEGATE_FACTORY_CLASS_NAME; - -/** - * EnMasse MQTT gateway main application class - */ -public class Application { - - private static final Logger LOG = LoggerFactory.getLogger(Application.class); - - private final Vertx vertx = Vertx.vertx(); - private final MqttGatewayOptions options; - private final MqttGateway mqttGateway; - - private AtomicBoolean running = new AtomicBoolean(); - - public Application(MqttGatewayOptions options, MqttGateway mqttGateway) { - this.options = options; - this.mqttGateway = mqttGateway; - } - - public void registerVerticles() { - - if (this.running.compareAndSet(false, true)) { - - long startupTimeout = this.options.getStartupTimeout().getSeconds(); - try { - - CountDownLatch latch = new CountDownLatch(1); - - Future startFuture = Future.future(); - startFuture.setHandler(done -> { - if (done.succeeded()) { - latch.countDown(); - } else { - LOG.error("Could not start MQTT gateway", done.cause()); - } - }); - - // start deploying more verticle instances - this.deployVerticles(startFuture); - - // wait for deploying end - if (latch.await(startupTimeout, TimeUnit.SECONDS)) { - LOG.info("MQTT gateway startup completed successfully"); - } else { - LOG.error("Startup timed out after {} seconds, shutting down ...", startupTimeout); - this.shutdown(); - } - - } catch (InterruptedException e) { - LOG.error("Startup process has been interrupted, shutting down ..."); - this.shutdown(); - } - } - } - - /** - * Execute verticles deploy operation - * - * @param resultHandler handler called when the deploy ends - */ - private void deployVerticles(Future resultHandler) { - - LOG.debug("Starting up MQTT gateway verticle"); - - Future result = Future.future(); - - this.vertx.deployVerticle(this.mqttGateway, done -> { - if (done.succeeded()) { - LOG.debug("Verticle instance deployed [{}]", done.result()); - result.complete(); - } else { - LOG.debug("Failed to deploy verticle instance {}", done.cause()); - result.fail(done.cause()); - } - }); - - result.setHandler(done -> { - if (done.succeeded()) { - resultHandler.complete(); - } else { - resultHandler.fail(done.cause()); - } - }); - - } - - public void shutdown() { - if (this.running.compareAndSet(true, false)) { - this.shutdown(this.options.getStartupTimeout().getSeconds(), result -> { - // do nothing ? - }); - } - } - - /** - * Execute Vert.x shutdown with related verticles - * @param timeout max timeout to wait for shutdown - * @param shutdownHandler handler called when the shutdown ends - */ - private void shutdown(long timeout, Handler shutdownHandler) { - - try { - - CountDownLatch latch = new CountDownLatch(1); - - if (this.vertx != null) { - - this.vertx.close(done -> { - if (done.failed()) { - LOG.error("Could not shut down MQTT gateway cleanly", done.cause()); - } - latch.countDown(); - }); - - if (latch.await(timeout, TimeUnit.SECONDS)) { - LOG.info("MQTT gateway shut down completed"); - shutdownHandler.handle(Boolean.TRUE); - } else { - LOG.error("Shut down of MQTT gateway timed out, aborting..."); - shutdownHandler.handle(Boolean.FALSE); - } - } - - } catch (InterruptedException e) { - LOG.error("Shut down of MQTT gateway has been interrupted, aborting..."); - shutdownHandler.handle(Boolean.FALSE); - } - } - - public static void main(String[] args) { - - if (System.getProperty(LOGGER_DELEGATE_FACTORY_CLASS_NAME) == null) { - System.setProperty(LOGGER_DELEGATE_FACTORY_CLASS_NAME, SLF4JLogDelegateFactory.class.getName()); - } - - Map env = System.getenv(); - - MqttGatewayOptions options = MqttGatewayOptions.fromEnv(env); - - LOG.info("MqttGateway starting with options: {}", options); - - MqttGateway gateway = new MqttGateway(options); - - Application app = new Application(options, gateway); - app.registerVerticles(); - - Runtime.getRuntime().addShutdownHook(new Thread() { - @Override - public void run() { - super.run(); - LOG.info("MqttGateway shutdown"); - app.shutdown(); - } - }); - } -} diff --git a/mqtt-gateway/src/main/java/enmasse/mqtt/MqttGateway.java b/mqtt-gateway/src/main/java/enmasse/mqtt/MqttGateway.java deleted file mode 100644 index 20b962ee0ad..00000000000 --- a/mqtt-gateway/src/main/java/enmasse/mqtt/MqttGateway.java +++ /dev/null @@ -1,183 +0,0 @@ -/* - * Copyright 2016-2018, EnMasse authors. - * License: Apache License 2.0 (see the file LICENSE or http://apache.org/licenses/LICENSE-2.0.html). - */ - -package enmasse.mqtt; - -import io.vertx.core.AbstractVerticle; -import io.vertx.core.CompositeFuture; -import io.vertx.core.Future; -import io.vertx.core.net.PemKeyCertOptions; -import io.vertx.core.net.SocketAddress; -import io.vertx.mqtt.MqttEndpoint; -import io.vertx.mqtt.MqttServer; -import io.vertx.mqtt.MqttServerOptions; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import java.util.List; -import java.util.Map; -import java.util.concurrent.ConcurrentHashMap; -import java.util.concurrent.ConcurrentMap; -import java.util.concurrent.Semaphore; -import java.util.stream.Collectors; - - -/** - * Vert.x based MQTT gateway for EnMasse - */ -public class MqttGateway extends AbstractVerticle { - - private static final Logger LOG = LoggerFactory.getLogger(MqttGateway.class); - private final MqttGatewayOptions options; - - private MqttServer server; - - private final Map bridges = new ConcurrentHashMap<>(); - private final ConcurrentMap clientIdSemaphores = new ConcurrentHashMap<>(); - - public MqttGateway(MqttGatewayOptions options) { - - this.options = options; - } - - - /** - * Start the MQTT server component - * - * @param startFuture - */ - private void bindMqttServer(Future startFuture) { - - MqttServerOptions options = new MqttServerOptions(); - options.setMaxMessageSize(this.options.getMaxMessageSize()); - options.setHost(this.options.getBindAddress()).setPort(this.options.getListenPort()); - options.setAutoClientId(true); - - if (this.options.isSsl()) { - - PemKeyCertOptions pemKeyCertOptions = new PemKeyCertOptions() - .setKeyPath(this.options.getKeyFile()) - .setCertPath(this.options.getCertFile()); - - options.setKeyCertOptions(pemKeyCertOptions) - .setSsl(this.options.isSsl()); - - LOG.info("SSL/TLS support enabled key {} cert {}", this.options.getKeyFile(), this.options.getCertFile()); - } - - this.server = MqttServer.create(this.vertx, options); - - this.server - .endpointHandler(this::handleMqttEndpointConnection) - .exceptionHandler(t -> {LOG.error("Error handling connection ", t);}) - .listen(done -> { - - if (done.succeeded()) { - - LOG.info("MQTT gateway running on {}:{}", this.options.getBindAddress(), this.server.actualPort()); - LOG.info("AMQP messaging service on {}:{}", this.options.getMessagingServiceHost(), this.options.getMessagingServicePort()); - startFuture.complete(); - } else { - LOG.error("Error while starting up MQTT gateway", done.cause()); - startFuture.fail(done.cause()); - } - - }); - } - - /** - * Handler for a connection request (CONNECT) received by a remote MQTT client - * - * @param mqttEndpoint MQTT local endpoint - */ - private void handleMqttEndpointConnection(MqttEndpoint mqttEndpoint) { - - final String clientIdentifier = mqttEndpoint.clientIdentifier(); - final SocketAddress remoteAddress = mqttEndpoint.remoteAddress(); - LOG.info("CONNECT from MQTT client {} at {}", clientIdentifier, remoteAddress); - - Semaphore clientIdSemaphore = clientIdSemaphores.computeIfAbsent(clientIdentifier, - s -> new Semaphore(1)); - - if (clientIdSemaphore.tryAcquire()) { - AmqpBridge bridge = new AmqpBridge(this.vertx, mqttEndpoint); - - bridge.mqttEndpointCloseHandler(amqpBridge -> { - - try { - this.bridges.remove(amqpBridge.id()); - } finally { - clientIdSemaphores.remove(clientIdentifier, clientIdSemaphore); - } - }).open(this.options.getMessagingServiceHost(), this.options.getMessagingServicePort(), done -> { - if (done.succeeded()) { - AmqpBridge newBridge = done.result(); - this.bridges.put(newBridge.id(), newBridge); - } else { - LOG.info("Error opening the AMQP bridge ...", done.cause()); - clientIdSemaphores.remove(clientIdentifier, clientIdSemaphore); - } - }); - } else { - AmqpBridge existingBridge = bridges.get(clientIdentifier); - if (existingBridge == null) { - // The semaphore is held but no bridge is present; another session must either be in the process of - // forming a bridge or removing one that is closing. - LOG.trace("No existing bridge found for {}", clientIdentifier); - vertx.setTimer(100, unused -> { - handleMqttEndpointConnection(mqttEndpoint); - }); - } else { - // If the ClientId represents a Client already connected to the Server then the Server MUST - // disconnect the existing Client [MQTT-3.1.4-2]. - LOG.info("MQTT client {} already in-use by {}", clientIdentifier, existingBridge.remoteAddress()); - existingBridge.close().compose(unused -> { - LOG.trace("MQTT Closing of existing client {} from {} completed (initiated by {})", - clientIdentifier, existingBridge.remoteAddress(), remoteAddress); - handleMqttEndpointConnection(mqttEndpoint); - return Future.succeededFuture(); - }); - } - } - } - - @Override - public void start(Future startFuture) throws Exception { - - LOG.info("Starting MQTT gateway verticle..."); - this.bindMqttServer(startFuture); - } - - @Override - public void stop(Future stopFuture) throws Exception { - - LOG.info("Stopping MQTT gateway verticle ..."); - - Future shutdownTracker = Future.future(); - shutdownTracker.setHandler(done -> { - if (done.succeeded()) { - LOG.info("MQTT gateway has been shut down successfully"); - stopFuture.complete(); - } else { - LOG.info("Error while shutting down MQTT gateway", done.cause()); - stopFuture.fail(done.cause()); - } - }); - - if (this.server != null) { - @SuppressWarnings("rawtypes") - List closeFutures = this.bridges.entrySet() - .stream() - .map(entry -> entry.getValue().close()) - .collect(Collectors.toList()); - - CompositeFuture.all(closeFutures).setHandler(done -> { - this.server.close(shutdownTracker); - }); - } else { - shutdownTracker.complete(); - } - } -} diff --git a/mqtt-gateway/src/main/java/enmasse/mqtt/MqttGatewayOptions.java b/mqtt-gateway/src/main/java/enmasse/mqtt/MqttGatewayOptions.java deleted file mode 100644 index c282e4eb00f..00000000000 --- a/mqtt-gateway/src/main/java/enmasse/mqtt/MqttGatewayOptions.java +++ /dev/null @@ -1,160 +0,0 @@ -/* - * Copyright 2016-2018, EnMasse authors. - * License: Apache License 2.0 (see the file LICENSE or http://apache.org/licenses/LICENSE-2.0.html). - */ - -package enmasse.mqtt; - -import java.time.Duration; -import java.util.Map; -import java.util.Optional; - -public class MqttGatewayOptions { - - // binding info for listening - private String bindAddress; - private int listenPort; - // mqtt server options - private int maxMessageSize; - // connection info to the messaging service - private String messagingServiceHost; - private int messagingServicePort; - - // SSL/TLS support stuff - private boolean ssl; - private String certFile; - private String keyFile; - - private Duration startupTimeout; - - - public String getBindAddress() { - return bindAddress; - } - - public void setBindAddress(String bindAddress) { - this.bindAddress = bindAddress; - } - - public int getListenPort() { - return listenPort; - } - - public void setListenPort(int listenPort) { - this.listenPort = listenPort; - } - - public int getMaxMessageSize() { - return maxMessageSize; - } - - public void setMaxMessageSize(int maxMessageSize) { - this.maxMessageSize = maxMessageSize; - } - - public String getMessagingServiceHost() { - return messagingServiceHost; - } - - public void setMessagingServiceHost(String messagingServiceHost) { - this.messagingServiceHost = messagingServiceHost; - } - - public int getMessagingServicePort() { - return messagingServicePort; - } - - public void setMessagingServicePort(int messagingServicePort) { - this.messagingServicePort = messagingServicePort; - } - - public boolean isSsl() { - return ssl; - } - - public void setSsl(boolean ssl) { - this.ssl = ssl; - } - - public String getCertFile() { - return certFile; - } - - public void setCertFile(String certFile) { - this.certFile = certFile; - } - - public String getKeyFile() { - return keyFile; - } - - public void setKeyFile(String keyFile) { - this.keyFile = keyFile; - } - - public Duration getStartupTimeout() { - return startupTimeout; - } - - public void setStartupTimeout(Duration startupTimeout) { - this.startupTimeout = startupTimeout; - } - - private static Optional getEnv(Map env, String envVar) { - return Optional.ofNullable(env.get(envVar)); - } - - public static MqttGatewayOptions fromEnv(Map env) { - - MqttGatewayOptions options = new MqttGatewayOptions(); - - options.setMessagingServiceHost(getEnv(env, "MESSAGING_SERVICE_HOST") - .orElse("0.0.0.0")); - - options.setMessagingServicePort(getEnv(env, "MESSAGING_SERVICE_PORT") - .map(Integer::parseInt) - .orElse(5672)); - - options.setStartupTimeout(getEnv(env, "ENMASSE_MQTT_STARTUPTIMEOUT") - .map(i -> Duration.ofSeconds(Long.parseLong(i))) - .orElse(Duration.ofSeconds(20))); - - options.setBindAddress(getEnv(env, "ENMASSE_MQTT_BINDADDRESS") - .orElse("0.0.0.0")); - - options.setListenPort(getEnv(env, "ENMASSE_MQTT_LISTENPORT") - .map(Integer::parseInt) - .orElse(1883)); - - options.setSsl(getEnv(env, "ENMASSE_MQTT_SSL") - .map(Boolean::parseBoolean) - .orElse(false)); - - options.setMaxMessageSize(getEnv(env, "ENMASSE_MQTT_MAXMESSAGESIZE") - .map(Integer::parseInt) - .orElse(131072)); - - options.setCertFile(getEnv(env, "ENMASSE_MQTT_CERTFILE") - .orElse("./src/test/resources/tls/server-cert.pem")); - - options.setKeyFile(getEnv(env, "ENMASSE_MQTT_KEYFILE") - .orElse("./src/test/resources/tls/server-key.pem")); - - return options; - } - - @Override - public String toString() { - return "MqttGatewayOptions{" + - "bindAddress='" + bindAddress + '\'' + - ", listenPort=" + listenPort + - ", maxMessageSize=" + maxMessageSize + - ", messagingServiceHost='" + messagingServiceHost + '\'' + - ", messagingServicePort=" + messagingServicePort + - ", ssl=" + ssl + - ", certFile='" + certFile + '\'' + - ", keyFile='" + keyFile + '\'' + - ", startupTimeout=" + startupTimeout + - '}'; - } -} diff --git a/mqtt-gateway/src/main/java/enmasse/mqtt/TopicMatcher.java b/mqtt-gateway/src/main/java/enmasse/mqtt/TopicMatcher.java deleted file mode 100644 index efaa12a982c..00000000000 --- a/mqtt-gateway/src/main/java/enmasse/mqtt/TopicMatcher.java +++ /dev/null @@ -1,58 +0,0 @@ -/* - * Copyright 2016-2018, EnMasse authors. - * License: Apache License 2.0 (see the file LICENSE or http://apache.org/licenses/LICENSE-2.0.html). - */ - -package enmasse.mqtt; - -import java.util.List; -import java.util.regex.Pattern; - -/** - * Provides matching feature between "wildcarded" topics (for subscription) - * with a fixed topic (for publishing) - */ -public class TopicMatcher { - - private static final String PLUS_WILDCARD = "\\+"; - private static final String SHARP_WILDCARD = "#"; - - private static final String PLUS_WILDCARD_REPLACE = "[^/]\\+"; - private static final String SHARP_WILDCARD_REPLACE = ".*"; - - /** - * Verify if the topic matches the "wildcarded" topic provided - * - * @param wildcardedTopic topic with wildcards (for subscription) - * @param topic fixed topic (for publishing) - * @return if there is a match - */ - public static boolean isMatch(String wildcardedTopic, String topic) { - - String topicReplaced = - wildcardedTopic.replaceAll(PLUS_WILDCARD, PLUS_WILDCARD_REPLACE).replaceAll(SHARP_WILDCARD, SHARP_WILDCARD_REPLACE); - - Pattern pattern = Pattern.compile(topicReplaced); - - return pattern.matcher(topic).matches(); - } - - /** - * Verify if the topic matches one of the "wildcarded" topics provided - * - * @param wildcardedTopics topics with wildcards (for subscription) - * @param topic fixed topic (for publishing) - * @return the "wildcarded" topic which matches - */ - public static String match(List wildcardedTopics, String topic) { - - for (String wildcardedTopic: wildcardedTopics) { - - if (isMatch(wildcardedTopic, topic)) { - return wildcardedTopic; - } - } - - return null; - } -} diff --git a/mqtt-gateway/src/main/java/enmasse/mqtt/endpoints/AmqpLwtServiceEndpoint.java b/mqtt-gateway/src/main/java/enmasse/mqtt/endpoints/AmqpLwtServiceEndpoint.java deleted file mode 100644 index 8c4ea08d082..00000000000 --- a/mqtt-gateway/src/main/java/enmasse/mqtt/endpoints/AmqpLwtServiceEndpoint.java +++ /dev/null @@ -1,91 +0,0 @@ -/* - * Copyright 2016-2018, EnMasse authors. - * License: Apache License 2.0 (see the file LICENSE or http://apache.org/licenses/LICENSE-2.0.html). - */ - -package enmasse.mqtt.endpoints; - -import enmasse.mqtt.messages.AmqpWillMessage; -import io.vertx.core.AsyncResult; -import io.vertx.core.Future; -import io.vertx.core.Handler; -import io.vertx.proton.ProtonDelivery; -import io.vertx.proton.ProtonQoS; -import io.vertx.proton.ProtonSender; -import org.apache.qpid.proton.amqp.messaging.Accepted; -import org.apache.qpid.proton.amqp.transport.ErrorCondition; -import org.apache.qpid.proton.amqp.transport.LinkError; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -/** - * Last Will and Testament Service (WS) endpoint class - */ -public class AmqpLwtServiceEndpoint { - - private static final Logger LOG = LoggerFactory.getLogger(AmqpLwtServiceEndpoint.class); - - public static final String LWT_SERVICE_ENDPOINT = "$lwt"; - - private ProtonSender sender; - - /** - * Constructor - * - * @param sender ProtonSender instance related to control address - */ - public AmqpLwtServiceEndpoint(ProtonSender sender) { - this.sender = sender; - } - - /** - * Open the endpoint, attaching the link - */ - public void open() { - - // attach sender link to $lwt - this.sender - .setQoS(ProtonQoS.AT_LEAST_ONCE) - .open(); - } - - /** - * Send the AMQP_WILL message to the Last Will and Testament Service - * - * @param amqpWillMessage AMQP_WILL message - * @param handler callback called on message delivered - */ - public void sendWill(AmqpWillMessage amqpWillMessage, Handler> handler) { - - // send AMQP_WILL message with will information - this.sender.send(amqpWillMessage.toAmqp(), delivery -> { - - if (delivery.getRemoteState() == Accepted.getInstance()) { - LOG.info("AMQP will delivery {}", delivery.getRemoteState()); - handler.handle(Future.succeededFuture(delivery)); - } else { - handler.handle(Future.failedFuture(String.format("AMQP will delivery %s", delivery.getRemoteState()))); - } - }); - } - - /** - * Close the endpoint, detaching the link - * - * @param isDetachForced if link should be detached with error - */ - public void close(boolean isDetachForced) { - - if (this.sender.isOpen()) { - - if (isDetachForced) { - ErrorCondition errorCondition = - new ErrorCondition(LinkError.DETACH_FORCED, "Link detached due to a brute MQTT client disconnection"); - this.sender.setCondition(errorCondition); - } - - // detach link - this.sender.close(); - } - } -} diff --git a/mqtt-gateway/src/main/java/enmasse/mqtt/endpoints/AmqpPublishData.java b/mqtt-gateway/src/main/java/enmasse/mqtt/endpoints/AmqpPublishData.java deleted file mode 100644 index 6b7462959af..00000000000 --- a/mqtt-gateway/src/main/java/enmasse/mqtt/endpoints/AmqpPublishData.java +++ /dev/null @@ -1,54 +0,0 @@ -/* - * Copyright 2016-2018, EnMasse authors. - * License: Apache License 2.0 (see the file LICENSE or http://apache.org/licenses/LICENSE-2.0.html). - */ - -package enmasse.mqtt.endpoints; - -import enmasse.mqtt.messages.AmqpPublishMessage; - -/** - * Class for bringing AMQP_PUBLISH to an MQTT client and the - * related message identifier assigned on publishing - */ -public class AmqpPublishData { - - private AmqpPublishMessage amqpPublishMessage; - private int messageId; - - /** - * AMQP_PUBLISH message to send - * @return - */ - public AmqpPublishMessage amqpPublishMessage() { - return this.amqpPublishMessage; - } - - /** - * Set the AMQP_PUBLISH message to send - * @param amqpPublishMessage - * @return current instance of the AmqpPublishData - */ - public AmqpPublishData setAmqpPublishMessage(AmqpPublishMessage amqpPublishMessage) { - this.amqpPublishMessage = amqpPublishMessage; - return this; - } - - /** - * Message identifier assigned to AMQP_PUBLISH message sent - * @return - */ - public int messageId() { - return this.messageId; - } - - /** - * Set the message identifier assigned to AMQP_PUBLISH message sent - * @param messageId message identifier - * @return current instance of the AmqpPublishData - */ - public AmqpPublishData setMessageId(int messageId) { - this.messageId = messageId; - return this; - } -} diff --git a/mqtt-gateway/src/main/java/enmasse/mqtt/endpoints/AmqpPublishEndpoint.java b/mqtt-gateway/src/main/java/enmasse/mqtt/endpoints/AmqpPublishEndpoint.java deleted file mode 100644 index 30d45fd9a63..00000000000 --- a/mqtt-gateway/src/main/java/enmasse/mqtt/endpoints/AmqpPublishEndpoint.java +++ /dev/null @@ -1,224 +0,0 @@ -/* - * Copyright 2016-2018, EnMasse authors. - * License: Apache License 2.0 (see the file LICENSE or http://apache.org/licenses/LICENSE-2.0.html). - */ - -package enmasse.mqtt.endpoints; - -import enmasse.mqtt.messages.AmqpPublishMessage; -import enmasse.mqtt.messages.AmqpPubrelMessage; -import io.netty.handler.codec.mqtt.MqttQoS; -import io.vertx.core.AsyncResult; -import io.vertx.core.Future; -import io.vertx.core.Handler; -import io.vertx.proton.ProtonDelivery; -import io.vertx.proton.ProtonQoS; -import io.vertx.proton.ProtonSender; -import org.apache.qpid.proton.amqp.messaging.Accepted; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import java.util.HashMap; -import java.util.Map; - -/** - * Publisher endpoint - */ -public class AmqpPublishEndpoint { - - private static final Logger LOG = LoggerFactory.getLogger(AmqpPublishEndpoint.class); - - public static final String AMQP_CLIENT_PUBREL_ENDPOINT_TEMPLATE = "$mqtt.%s.pubrel"; - - // all delivery for published messages if they need settlement (messageId -> delivery) - private Map deliveries; - // links for publishing message on topic (topic -> link/senders couple) - private Map publishers; - // sender for PUBREL messages - private ProtonSender senderPubrel; - - /** - * Constructor - * - * @param senderPubrel ProtonSender instance related to client PUBREL address - */ - public AmqpPublishEndpoint(ProtonSender senderPubrel) { - this.senderPubrel = senderPubrel; - } - - /** - * Open the endpoint - */ - public void open() { - - this.deliveries = new HashMap<>(); - this.publishers = new HashMap<>(); - } - - /** - * Check if a publisher already exists for the specified topic - * - * @param topic topic to check the publisher - * @return if publisher already exists - */ - public boolean isPublisher(String topic) { - - return this.publishers.containsKey(topic); - } - - /** - * Add a publisher to the endpoint - * - * @param topic topic for which adding the publisher - * @param amqpPublisher publisher to add - */ - public void addPublisher(String topic, AmqpPublisher amqpPublisher) { - - if (this.publishers.containsKey(topic)) { - throw new IllegalStateException(String.format("AMQP publisher for %s already exists !", topic)); - } - this.publishers.put(topic, amqpPublisher); - } - - /** - * Send the AMQP_PUBLISH to the attached topic/address - * @param amqpPublishMessage AMQP_PUBLISH message - * @param mqttPacketId - */ - - public void publish(AmqpPublishMessage amqpPublishMessage, - final int mqttPacketId, Handler> handler) { - - // send AMQP_PUBLISH message - - AmqpPublisher publisher = this.publishers.get(amqpPublishMessage.topic()); - - // use sender for QoS 0/1 messages - if (amqpPublishMessage.qos() != MqttQoS.EXACTLY_ONCE) { - - // attach sender link on "topic" (if doesn't exist yet) - if (!publisher.senderQoS01().isOpen()) { - - publisher.senderQoS01() - .setQoS(ProtonQoS.AT_LEAST_ONCE) - .open(); - - // TODO: think about starting a timer for inactivity on this link for detaching ? - } - - if (amqpPublishMessage.qos() == MqttQoS.AT_MOST_ONCE) { - - publisher.senderQoS01().send(amqpPublishMessage.toAmqp()); - handler.handle(Future.succeededFuture(null)); - - } else { - - publisher.senderQoS01().send(amqpPublishMessage.toAmqp(), delivery -> { - - if (delivery.getRemoteState() == Accepted.getInstance()) { - LOG.info("AMQP publish delivery {}", delivery.getRemoteState()); - handler.handle(Future.succeededFuture(delivery)); - } else { - handler.handle(Future.failedFuture(String.format("AMQP publish delivery %s", delivery.getRemoteState()))); - } - }); - - } - - // use sender for QoS 2 messages - } else { - - // attach sender link on "topic" (if doesn't exist yet) - if (!publisher.senderQoS2().isOpen()) { - - publisher.senderQoS2() - // TODO: Vert.x Proton doesn't support EXACTLY_ONCE - .open(); - - // TODO: think about starting a timer for inactivity on this link for detaching ? - } - - publisher.senderQoS2().send(amqpPublishMessage.toAmqp(), delivery -> { - - if (delivery.getRemoteState() == Accepted.getInstance()) { - LOG.info("AMQP publish delivery {}", delivery.getRemoteState()); - - // received disposition not settled, store for future settlement - if (!delivery.remotelySettled()) { - this.deliveries.put(mqttPacketId, delivery); - } - - handler.handle(Future.succeededFuture(delivery)); - } else { - handler.handle(Future.failedFuture(String.format("AMQP publish delivery %s", delivery.getRemoteState()))); - } - }); - } - - } - - /** - * Send the AMQP_PUBREL to the related client pubrel address - * - * @param amqpPubrelMessage AMQP_PUBREL message - */ - public void publish(AmqpPubrelMessage amqpPubrelMessage, Handler> handler) { - - // send AMQP_PUBREL message - - if (!this.senderPubrel.isOpen()) { - - this.senderPubrel - .setQoS(ProtonQoS.AT_LEAST_ONCE) - .open(); - - // TODO: think about starting a timer for inactivity on this link for detaching ? - } - - this.senderPubrel.send(amqpPubrelMessage.toAmqp(), delivery -> { - - if (delivery.getRemoteState() == Accepted.getInstance()) { - LOG.info("AMQP pubrel delivery {}", delivery.getRemoteState()); - handler.handle(Future.succeededFuture(delivery)); - } else { - handler.handle(Future.failedFuture(String.format("AMQP pubrel delivery %s", delivery.getRemoteState()))); - } - - }); - } - - /** - * Close the endpoint, detaching the links - */ - public void close() { - - // detach links - for (Map.Entry entry: this.publishers.entrySet()) { - - AmqpPublisher publisher = entry.getValue(); - if (publisher.isOpen()) { - publisher.close(); - } - } - - if (this.senderPubrel.isOpen()) { - this.senderPubrel.close(); - } - - this.publishers.clear(); - this.deliveries.clear(); - } - - /** - * Settle the delivery for a received message - * - * @param messageId message identifier to settle - */ - public void settle(Object messageId) { - - if (this.deliveries.containsKey(messageId)) { - ProtonDelivery delivery = this.deliveries.remove(messageId); - delivery.disposition(Accepted.getInstance(), true); - } - } -} diff --git a/mqtt-gateway/src/main/java/enmasse/mqtt/endpoints/AmqpPublisher.java b/mqtt-gateway/src/main/java/enmasse/mqtt/endpoints/AmqpPublisher.java deleted file mode 100644 index bd21a86926a..00000000000 --- a/mqtt-gateway/src/main/java/enmasse/mqtt/endpoints/AmqpPublisher.java +++ /dev/null @@ -1,67 +0,0 @@ -/* - * Copyright 2016-2018, EnMasse authors. - * License: Apache License 2.0 (see the file LICENSE or http://apache.org/licenses/LICENSE-2.0.html). - */ - -package enmasse.mqtt.endpoints; - -import io.vertx.proton.ProtonSender; - -/** - * AMQP publisher with links couple for publishing with QoS 0/1 and QoS 2 - */ -public class AmqpPublisher { - - private final ProtonSender senderQoS01; - private final ProtonSender senderQoS2; - - /** - * Constructor - * - * @param senderQoS01 ProtonSender instance related to the publishing address for QoS 0 and 1 - * @param senderQoS2 ProtonSender instance related to the publishing address for QoS 2 - */ - public AmqpPublisher(ProtonSender senderQoS01, ProtonSender senderQoS2) { - this.senderQoS01 = senderQoS01; - this.senderQoS2 = senderQoS2; - } - - /** - * Close and detach the links - */ - public void close() { - - if (this.senderQoS01.isOpen()) { - this.senderQoS01.close(); - } - - if (this.senderQoS2.isOpen()) { - this.senderQoS2.close(); - } - } - - /** - * If the publisher is opened - * @return - */ - public boolean isOpen() { - - return (this.senderQoS01.isOpen() || this.senderQoS2.isOpen()); - } - - /** - * ProtonSender instance related to the publishing address for QoS 0 and 1 - * @return - */ - public ProtonSender senderQoS01() { - return this.senderQoS01; - } - - /** - * ProtonSender instance related to the publishing address for QoS 2 - * @return - */ - public ProtonSender senderQoS2() { - return this.senderQoS2; - } -} diff --git a/mqtt-gateway/src/main/java/enmasse/mqtt/endpoints/AmqpReceiver.java b/mqtt-gateway/src/main/java/enmasse/mqtt/endpoints/AmqpReceiver.java deleted file mode 100644 index d4946d764ad..00000000000 --- a/mqtt-gateway/src/main/java/enmasse/mqtt/endpoints/AmqpReceiver.java +++ /dev/null @@ -1,67 +0,0 @@ -/* - * Copyright 2016-2018, EnMasse authors. - * License: Apache License 2.0 (see the file LICENSE or http://apache.org/licenses/LICENSE-2.0.html). - */ - -package enmasse.mqtt.endpoints; - -import io.vertx.proton.ProtonReceiver; - -/** - * AMQP receiver with links couple for receiving on control and publish addresses - */ -public class AmqpReceiver { - - private final ProtonReceiver receiverControl; - private final ProtonReceiver receiverPublish; - - /** - * Constructor - * - * @param receiverControl ProtonReceiver instance related to the control address - * @param receiverPublish ProtonReceiver instance related to the publish address - */ - public AmqpReceiver(ProtonReceiver receiverControl, ProtonReceiver receiverPublish) { - this.receiverControl = receiverControl; - this.receiverPublish = receiverPublish; - } - - /** - * Close and detach the links - */ - public void close() { - - if (this.receiverControl.isOpen()) { - this.receiverControl.close(); - } - - if (this.receiverPublish.isOpen()) { - this.receiverPublish.close(); - } - } - - /** - * If the receiver is opened - * @return - */ - public boolean isOpen() { - - return (this.receiverControl.isOpen() || this.receiverPublish.isOpen()); - } - - /** - * ProtonReceiver instance related to the control address - * @return - */ - public ProtonReceiver receiverControl() { - return this.receiverControl; - } - - /** - * ProtonReceiver instance related to the publish address - * @return - */ - public ProtonReceiver receiverPublish() { - return this.receiverPublish; - } -} diff --git a/mqtt-gateway/src/main/java/enmasse/mqtt/endpoints/AmqpReceiverEndpoint.java b/mqtt-gateway/src/main/java/enmasse/mqtt/endpoints/AmqpReceiverEndpoint.java deleted file mode 100644 index 3924edbea46..00000000000 --- a/mqtt-gateway/src/main/java/enmasse/mqtt/endpoints/AmqpReceiverEndpoint.java +++ /dev/null @@ -1,230 +0,0 @@ -/* - * Copyright 2016-2018, EnMasse authors. - * License: Apache License 2.0 (see the file LICENSE or http://apache.org/licenses/LICENSE-2.0.html). - */ - -package enmasse.mqtt.endpoints; - -import enmasse.mqtt.messages.AmqpPublishMessage; -import enmasse.mqtt.messages.AmqpPubrelMessage; -import enmasse.mqtt.messages.AmqpSubscriptionsMessage; -import io.vertx.core.Handler; -import io.vertx.proton.ProtonDelivery; -import io.vertx.proton.ProtonQoS; -import org.apache.qpid.proton.amqp.messaging.Accepted; -import org.apache.qpid.proton.message.Message; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import java.util.HashMap; -import java.util.Map; - -/** - * Receiver endpoint - */ -public class AmqpReceiverEndpoint { - - private static final Logger LOG = LoggerFactory.getLogger(AmqpReceiverEndpoint.class); - - public static final String CLIENT_CONTROL_ENDPOINT_TEMPLATE = "$mqtt.to.%s.control"; - public static final String CLIENT_PUBLISH_ENDPOINT_TEMPLATE = "$mqtt.to.%s.publish"; - - private AmqpReceiver receiver; - - // handler called when AMQP_SUBSCRIPTIONS is received - private Handler subscriptionsHandler; - // handler called when AMQP_PUBLISH is received - private Handler publishHandler; - // handler called when AMQP_PUBREL is received - private Handler pubrelHandler; - // all delivery for received messages if they need settlement (messageId -> delivery) - private Map deliveries; - - /** - * Constructor - * - * @param receiver receiver instance related to unique client addresses - */ - public AmqpReceiverEndpoint(AmqpReceiver receiver) { - this.receiver = receiver; - } - - /** - * Set the session handler called when AMQP_SUBSCRIPTIONS is received - * - * @param handler the handler - * @return the current AmqpReceiverEndpoint instance - */ - public AmqpReceiverEndpoint subscriptionsHandler(Handler handler) { - - this.subscriptionsHandler = handler; - return this; - } - - /** - * Set the session handler called when AMQP_PUBLISH is received - * - * @param handler the handler - * @return the current AmqpReceiverEndpoint instance - */ - public AmqpReceiverEndpoint publishHandler(Handler handler) { - - this.publishHandler = handler; - return this; - } - - /** - * Set the session handler called when AMQP_PUBREL is received - * - * @param handler the handler - * @return the current AmqpReceiverEndpoint instance - */ - public AmqpReceiverEndpoint pubrelHandler(Handler handler) { - - this.pubrelHandler = handler; - return this; - } - - /** - * Handler for the receiver for handling incoming raw AMQP message - * from the Subscription Service - * - * @param delivery AMQP delivery information - * @param message raw AMQP message - */ - private void messageHandler(ProtonDelivery delivery, Message message) { - - LOG.info("Received {}", message); - - // messages without subject are just AMQP_PUBLISH messages - if (message.getSubject() == null) { - - AmqpPublishData amqpPublishData = new AmqpPublishData(); - amqpPublishData.setAmqpPublishMessage(AmqpPublishMessage.from(message)); - - this.handlePublish(amqpPublishData); - // settlement depends on the QoS levels that could be different from the current one in the - // publish message. The AMQP bridge checks the granted QoS as well (MQTT 3.1.1) - if (!delivery.remotelySettled()) { - this.deliveries.put(amqpPublishData.messageId(), delivery); - } - - } else { - - switch (message.getSubject()) { - - case AmqpSubscriptionsMessage.AMQP_SUBJECT: - - this.handleSession(AmqpSubscriptionsMessage.from(message)); - delivery.disposition(Accepted.getInstance(), true); - - break; - - case AmqpPubrelMessage.AMQP_SUBJECT: - - if (!delivery.remotelySettled()) { - this.deliveries.put(message.getMessageId(), delivery); - } - this.handlePubrel(AmqpPubrelMessage.from(message)); - - break; - } - - } - - } - - /** - * Open the control endpoint, attaching the link - */ - public void openControl() { - - this.deliveries = new HashMap<>(); - - // attach receiver link on the $mqtt.to..control address for receiving messages (from SS) - // define handler for received messages - // - AMQP_SUBSCRIPTIONS after sent AMQP_LIST -> for writing CONNACK (session-present) - this.receiver.receiverControl() - .setQoS(ProtonQoS.AT_LEAST_ONCE) - .handler(this::messageHandler) - .open(); - } - - /** - * Open the publish endpoint, attaching the link - */ - public void openPublish() { - - // attach receiver link on the $mqtt.to..publish address for receiving published messages - // define handler for received messages - // - AMQP_PUBLISH for every AMQP published message - // - AMQP_PUBREL for handling QoS 2 - this.receiver.receiverPublish() - .setQoS(ProtonQoS.AT_LEAST_ONCE) - .handler(this::messageHandler) - .open(); - } - - /** - * Close the endpoint, detaching the link - */ - public void close() { - - if (this.receiver.isOpen()) { - // detach links - this.receiver.close(); - } - - this.deliveries.clear(); - } - - /** - * Settle the delivery for a received message - * - * @param messageId message identifier to settle - */ - public void settle(Object messageId) { - - if (this.deliveries.containsKey(messageId)) { - ProtonDelivery delivery = this.deliveries.remove(messageId); - delivery.disposition(Accepted.getInstance(), true); - LOG.info("AMQP message [{}] settled", messageId); - } - } - - /** - * Used for calling the session handler when AMQP_SUBSCRIPTIONS is received - * - * @param amqpSubscriptionsMessage AMQP_SUBSCRIPTIONS message - */ - private void handleSession(AmqpSubscriptionsMessage amqpSubscriptionsMessage) { - - if (this.subscriptionsHandler != null) { - this.subscriptionsHandler.handle(amqpSubscriptionsMessage); - } - } - - /** - * Used for calling the session handler when AMQP_PUBLISH is received - * - * @param amqpPublishData object with the AMQP_PUBLISH message - */ - private void handlePublish(AmqpPublishData amqpPublishData) { - - if (this.publishHandler != null) { - this.publishHandler.handle(amqpPublishData); - } - } - - /** - * Used for calling the pubrel handler when AMQP_PUBREL is received - * - * @param amqpPubrelMessage AMQP_PUBREL message - */ - private void handlePubrel(AmqpPubrelMessage amqpPubrelMessage) { - - if (this.pubrelHandler != null) { - this.pubrelHandler.handle(amqpPubrelMessage); - } - } -} diff --git a/mqtt-gateway/src/main/java/enmasse/mqtt/endpoints/AmqpSubscriptionServiceEndpoint.java b/mqtt-gateway/src/main/java/enmasse/mqtt/endpoints/AmqpSubscriptionServiceEndpoint.java deleted file mode 100644 index 3b32673bf3c..00000000000 --- a/mqtt-gateway/src/main/java/enmasse/mqtt/endpoints/AmqpSubscriptionServiceEndpoint.java +++ /dev/null @@ -1,145 +0,0 @@ -/* - * Copyright 2016-2018, EnMasse authors. - * License: Apache License 2.0 (see the file LICENSE or http://apache.org/licenses/LICENSE-2.0.html). - */ - -package enmasse.mqtt.endpoints; - -import enmasse.mqtt.messages.AmqpCloseMessage; -import enmasse.mqtt.messages.AmqpListMessage; -import enmasse.mqtt.messages.AmqpSubscribeMessage; -import enmasse.mqtt.messages.AmqpUnsubscribeMessage; -import io.vertx.core.AsyncResult; -import io.vertx.core.Future; -import io.vertx.core.Handler; -import io.vertx.proton.ProtonDelivery; -import io.vertx.proton.ProtonQoS; -import io.vertx.proton.ProtonSender; -import org.apache.qpid.proton.amqp.messaging.Accepted; -import org.apache.qpid.proton.amqp.messaging.Rejected; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -/** - * Subscription Service (SS) endpoint class - */ -public class AmqpSubscriptionServiceEndpoint { - - private static final Logger LOG = LoggerFactory.getLogger(AmqpSubscriptionServiceEndpoint.class); - - public static final String SUBSCRIPTION_SERVICE_ENDPOINT = "$subctrl"; - - private ProtonSender sender; - - /** - * Constructor - * - * @param sender ProtonSender instance related to control address - */ - public AmqpSubscriptionServiceEndpoint(ProtonSender sender) { - this.sender = sender; - } - - /** - * Send the AMQP_LIST message to the Subscription Service - * - * @param amqpSessionMessage AMQP_LIST message - * @param handler callback called on message delivered - */ - public void sendList(AmqpListMessage amqpSessionMessage, Handler> handler) { - - // send AMQP_LIST message - this.sender.send(amqpSessionMessage.toAmqp(), delivery -> { - - if (delivery.getRemoteState() == Accepted.getInstance()) { - LOG.info("AMQP list delivery {}", delivery.getRemoteState()); - handler.handle(Future.succeededFuture(delivery)); - } else { - handler.handle(Future.failedFuture(String.format("AMQP list delivery %s", delivery.getRemoteState()))); - } - }); - } - - /** - * Send the AMQP_CLOSE message to the Subscription Service - * - * @param amqpCloseMessage AMQP_CLOSE message - * @param handler callback called on message delivered - */ - public void sendClose(AmqpCloseMessage amqpCloseMessage, Handler> handler) { - - // send AMQP_CLOSE message - this.sender.send(amqpCloseMessage.toAmqp(), delivery -> { - - if (delivery.getRemoteState() == Accepted.getInstance()) { - LOG.info("AMQP close delivery {}", delivery.getRemoteState()); - handler.handle(Future.succeededFuture(delivery)); - } else { - handler.handle(Future.failedFuture(String.format("AMQP close delivery %s", delivery.getRemoteState()))); - } - }); - } - - /** - * Send the AMQP_SUBSCRIBE message to the Subscription Service - * - * @param amqpSubscribeMessage AMQP_SUBSCRIBE message - * @param handler callback called on message delivered - */ - public void sendSubscribe(AmqpSubscribeMessage amqpSubscribeMessage, Handler> handler) { - - // send AMQP_SUBSCRIBE message - this.sender.send(amqpSubscribeMessage.toAmqp(), delivery -> { - - if ((delivery.getRemoteState() == Accepted.getInstance()) || - (delivery.getRemoteState() instanceof Rejected)) { - LOG.info("AMQP subscribe delivery {}", delivery.getRemoteState()); - handler.handle(Future.succeededFuture(delivery)); - } else { - handler.handle(Future.failedFuture(String.format("AMQP subscribe delivery %s", delivery.getRemoteState()))); - } - }); - } - - /** - * Send the AMQP_UNSUBSCRIBE message to the Subscription Service - * - * @param amqpUnsubscribeMessage AMQP_UNSUBSCRIBE message - * @param handler callback called on message delivered - */ - public void sendUnsubscribe(AmqpUnsubscribeMessage amqpUnsubscribeMessage, Handler> handler) { - - // send AMQP_UNSUBSCRIBE message - this.sender.send(amqpUnsubscribeMessage.toAmqp(), delivery -> { - - if (delivery.getRemoteState() == Accepted.getInstance()) { - LOG.info("AMQP unsubscribe delivery {}", delivery.getRemoteState()); - handler.handle(Future.succeededFuture(delivery)); - } else { - handler.handle(Future.failedFuture(String.format("AMQP unsubscribe delivery %s", delivery.getRemoteState()))); - } - }); - } - - /** - * Open the endpoint, attaching the link - */ - public void open() { - - // attach sender link to $subctrl - this.sender - .setQoS(ProtonQoS.AT_LEAST_ONCE) - .open(); - } - - /** - * Close the endpoint, detaching the link - */ - public void close() { - - if (this.sender.isOpen()) { - // detach link - this.sender.close(); - } - } -} diff --git a/mqtt-gateway/src/main/java/enmasse/mqtt/messages/AmqpCloseMessage.java b/mqtt-gateway/src/main/java/enmasse/mqtt/messages/AmqpCloseMessage.java deleted file mode 100644 index dfecda1171b..00000000000 --- a/mqtt-gateway/src/main/java/enmasse/mqtt/messages/AmqpCloseMessage.java +++ /dev/null @@ -1,76 +0,0 @@ -/* - * Copyright 2016-2018, EnMasse authors. - * License: Apache License 2.0 (see the file LICENSE or http://apache.org/licenses/LICENSE-2.0.html). - */ - -package enmasse.mqtt.messages; - -import io.vertx.proton.ProtonHelper; -import org.apache.qpid.proton.message.Message; - -/** - * Represents an AMQP_CLOSE message - */ -public class AmqpCloseMessage { - - public static final String AMQP_SUBJECT = "close"; - - private final String clientId; - - /** - * Constructor - * - * @param clientId client identifier - */ - public AmqpCloseMessage(String clientId) { - - this.clientId = clientId; - } - - /** - * Return an AMQP_CLOSE message from the raw AMQP one - * - * @param message raw AMQP message - * @return AMQP_CLOSE message - */ - public static AmqpCloseMessage from(Message message) { - - if (!message.getSubject().equals(AMQP_SUBJECT)) { - throw new IllegalArgumentException(String.format("AMQP message subject is no %s", AMQP_SUBJECT)); - } - - return new AmqpCloseMessage(AmqpHelper.getClientIdFromPublishAddress((String) message.getCorrelationId())); - } - - /** - * Return a raw AMQP message - * - * @return - */ - public Message toAmqp() { - - Message message = ProtonHelper.message(); - - message.setSubject(AMQP_SUBJECT); - - message.setCorrelationId(String.format(AmqpHelper.AMQP_CLIENT_PUBLISH_ADDRESS_TEMPLATE, this.clientId)); - - return message; - } - - /** - * Client identifier - * @return - */ - public String clientId() { - return this.clientId; - } - - @Override - public String toString() { - - return "AmqpCloseMessage{" + - "clientId=" + this.clientId + - "}"; - } -} diff --git a/mqtt-gateway/src/main/java/enmasse/mqtt/messages/AmqpHelper.java b/mqtt-gateway/src/main/java/enmasse/mqtt/messages/AmqpHelper.java deleted file mode 100644 index d8141b19029..00000000000 --- a/mqtt-gateway/src/main/java/enmasse/mqtt/messages/AmqpHelper.java +++ /dev/null @@ -1,71 +0,0 @@ -/* - * Copyright 2016-2018, EnMasse authors. - * License: Apache License 2.0 (see the file LICENSE or http://apache.org/licenses/LICENSE-2.0.html). - */ - -package enmasse.mqtt.messages; - -/** - * Helper class for AMQP side - */ -public final class AmqpHelper { - - public static final String AMQP_CLIENT_CONTROL_ADDRESS_TEMPLATE = "$mqtt.to.%s.control"; - public static final String AMQP_CLIENT_PUBLISH_ADDRESS_TEMPLATE = "$mqtt.to.%s.publish"; - public static final String AMQP_CLIENT_PUBREL_ADDRESS_TEMPLATE = "$mqtt.%s.pubrel"; - - /** - * Extract client identifier from the unique client control address - * - * @param address the address - * @return the client identifier - */ - public static String getClientIdFromControlAddress(String address) { - - return address.substring(address.indexOf("$mqtt.to.") + "$mqtt.to.".length(), address.indexOf(".control")); - } - - /** - * Return the unique client control address from the client identifier - * - * @param clientId the client identifier - * @return the address - */ - public static String getControlClientAddress(String clientId) { - - return String.format(AMQP_CLIENT_CONTROL_ADDRESS_TEMPLATE, clientId); - } - - /** - * Extract client identifier from the unique client publish address - * - * @param address the address - * @return the client identifier - */ - public static String getClientIdFromPublishAddress(String address) { - - return address.substring(address.indexOf("$mqtt.to.") + "$mqtt.to.".length(), address.indexOf(".publish")); - } - - /** - * Return the unique client publish address from the client identifier - * - * @param clientId the client identifier - * @return the address - */ - public static String getPublishClientAddress(String clientId) { - - return String.format(AMQP_CLIENT_PUBLISH_ADDRESS_TEMPLATE, clientId); - } - - /** - * Return client identifier from the pubrel client address - * - * @param address the address - * @return the client identifier - */ - public static String getClientIdFromPubrelAddress(String address) { - - return address.substring(address.indexOf("$mqtt.") + "$mqtt.".length(), address.indexOf(".pubrel")); - } -} diff --git a/mqtt-gateway/src/main/java/enmasse/mqtt/messages/AmqpListMessage.java b/mqtt-gateway/src/main/java/enmasse/mqtt/messages/AmqpListMessage.java deleted file mode 100644 index 110e658a083..00000000000 --- a/mqtt-gateway/src/main/java/enmasse/mqtt/messages/AmqpListMessage.java +++ /dev/null @@ -1,77 +0,0 @@ -/* - * Copyright 2016-2018, EnMasse authors. - * License: Apache License 2.0 (see the file LICENSE or http://apache.org/licenses/LICENSE-2.0.html). - */ - -package enmasse.mqtt.messages; - -import io.vertx.proton.ProtonHelper; -import org.apache.qpid.proton.message.Message; - -/** - * Represents an AMQP_LIST message - */ -public class AmqpListMessage { - - public static final String AMQP_SUBJECT = "list"; - - private final String clientId; - - /** - * Constructor - * - * @param clientId client identifier - */ - public AmqpListMessage(String clientId) { - - this.clientId = clientId; - } - - /** - * Return an AMQP_LIST message from the raw AMQP one - * - * @param message raw AMQP message - * @return AMQP_LIST message - */ - public static AmqpListMessage from(Message message) { - - if (!message.getSubject().equals(AMQP_SUBJECT)) { - throw new IllegalArgumentException(String.format("AMQP message subject is no %s", AMQP_SUBJECT)); - } - - return new AmqpListMessage(AmqpHelper.getClientIdFromPublishAddress((String) message.getCorrelationId())); - } - - /** - * Return a raw AMQP message - * - * @return - */ - public Message toAmqp() { - - Message message = ProtonHelper.message(); - - message.setSubject(AMQP_SUBJECT); - - message.setCorrelationId(String.format(AmqpHelper.AMQP_CLIENT_PUBLISH_ADDRESS_TEMPLATE, this.clientId)); - message.setReplyTo(String.format(AmqpHelper.AMQP_CLIENT_CONTROL_ADDRESS_TEMPLATE, this.clientId)); - - return message; - } - - /** - * Client identifier - * @return - */ - public String clientId() { - return this.clientId; - } - - @Override - public String toString() { - - return "AmqpListMessage{" + - "clientId=" + this.clientId + - "}"; - } -} diff --git a/mqtt-gateway/src/main/java/enmasse/mqtt/messages/AmqpPublishMessage.java b/mqtt-gateway/src/main/java/enmasse/mqtt/messages/AmqpPublishMessage.java deleted file mode 100644 index 2a14be47a60..00000000000 --- a/mqtt-gateway/src/main/java/enmasse/mqtt/messages/AmqpPublishMessage.java +++ /dev/null @@ -1,220 +0,0 @@ -/* - * Copyright 2016-2018, EnMasse authors. - * License: Apache License 2.0 (see the file LICENSE or http://apache.org/licenses/LICENSE-2.0.html). - */ - -package enmasse.mqtt.messages; - -import io.netty.handler.codec.mqtt.MqttQoS; -import io.vertx.core.buffer.Buffer; -import io.vertx.proton.ProtonHelper; -import org.apache.qpid.proton.amqp.Binary; -import org.apache.qpid.proton.amqp.Symbol; -import org.apache.qpid.proton.amqp.messaging.ApplicationProperties; -import org.apache.qpid.proton.amqp.messaging.Data; -import org.apache.qpid.proton.amqp.messaging.Header; -import org.apache.qpid.proton.amqp.messaging.MessageAnnotations; -import org.apache.qpid.proton.amqp.messaging.Section; -import org.apache.qpid.proton.message.Message; - -import java.util.HashMap; -import java.util.Map; - -/** - * Represents an AMQP_PUBLISH message - */ -public class AmqpPublishMessage { - - private static final String AMQP_RETAIN_ANNOTATION = "x-opt-retain-message"; - private static final String AMQP_QOS_ANNOTATION = "x-opt-mqtt-qos"; - private static final String AMQ_ORIG_ADDRESS_ANNOTATION = "_AMQ_ORIG_ADDRESS"; - - private final Object messageId; - private final MqttQoS qos; - private final boolean isDup; - private final boolean isRetain; - private final String topic; - private final Buffer payload; - - /** - * Constructor - * - * @param messageId message identifier - * @param qos MQTT QoS level - * @param isDup if the message is a duplicate - * @param isRetain if the message needs to be retained - * @param topic topic on which the message is published - * @param payload message payload - */ - public AmqpPublishMessage(Object messageId, MqttQoS qos, boolean isDup, boolean isRetain, String topic, Buffer payload) { - - this.messageId = messageId; - this.qos = qos; - this.isDup = isDup; - this.isRetain = isRetain; - this.topic = topic; - this.payload = payload; - } - - /** - * Return an AMQP_PUBLISH message from the raw AMQP one - * - * @param message raw AMQP message - * @return AMQP_PUBLISH message - */ - public static AmqpPublishMessage from(Message message) { - - boolean isRetain = false; - MqttQoS qos = MqttQoS.AT_MOST_ONCE; - - String topic = message.getAddress(); - - // raw AMQP messages published from native AMQP clients could not have annotations - MessageAnnotations messageAnnotations = message.getMessageAnnotations(); - if (messageAnnotations == null) { - - if (message.getHeader() != null) { - // if qos annotation isn't present, fallback to "durable" header field - qos = ((message.getHeader().getDurable() == null) || !message.getHeader().getDurable()) - ? MqttQoS.AT_MOST_ONCE : MqttQoS.AT_LEAST_ONCE; - } - - } else { - - if (messageAnnotations.getValue().containsKey(Symbol.valueOf(AMQ_ORIG_ADDRESS_ANNOTATION))) { - topic = (String) messageAnnotations.getValue().get(Symbol.valueOf(AMQ_ORIG_ADDRESS_ANNOTATION)); - } - - if (messageAnnotations.getValue().containsKey(Symbol.valueOf(AMQP_RETAIN_ANNOTATION))) { - isRetain = (boolean) messageAnnotations.getValue().get(Symbol.valueOf(AMQP_RETAIN_ANNOTATION)); - } - - if (messageAnnotations.getValue().containsKey(Symbol.valueOf(AMQP_QOS_ANNOTATION))) { - int value = (int) messageAnnotations.getValue().get(Symbol.valueOf(AMQP_QOS_ANNOTATION)); - qos = MqttQoS.valueOf(value); - } else { - - if (message.getHeader() != null) { - // if qos annotation isn't present, fallback to "durable" header field - qos = ((message.getHeader().getDurable() == null) || !message.getHeader().getDurable()) - ? MqttQoS.AT_MOST_ONCE : MqttQoS.AT_LEAST_ONCE; - } - } - } - - boolean isDup = (message.getDeliveryCount() > 0); - - // TODO: to remove - // workaround for the Artemis broker which change the original "To" property - // in the AMQP message when message-id is null - ApplicationProperties applicationProperties = message.getApplicationProperties(); - if (applicationProperties != null) { - - Object amqOrigAddress = applicationProperties.getValue().get("_AMQ_ORIG_ADDRESS"); - topic = (amqOrigAddress != null) ? amqOrigAddress.toString() : topic; - } - - Section section = message.getBody(); - if ((section != null) && (section instanceof Data)) { - - Buffer payload = Buffer.buffer(((Data) section).getValue().getArray()); - return new AmqpPublishMessage(message.getMessageId(), qos, isDup, isRetain, topic, payload); - - } else { - throw new IllegalArgumentException("AMQP message wrong body type"); - } - } - - /** - * Return a raw AMQP message - * - * @return - */ - public Message toAmqp() { - - Message message = ProtonHelper.message(); - - message.setMessageId(this.messageId); - - Map map = new HashMap<>(); - map.put(Symbol.valueOf(AMQP_RETAIN_ANNOTATION), this.isRetain); - map.put(Symbol.valueOf(AMQP_QOS_ANNOTATION), this.qos.value()); - MessageAnnotations messageAnnotations = new MessageAnnotations(map); - message.setMessageAnnotations(messageAnnotations); - - message.setAddress(this.topic); - - Header header = new Header(); - header.setDurable(this.qos != MqttQoS.AT_MOST_ONCE); - message.setHeader(header); - - message.setDeliveryCount(this.isDup ? 1 : 0); - - // the payload could be null (or empty) - if (this.payload != null) - message.setBody(new Data(new Binary(this.payload.getBytes()))); - - return message; - } - - /** - * Message identifier - * @return - */ - public Object messageId() { - return messageId; - } - - /** - * MQTT QoS level - * @return - */ - public MqttQoS qos() { - return this.qos; - } - - /** - * If the message is a duplicate - * @return - */ - public boolean isDup() { - return this.isDup; - } - - /** - * If the message needs to be retained - * @return - */ - public boolean isRetain() { - return this.isRetain; - } - - /** - * Topic on which the message is published - * @return - */ - public String topic() { - return this.topic; - } - - /** - * Message payload - * @return - */ - public Buffer payload() { - return this.payload; - } - - @Override - public String toString() { - - return "AmqpPublishMessage{" + - "messageId=" + this.messageId + - ", qos=" + this.qos + - ", isDup=" + this.isDup + - ", isRetain=" + this.isRetain + - ", topic=" + this.topic + - ", payload=" + this.payload + - "}"; - } -} diff --git a/mqtt-gateway/src/main/java/enmasse/mqtt/messages/AmqpPubrelMessage.java b/mqtt-gateway/src/main/java/enmasse/mqtt/messages/AmqpPubrelMessage.java deleted file mode 100644 index ae4126a55d2..00000000000 --- a/mqtt-gateway/src/main/java/enmasse/mqtt/messages/AmqpPubrelMessage.java +++ /dev/null @@ -1,76 +0,0 @@ -/* - * Copyright 2016-2018, EnMasse authors. - * License: Apache License 2.0 (see the file LICENSE or http://apache.org/licenses/LICENSE-2.0.html). - */ - -package enmasse.mqtt.messages; - -import io.vertx.proton.ProtonHelper; -import org.apache.qpid.proton.message.Message; - -/** - * Represents an AMQP_PUBREL message - */ -public class AmqpPubrelMessage { - - public static final String AMQP_SUBJECT = "pubrel"; - - private final Object messageId; - - /** - * Constructor - * - * @param messageId message identifier - */ - public AmqpPubrelMessage(Object messageId) { - - this.messageId = messageId; - } - - /** - * Return an AMQP_PUBREL message from the raw AMQP one - * - * @param message raw AMQP message - * @return AMQP_PUBREL message - */ - public static AmqpPubrelMessage from(Message message) { - - if (!message.getSubject().equals(AMQP_SUBJECT)) { - throw new IllegalArgumentException(String.format("AMQP message subject is no %s", AMQP_SUBJECT)); - } - - return new AmqpPubrelMessage(message.getMessageId()); - } - - /** - * Return a raw AMQP message - * - * @return - */ - public Message toAmqp() { - - Message message = ProtonHelper.message(); - - message.setSubject(AMQP_SUBJECT); - - message.setMessageId(this.messageId); - - return message; - } - - /** - * Message identifier - * @return - */ - public Object messageId() { - return messageId; - } - - @Override - public String toString() { - - return "AmqpPubrelMessage{" + - "messageId=" + this.messageId + - "}"; - } -} diff --git a/mqtt-gateway/src/main/java/enmasse/mqtt/messages/AmqpSubscribeMessage.java b/mqtt-gateway/src/main/java/enmasse/mqtt/messages/AmqpSubscribeMessage.java deleted file mode 100644 index 1d49fb987f7..00000000000 --- a/mqtt-gateway/src/main/java/enmasse/mqtt/messages/AmqpSubscribeMessage.java +++ /dev/null @@ -1,122 +0,0 @@ -/* - * Copyright 2016-2018, EnMasse authors. - * License: Apache License 2.0 (see the file LICENSE or http://apache.org/licenses/LICENSE-2.0.html). - */ - -package enmasse.mqtt.messages; - -import io.netty.handler.codec.mqtt.MqttQoS; -import io.vertx.proton.ProtonHelper; -import org.apache.qpid.proton.amqp.messaging.AmqpValue; -import org.apache.qpid.proton.amqp.messaging.Section; -import org.apache.qpid.proton.message.Message; - -import java.util.ArrayList; -import java.util.HashMap; -import java.util.List; -import java.util.Map; - -/** - * Represents an AMQP_SUBSCRIBE message - */ -public class AmqpSubscribeMessage { - - public static final String AMQP_SUBJECT = "subscribe"; - - private final String clientId; - private final List topicSubscriptions; - - /** - * Constructor - * @param clientId client identifier - * @param topicSubscriptions list with topics and related quality of service levels - */ - public AmqpSubscribeMessage(String clientId, List topicSubscriptions) { - - this.clientId = clientId; - this.topicSubscriptions = topicSubscriptions; - } - - /** - * Return an AMQP_SUBSCRIBE message from the raw AMQP one - * - * @param message raw AMQP message - * @return AMQP_SUBSCRIBE message - */ - @SuppressWarnings("unchecked") - public static AmqpSubscribeMessage from(Message message) { - - if (!message.getSubject().equals(AMQP_SUBJECT)) { - throw new IllegalArgumentException(String.format("AMQP message subject is no %s", AMQP_SUBJECT)); - } - - Section section = message.getBody(); - if ((section != null) && (section instanceof AmqpValue)) { - - Map map = (Map) ((AmqpValue) section).getValue(); - - // build the unique topic subscriptions list - List topicSubscriptions = new ArrayList<>(); - for (Map.Entry entry: map.entrySet()) { - topicSubscriptions.add(new AmqpTopicSubscription(entry.getKey(), MqttQoS.valueOf(Integer.valueOf(entry.getValue())))); - } - - return new AmqpSubscribeMessage(AmqpHelper.getClientIdFromPublishAddress((String) message.getCorrelationId()), - topicSubscriptions); - - } else { - throw new IllegalArgumentException("AMQP message wrong body type"); - } - } - - /** - * Return a raw AMQP message - * - * @return - */ - public Message toAmqp() { - - Message message = ProtonHelper.message(); - - message.setSubject(AMQP_SUBJECT); - - message.setCorrelationId(String.format(AmqpHelper.AMQP_CLIENT_PUBLISH_ADDRESS_TEMPLATE, this.clientId)); - - // map with topic -> qos (in String format) - Map map = new HashMap<>(); - - this.topicSubscriptions.stream().forEach(amqpTopicSubscription -> { - - map.put(amqpTopicSubscription.topic(), String.valueOf(amqpTopicSubscription.qos().value())); - }); - - message.setBody(new AmqpValue(map)); - - return message; - } - - /** - * Client identifier - * @return - */ - public String clientId() { - return this.clientId; - } - - /** - * List with topics and related quolity of service levels - * @return - */ - public List topicSubscriptions() { - return this.topicSubscriptions; - } - - @Override - public String toString() { - - return "AmqpSubscribeMessage{" + - "clientId=" + this.clientId + - ", topicSubscriptions=" + this.topicSubscriptions + - "}"; - } -} diff --git a/mqtt-gateway/src/main/java/enmasse/mqtt/messages/AmqpSubscriptionsMessage.java b/mqtt-gateway/src/main/java/enmasse/mqtt/messages/AmqpSubscriptionsMessage.java deleted file mode 100644 index e6d01036899..00000000000 --- a/mqtt-gateway/src/main/java/enmasse/mqtt/messages/AmqpSubscriptionsMessage.java +++ /dev/null @@ -1,108 +0,0 @@ -/* - * Copyright 2016-2018, EnMasse authors. - * License: Apache License 2.0 (see the file LICENSE or http://apache.org/licenses/LICENSE-2.0.html). - */ - -package enmasse.mqtt.messages; - -import io.netty.handler.codec.mqtt.MqttQoS; -import io.vertx.proton.ProtonHelper; -import org.apache.qpid.proton.amqp.messaging.AmqpValue; -import org.apache.qpid.proton.amqp.messaging.Section; -import org.apache.qpid.proton.message.Message; - -import java.util.ArrayList; -import java.util.HashMap; -import java.util.List; -import java.util.Map; - -/** - * Represents an AMQP_SUBSCRIPTIONS message - */ -public class AmqpSubscriptionsMessage { - - public static final String AMQP_SUBJECT = "subscriptions"; - - private final List topicSubscriptions; - - /** - * Constructor - * - * @param topicSubscriptions list with topics and related quality of service levels - */ - public AmqpSubscriptionsMessage(List topicSubscriptions) { - - this.topicSubscriptions = topicSubscriptions; - } - - /** - * Return an AMQP_SUBSCRIPTIONS message from the raw AMQP one - * - * @param message raw AMQP message - * @return AMQP_SUBSCRIPTIONS message - */ - @SuppressWarnings("unchecked") - public static AmqpSubscriptionsMessage from(Message message) { - - if (!message.getSubject().equals(AMQP_SUBJECT)) { - throw new IllegalArgumentException(String.format("AMQP message subject is no %s", AMQP_SUBJECT)); - } - - Section section = message.getBody(); - if ((section != null) && (section instanceof AmqpValue)) { - - Map map = (Map) ((AmqpValue) section).getValue(); - - // build the unique topic subscriptions list - List topicSubscriptions = new ArrayList<>(); - for (Map.Entry entry: map.entrySet()) { - topicSubscriptions.add(new AmqpTopicSubscription(entry.getKey(), MqttQoS.valueOf(Integer.valueOf(entry.getValue())))); - } - - return new AmqpSubscriptionsMessage(topicSubscriptions); - - } else { - throw new IllegalArgumentException("AMQP message wrong body type"); - } - } - - /** - * Return a raw AMQP message - * - * @return - */ - public Message toAmqp() { - - Message message = ProtonHelper.message(); - - message.setSubject(AMQP_SUBJECT); - - // map with topic -> qos (in String format) - Map map = new HashMap<>(); - - this.topicSubscriptions.stream().forEach(amqpTopicSubscription -> { - - map.put(amqpTopicSubscription.topic(), String.valueOf(amqpTopicSubscription.qos().value())); - }); - - message.setBody(new AmqpValue(map)); - - return message; - } - - /** - * List with topics and related quolity of service levels - * @return - */ - public List topicSubscriptions() { - return this.topicSubscriptions; - } - - @Override - public String toString() { - - return "AmqpSubscriptionsMessage{" + - "topicSubscriptions=" + this.topicSubscriptions + - "}"; - } -} diff --git a/mqtt-gateway/src/main/java/enmasse/mqtt/messages/AmqpTopicSubscription.java b/mqtt-gateway/src/main/java/enmasse/mqtt/messages/AmqpTopicSubscription.java deleted file mode 100644 index 29ad692de2d..00000000000 --- a/mqtt-gateway/src/main/java/enmasse/mqtt/messages/AmqpTopicSubscription.java +++ /dev/null @@ -1,55 +0,0 @@ -/* - * Copyright 2016-2018, EnMasse authors. - * License: Apache License 2.0 (see the file LICENSE or http://apache.org/licenses/LICENSE-2.0.html). - */ - -package enmasse.mqtt.messages; - -import io.netty.handler.codec.mqtt.MqttQoS; - -/** - * Represent a subscription to a topic in the AMQP way - */ -public class AmqpTopicSubscription { - - private final String topic; - private final MqttQoS qos; - - /** - * Constructor - * - * @param topic topic name for the subscription - * @param qos quality of service level - */ - public AmqpTopicSubscription(String topic, MqttQoS qos) { - this.topic = topic; - this.qos = qos; - } - - /** - * Subscription topic name - * - * @return - */ - public String topic() { - return topic; - } - - /** - * Quality of Service level for the subscription - * - * @return - */ - public MqttQoS qos() { - return qos; - } - - @Override - public String toString() { - - return "AmqpTopicSubscription{" + - "topic=" + this.topic + - ", qos=" + this.qos + - "}"; - } -} diff --git a/mqtt-gateway/src/main/java/enmasse/mqtt/messages/AmqpUnsubscribeMessage.java b/mqtt-gateway/src/main/java/enmasse/mqtt/messages/AmqpUnsubscribeMessage.java deleted file mode 100644 index 7f4c0295e90..00000000000 --- a/mqtt-gateway/src/main/java/enmasse/mqtt/messages/AmqpUnsubscribeMessage.java +++ /dev/null @@ -1,104 +0,0 @@ -/* - * Copyright 2016-2018, EnMasse authors. - * License: Apache License 2.0 (see the file LICENSE or http://apache.org/licenses/LICENSE-2.0.html). - */ - -package enmasse.mqtt.messages; - -import io.vertx.proton.ProtonHelper; -import org.apache.qpid.proton.amqp.messaging.AmqpValue; -import org.apache.qpid.proton.amqp.messaging.Section; -import org.apache.qpid.proton.message.Message; - -import java.util.List; - -/** - * Represents an AMQP_UNSUBSCRIBE message - */ -public class AmqpUnsubscribeMessage { - - public static final String AMQP_SUBJECT = "unsubscribe"; - - private final String clientId; - private final List topics; - - /** - * Constructor - * @param clientId client identifier - * @param topics topics to subscribe - */ - public AmqpUnsubscribeMessage(String clientId, List topics) { - - this.clientId = clientId; - this.topics = topics; - } - - /** - * Return an AMQP_UNSUBSCRIBE message from the raw AMQP one - * - * @param message raw AMQP message - * @return AMQP_UNSUBSCRIBE message - */ - @SuppressWarnings("unchecked") - public static AmqpUnsubscribeMessage from(Message message) { - - if (!message.getSubject().equals(AMQP_SUBJECT)) { - throw new IllegalArgumentException(String.format("AMQP message subject is no %s", AMQP_SUBJECT)); - } - - Section section = message.getBody(); - if ((section != null) && (section instanceof AmqpValue)) { - - List topics = (List) ((AmqpValue) section).getValue(); - - return new AmqpUnsubscribeMessage(AmqpHelper.getClientIdFromPublishAddress((String) message.getCorrelationId()), - topics); - - } else { - throw new IllegalArgumentException("AMQP message wrong body type"); - } - } - - /** - * Return a raw AMQP message - * - * @return - */ - public Message toAmqp() { - - Message message = ProtonHelper.message(); - - message.setSubject(AMQP_SUBJECT); - - message.setCorrelationId(String.format(AmqpHelper.AMQP_CLIENT_PUBLISH_ADDRESS_TEMPLATE, this.clientId)); - - message.setBody(new AmqpValue(this.topics)); - - return message; - } - - /** - * Client identifier - * @return - */ - public String clientId() { - return this.clientId; - } - - /** - * Topics to subscribe - * @return - */ - public List topics() { - return this.topics; - } - - @Override - public String toString() { - - return "AmqpUnsubscribeMessage{" + - "clientId=" + this.clientId + - ", topics=" + this.topics + - "}"; - } -} diff --git a/mqtt-gateway/src/main/java/enmasse/mqtt/messages/AmqpWillMessage.java b/mqtt-gateway/src/main/java/enmasse/mqtt/messages/AmqpWillMessage.java deleted file mode 100644 index ae847c98f93..00000000000 --- a/mqtt-gateway/src/main/java/enmasse/mqtt/messages/AmqpWillMessage.java +++ /dev/null @@ -1,176 +0,0 @@ -/* - * Copyright 2016-2018, EnMasse authors. - * License: Apache License 2.0 (see the file LICENSE or http://apache.org/licenses/LICENSE-2.0.html). - */ - -package enmasse.mqtt.messages; - -import io.netty.handler.codec.mqtt.MqttQoS; -import io.vertx.core.buffer.Buffer; -import io.vertx.proton.ProtonHelper; -import org.apache.qpid.proton.amqp.Binary; -import org.apache.qpid.proton.amqp.Symbol; -import org.apache.qpid.proton.amqp.messaging.Data; -import org.apache.qpid.proton.amqp.messaging.Header; -import org.apache.qpid.proton.amqp.messaging.MessageAnnotations; -import org.apache.qpid.proton.amqp.messaging.Section; -import org.apache.qpid.proton.message.Message; - -import java.util.HashMap; -import java.util.Map; - -/** - * Represents an AMQP_WILL message - */ -public class AmqpWillMessage { - - public static final String AMQP_SUBJECT = "will"; - - private static final String AMQP_RETAIN_ANNOTATION = "x-opt-retain-message"; - private static final String AMQP_QOS_ANNOTATION = "x-opt-mqtt-qos"; - - private final boolean isRetain; - private final String topic; - private final MqttQoS qos; - private final Buffer payload; - - /** - * Constructor - * - * @param isRetain will retain flag - * @param topic will topic - * @param qos MQTT QoS level - * @param payload will message payload - */ - public AmqpWillMessage(boolean isRetain, String topic, MqttQoS qos, Buffer payload) { - - this.isRetain = isRetain; - this.topic = topic; - this.qos = qos; - this.payload = payload; - } - - /** - * Return an AMQP_WILL message from the raw AMQP one - * - * @param message raw AMQP message - * @return AMQP_WILL message - */ - public static AmqpWillMessage from(Message message) { - - if (!message.getSubject().equals(AMQP_SUBJECT)) { - throw new IllegalArgumentException(String.format("AMQP message subject is no %s", AMQP_SUBJECT)); - } - - MessageAnnotations messageAnnotations = message.getMessageAnnotations(); - if (messageAnnotations == null) { - throw new IllegalArgumentException("AMQP message has no annotations"); - } else { - - boolean isRetain = false; - if (messageAnnotations.getValue().containsKey(Symbol.valueOf(AMQP_RETAIN_ANNOTATION))) { - isRetain = (boolean) messageAnnotations.getValue().get(Symbol.valueOf(AMQP_RETAIN_ANNOTATION)); - } - - MqttQoS qos; - if (messageAnnotations.getValue().containsKey(Symbol.valueOf(AMQP_QOS_ANNOTATION))) { - int value = (int) messageAnnotations.getValue().get(Symbol.valueOf(AMQP_QOS_ANNOTATION)); - qos = MqttQoS.valueOf(value); - } else { - - if (message.getHeader() != null) { - // if qos annotation isn't present, fallback to "durable" header field - qos = ((message.getHeader().getDurable() == null) || !message.getHeader().getDurable()) - ? MqttQoS.AT_MOST_ONCE : MqttQoS.AT_LEAST_ONCE; - } else { - qos = MqttQoS.AT_MOST_ONCE; - } - } - - String topic = message.getAddress(); - - Section section = message.getBody(); - if ((section != null) && (section instanceof Data)) { - - Buffer payload = Buffer.buffer(((Data) section).getValue().getArray()); - return new AmqpWillMessage(isRetain, topic, qos, payload); - - } else { - throw new IllegalArgumentException("AMQP message wrong body type"); - } - } - } - - /** - * Return a raw AMQP message - * - * @return - */ - public Message toAmqp() { - - Message message = ProtonHelper.message(); - - message.setSubject(AMQP_SUBJECT); - - Map map = new HashMap<>(); - map.put(Symbol.valueOf(AMQP_RETAIN_ANNOTATION), this.isRetain); - map.put(Symbol.valueOf(AMQP_QOS_ANNOTATION), this.qos.value()); - MessageAnnotations messageAnnotations = new MessageAnnotations(map); - message.setMessageAnnotations(messageAnnotations); - - message.setAddress(this.topic); - - Header header = new Header(); - header.setDurable(this.qos != MqttQoS.AT_MOST_ONCE); - message.setHeader(header); - - // the payload could be null (or empty) - if (this.payload != null) - message.setBody(new Data(new Binary(this.payload.getBytes()))); - - return message; - } - - /** - * Will retain flag - * @return - */ - public boolean isRetain() { - return this.isRetain; - } - - /** - * Will topic - * @return - */ - public String topic() { - return this.topic; - } - - /** - * MQTT QoS level - * @return - */ - public MqttQoS qos() { - return this.qos; - } - - /** - * Will message payload - * @return - */ - public Buffer payload() { - return this.payload; - } - - @Override - public String toString() { - - return "AmqpWillMessage{" + - "isRetain=" + this.isRetain + - ", topic=" + this.topic + - ", qos=" + this.qos + - ", payload=" + this.payload + - "}"; - } -} diff --git a/mqtt-gateway/src/main/resources/logback.xml b/mqtt-gateway/src/main/resources/logback.xml deleted file mode 100644 index 9ed3476bbee..00000000000 --- a/mqtt-gateway/src/main/resources/logback.xml +++ /dev/null @@ -1,15 +0,0 @@ - - - - - - - %d{yyyy-MM-dd'T'HH:mm:ss.SSS'Z',GMT} %-5p [%c{0}] %m%n - - - - - - - - diff --git a/mqtt-gateway/src/test/java/enmasse/mqtt/TopicMatcherTest.java b/mqtt-gateway/src/test/java/enmasse/mqtt/TopicMatcherTest.java deleted file mode 100644 index 70ab6242981..00000000000 --- a/mqtt-gateway/src/test/java/enmasse/mqtt/TopicMatcherTest.java +++ /dev/null @@ -1,100 +0,0 @@ -/* - * Copyright 2016-2018, EnMasse authors. - * License: Apache License 2.0 (see the file LICENSE or http://apache.org/licenses/LICENSE-2.0.html). - */ - -package enmasse.mqtt; - - -import org.junit.jupiter.api.Test; - -import static org.junit.jupiter.api.Assertions.assertFalse; -import static org.junit.jupiter.api.Assertions.assertTrue; - -/** - * Tests related to topic wildcards matches - */ -public class TopicMatcherTest { - - @Test - public void testFixedTopicMatch() { - - String subTopic = "mytopic/foo"; - String pubTopic = "mytopic/foo"; - - assertTrue(TopicMatcher.isMatch(subTopic, pubTopic)); - } - - @Test - public void testFixedTopicNotMatch() { - - String subTopic = "mytopic/foo"; - String pubTopic = "mytopic/bar"; - - assertFalse(TopicMatcher.isMatch(subTopic, pubTopic)); - } - - @Test - public void testTopicSharpWildcardMatch() { - - String subTopic = "mytopic/#"; - String pubTopic = "mytopic/foo"; - - assertTrue(TopicMatcher.isMatch(subTopic, pubTopic)); - - pubTopic = "mytopic/foo/bar"; - - assertTrue(TopicMatcher.isMatch(subTopic, pubTopic)); - } - - @Test - public void testTopicSharpWildcardNotMatch() { - - String subTopic = "mytopic/#"; - String pubTopic = "mytopic"; - - assertFalse(TopicMatcher.isMatch(subTopic, pubTopic)); - } - - @Test - public void testTopicPlusWildcardMatch() { - - String subTopic = "mytopic/+/bar"; - String pubTopic = "mytopic/foo/bar"; - - assertTrue(TopicMatcher.isMatch(subTopic, pubTopic)); - - pubTopic = "mytopic/another/bar"; - - assertTrue(TopicMatcher.isMatch(subTopic, pubTopic)); - - subTopic = "mytopic/foo/+"; - pubTopic = "mytopic/foo/bar"; - - assertTrue(TopicMatcher.isMatch(subTopic, pubTopic)); - - subTopic = "mytopic/+/bar/+/foo"; - pubTopic = "mytopic/a/bar/b/foo"; - - assertTrue(TopicMatcher.isMatch(subTopic, pubTopic)); - } - - @Test - public void testTopicPlusWildcardNotMatch() { - - String subTopic = "mytopic/+/bar"; - String pubTopic = "mytopic/bar"; - - assertFalse(TopicMatcher.isMatch(subTopic, pubTopic)); - - subTopic = "mytopic/foo/+"; - pubTopic = "mytopic/foo"; - - assertFalse(TopicMatcher.isMatch(subTopic, pubTopic)); - - subTopic = "mytopic/+/bar/+/foo"; - pubTopic = "mytopic/a/bar/b/foo/c"; - - assertFalse(TopicMatcher.isMatch(subTopic, pubTopic)); - } -} diff --git a/mqtt-gateway/src/test/resources/tls/client-truststore.jks b/mqtt-gateway/src/test/resources/tls/client-truststore.jks deleted file mode 100644 index b8ea06f1073..00000000000 Binary files a/mqtt-gateway/src/test/resources/tls/client-truststore.jks and /dev/null differ diff --git a/mqtt-gateway/src/test/resources/tls/server-cert.pem b/mqtt-gateway/src/test/resources/tls/server-cert.pem deleted file mode 100644 index 13668ffad0d..00000000000 --- a/mqtt-gateway/src/test/resources/tls/server-cert.pem +++ /dev/null @@ -1,20 +0,0 @@ ------BEGIN CERTIFICATE----- -MIIDVzCCAj+gAwIBAgIJAPwyPGH7W45WMA0GCSqGSIb3DQEBCwUAMEIxCzAJBgNV -BAYTAlhYMRUwEwYDVQQHDAxEZWZhdWx0IENpdHkxHDAaBgNVBAoME0RlZmF1bHQg -Q29tcGFueSBMdGQwHhcNMTcwMTAyMDk1MjM1WhcNMTcwMjAxMDk1MjM1WjBCMQsw -CQYDVQQGEwJYWDEVMBMGA1UEBwwMRGVmYXVsdCBDaXR5MRwwGgYDVQQKDBNEZWZh -dWx0IENvbXBhbnkgTHRkMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA -90AePtCNSrXyp73BcuR1uqz0tl0dcwiaWZFc6LIFuhHm/0qXzn1bIkuusKwLFugd -q4MJPRHqzjlRvo1m4P6ATlB2GVP4dKbfNnYQ5sF0bNoxR1F8LuBPH7PiOEpfW64/ -YqvVKfW3F6Z77jWvNBrWicAesohiquI9aiaizIJhwUntOFvN+GDLq8dHb0a6wxS0 -vj9XkxNMtGkSYUUCYy8GQ9Z3BYHwVGVvDIWF6oLIitRny8/HLgpTfNXDxEATusL5 -IxWaXO/LfxOC4j4NEpwZKwDSgiSXPPZ84rskS6erUNWlAe4P0jz5ZvT9E7iYaUlX -1KhteIPIcvG4ZPeOMu21dQIDAQABo1AwTjAdBgNVHQ4EFgQUm4/0N9Ax9vBJLxN+ -RKE3eTtDLcowHwYDVR0jBBgwFoAUm4/0N9Ax9vBJLxN+RKE3eTtDLcowDAYDVR0T -BAUwAwEB/zANBgkqhkiG9w0BAQsFAAOCAQEA9pkGGUVUw9EzrJ0Q9J820J8YnosH -rjnKUI3RPUsLUL+/C9FGp0OAB0d2i5t7T9cIgkRSlm1piCEZX9Xr5SuVgV0YX5dj -VTglQzH4V2xf7x/LSYI/nI+pfj2TX39tEgXPeBSv6ZHszRiWgm2Q5qZWingMtlSW -yFKLzwcUlr1h3XCilWewfmOM13XH0TtXvZCRMLmz06C2gA1K6h4hKjSdp9wEAn8R -LnhXhDDLUcPS5+uipMPXpKL3EoviipNNOBgdArUdvCtrjBInQy5zcrm+mYTMJAFe -5muxtHwTSPdINGLaWtSqd7fOIphfX4siJbJ8SLRdyv82HvyZ9FdabSZDeg== ------END CERTIFICATE----- diff --git a/mqtt-gateway/src/test/resources/tls/server-key.pem b/mqtt-gateway/src/test/resources/tls/server-key.pem deleted file mode 100644 index d0e93683829..00000000000 --- a/mqtt-gateway/src/test/resources/tls/server-key.pem +++ /dev/null @@ -1,28 +0,0 @@ ------BEGIN PRIVATE KEY----- -MIIEwAIBADANBgkqhkiG9w0BAQEFAASCBKowggSmAgEAAoIBAQD3QB4+0I1KtfKn -vcFy5HW6rPS2XR1zCJpZkVzosgW6Eeb/SpfOfVsiS66wrAsW6B2rgwk9EerOOVG+ -jWbg/oBOUHYZU/h0pt82dhDmwXRs2jFHUXwu4E8fs+I4Sl9brj9iq9Up9bcXpnvu -Na80GtaJwB6yiGKq4j1qJqLMgmHBSe04W834YMurx0dvRrrDFLS+P1eTE0y0aRJh -RQJjLwZD1ncFgfBUZW8MhYXqgsiK1GfLz8cuClN81cPEQBO6wvkjFZpc78t/E4Li -Pg0SnBkrANKCJJc89nziuyRLp6tQ1aUB7g/SPPlm9P0TuJhpSVfUqG14g8hy8bhk -944y7bV1AgMBAAECggEBALhh52w+iG+Dmi6/UokEmEsa+e8ZJ4IGk1xiC+A4Cg9F -lHcjQGwrjY/Y0rBLzIVLmfyyXmxddTZPBofM2UDGahZ86GVg+nK7ktABYf9Dmzgl -ywP9X1dxzW+97OC/jbdIDVre+7sr01teuyLchAieTwkw7XOEQ9hdmSEVKOjL/mxt -5qC6CmMnmal+zIzWBgBvjUtUI9vYx561L97F5w5qkg1aA3D9Yz41IftImkpH3v2q -1zG4aEFUrBQXTp5L/xdg5FIVYO54sY3vvCcKe+z+6WsXe1u+s7IjjGuf6nn5P3DJ -x6N8ByhXDhFHV9EHlLRWV70tKxLPXQ/mve2L+cEEhW0CgYEA/8LrNp6IbrAUcLR4 -sdFiyqdxox0xCrx6xXhEr9W6jSfWMLrYY09gvKK572umXSXIGRf09MdQ3nPrAloa -6PHf6OXmGKgs11MDHU9u2MJlVvxPP1k+YH1oN29wDaj26PFnA8/Vxij9HKEYZk6g -2RyxBx/S3czfb5VPUbI9jQYl8vcCgYEA93sqsEnSbfA1hz50kynu5btMJ/W8hoQ5 -FiV51gVnRUzRywYzoVsb9qMLVQaMCjsjoh2ma58OE3NuWlztI4R+XlFX3aqXV24g -AXhP2fbz7zFtrpHCgkbajOGG0qKxIQZCFpGiOpWEPOvMgJm22YQiSm8G6bfa2Dpa -uscv3V9uU/MCgYEA1CqAMRkmGJxc+HndvbTy/SYWcLegnUVpmzJ/2FW2oa4wUtBM -/WU13IYpNGHa6l6TNa3X+M73WPJUO+k/dYpgsrU0QbQRLragWMoyLAJnSteXbfe8 -dRLTR1lcXRiTav4G9PSOZApQnlVNBmGnsTPJ7x0JMhHoMIpt+FmiEcDN7lUCgYEA -vg13PgSjNkxnPyJmcfGdBg2tMW7UxdTa5jgHdaWWpjCVWD29/qcyNVkGxdgOSXkb -J09v02xFagiWcYy1jDYeuZ3sRU3RhZILwDU91VyB/mnOGmbToip5ggFcAXxxXLQq -opxaonTaJdLLrOLe+fIwR0s2WtwXk0BVFuUKzA2Sc6MCgYEAoCHAS8KvM5y3sSGm -FYSTRkE6kOfVL0XpMby1GkGIRWCrfFmZOl1QM8B+gBTpxhQgoLWBU60xxYN0wQw9 -WN74fX/wejNff/yz42ifHBmH9J33TvgZDpmQK1JnlU1QFUaknqcaoire92thbHlM -h6jw9EMdK7cPBac8fmzEVwVY8Ns= ------END PRIVATE KEY----- diff --git a/mqtt-lwt/.gitignore b/mqtt-lwt/.gitignore deleted file mode 100644 index 408ec85fc20..00000000000 --- a/mqtt-lwt/.gitignore +++ /dev/null @@ -1,14 +0,0 @@ -# Gradle stuff -.gradle -/build/ - -# Eclipse stuff -**/.project -**/.settings/* -**/.prefs -**/.classpath -/target/ - -# IntelliJ IDEA specific -.idea/ -*.iml diff --git a/mqtt-lwt/Dockerfile b/mqtt-lwt/Dockerfile deleted file mode 100644 index 54d74dcbfe0..00000000000 --- a/mqtt-lwt/Dockerfile +++ /dev/null @@ -1,10 +0,0 @@ -FROM quay.io/enmasse/java-base:11-5 - -ARG version -ARG maven_version -ARG revision - -ENV VERSION=${version} REVISION=${revision} MAVEN_VERSION=${maven_version} -ADD target/mqtt-lwt-${maven_version}.jar /mqtt-lwt.jar - -CMD ["/opt/run-java/launch_java.sh", "-jar", "/mqtt-lwt.jar"] diff --git a/mqtt-lwt/LICENSE b/mqtt-lwt/LICENSE deleted file mode 100644 index 8dada3edaf5..00000000000 --- a/mqtt-lwt/LICENSE +++ /dev/null @@ -1,201 +0,0 @@ - Apache License - Version 2.0, January 2004 - http://www.apache.org/licenses/ - - TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION - - 1. Definitions. - - "License" shall mean the terms and conditions for use, reproduction, - and distribution as defined by Sections 1 through 9 of this document. - - "Licensor" shall mean the copyright owner or entity authorized by - the copyright owner that is granting the License. - - "Legal Entity" shall mean the union of the acting entity and all - other entities that control, are controlled by, or are under common - control with that entity. For the purposes of this definition, - "control" means (i) the power, direct or indirect, to cause the - direction or management of such entity, whether by contract or - otherwise, or (ii) ownership of fifty percent (50%) or more of the - outstanding shares, or (iii) beneficial ownership of such entity. - - "You" (or "Your") shall mean an individual or Legal Entity - exercising permissions granted by this License. - - "Source" form shall mean the preferred form for making modifications, - including but not limited to software source code, documentation - source, and configuration files. - - "Object" form shall mean any form resulting from mechanical - transformation or translation of a Source form, including but - not limited to compiled object code, generated documentation, - and conversions to other media types. - - "Work" shall mean the work of authorship, whether in Source or - Object form, made available under the License, as indicated by a - copyright notice that is included in or attached to the work - (an example is provided in the Appendix below). - - "Derivative Works" shall mean any work, whether in Source or Object - form, that is based on (or derived from) the Work and for which the - editorial revisions, annotations, elaborations, or other modifications - represent, as a whole, an original work of authorship. For the purposes - of this License, Derivative Works shall not include works that remain - separable from, or merely link (or bind by name) to the interfaces of, - the Work and Derivative Works thereof. - - "Contribution" shall mean any work of authorship, including - the original version of the Work and any modifications or additions - to that Work or Derivative Works thereof, that is intentionally - submitted to Licensor for inclusion in the Work by the copyright owner - or by an individual or Legal Entity authorized to submit on behalf of - the copyright owner. For the purposes of this definition, "submitted" - means any form of electronic, verbal, or written communication sent - to the Licensor or its representatives, including but not limited to - communication on electronic mailing lists, source code control systems, - and issue tracking systems that are managed by, or on behalf of, the - Licensor for the purpose of discussing and improving the Work, but - excluding communication that is conspicuously marked or otherwise - designated in writing by the copyright owner as "Not a Contribution." - - "Contributor" shall mean Licensor and any individual or Legal Entity - on behalf of whom a Contribution has been received by Licensor and - subsequently incorporated within the Work. - - 2. Grant of Copyright License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - copyright license to reproduce, prepare Derivative Works of, - publicly display, publicly perform, sublicense, and distribute the - Work and such Derivative Works in Source or Object form. - - 3. Grant of Patent License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - (except as stated in this section) patent license to make, have made, - use, offer to sell, sell, import, and otherwise transfer the Work, - where such license applies only to those patent claims licensable - by such Contributor that are necessarily infringed by their - Contribution(s) alone or by combination of their Contribution(s) - with the Work to which such Contribution(s) was submitted. If You - institute patent litigation against any entity (including a - cross-claim or counterclaim in a lawsuit) alleging that the Work - or a Contribution incorporated within the Work constitutes direct - or contributory patent infringement, then any patent licenses - granted to You under this License for that Work shall terminate - as of the date such litigation is filed. - - 4. Redistribution. You may reproduce and distribute copies of the - Work or Derivative Works thereof in any medium, with or without - modifications, and in Source or Object form, provided that You - meet the following conditions: - - (a) You must give any other recipients of the Work or - Derivative Works a copy of this License; and - - (b) You must cause any modified files to carry prominent notices - stating that You changed the files; and - - (c) You must retain, in the Source form of any Derivative Works - that You distribute, all copyright, patent, trademark, and - attribution notices from the Source form of the Work, - excluding those notices that do not pertain to any part of - the Derivative Works; and - - (d) If the Work includes a "NOTICE" text file as part of its - distribution, then any Derivative Works that You distribute must - include a readable copy of the attribution notices contained - within such NOTICE file, excluding those notices that do not - pertain to any part of the Derivative Works, in at least one - of the following places: within a NOTICE text file distributed - as part of the Derivative Works; within the Source form or - documentation, if provided along with the Derivative Works; or, - within a display generated by the Derivative Works, if and - wherever such third-party notices normally appear. The contents - of the NOTICE file are for informational purposes only and - do not modify the License. You may add Your own attribution - notices within Derivative Works that You distribute, alongside - or as an addendum to the NOTICE text from the Work, provided - that such additional attribution notices cannot be construed - as modifying the License. - - You may add Your own copyright statement to Your modifications and - may provide additional or different license terms and conditions - for use, reproduction, or distribution of Your modifications, or - for any such Derivative Works as a whole, provided Your use, - reproduction, and distribution of the Work otherwise complies with - the conditions stated in this License. - - 5. Submission of Contributions. Unless You explicitly state otherwise, - any Contribution intentionally submitted for inclusion in the Work - by You to the Licensor shall be under the terms and conditions of - this License, without any additional terms or conditions. - Notwithstanding the above, nothing herein shall supersede or modify - the terms of any separate license agreement you may have executed - with Licensor regarding such Contributions. - - 6. Trademarks. This License does not grant permission to use the trade - names, trademarks, service marks, or product names of the Licensor, - except as required for reasonable and customary use in describing the - origin of the Work and reproducing the content of the NOTICE file. - - 7. Disclaimer of Warranty. Unless required by applicable law or - agreed to in writing, Licensor provides the Work (and each - Contributor provides its Contributions) on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or - implied, including, without limitation, any warranties or conditions - of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A - PARTICULAR PURPOSE. You are solely responsible for determining the - appropriateness of using or redistributing the Work and assume any - risks associated with Your exercise of permissions under this License. - - 8. Limitation of Liability. In no event and under no legal theory, - whether in tort (including negligence), contract, or otherwise, - unless required by applicable law (such as deliberate and grossly - negligent acts) or agreed to in writing, shall any Contributor be - liable to You for damages, including any direct, indirect, special, - incidental, or consequential damages of any character arising as a - result of this License or out of the use or inability to use the - Work (including but not limited to damages for loss of goodwill, - work stoppage, computer failure or malfunction, or any and all - other commercial damages or losses), even if such Contributor - has been advised of the possibility of such damages. - - 9. Accepting Warranty or Additional Liability. While redistributing - the Work or Derivative Works thereof, You may choose to offer, - and charge a fee for, acceptance of support, warranty, indemnity, - or other liability obligations and/or rights consistent with this - License. However, in accepting such obligations, You may act only - on Your own behalf and on Your sole responsibility, not on behalf - of any other Contributor, and only if You agree to indemnify, - defend, and hold each Contributor harmless for any liability - incurred by, or claims asserted against, such Contributor by reason - of your accepting any such warranty or additional liability. - - END OF TERMS AND CONDITIONS - - APPENDIX: How to apply the Apache License to your work. - - To apply the Apache License to your work, attach the following - boilerplate notice, with the fields enclosed by brackets "{}" - replaced with your own identifying information. (Don't include - the brackets!) The text should be enclosed in the appropriate - comment syntax for the file format. We also recommend that a - file or class name and description of purpose be included on the - same "printed page" as the copyright notice for easier - identification within third-party archives. - - Copyright {yyyy} {name of copyright owner} - - 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. diff --git a/mqtt-lwt/Makefile b/mqtt-lwt/Makefile deleted file mode 100644 index 27e8bca799a..00000000000 --- a/mqtt-lwt/Makefile +++ /dev/null @@ -1 +0,0 @@ -include ../Makefile.java.mk diff --git a/mqtt-lwt/README.md b/mqtt-lwt/README.md deleted file mode 100644 index 21a3fc74cbd..00000000000 --- a/mqtt-lwt/README.md +++ /dev/null @@ -1,5 +0,0 @@ -# MQTT Last Will and Testament service - -MQTT Last Will and Testament service component for EnMasse which provides "will" messages handling for remote MQTT clients. Through this components, the "will" information sent by an MQTT client on connection are stored; when the client disconnects not in the clean way (DISCONNECT message), this components provides to send the "will" message to all the MQTT subscribed client. - -This implementation follow the specification defined by the following documentation for bringing [MQTT over AMQP](../documentation/design_docs/mqtt-over-amqp) diff --git a/mqtt-lwt/pom.xml b/mqtt-lwt/pom.xml deleted file mode 100644 index 6ceb7a686f3..00000000000 --- a/mqtt-lwt/pom.xml +++ /dev/null @@ -1,92 +0,0 @@ - - - - io.enmasse - enmasse - 0.32-SNAPSHOT - - 4.0.0 - mqtt-lwt - - - io.vertx - vertx-proton - compile - - - org.slf4j - slf4j-api - compile - - - ch.qos.logback - logback-classic - runtime - - - io.netty - netty-codec-mqtt - compile - - - org.junit.jupiter - junit-jupiter-api - test - - - org.junit.jupiter - junit-jupiter-engine - test - - - org.junit.platform - junit-platform-launcher - test - - - io.vertx - vertx-junit5 - test - - - io.enmasse - amqp-utils - test - - - - - - - maven-shade-plugin - - - package - - shade - - - - - - - enmasse.mqtt.Application - - - - - *:* - - META-INF/*.SF - META-INF/*.DSA - META-INF/*.RSA - - - - - - - - - - diff --git a/mqtt-lwt/src/main/java/enmasse/mqtt/Application.java b/mqtt-lwt/src/main/java/enmasse/mqtt/Application.java deleted file mode 100644 index b487df2b73e..00000000000 --- a/mqtt-lwt/src/main/java/enmasse/mqtt/Application.java +++ /dev/null @@ -1,177 +0,0 @@ -/* - * Copyright 2016-2018, EnMasse authors. - * License: Apache License 2.0 (see the file LICENSE or http://apache.org/licenses/LICENSE-2.0.html). - */ - -package enmasse.mqtt; - -import io.vertx.core.Future; -import io.vertx.core.Handler; -import io.vertx.core.Vertx; -import io.vertx.core.logging.SLF4JLogDelegateFactory; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import java.util.Map; -import java.util.concurrent.CountDownLatch; -import java.util.concurrent.TimeUnit; -import java.util.concurrent.atomic.AtomicBoolean; - -import static io.vertx.core.logging.LoggerFactory.LOGGER_DELEGATE_FACTORY_CLASS_NAME; - -/** - * EnMasse MQTT Last Will and Testament service main application class - */ -public class Application { - - private static final Logger LOG = LoggerFactory.getLogger(Application.class); - - private final Vertx vertx = Vertx.vertx(); - private final MqttLwtOptions options; - private final MqttLwt mqttLwt; - - private AtomicBoolean running = new AtomicBoolean(); - - public Application(MqttLwtOptions options, MqttLwt mqttLwt) { - this.options = options; - this.mqttLwt = mqttLwt; - } - - public void registerVerticles() { - - if (this.running.compareAndSet(false, true)) { - - - try { - CountDownLatch latch = new CountDownLatch(1); - - Future startFuture = Future.future(); - startFuture.setHandler(done -> { - if (done.succeeded()) { - latch.countDown(); - } else { - LOG.error("Could not start MQTT LWT service", done.cause()); - } - }); - - // start deploying more verticle instances - this.deployVerticles(1, startFuture); - - // wait for deploying end - long startupTimeout = this.options.getStartupTimeout().getSeconds(); - if (latch.await(startupTimeout, TimeUnit.SECONDS)) { - LOG.info("MQTT LWT service startup completed successfully"); - } else { - LOG.error("Startup timed out after {} seconds, shutting down ...", startupTimeout); - this.shutdown(); - } - - } catch (InterruptedException e) { - - LOG.error("Startup process has been interrupted, shutting down ..."); - this.shutdown(); - } - } - } - - /** - * Execute verticles deploy operation - * - * @param instanceCount number of verticle instances to deploy - * @param resultHandler handler called when the deploy ends - */ - private void deployVerticles(int instanceCount, Future resultHandler) { - - LOG.debug("Starting up {} instances of MQTT LWT service verticle", instanceCount); - - Future result = Future.future(); - - this.vertx.deployVerticle(this.mqttLwt, done -> { - if (done.succeeded()) { - LOG.debug("Verticle instance deployed [{}]", done.result()); - result.complete(); - } else { - LOG.debug("Failed to deploy verticle instance {}", done.cause()); - result.fail(done.cause()); - } - }); - - // combine all futures related to verticle instances deploy - result.setHandler(done -> { - if (done.succeeded()) { - resultHandler.complete(); - } else { - resultHandler.fail(done.cause()); - } - }); - } - - public void shutdown() { - if (this.running.compareAndSet(true, false)) { - this.shutdown(this.options.getStartupTimeout().getSeconds(), result -> { - // do nothing ? - }); - } - } - - /** - * Execute Vert.x shutdown with related verticles - * @param timeout max timeout to wait for shutdown - * @param shutdownHandler handler called when the shutdown ends - */ - private void shutdown(long timeout, Handler shutdownHandler) { - - try { - CountDownLatch latch = new CountDownLatch(1); - - if (this.vertx != null) { - - this.vertx.close(done -> { - if (done.failed()) { - LOG.error("Could not shut down MQTT LWT service cleanly", done.cause()); - } - latch.countDown(); - }); - - if (latch.await(timeout, TimeUnit.SECONDS)) { - LOG.info("MQTT LWT service shut down completed"); - shutdownHandler.handle(Boolean.TRUE); - } else { - LOG.error("Shut down of MQTT LWT service timed out, aborting..."); - shutdownHandler.handle(Boolean.FALSE); - } - } - - } catch (InterruptedException e) { - LOG.error("Shut down of MQTT LWT service has been interrupted, aborting..."); - shutdownHandler.handle(Boolean.FALSE); - } - } - - public static void main(String[] args) { - - if (System.getProperty(LOGGER_DELEGATE_FACTORY_CLASS_NAME) == null) { - System.setProperty(LOGGER_DELEGATE_FACTORY_CLASS_NAME, SLF4JLogDelegateFactory.class.getName()); - } - - Map env = System.getenv(); - - MqttLwtOptions options = MqttLwtOptions.fromEnv(env); - - LOG.info("MQTT LWT starting with options: {}", options); - - MqttLwt mqttLwt = new MqttLwt(options); - - Application app = new Application(options, mqttLwt); - app.registerVerticles(); - - Runtime.getRuntime().addShutdownHook(new Thread() { - @Override - public void run() { - super.run(); - LOG.info("MQTT LWT shutdown"); - app.shutdown(); - } - }); - } -} diff --git a/mqtt-lwt/src/main/java/enmasse/mqtt/DisconnectionData.java b/mqtt-lwt/src/main/java/enmasse/mqtt/DisconnectionData.java deleted file mode 100644 index 1b7d8164409..00000000000 --- a/mqtt-lwt/src/main/java/enmasse/mqtt/DisconnectionData.java +++ /dev/null @@ -1,45 +0,0 @@ -/* - * Copyright 2016-2018, EnMasse authors. - * License: Apache License 2.0 (see the file LICENSE or http://apache.org/licenses/LICENSE-2.0.html). - */ - - -package enmasse.mqtt; - -/** - * Provides information about client disconnection - */ -public class DisconnectionData { - - private String clientId; - private boolean isError; - - /** - * Constructor - * - * @param clientId client identifier disconnected - * @param isError if there is a disconnection error - */ - public DisconnectionData(String clientId, boolean isError) { - this.clientId = clientId; - this.isError = isError; - } - - /** - * Client identifier disconnected - * - * @return - */ - public String clientId() { - return this.clientId; - } - - /** - * If there is a disconnection error - * - * @return - */ - public boolean isError() { - return this.isError; - } -} diff --git a/mqtt-lwt/src/main/java/enmasse/mqtt/MqttLwt.java b/mqtt-lwt/src/main/java/enmasse/mqtt/MqttLwt.java deleted file mode 100644 index 90784bf019d..00000000000 --- a/mqtt-lwt/src/main/java/enmasse/mqtt/MqttLwt.java +++ /dev/null @@ -1,249 +0,0 @@ -/* - * Copyright 2016-2018, EnMasse authors. - * License: Apache License 2.0 (see the file LICENSE or http://apache.org/licenses/LICENSE-2.0.html). - */ - -package enmasse.mqtt; - -import enmasse.mqtt.endpoints.AmqpLwtEndpoint; -import enmasse.mqtt.endpoints.AmqpPublishEndpoint; -import enmasse.mqtt.messages.AmqpPublishMessage; -import enmasse.mqtt.messages.AmqpWillMessage; -import enmasse.mqtt.storage.LwtStorage; -import enmasse.mqtt.storage.impl.InMemoryLwtStorage; -import io.vertx.core.AbstractVerticle; -import io.vertx.core.Future; -import io.vertx.core.net.PemKeyCertOptions; -import io.vertx.core.net.PemTrustOptions; -import io.vertx.proton.ProtonClient; -import io.vertx.proton.ProtonClientOptions; -import io.vertx.proton.ProtonConnection; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import java.io.File; - -/** - * Vert.x based MQTT Last Will and Testament service for EnMasse - */ -public class MqttLwt extends AbstractVerticle { - - private static final Logger LOG = LoggerFactory.getLogger(MqttLwt.class); - - private static final String CONTAINER_ID = "lwt-service"; - private final MqttLwtOptions options; - private final LwtStorage lwtStorage; - - - private ProtonClient client; - - private AmqpLwtEndpoint lwtEndpoint; - private AmqpPublishEndpoint publishEndpoint; - - public MqttLwt(MqttLwtOptions options) { - - this.options = options; - this.lwtStorage = new InMemoryLwtStorage(); - } - - @Override - public void start(Future startFuture) throws Exception { - - LOG.info("Starting MQTT LWT service verticle..."); - this.connect(startFuture); - } - - @Override - public void stop(Future stopFuture) throws Exception { - - this.lwtEndpoint.close(); - this.publishEndpoint.close(); - this.lwtStorage.close(); - LOG.info("Stopping MQTT LWT service verticle..."); - stopFuture.complete(); - } - - /** - * Connect to the AMQP messaging network - * - * @param startFuture - */ - private void connect(Future startFuture) { - - this.client = ProtonClient.create(this.vertx); - - ProtonClientOptions options = this.createClientOptions(); - - - Future lwtConnFuture = Future.future(); - - // connecting to the messaging service internal (router network) - this.client.connect(options, this.options.getMessagingServiceHost(), this.options.getRouteContainerPort(), done -> { - - if (done.succeeded()) { - - ProtonConnection connection = done.result(); - connection.setContainer(CONTAINER_ID); - - // TODO - this.lwtEndpoint = new AmqpLwtEndpoint(connection); - this.lwtEndpoint - .willHandler(this::handleWill) - .disconnectionHandler(this::handleDisconnection); - this.lwtEndpoint.open(); - - connection.openHandler(o -> { - LOG.info("MQTT LWT service connected to the messaging service internal ..."); - lwtConnFuture.complete(); - }); - - } else { - - LOG.error("Error connecting MQTT LWT service to the messaging service internal ...", done.cause()); - - lwtConnFuture.fail(done.cause()); - } - - }); - - // compose the connection with messaging service - lwtConnFuture.compose(v -> { - - Future publishConnFuture = Future.future(); - - this.client.connect(options, this.options.getMessagingServiceHost(), this.options.getMessagingServiceNormalPort(), done -> { - - if (done.succeeded()) { - - - ProtonConnection connection = done.result(); - connection.setContainer(CONTAINER_ID); - - // TODO - this.publishEndpoint = new AmqpPublishEndpoint(connection); - this.publishEndpoint.open(); - - connection.openHandler(o -> { - LOG.info("MQTT LWT service connected to the messaging service ..."); - publishConnFuture.complete(); - }); - - } else { - - LOG.error("Error connecting MQTT LWT service to the messaging service ...", done.cause()); - - publishConnFuture.fail(done.cause()); - } - - }); - - return publishConnFuture; - - // compose the connection to the messaging service with connection to storage service - }).compose(v -> { - - // connecting to the storage service - this.lwtStorage.open(done -> { - - if (done.succeeded()) { - - LOG.info("MQTT LWT service connected to the storage service ..."); - - // TODO - - startFuture.complete(); - - } else { - - LOG.error("Error connecting MQTT LWT service to the storage service ...", done.cause()); - - startFuture.fail(done.cause()); - } - - }); - - }, startFuture); - } - - /** - * Create an options instance for the ProtonClient - * - * @return ProtonClient options instance - */ - private ProtonClientOptions createClientOptions() { - - ProtonClientOptions options = new ProtonClientOptions(); - options.setConnectTimeout(5000); - options.setReconnectAttempts(-1).setReconnectInterval(1000); // reconnect forever, every 1000 millisecs - - String certDir = this.options.getCertDir(); - if (certDir != null) { - options.setSsl(true) - .addEnabledSaslMechanism("EXTERNAL") - .setHostnameVerificationAlgorithm("") - .setPemTrustOptions(new PemTrustOptions() - .addCertPath(new File(certDir, "ca.crt").getAbsolutePath())) - .setPemKeyCertOptions(new PemKeyCertOptions() - .addCertPath(new File(certDir, "tls.crt").getAbsolutePath()) - .addKeyPath(new File(certDir, "tls.key").getAbsolutePath())); - } - return options; - } - - private void handleWill(WillData willData) { - - // will message received, check for updating or adding - this.lwtStorage.get(willData.clientId(), done -> { - - if (done.succeeded()) { - this.lwtStorage.update(willData.clientId(), willData.amqpWillMessage(), ar -> { - - LOG.info("Updated will for client {}", willData.clientId()); - }); - } else { - this.lwtStorage.add(willData.clientId(), willData.amqpWillMessage(), ar -> { - - LOG.info("Added will for client {}", willData.clientId()); - }); - } - }); - } - - private void handleDisconnection(DisconnectionData disconnectionData) { - - // clean disconnection, just delete will message - if (!disconnectionData.isError()) { - - this.lwtStorage.delete(disconnectionData.clientId(), done -> { - - LOG.info("Deleted will for client {}", disconnectionData.clientId()); - }); - } else { - - // brute disconnection, get will message and deliver it - this.lwtStorage.get(disconnectionData.clientId(), ar -> { - - if (ar.succeeded()) { - - AmqpWillMessage amqpWillMessage = ar.result(); - - AmqpPublishMessage amqpPublishMessage = - new AmqpPublishMessage(amqpWillMessage.qos(), false, amqpWillMessage.isRetain(), amqpWillMessage.topic(), amqpWillMessage.payload()); - - this.publishEndpoint.publish(amqpPublishMessage, ar1 -> { - - if (ar1.succeeded()) { - - LOG.info("Published will message for client {}", disconnectionData.clientId()); - - this.lwtStorage.delete(disconnectionData.clientId(), ar2 -> { - - LOG.info("Deleted will for client {}", disconnectionData.clientId()); - }); - } - }); - } - }); - } - } -} diff --git a/mqtt-lwt/src/main/java/enmasse/mqtt/MqttLwtOptions.java b/mqtt-lwt/src/main/java/enmasse/mqtt/MqttLwtOptions.java deleted file mode 100644 index e759289ed61..00000000000 --- a/mqtt-lwt/src/main/java/enmasse/mqtt/MqttLwtOptions.java +++ /dev/null @@ -1,98 +0,0 @@ -/* - * Copyright 2016-2018, EnMasse authors. - * License: Apache License 2.0 (see the file LICENSE or http://apache.org/licenses/LICENSE-2.0.html). - */ - -package enmasse.mqtt; - -import java.time.Duration; -import java.util.Map; -import java.util.Optional; - -public class MqttLwtOptions { - private String messagingServiceHost; - private int messagingServiceNormalPort; - private int routeContainerPort; - private String certDir; - private Duration startupTimeout; - - public String getMessagingServiceHost() { - return messagingServiceHost; - } - - public void setMessagingServiceHost(String messagingServiceHost) { - this.messagingServiceHost = messagingServiceHost; - } - - public int getMessagingServiceNormalPort() { - return messagingServiceNormalPort; - } - - public void setMessagingServiceNormalPort(int messagingServiceNormalPort) { - this.messagingServiceNormalPort = messagingServiceNormalPort; - } - - public int getRouteContainerPort() { - return routeContainerPort; - } - - public void setRouteContainerPort(int routeContainerPort) { - this.routeContainerPort = routeContainerPort; - } - - public String getCertDir() { - return certDir; - } - - public void setCertDir(String certDir) { - this.certDir = certDir; - } - - public Duration getStartupTimeout() { - return startupTimeout; - } - - public void setStartupTimeout(Duration startupTimeout) { - this.startupTimeout = startupTimeout; - } - - private static Optional getEnv(Map env, String envVar) { - return Optional.ofNullable(env.get(envVar)); - } - - public static MqttLwtOptions fromEnv(Map env) { - - MqttLwtOptions options = new MqttLwtOptions(); - - options.setMessagingServiceHost(getEnv(env, "MESSAGING_SERVICE_HOST") - .orElse("0.0.0.0")); - - options.setMessagingServiceNormalPort(getEnv(env, "MESSAGING_SERVICE_NORMAL_PORT") - .map(Integer::parseInt) - .orElse(5672)); - - options.setRouteContainerPort(getEnv(env, "MESSAGING_SERVICE_ROUTE_CONTAINER_PORT") - .map(Integer::parseInt) - .orElse(55671)); - - options.setStartupTimeout(getEnv(env, "ENMASSE_MQTT_STARTUPTIMEOUT") - .map(i -> Duration.ofSeconds(Long.parseLong(i))) - .orElse(Duration.ofMinutes(5))); - - options.setCertDir(getEnv(env, "CERT_DIR") - .orElseThrow(() -> new IllegalArgumentException("CERT_DIR is required"))); - - return options; - } - - @Override - public String toString() { - return "MqttLwtOptions{" + - "messagingServiceHost='" + messagingServiceHost + '\'' + - ", messagingServiceNormalPort=" + messagingServiceNormalPort + - ", routeContainerPort=" + routeContainerPort + - ", certDir='" + certDir + '\'' + - ", startupTimeout=" + startupTimeout + - '}'; - } -} diff --git a/mqtt-lwt/src/main/java/enmasse/mqtt/WillData.java b/mqtt-lwt/src/main/java/enmasse/mqtt/WillData.java deleted file mode 100644 index 669f43d1149..00000000000 --- a/mqtt-lwt/src/main/java/enmasse/mqtt/WillData.java +++ /dev/null @@ -1,46 +0,0 @@ -/* - * Copyright 2016-2018, EnMasse authors. - * License: Apache License 2.0 (see the file LICENSE or http://apache.org/licenses/LICENSE-2.0.html). - */ - -package enmasse.mqtt; - -import enmasse.mqtt.messages.AmqpWillMessage; - -/** - * Provides "will" information - */ -public class WillData { - - private AmqpWillMessage amqpWillMessage; - private String clientId; - - /** - * Constructor - * - * @param clientId client identifier related to the AMQP_WILL message - * @param amqpWillMessage AMQP_WILL message - */ - public WillData(String clientId, AmqpWillMessage amqpWillMessage) { - this.clientId = clientId; - this.amqpWillMessage = amqpWillMessage; - } - - /** - * AMQP_WILL message - * - * @return - */ - public AmqpWillMessage amqpWillMessage() { - return this.amqpWillMessage; - } - - /** - * Client identifier related to the AMQP_WILL message - * - * @return - */ - public String clientId() { - return this.clientId; - } -} diff --git a/mqtt-lwt/src/main/java/enmasse/mqtt/endpoints/AmqpLwtEndpoint.java b/mqtt-lwt/src/main/java/enmasse/mqtt/endpoints/AmqpLwtEndpoint.java deleted file mode 100644 index 86684731e28..00000000000 --- a/mqtt-lwt/src/main/java/enmasse/mqtt/endpoints/AmqpLwtEndpoint.java +++ /dev/null @@ -1,186 +0,0 @@ -/* - * Copyright 2016-2018, EnMasse authors. - * License: Apache License 2.0 (see the file LICENSE or http://apache.org/licenses/LICENSE-2.0.html). - */ - -package enmasse.mqtt.endpoints; - -import enmasse.mqtt.DisconnectionData; -import enmasse.mqtt.WillData; -import enmasse.mqtt.messages.AmqpWillMessage; -import io.vertx.core.AsyncResult; -import io.vertx.core.Handler; -import io.vertx.proton.ProtonConnection; -import io.vertx.proton.ProtonDelivery; -import io.vertx.proton.ProtonQoS; -import io.vertx.proton.ProtonReceiver; -import org.apache.qpid.proton.amqp.messaging.Accepted; -import org.apache.qpid.proton.amqp.transport.AmqpError; -import org.apache.qpid.proton.amqp.transport.ErrorCondition; -import org.apache.qpid.proton.message.Message; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -/** - * LWT endpoint - */ -public class AmqpLwtEndpoint { - - private static final Logger LOG = LoggerFactory.getLogger(AmqpLwtEndpoint.class); - - public static final int AMQP_WILL_CREDITS = 1; - public static final String LWT_SERVICE_ENDPOINT = "$lwt"; - - private ProtonConnection connection; - - private Handler willHandler; - private Handler disconnectionHandler; - - /** - * Constructor - * - * @param connection ProtonConnection instance - */ - public AmqpLwtEndpoint(ProtonConnection connection) { - this.connection = connection; - } - - /** - * Open the endpoint, opening the connection - */ - public void open() { - - if ((this.willHandler == null) || - (this.disconnectionHandler == null)) { - throw new IllegalStateException("Handlers for received will and disconnection must be set"); - } - - this.connection - .sessionOpenHandler(session -> session.open()) - .receiverOpenHandler(this::receiverHandler) - .open(); - } - - /** - * Close the endpoint, closing the connection - */ - public void close() { - - // TODO : check what to close other than connection while this class evolves - if (this.connection != null) { - this.connection.close(); - } - } - - private void receiverHandler(ProtonReceiver receiver) { - - LOG.info("Attaching link request"); - - // the LWT service supports only the control address - if (!receiver.getRemoteTarget().getAddress().equals(LWT_SERVICE_ENDPOINT)) { - - ErrorCondition errorCondition = - new ErrorCondition(AmqpError.NOT_FOUND, "The provided address isn't supported"); - - receiver.setCondition(errorCondition) - .close(); - } else { - - receiver.setTarget(receiver.getRemoteTarget()) - .setQoS(ProtonQoS.AT_LEAST_ONCE) - .handler((delivery, message) -> { - this.messageHandler(receiver, delivery, message); - }) - .closeHandler(ar -> { - this.closeHandler(receiver, ar); - }) - .detachHandler(ar -> { - this.closeHandler(receiver, ar); - }) - .setPrefetch(0) - .open(); - - receiver.flow(AMQP_WILL_CREDITS); - } - } - - private void messageHandler(ProtonReceiver receiver, ProtonDelivery delivery, Message message) { - - try { - - AmqpWillMessage amqpWillMessage = AmqpWillMessage.from(message); - - LOG.info("Received will on topic [{}] by client [{}]", amqpWillMessage.topic(), receiver.getName()); - - // TODO : having a callback to check if handling went well and send right disposition ? - this.willHandler.handle(new WillData(receiver.getName(), amqpWillMessage)); - - delivery.disposition(Accepted.getInstance(), true); - - // NOTE : after receiving the AMQP_WILL, a new credit is issued because - // with AMQP we want to change the "will" message during the client life - receiver.flow(AMQP_WILL_CREDITS); - - } catch (IllegalArgumentException ex) { - - LOG.error("Error decoding will message", ex); - - ErrorCondition errorCondition = - new ErrorCondition(AmqpError.DECODE_ERROR, "Received message is not a will message"); - - receiver.setCondition(errorCondition) - .close(); - } - } - - private void closeHandler(ProtonReceiver receiver, AsyncResult ar) { - - // link detached without error, so the "will" should be cleared and not sent - if (ar.succeeded()) { - - LOG.info("Clean disconnection from {}", receiver.getName()); - - // TODO: for now nothing to do ? - - // link detached with error, so the "will" should be sent - } else { - - LOG.info("Brute disconnection from {}", receiver.getName()); - - ErrorCondition errorCondition = new ErrorCondition(receiver.getRemoteCondition().getCondition(), - String.format("client detached with: %s", receiver.getRemoteCondition().getDescription())); - - receiver.setCondition(errorCondition); - } - - receiver.close(); - - if (this.disconnectionHandler != null) { - this.disconnectionHandler.handle(new DisconnectionData(receiver.getName(), ar.failed())); - } - } - - /** - * Set the handler called when an AMQP_WILL message is received on the endpoint - * - * @param handler the handler - * @return a reference to the current AmqpLwtEndpoint instance - */ - public AmqpLwtEndpoint willHandler(Handler handler) { - - this.willHandler = handler; - return this; - } - - /** - * Set the handler called when a client disconnect on this endpoint - * - * @param handler the handler - * @return a reference to the current AmqpLwtEndpoint instance - */ - public AmqpLwtEndpoint disconnectionHandler(Handler handler) { - - this.disconnectionHandler = handler; - return this; - } -} diff --git a/mqtt-lwt/src/main/java/enmasse/mqtt/endpoints/AmqpPublishEndpoint.java b/mqtt-lwt/src/main/java/enmasse/mqtt/endpoints/AmqpPublishEndpoint.java deleted file mode 100644 index 44bf247dd13..00000000000 --- a/mqtt-lwt/src/main/java/enmasse/mqtt/endpoints/AmqpPublishEndpoint.java +++ /dev/null @@ -1,108 +0,0 @@ -/* - * Copyright 2016-2018, EnMasse authors. - * License: Apache License 2.0 (see the file LICENSE or http://apache.org/licenses/LICENSE-2.0.html). - */ - -package enmasse.mqtt.endpoints; - -import enmasse.mqtt.messages.AmqpPublishMessage; -import io.netty.handler.codec.mqtt.MqttQoS; -import io.vertx.core.AsyncResult; -import io.vertx.core.Future; -import io.vertx.core.Handler; -import io.vertx.proton.ProtonConnection; -import io.vertx.proton.ProtonDelivery; -import io.vertx.proton.ProtonQoS; -import io.vertx.proton.ProtonSender; -import org.apache.qpid.proton.amqp.messaging.Accepted; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -/** - * Publisher endpoint - */ -public class AmqpPublishEndpoint { - - private static final Logger LOG = LoggerFactory.getLogger(AmqpPublishEndpoint.class); - - private ProtonConnection connection; - - /** - * Constructor - * - * @param connection ProtonConnection instance - */ - public AmqpPublishEndpoint(ProtonConnection connection) { - this.connection = connection; - } - - /** - * Open the endpoint, opening the connection - */ - public void open() { - - this.connection - .sessionOpenHandler(session -> session.open()) - .open(); - } - - /** - * Send the AMQP_PUBLISH to the attached topic/address - * - * @param amqpPublishMessage AMQP_PUBLISH message - */ - public void publish(AmqpPublishMessage amqpPublishMessage, Handler> handler) { - - // send AMQP_PUBLISH message - - LOG.info("Will ready for publishing on topic [{}]", amqpPublishMessage.topic()); - - // use sender for QoS 0/1 messages - if (amqpPublishMessage.qos() != MqttQoS.EXACTLY_ONCE) { - - ProtonSender sender = this.connection.createSender(amqpPublishMessage.topic()); - - sender.setQoS(ProtonQoS.AT_LEAST_ONCE) - .open(); - - if (amqpPublishMessage.qos() == MqttQoS.AT_MOST_ONCE) { - - sender.send(amqpPublishMessage.toAmqp()); - sender.close(); - LOG.info("AMQP published on {}", amqpPublishMessage.topic()); - - handler.handle(Future.succeededFuture(null)); - - } else { - - sender.send(amqpPublishMessage.toAmqp(), delivery -> { - - if (delivery.getRemoteState() == Accepted.getInstance()) { - LOG.info("AMQP publish delivery {}", delivery.getRemoteState()); - handler.handle(Future.succeededFuture(delivery)); - } else { - handler.handle(Future.failedFuture(String.format("AMQP publish delivery %s", delivery.getRemoteState()))); - } - - sender.close(); - }); - } - - // use sender for QoS 2 messages - } else { - - // TODO - } - } - - /** - * Close the endpoint, closing the connection - */ - public void close() { - - // TODO : check what to close other than connection while this class evolves - if (this.connection != null) { - this.connection.close(); - } - } -} diff --git a/mqtt-lwt/src/main/java/enmasse/mqtt/messages/AmqpPublishMessage.java b/mqtt-lwt/src/main/java/enmasse/mqtt/messages/AmqpPublishMessage.java deleted file mode 100644 index 8d63bc1c886..00000000000 --- a/mqtt-lwt/src/main/java/enmasse/mqtt/messages/AmqpPublishMessage.java +++ /dev/null @@ -1,200 +0,0 @@ -/* - * Copyright 2016-2018, EnMasse authors. - * License: Apache License 2.0 (see the file LICENSE or http://apache.org/licenses/LICENSE-2.0.html). - */ - -package enmasse.mqtt.messages; - -import io.netty.handler.codec.mqtt.MqttQoS; -import io.vertx.core.buffer.Buffer; -import io.vertx.proton.ProtonHelper; -import org.apache.qpid.proton.amqp.Binary; -import org.apache.qpid.proton.amqp.Symbol; -import org.apache.qpid.proton.amqp.messaging.ApplicationProperties; -import org.apache.qpid.proton.amqp.messaging.Data; -import org.apache.qpid.proton.amqp.messaging.Header; -import org.apache.qpid.proton.amqp.messaging.MessageAnnotations; -import org.apache.qpid.proton.amqp.messaging.Section; -import org.apache.qpid.proton.message.Message; - -import java.util.HashMap; -import java.util.Map; - -/** - * Represents an AMQP_PUBLISH message - */ -public class AmqpPublishMessage { - - private static final String AMQP_RETAIN_ANNOTATION = "x-opt-retain-message"; - private static final String AMQP_QOS_ANNOTATION = "x-opt-mqtt-qos"; - - private final MqttQoS qos; - private final boolean isDup; - private final boolean isRetain; - private final String topic; - private final Buffer payload; - - /** - * Constructor - * @param qos MQTT QoS level - * @param isDup if the message is a duplicate - * @param isRetain if the message needs to be retained - * @param topic topic on which the message is published - * @param payload message payload - */ - public AmqpPublishMessage(MqttQoS qos, boolean isDup, boolean isRetain, String topic, Buffer payload) { - this.qos = qos; - this.isDup = isDup; - this.isRetain = isRetain; - this.topic = topic; - this.payload = payload; - } - - /** - * Return an AMQP_PUBLISH message from the raw AMQP one - * - * @param message raw AMQP message - * @return AMQP_PUBLISH message - */ - public static AmqpPublishMessage from(Message message) { - - boolean isRetain = false; - MqttQoS qos = MqttQoS.AT_MOST_ONCE; - - // raw AMQP messages published from native AMQP clients could not have annotations - MessageAnnotations messageAnnotations = message.getMessageAnnotations(); - if (messageAnnotations == null) { - - if (message.getHeader() != null) { - // if qos annotation isn't present, fallback to "durable" header field - qos = ((message.getHeader().getDurable() == null) || !message.getHeader().getDurable()) - ? MqttQoS.AT_MOST_ONCE : MqttQoS.AT_LEAST_ONCE; - } - - } else { - - if (messageAnnotations.getValue().containsKey(Symbol.valueOf(AMQP_RETAIN_ANNOTATION))) { - isRetain = (boolean) messageAnnotations.getValue().get(Symbol.valueOf(AMQP_RETAIN_ANNOTATION)); - } - - if (messageAnnotations.getValue().containsKey(Symbol.valueOf(AMQP_QOS_ANNOTATION))) { - int value = (int) messageAnnotations.getValue().get(Symbol.valueOf(AMQP_QOS_ANNOTATION)); - qos = MqttQoS.valueOf(value); - } else { - - if (message.getHeader() != null) { - // if qos annotation isn't present, fallback to "durable" header field - qos = ((message.getHeader().getDurable() == null) || !message.getHeader().getDurable()) - ? MqttQoS.AT_MOST_ONCE : MqttQoS.AT_LEAST_ONCE; - } - } - } - - boolean isDup = (message.getDeliveryCount() > 0); - - String topic = message.getAddress(); - - // TODO: to remove - // workaround for the Artemis broker which change the original "To" property - // in the AMQP message when message-id is null - ApplicationProperties applicationProperties = message.getApplicationProperties(); - if (applicationProperties != null) { - - Object amqOrigAddress = applicationProperties.getValue().get("_AMQ_ORIG_ADDRESS"); - topic = (amqOrigAddress != null) ? amqOrigAddress.toString() : topic; - } - - Section section = message.getBody(); - if ((section != null) && (section instanceof Data)) { - - Buffer payload = Buffer.buffer(((Data) section).getValue().getArray()); - return new AmqpPublishMessage(qos, isDup, isRetain, topic, payload); - - } else { - throw new IllegalArgumentException("AMQP message wrong body type"); - } - } - - /** - * Return a raw AMQP message - * - * @return - */ - public Message toAmqp() { - - Message message = ProtonHelper.message(); - - Map map = new HashMap<>(); - map.put(Symbol.valueOf(AMQP_RETAIN_ANNOTATION), this.isRetain); - map.put(Symbol.valueOf(AMQP_QOS_ANNOTATION), this.qos.value()); - MessageAnnotations messageAnnotations = new MessageAnnotations(map); - message.setMessageAnnotations(messageAnnotations); - - message.setAddress(this.topic); - - Header header = new Header(); - header.setDurable(this.qos != MqttQoS.AT_MOST_ONCE); - message.setHeader(header); - - message.setDeliveryCount(this.isDup ? 1 : 0); - - // the payload could be null (or empty) - if (this.payload != null) - message.setBody(new Data(new Binary(this.payload.getBytes()))); - - return message; - } - - /** - * MQTT QoS level - * @return - */ - public MqttQoS qos() { - return this.qos; - } - - /** - * If the message is a duplicate - * @return - */ - public boolean isDup() { - return this.isDup; - } - - /** - * If the message needs to be retained - * @return - */ - public boolean isRetain() { - return this.isRetain; - } - - /** - * Topic on which the message is published - * @return - */ - public String topic() { - return this.topic; - } - - /** - * Message payload - * @return - */ - public Buffer payload() { - return this.payload; - } - - @Override - public String toString() { - - return "AmqpPublishMessage{" + - "qos=" + this.qos + - ", isDup=" + this.isDup + - ", isRetain=" + this.isRetain + - ", topic=" + this.topic + - ", payload=" + this.payload + - "}"; - } -} - diff --git a/mqtt-lwt/src/main/java/enmasse/mqtt/messages/AmqpWillMessage.java b/mqtt-lwt/src/main/java/enmasse/mqtt/messages/AmqpWillMessage.java deleted file mode 100644 index ae847c98f93..00000000000 --- a/mqtt-lwt/src/main/java/enmasse/mqtt/messages/AmqpWillMessage.java +++ /dev/null @@ -1,176 +0,0 @@ -/* - * Copyright 2016-2018, EnMasse authors. - * License: Apache License 2.0 (see the file LICENSE or http://apache.org/licenses/LICENSE-2.0.html). - */ - -package enmasse.mqtt.messages; - -import io.netty.handler.codec.mqtt.MqttQoS; -import io.vertx.core.buffer.Buffer; -import io.vertx.proton.ProtonHelper; -import org.apache.qpid.proton.amqp.Binary; -import org.apache.qpid.proton.amqp.Symbol; -import org.apache.qpid.proton.amqp.messaging.Data; -import org.apache.qpid.proton.amqp.messaging.Header; -import org.apache.qpid.proton.amqp.messaging.MessageAnnotations; -import org.apache.qpid.proton.amqp.messaging.Section; -import org.apache.qpid.proton.message.Message; - -import java.util.HashMap; -import java.util.Map; - -/** - * Represents an AMQP_WILL message - */ -public class AmqpWillMessage { - - public static final String AMQP_SUBJECT = "will"; - - private static final String AMQP_RETAIN_ANNOTATION = "x-opt-retain-message"; - private static final String AMQP_QOS_ANNOTATION = "x-opt-mqtt-qos"; - - private final boolean isRetain; - private final String topic; - private final MqttQoS qos; - private final Buffer payload; - - /** - * Constructor - * - * @param isRetain will retain flag - * @param topic will topic - * @param qos MQTT QoS level - * @param payload will message payload - */ - public AmqpWillMessage(boolean isRetain, String topic, MqttQoS qos, Buffer payload) { - - this.isRetain = isRetain; - this.topic = topic; - this.qos = qos; - this.payload = payload; - } - - /** - * Return an AMQP_WILL message from the raw AMQP one - * - * @param message raw AMQP message - * @return AMQP_WILL message - */ - public static AmqpWillMessage from(Message message) { - - if (!message.getSubject().equals(AMQP_SUBJECT)) { - throw new IllegalArgumentException(String.format("AMQP message subject is no %s", AMQP_SUBJECT)); - } - - MessageAnnotations messageAnnotations = message.getMessageAnnotations(); - if (messageAnnotations == null) { - throw new IllegalArgumentException("AMQP message has no annotations"); - } else { - - boolean isRetain = false; - if (messageAnnotations.getValue().containsKey(Symbol.valueOf(AMQP_RETAIN_ANNOTATION))) { - isRetain = (boolean) messageAnnotations.getValue().get(Symbol.valueOf(AMQP_RETAIN_ANNOTATION)); - } - - MqttQoS qos; - if (messageAnnotations.getValue().containsKey(Symbol.valueOf(AMQP_QOS_ANNOTATION))) { - int value = (int) messageAnnotations.getValue().get(Symbol.valueOf(AMQP_QOS_ANNOTATION)); - qos = MqttQoS.valueOf(value); - } else { - - if (message.getHeader() != null) { - // if qos annotation isn't present, fallback to "durable" header field - qos = ((message.getHeader().getDurable() == null) || !message.getHeader().getDurable()) - ? MqttQoS.AT_MOST_ONCE : MqttQoS.AT_LEAST_ONCE; - } else { - qos = MqttQoS.AT_MOST_ONCE; - } - } - - String topic = message.getAddress(); - - Section section = message.getBody(); - if ((section != null) && (section instanceof Data)) { - - Buffer payload = Buffer.buffer(((Data) section).getValue().getArray()); - return new AmqpWillMessage(isRetain, topic, qos, payload); - - } else { - throw new IllegalArgumentException("AMQP message wrong body type"); - } - } - } - - /** - * Return a raw AMQP message - * - * @return - */ - public Message toAmqp() { - - Message message = ProtonHelper.message(); - - message.setSubject(AMQP_SUBJECT); - - Map map = new HashMap<>(); - map.put(Symbol.valueOf(AMQP_RETAIN_ANNOTATION), this.isRetain); - map.put(Symbol.valueOf(AMQP_QOS_ANNOTATION), this.qos.value()); - MessageAnnotations messageAnnotations = new MessageAnnotations(map); - message.setMessageAnnotations(messageAnnotations); - - message.setAddress(this.topic); - - Header header = new Header(); - header.setDurable(this.qos != MqttQoS.AT_MOST_ONCE); - message.setHeader(header); - - // the payload could be null (or empty) - if (this.payload != null) - message.setBody(new Data(new Binary(this.payload.getBytes()))); - - return message; - } - - /** - * Will retain flag - * @return - */ - public boolean isRetain() { - return this.isRetain; - } - - /** - * Will topic - * @return - */ - public String topic() { - return this.topic; - } - - /** - * MQTT QoS level - * @return - */ - public MqttQoS qos() { - return this.qos; - } - - /** - * Will message payload - * @return - */ - public Buffer payload() { - return this.payload; - } - - @Override - public String toString() { - - return "AmqpWillMessage{" + - "isRetain=" + this.isRetain + - ", topic=" + this.topic + - ", qos=" + this.qos + - ", payload=" + this.payload + - "}"; - } -} diff --git a/mqtt-lwt/src/main/java/enmasse/mqtt/storage/LwtStorage.java b/mqtt-lwt/src/main/java/enmasse/mqtt/storage/LwtStorage.java deleted file mode 100644 index e55a7f80a2b..00000000000 --- a/mqtt-lwt/src/main/java/enmasse/mqtt/storage/LwtStorage.java +++ /dev/null @@ -1,62 +0,0 @@ -/* - * Copyright 2016-2018, EnMasse authors. - * License: Apache License 2.0 (see the file LICENSE or http://apache.org/licenses/LICENSE-2.0.html). - */ - -package enmasse.mqtt.storage; - -import enmasse.mqtt.messages.AmqpWillMessage; -import io.vertx.core.AsyncResult; -import io.vertx.core.Handler; - -/** - * LWT Storage Service interface - */ -public interface LwtStorage { - - /** - * Open and connect to the storage service - * - * @param handler handler called at the end of connection attempt - */ - void open(Handler> handler); - - /** - * Store the provided "will" information - * - * @param clientId client identifier for the "will" information - * @param willMessage "will" information to store - * @param handler handler called with the result code - */ - void add(String clientId, AmqpWillMessage willMessage, Handler> handler); - - /** - * Get "will" information for the specified client - * - * @param clientId client identifier for which getting "will" information - * @param handler handler called with the "will" information retrieved - */ - void get(String clientId, Handler> handler); - - /** - * Update "will" information for the specified client - * - * @param clientId client identifier for which updating "will" information - * @param willMessage "will" information to update - * @param handler handler called with the result code - */ - void update(String clientId, AmqpWillMessage willMessage, Handler> handler); - - /** - * Delete "will" information for the specified client - * - * @param clientId client identifier for which deleting "will" information - * @param handler handler called with the result code - */ - void delete(String clientId, Handler> handler); - - /** - * Close and disconnect from the storage service - */ - void close(); -} diff --git a/mqtt-lwt/src/main/java/enmasse/mqtt/storage/impl/InMemoryLwtStorage.java b/mqtt-lwt/src/main/java/enmasse/mqtt/storage/impl/InMemoryLwtStorage.java deleted file mode 100644 index b042b84f424..00000000000 --- a/mqtt-lwt/src/main/java/enmasse/mqtt/storage/impl/InMemoryLwtStorage.java +++ /dev/null @@ -1,97 +0,0 @@ -/* - * Copyright 2016-2018, EnMasse authors. - * License: Apache License 2.0 (see the file LICENSE or http://apache.org/licenses/LICENSE-2.0.html). - */ - -package enmasse.mqtt.storage.impl; - -import enmasse.mqtt.messages.AmqpWillMessage; -import enmasse.mqtt.storage.LwtStorage; -import io.vertx.core.AsyncResult; -import io.vertx.core.Future; -import io.vertx.core.Handler; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import java.util.HashMap; -import java.util.Map; - -/** - * In memory implementation of the LWT Storage service - */ -public class InMemoryLwtStorage implements LwtStorage { - - public static final Logger LOG = LoggerFactory.getLogger(InMemoryLwtStorage.class); - - Map wills; - - @Override - public void open(Handler> handler) { - - if (wills == null) { - wills = new HashMap<>(); - } - - handler.handle(Future.succeededFuture()); - } - - @Override - public void add(String clientId, AmqpWillMessage willMessage, Handler> handler) { - - if (this.wills.containsKey(clientId)) { - LOG.warn("Will already existing for the client {}", clientId); - handler.handle(Future.failedFuture(new IllegalArgumentException("Will already existing for the client"))); - } else { - this.wills.put(clientId, willMessage); - LOG.info("Will added for the client {}", clientId); - handler.handle(Future.succeededFuture()); - } - } - - @Override - public void get(String clientId, Handler> handler) { - - if (!this.wills.containsKey(clientId)) { - LOG.warn("No will for the client {}", clientId); - handler.handle(Future.failedFuture(new IllegalArgumentException("No will for the client"))); - } else { - LOG.info("Will retrieved for the client {}", clientId); - handler.handle(Future.succeededFuture(this.wills.get(clientId))); - } - } - - @Override - public void update(String clientId, AmqpWillMessage willMessage, Handler> handler) { - - if (!this.wills.containsKey(clientId)) { - LOG.warn("No will for the client {}", clientId); - handler.handle(Future.failedFuture(new IllegalArgumentException("No will for the client"))); - } else { - this.wills.put(clientId, willMessage); - LOG.info("Will updated for the client {}", clientId); - handler.handle(Future.succeededFuture()); - } - } - - @Override - public void delete(String clientId, Handler> handler) { - - if (!this.wills.containsKey(clientId)) { - LOG.warn("No will for the client {}", clientId); - handler.handle(Future.failedFuture(new IllegalArgumentException("No will for the client"))); - } else { - this.wills.remove(clientId); - LOG.error("Will deleted for the client {}", clientId); - handler.handle(Future.succeededFuture()); - } - } - - @Override - public void close() { - - // TODO - if (wills != null) { - wills.clear(); - } - } -} diff --git a/mqtt-lwt/src/main/resources/logback.xml b/mqtt-lwt/src/main/resources/logback.xml deleted file mode 100644 index 9ed3476bbee..00000000000 --- a/mqtt-lwt/src/main/resources/logback.xml +++ /dev/null @@ -1,15 +0,0 @@ - - - - - - - %d{yyyy-MM-dd'T'HH:mm:ss.SSS'Z',GMT} %-5p [%c{0}] %m%n - - - - - - - - diff --git a/mqtt-lwt/src/test/java/enmasse/mqtt/InMemoryLwtStorageTest.java b/mqtt-lwt/src/test/java/enmasse/mqtt/InMemoryLwtStorageTest.java deleted file mode 100644 index 6826966cd51..00000000000 --- a/mqtt-lwt/src/test/java/enmasse/mqtt/InMemoryLwtStorageTest.java +++ /dev/null @@ -1,151 +0,0 @@ -/* - * Copyright 2016-2018, EnMasse authors. - * License: Apache License 2.0 (see the file LICENSE or http://apache.org/licenses/LICENSE-2.0.html). - */ - -package enmasse.mqtt; - -import enmasse.mqtt.messages.AmqpWillMessage; -import enmasse.mqtt.storage.LwtStorage; -import enmasse.mqtt.storage.impl.InMemoryLwtStorage; -import io.netty.handler.codec.mqtt.MqttQoS; -import io.vertx.core.buffer.Buffer; -import io.vertx.junit5.VertxExtension; -import io.vertx.junit5.VertxTestContext; -import org.junit.jupiter.api.AfterEach; -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Test; -import org.junit.jupiter.api.extension.ExtendWith; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import static org.junit.jupiter.api.Assertions.*; - -/** - * Tests related to the LWT storage service only - */ -@ExtendWith(VertxExtension.class) -public class InMemoryLwtStorageTest { - - protected final Logger LOG = LoggerFactory.getLogger(InMemoryLwtStorageTest.class); - private static final AmqpWillMessage WILL_MESSAGE = - new AmqpWillMessage(true, "will_topic", MqttQoS.AT_MOST_ONCE, Buffer.buffer("Hello")); - private static final String CLIENT_ID = "client_id"; - private LwtStorage lwtStorage; - - @BeforeEach - public void before(VertxTestContext context) { - - this.lwtStorage = new InMemoryLwtStorage(); - this.lwtStorage.open(context.succeeding(id -> context.completeNow())); - } - - @AfterEach - public void after() { - this.lwtStorage.close(); - } - - @Test - public void addNotExistingWill(VertxTestContext context) { - this.lwtStorage.add(CLIENT_ID, WILL_MESSAGE, done -> { - if (done.succeeded()) { - this.lwtStorage.get(CLIENT_ID, done1 -> { - if (done1.succeeded()) { - AmqpWillMessage willMessage1 = done1.result(); - context.verify(() -> assertEquals(willMessage1, WILL_MESSAGE)); - LOG.info("Added a not existing will"); - } else { - context.verify(() -> fail("")); - } - context.completeNow(); - }); - } else { - context.verify(() -> fail("")); - context.completeNow(); - } - }); - } - - @Test - public void addExistingWill(VertxTestContext context) { - this.lwtStorage.add(CLIENT_ID, WILL_MESSAGE, done -> { - if (done.succeeded()) { - this.lwtStorage.add(CLIENT_ID, WILL_MESSAGE, done1 -> { - context.verify(() -> assertTrue(!done1.succeeded())); - LOG.info("Will to add already exists"); - context.completeNow(); - }); - } else { - context.verify(() -> fail("")); - context.completeNow(); - } - }); - } - - @Test - public void updateExistingWill(VertxTestContext context) { - this.lwtStorage.add(CLIENT_ID, WILL_MESSAGE, done -> { - if (done.succeeded()) { - AmqpWillMessage willMessage1 = new AmqpWillMessage(false, "will_topic_1", MqttQoS.AT_LEAST_ONCE, Buffer.buffer("Hello_1")); - this.lwtStorage.update(CLIENT_ID, willMessage1, done1 -> { - if (done1.succeeded()) { - this.lwtStorage.get(CLIENT_ID, done2 -> { - if (done2.succeeded()) { - AmqpWillMessage willMessage2 = done2.result(); - // updated message not equals to the original one but to the one got from the storage - context.verify(() -> assertTrue(!willMessage1.equals(WILL_MESSAGE) && willMessage1.equals(willMessage2))); - LOG.info("Existing will updated"); - - } else { - context.verify(() -> fail("")); - } - context.completeNow(); - }); - - } else { - context.verify(() -> fail("")); - context.completeNow(); - } - }); - } else { - context.verify(() -> fail("")); - context.completeNow(); - } - }); - } - - @Test - public void updateNotExistingWill(VertxTestContext context) { - this.lwtStorage.update(CLIENT_ID, WILL_MESSAGE, done -> { - context.verify(() -> assertTrue(!done.succeeded())); - LOG.info("Trying to update a not existing will"); - context.completeNow(); - }); - } - - @Test - public void deleteExistingWill(VertxTestContext context) { - this.lwtStorage.add(CLIENT_ID, WILL_MESSAGE, done -> { - if (done.succeeded()) { - this.lwtStorage.delete(CLIENT_ID, done1 -> { - context.verify(() -> assertTrue(done1.succeeded())); - LOG.info("Existing will deleted"); - context.completeNow(); - }); - } else { - context.verify(() -> fail("")); - context.completeNow(); - } - }); - } - - @Test - public void deleteNotExistingWill(VertxTestContext context) { - this.lwtStorage.delete(CLIENT_ID, done -> { - context.verify(() -> assertTrue(!done.succeeded())); - LOG.info("Trying to delete a not existing will"); - context.completeNow(); - }); - } - -} diff --git a/mqtt-lwt/src/test/resources/client-certs/ca.crt b/mqtt-lwt/src/test/resources/client-certs/ca.crt deleted file mode 100644 index 358da772e40..00000000000 --- a/mqtt-lwt/src/test/resources/client-certs/ca.crt +++ /dev/null @@ -1,20 +0,0 @@ ------BEGIN CERTIFICATE----- -MIIDVzCCAj+gAwIBAgIJAMhDI67BWr7DMA0GCSqGSIb3DQEBCwUAMEIxCzAJBgNV -BAYTAlhYMRUwEwYDVQQHDAxEZWZhdWx0IENpdHkxHDAaBgNVBAoME0RlZmF1bHQg -Q29tcGFueSBMdGQwHhcNMTcwODI0MTIxOTUwWhcNNDcxMDA2MTIxOTUwWjBCMQsw -CQYDVQQGEwJYWDEVMBMGA1UEBwwMRGVmYXVsdCBDaXR5MRwwGgYDVQQKDBNEZWZh -dWx0IENvbXBhbnkgTHRkMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA -6ZLjStOAl76mYf/YTUBnBZ7zztBv6eroj1+CneEBamh2VxeWkmrGqbyE3dJT1Ydb -1kIl6bxJYXjUJAtSNY+Jw3PQzJdBDzXp3nSG1aa4nFlUXplbJmUa0atkWiL/4F3X -kqa78Qvqk/hGRs45wj3p9ZqNTy85n5RnV+OfepC8oK3odzmD3v9TLHDNH5P/5MET -k+j7iewsMU5DJ+Z4quL9JBQStY32B6qmEqwy/9evIJRDVxH1fCMQQXOcNQTZkxUN -gCz0LqRuHzt0sL8HUzM8zQhuFXFUwBoHs8emfMTGY6p5B2zn2nnywYw9oY8WEAo9 -TtF5zuDKE+Czs3VN/iFJXQIDAQABo1AwTjAdBgNVHQ4EFgQU72Kl6e/TslxG1fc5 -kN5boVGvDeAwHwYDVR0jBBgwFoAU72Kl6e/TslxG1fc5kN5boVGvDeAwDAYDVR0T -BAUwAwEB/zANBgkqhkiG9w0BAQsFAAOCAQEAW6GDFZ3r6WYk8/oQerBD5Y8iub2C -AplN1gcOpYc3XuM0Ce6UAKXTmu/CtR4lKt+gPVQoLxpJhYlQD2fwYpj/pvAjs81c -nbqyqPCsyO85nNs3XzemhYnm8nS3RH0u2Z2hNJIG5poB16Ysm+aiNSlucn/RfFFQ -ErLvxra3ggdAD3YN1Q67sAFMAP6lg0TDISpL7tRa0rIhsk79jpo0aXJQ7h0jdxF9 -IsfrclD4hLtvh8LOwb2JhvuEicMK67szvtiE5KU5VpuweWrZJYlUpbDbZVaWzQTL -iB0Emw/JNul85VaDwvkFvX1XIt0MCM/molF/PCaW5QekGa8DC+F5lDFvRQ== ------END CERTIFICATE----- diff --git a/mqtt-lwt/src/test/resources/client-certs/tls.crt b/mqtt-lwt/src/test/resources/client-certs/tls.crt deleted file mode 100644 index 9392a31aeb4..00000000000 --- a/mqtt-lwt/src/test/resources/client-certs/tls.crt +++ /dev/null @@ -1,18 +0,0 @@ ------BEGIN CERTIFICATE----- -MIIC4zCCAcsCCQCF+ECOV6YYQDANBgkqhkiG9w0BAQUFADBCMQswCQYDVQQGEwJY -WDEVMBMGA1UEBwwMRGVmYXVsdCBDaXR5MRwwGgYDVQQKDBNEZWZhdWx0IENvbXBh -bnkgTHRkMB4XDTE3MDgyNDEyMjA0MloXDTQ3MTAwNjEyMjA0MlowJTETMBEGA1UE -CgwKaW8uZW5tYXNzZTEOMAwGA1UEAwwFYWRtaW4wggEiMA0GCSqGSIb3DQEBAQUA -A4IBDwAwggEKAoIBAQDEVzrEfzY5q4ineh3dCZFiOR0WBZskwuX7rk195GQUM6WN -HSMArdfaB3xWuEf6IqyVDiDPk9pN0+9WACYo9ZkWqMHuWnCpIkpxDdvY1UG3vRH4 -OsmtmzRKAF2IHepPE1R0Soi5ZnPftpEOtpVfkKrWsTIlk23YV9i/g8WORf3YyqC6 -99XkfcK0l4bFGbDjR2aOKHFw5sueczpXIXTF5h6j9bkqgfbAwjVv8OaeVmZAeULL -91ksRbV/BrdfjDisgi/i9wy8Gz15XbjhjYA/hMsVMH8TOPWaIB5RUXAPEy7+Trgw -pEMS4Aod0bTmrgkVvx26KuGCB+SSP/Y9+Id/nX7bAgMBAAEwDQYJKoZIhvcNAQEF -BQADggEBAFOf9l8kdaw4d2K9lQbNL6t9Q9+URBFSQUqIm0HY72Jt1uwwHX5lSh3H -MNXgyAc2kqWIBexzyhurvxUyjuWmD3L4NuT0vuP+d5zBF39yGsveg3NyY4wICrEl -Ozqo6TWVirkwW0VKJWgZffi1QjYBKgTravF29LQk+S1QPu0tod2r5C5jHhKn9/P4 -sWsAPToWD8iLmip4jVBgB0N4e5PSPxmVJkjTlyIM+xCVpM/OVyiUCg1VIDnHG4YS -2NQpDFG7IOPviitcog0MZBqb0HbaevoJ8Cs7mDakf5WR++9MvCAj4vHsOkTwdsgX -9LnwmuBJpF+MBJYX/BO+4Y7xzugdDFo= ------END CERTIFICATE----- diff --git a/mqtt-lwt/src/test/resources/client-certs/tls.key b/mqtt-lwt/src/test/resources/client-certs/tls.key deleted file mode 100644 index 4b6e051258e..00000000000 --- a/mqtt-lwt/src/test/resources/client-certs/tls.key +++ /dev/null @@ -1,28 +0,0 @@ ------BEGIN PRIVATE KEY----- -MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQDEVzrEfzY5q4in -eh3dCZFiOR0WBZskwuX7rk195GQUM6WNHSMArdfaB3xWuEf6IqyVDiDPk9pN0+9W -ACYo9ZkWqMHuWnCpIkpxDdvY1UG3vRH4OsmtmzRKAF2IHepPE1R0Soi5ZnPftpEO -tpVfkKrWsTIlk23YV9i/g8WORf3YyqC699XkfcK0l4bFGbDjR2aOKHFw5sueczpX -IXTF5h6j9bkqgfbAwjVv8OaeVmZAeULL91ksRbV/BrdfjDisgi/i9wy8Gz15Xbjh -jYA/hMsVMH8TOPWaIB5RUXAPEy7+TrgwpEMS4Aod0bTmrgkVvx26KuGCB+SSP/Y9 -+Id/nX7bAgMBAAECggEBAIx0pnrZUk5RMtI0Xta4OIPevbr6SbEzp9izKYZs81cs -Vzsq49Zhhq/XeRCEhiEf2vxKfhhg+eOBQI7yTlowz9JQ6WL82el0uXJE6sgkTv4F -SKyeSZZjGDX0KvTsCamF9321sDeJXk1kykKPRS53TG+HlYsh+4uv1EO1HYeY9G63 -zGpS9REyYeJcLvyqhV0dJxSFgMrlOx5ZkxpJ+Y/1UU5E7dZyESWqI66kzA+fLt03 -opZCGleB3i9zq6MrgkTrZfwaIbyZ5vkUdvOTbvrciee/yDnoFl2w+PS47TpERAEZ -czwZbv1KOwJRiPR3Kx65fsOTAvRaYEo7PqlzqR8sEAkCgYEA7348W91fS3VSSLzA -wT2tzJfbjLvoOhEFIELQU3HU0rsy0k8JGDga0+8OtJzYz7wnYazJgzvcD3D9xeHg -V5i+SxWNGvVepeM/DAsCqjwCpDPXKxcQ5onuE7yWqNyEsKltGQJcZJDNjAddbh0/ -Wgatq6cUarhSMRrmK3/DEdY6qw8CgYEA0d+WNnclyar4l79xXD7OHO/kYpKSaKrR -1AMh8pqmPQr1Y1jSVxWlMd+hPXFtbvWkbOMGA+KTK9U5x9nLBHmCvD1jwNNFMR3a -ItIbO7ewGjizYCyfDOhZXRV29Gn86CLCARaXAZvrqt2PLxKDgbBgP1jYeYK70ZJR -FrLnbsyBn3UCgYAtlm7w4auxFm0KUXuzjQUXS9TL7jzfSLwcDvvO1n03e9DJvaey -h4N70iaPdMfEi0FH03csVumlutFjqIJmyjl222xXtFtG4oHuze28791k2kVb+3EJ -ITpDYYOgMxHC+w08VHm33uRxgjljq4eSoHDQmuZFBj8G/+LOZux/6bKTOwKBgBI5 -gzbJk+t2QE4VHsfu4EEdB80Tg0k5hC0P9JO3oDXuzK5K2UEcrRKuo7bNMb6P8C5Q -bIZL15HX20OigwGpd5PXFOF6tyhzuiyJ6DmAUveD8veoCEu7pX7yzTieN4sE8anc -h1JpcsaMpNFmnyrU4Ra/JVUv6BVjykzZTkWN1re9AoGAPDug53UDB+A6JU6zS0Va -nZfxBsXgP3WdHv0zQ2FwA50Tu2l7uX3wXTy6LkPv1/mpsAP2AsLDSAUPrifjXh0B -DUpZGElUcEBb0oQ552DDvq2S7S1zhLItmSFE4//yB1UrvTIAB0IdmEQMFOVu88yg -CgSgREWZy7gzoeKEM0WPSZI= ------END PRIVATE KEY----- diff --git a/mqtt-lwt/src/test/resources/router-certs/ca.crt b/mqtt-lwt/src/test/resources/router-certs/ca.crt deleted file mode 100644 index 358da772e40..00000000000 --- a/mqtt-lwt/src/test/resources/router-certs/ca.crt +++ /dev/null @@ -1,20 +0,0 @@ ------BEGIN CERTIFICATE----- -MIIDVzCCAj+gAwIBAgIJAMhDI67BWr7DMA0GCSqGSIb3DQEBCwUAMEIxCzAJBgNV -BAYTAlhYMRUwEwYDVQQHDAxEZWZhdWx0IENpdHkxHDAaBgNVBAoME0RlZmF1bHQg -Q29tcGFueSBMdGQwHhcNMTcwODI0MTIxOTUwWhcNNDcxMDA2MTIxOTUwWjBCMQsw -CQYDVQQGEwJYWDEVMBMGA1UEBwwMRGVmYXVsdCBDaXR5MRwwGgYDVQQKDBNEZWZh -dWx0IENvbXBhbnkgTHRkMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA -6ZLjStOAl76mYf/YTUBnBZ7zztBv6eroj1+CneEBamh2VxeWkmrGqbyE3dJT1Ydb -1kIl6bxJYXjUJAtSNY+Jw3PQzJdBDzXp3nSG1aa4nFlUXplbJmUa0atkWiL/4F3X -kqa78Qvqk/hGRs45wj3p9ZqNTy85n5RnV+OfepC8oK3odzmD3v9TLHDNH5P/5MET -k+j7iewsMU5DJ+Z4quL9JBQStY32B6qmEqwy/9evIJRDVxH1fCMQQXOcNQTZkxUN -gCz0LqRuHzt0sL8HUzM8zQhuFXFUwBoHs8emfMTGY6p5B2zn2nnywYw9oY8WEAo9 -TtF5zuDKE+Czs3VN/iFJXQIDAQABo1AwTjAdBgNVHQ4EFgQU72Kl6e/TslxG1fc5 -kN5boVGvDeAwHwYDVR0jBBgwFoAU72Kl6e/TslxG1fc5kN5boVGvDeAwDAYDVR0T -BAUwAwEB/zANBgkqhkiG9w0BAQsFAAOCAQEAW6GDFZ3r6WYk8/oQerBD5Y8iub2C -AplN1gcOpYc3XuM0Ce6UAKXTmu/CtR4lKt+gPVQoLxpJhYlQD2fwYpj/pvAjs81c -nbqyqPCsyO85nNs3XzemhYnm8nS3RH0u2Z2hNJIG5poB16Ysm+aiNSlucn/RfFFQ -ErLvxra3ggdAD3YN1Q67sAFMAP6lg0TDISpL7tRa0rIhsk79jpo0aXJQ7h0jdxF9 -IsfrclD4hLtvh8LOwb2JhvuEicMK67szvtiE5KU5VpuweWrZJYlUpbDbZVaWzQTL -iB0Emw/JNul85VaDwvkFvX1XIt0MCM/molF/PCaW5QekGa8DC+F5lDFvRQ== ------END CERTIFICATE----- diff --git a/mqtt-lwt/src/test/resources/router-certs/tls.crt b/mqtt-lwt/src/test/resources/router-certs/tls.crt deleted file mode 100644 index 12c7eda6fa4..00000000000 --- a/mqtt-lwt/src/test/resources/router-certs/tls.crt +++ /dev/null @@ -1,18 +0,0 @@ ------BEGIN CERTIFICATE----- -MIIC5zCCAc8CCQCR/0CzLhQvPDANBgkqhkiG9w0BAQUFADBCMQswCQYDVQQGEwJY -WDEVMBMGA1UEBwwMRGVmYXVsdCBDaXR5MRwwGgYDVQQKDBNEZWZhdWx0IENvbXBh -bnkgTHRkMB4XDTE3MDgyNDEyMjA0M1oXDTQ3MTAwNjEyMjA0M1owKTETMBEGA1UE -CgwKaW8uZW5tYXNzZTESMBAGA1UEAwwJcWRyb3V0ZXJkMIIBIjANBgkqhkiG9w0B -AQEFAAOCAQ8AMIIBCgKCAQEAvcKggvmHuIZAXCh+1qDxybRQXYAWx/mPIEh2uPu7 -rwHpeKPOTUnS+B38DEwjTQ5rglocCyWVwAkqG4Xruq4zKmjPkYVipFoqKb5pV6ui -Z9J6WKeiHPK5KTAHZJXVc9KJSHDczBM4CRlCUQrIs0XZKE3oowb/4T6y2taF4wFz -OO7Rke9AJ8KPfpW1UEw1H0qIje7mJ0+mrJRM7GrW2mQMzE8J2/oNRmqyg52xUypr -GUVdKUICrjo+xZp4hKg2/oN02yPB4MzQRMc2pmfKeQeuE6NUfmnCpzdl44wdT5S9 -BoOx9KEZY//Wa3pK3FwDCtNWbpTOarnvjUkB4v4NF7uYmQIDAQABMA0GCSqGSIb3 -DQEBBQUAA4IBAQDA003so6RrdXEgK8cbslsJ+Tcl/WlOFEqiEU81Xs7bYR2yyCXq -Ys44OFLCLG39wxMl3BGAqViOPnIgxz9JXOAZuIduIF5DkC4NYN5fROwx2HBGOpaY -W7EOuGKEV12QsBAUkmJoC064tjxOhgO8U62enVzcefkj2x41PYMyeo5Ce8rQaYX9 -x6XuDtOjNmmgUzSIRDJVGwoxwtHdABVUARetCJsC6fRPfsIt9TvFYxYmpGawHs8N -xEFV9aqyhk/rC1fy3NVQ5rDLzTilX080HuMkq8l/ciwRrokA1saYGbUr5HeTPGmw -DyHdCjfeZtzowv1098B49DE0kUOQUiNM1mGd ------END CERTIFICATE----- diff --git a/mqtt-lwt/src/test/resources/router-certs/tls.key b/mqtt-lwt/src/test/resources/router-certs/tls.key deleted file mode 100644 index db6ca094d19..00000000000 --- a/mqtt-lwt/src/test/resources/router-certs/tls.key +++ /dev/null @@ -1,28 +0,0 @@ ------BEGIN PRIVATE KEY----- -MIIEvAIBADANBgkqhkiG9w0BAQEFAASCBKYwggSiAgEAAoIBAQC9wqCC+Ye4hkBc -KH7WoPHJtFBdgBbH+Y8gSHa4+7uvAel4o85NSdL4HfwMTCNNDmuCWhwLJZXACSob -heu6rjMqaM+RhWKkWiopvmlXq6Jn0npYp6Ic8rkpMAdkldVz0olIcNzMEzgJGUJR -CsizRdkoTeijBv/hPrLa1oXjAXM47tGR70Anwo9+lbVQTDUfSoiN7uYnT6aslEzs -atbaZAzMTwnb+g1GarKDnbFTKmsZRV0pQgKuOj7FmniEqDb+g3TbI8HgzNBExzam -Z8p5B64To1R+acKnN2XjjB1PlL0Gg7H0oRlj/9ZrekrcXAMK01ZulM5que+NSQHi -/g0Xu5iZAgMBAAECggEAQkwqajgmG+kDuW8IiZInh7Iw1cn3q5xcTgojOJPyyb0g -Rd7tKAGf7MIFGoLLXpOoKh2zaI2UGUjaZk1Ow6UzCSEfTBdPFF7QFk+JXHEBx8Y4 -qpq+v+BpoqM+If1bWyD2h9GuwroAPOWPTuM2pbPEoBlAKx057wdJ071Zf6s1B3GG -rqGOsu0U7BonpZmRkP0oA7gpNxMNTFI2b6hPhlXmMT+r3Ztb9YP/DrIdngIRRenv -g8EGtR/VT3skA1+wunuo8ru/SECLwfFQuyR8eMaeTgsMib8sg0su/Kg3teZRFBQr -XQsZe/OBfUc/173DpBiN6pZw1pwb8lMBcGzWWljj0QKBgQDl7uURIh8V/bJJn60z -Hjbl0FXQLpQiX5q+HDzfbW5nDmvS6DI3LKBaW34RLhpb5SOBBm92hbS2p2fff1yV -MwC+XSnr6cmwO/PMC8CLHNd4SrRenvVOjKGqShKGDGbZq8KXdFW1kfqkdh52nKHU -D2F98GK0SfTGlQrFhYvdr87CTQKBgQDTRdYfodrjC519SnCprAea/nFlUSf2qXFt -QDMp1M9+X6BSB0EpLFyZ72FrWVuDcpKzL3Bu9AAwKyl4Cysj7EPfpKQ5/FYHSVvg -zJ3We+rmuFwnZ6AfJi62ZwD+bHI5PRytluhQRrvS4cBDIEfqHKMvYuC0Bu6+y3c5 -uLtUnNQdfQKBgBVk/ydy91WCf55eHy2MeCs2EWYHj30LTEi4M+nOMuGf8+Rx+oor -utftf3N1yLoYPg95W9G5azE7rmybWy/+yOjtnkL3Q+8EBRFNXqMTEfF9cf8D52th -poFD1yll6gQC+V5CUt9ML/gxjXAX7u7eZ4HiZizEXORFZfUvEe6UtzZZAoGABlQC -LOJhNwnDBtyy8TIY7DiQ1vMOWmIK5rXUkn0wVd2xZKOY/Q26HFZCOb/scMx+vmU+ -XeRrGQZB8LmM6j+KbOl2jdIUFZXJ7brTQ3hkudJt2DRPZH91SLtfGT/QUAyXjk+F -19m2iIOAicIuxUFkVegFSP8oKYjYol75JXipFdUCgYBzk8C3lWcZOjtYTaQtSQCU -q9pylKtgDzXSxzeeQNTb9qAMTaAoYSDyRgV74ew0P330XBdE4AwU5FqU6OXjAGPW -TZQ1KCi+ycM43jpFVw0Iz1DWbATsFIi8LyuEHsG0/ceihlGD1tji0XO7+0SffX08 -2KZdbh45GaMehOmp91EvKQ== ------END PRIVATE KEY----- diff --git a/none-authservice/Dockerfile b/none-authservice/Dockerfile deleted file mode 100644 index 2c4a39ef770..00000000000 --- a/none-authservice/Dockerfile +++ /dev/null @@ -1,10 +0,0 @@ -FROM quay.io/enmasse/java-base:11-5 - -ARG version -ARG maven_version -ARG revision -ENV VERSION=${version} REVISION=${revision} MAVEN_VERSION=${maven_version} - -ADD target/none-authservice-${maven_version}.jar /none-authservice.jar - -CMD ["/opt/run-java/launch_java.sh", "-jar", "/none-authservice.jar"] diff --git a/none-authservice/LICENSE b/none-authservice/LICENSE deleted file mode 100644 index 8dada3edaf5..00000000000 --- a/none-authservice/LICENSE +++ /dev/null @@ -1,201 +0,0 @@ - Apache License - Version 2.0, January 2004 - http://www.apache.org/licenses/ - - TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION - - 1. Definitions. - - "License" shall mean the terms and conditions for use, reproduction, - and distribution as defined by Sections 1 through 9 of this document. - - "Licensor" shall mean the copyright owner or entity authorized by - the copyright owner that is granting the License. - - "Legal Entity" shall mean the union of the acting entity and all - other entities that control, are controlled by, or are under common - control with that entity. For the purposes of this definition, - "control" means (i) the power, direct or indirect, to cause the - direction or management of such entity, whether by contract or - otherwise, or (ii) ownership of fifty percent (50%) or more of the - outstanding shares, or (iii) beneficial ownership of such entity. - - "You" (or "Your") shall mean an individual or Legal Entity - exercising permissions granted by this License. - - "Source" form shall mean the preferred form for making modifications, - including but not limited to software source code, documentation - source, and configuration files. - - "Object" form shall mean any form resulting from mechanical - transformation or translation of a Source form, including but - not limited to compiled object code, generated documentation, - and conversions to other media types. - - "Work" shall mean the work of authorship, whether in Source or - Object form, made available under the License, as indicated by a - copyright notice that is included in or attached to the work - (an example is provided in the Appendix below). - - "Derivative Works" shall mean any work, whether in Source or Object - form, that is based on (or derived from) the Work and for which the - editorial revisions, annotations, elaborations, or other modifications - represent, as a whole, an original work of authorship. For the purposes - of this License, Derivative Works shall not include works that remain - separable from, or merely link (or bind by name) to the interfaces of, - the Work and Derivative Works thereof. - - "Contribution" shall mean any work of authorship, including - the original version of the Work and any modifications or additions - to that Work or Derivative Works thereof, that is intentionally - submitted to Licensor for inclusion in the Work by the copyright owner - or by an individual or Legal Entity authorized to submit on behalf of - the copyright owner. For the purposes of this definition, "submitted" - means any form of electronic, verbal, or written communication sent - to the Licensor or its representatives, including but not limited to - communication on electronic mailing lists, source code control systems, - and issue tracking systems that are managed by, or on behalf of, the - Licensor for the purpose of discussing and improving the Work, but - excluding communication that is conspicuously marked or otherwise - designated in writing by the copyright owner as "Not a Contribution." - - "Contributor" shall mean Licensor and any individual or Legal Entity - on behalf of whom a Contribution has been received by Licensor and - subsequently incorporated within the Work. - - 2. Grant of Copyright License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - copyright license to reproduce, prepare Derivative Works of, - publicly display, publicly perform, sublicense, and distribute the - Work and such Derivative Works in Source or Object form. - - 3. Grant of Patent License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - (except as stated in this section) patent license to make, have made, - use, offer to sell, sell, import, and otherwise transfer the Work, - where such license applies only to those patent claims licensable - by such Contributor that are necessarily infringed by their - Contribution(s) alone or by combination of their Contribution(s) - with the Work to which such Contribution(s) was submitted. If You - institute patent litigation against any entity (including a - cross-claim or counterclaim in a lawsuit) alleging that the Work - or a Contribution incorporated within the Work constitutes direct - or contributory patent infringement, then any patent licenses - granted to You under this License for that Work shall terminate - as of the date such litigation is filed. - - 4. Redistribution. You may reproduce and distribute copies of the - Work or Derivative Works thereof in any medium, with or without - modifications, and in Source or Object form, provided that You - meet the following conditions: - - (a) You must give any other recipients of the Work or - Derivative Works a copy of this License; and - - (b) You must cause any modified files to carry prominent notices - stating that You changed the files; and - - (c) You must retain, in the Source form of any Derivative Works - that You distribute, all copyright, patent, trademark, and - attribution notices from the Source form of the Work, - excluding those notices that do not pertain to any part of - the Derivative Works; and - - (d) If the Work includes a "NOTICE" text file as part of its - distribution, then any Derivative Works that You distribute must - include a readable copy of the attribution notices contained - within such NOTICE file, excluding those notices that do not - pertain to any part of the Derivative Works, in at least one - of the following places: within a NOTICE text file distributed - as part of the Derivative Works; within the Source form or - documentation, if provided along with the Derivative Works; or, - within a display generated by the Derivative Works, if and - wherever such third-party notices normally appear. The contents - of the NOTICE file are for informational purposes only and - do not modify the License. You may add Your own attribution - notices within Derivative Works that You distribute, alongside - or as an addendum to the NOTICE text from the Work, provided - that such additional attribution notices cannot be construed - as modifying the License. - - You may add Your own copyright statement to Your modifications and - may provide additional or different license terms and conditions - for use, reproduction, or distribution of Your modifications, or - for any such Derivative Works as a whole, provided Your use, - reproduction, and distribution of the Work otherwise complies with - the conditions stated in this License. - - 5. Submission of Contributions. Unless You explicitly state otherwise, - any Contribution intentionally submitted for inclusion in the Work - by You to the Licensor shall be under the terms and conditions of - this License, without any additional terms or conditions. - Notwithstanding the above, nothing herein shall supersede or modify - the terms of any separate license agreement you may have executed - with Licensor regarding such Contributions. - - 6. Trademarks. This License does not grant permission to use the trade - names, trademarks, service marks, or product names of the Licensor, - except as required for reasonable and customary use in describing the - origin of the Work and reproducing the content of the NOTICE file. - - 7. Disclaimer of Warranty. Unless required by applicable law or - agreed to in writing, Licensor provides the Work (and each - Contributor provides its Contributions) on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or - implied, including, without limitation, any warranties or conditions - of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A - PARTICULAR PURPOSE. You are solely responsible for determining the - appropriateness of using or redistributing the Work and assume any - risks associated with Your exercise of permissions under this License. - - 8. Limitation of Liability. In no event and under no legal theory, - whether in tort (including negligence), contract, or otherwise, - unless required by applicable law (such as deliberate and grossly - negligent acts) or agreed to in writing, shall any Contributor be - liable to You for damages, including any direct, indirect, special, - incidental, or consequential damages of any character arising as a - result of this License or out of the use or inability to use the - Work (including but not limited to damages for loss of goodwill, - work stoppage, computer failure or malfunction, or any and all - other commercial damages or losses), even if such Contributor - has been advised of the possibility of such damages. - - 9. Accepting Warranty or Additional Liability. While redistributing - the Work or Derivative Works thereof, You may choose to offer, - and charge a fee for, acceptance of support, warranty, indemnity, - or other liability obligations and/or rights consistent with this - License. However, in accepting such obligations, You may act only - on Your own behalf and on Your sole responsibility, not on behalf - of any other Contributor, and only if You agree to indemnify, - defend, and hold each Contributor harmless for any liability - incurred by, or claims asserted against, such Contributor by reason - of your accepting any such warranty or additional liability. - - END OF TERMS AND CONDITIONS - - APPENDIX: How to apply the Apache License to your work. - - To apply the Apache License to your work, attach the following - boilerplate notice, with the fields enclosed by brackets "{}" - replaced with your own identifying information. (Don't include - the brackets!) The text should be enclosed in the appropriate - comment syntax for the file format. We also recommend that a - file or class name and description of purpose be included on the - same "printed page" as the copyright notice for easier - identification within third-party archives. - - Copyright {yyyy} {name of copyright owner} - - 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. diff --git a/none-authservice/Makefile b/none-authservice/Makefile deleted file mode 100644 index 27e8bca799a..00000000000 --- a/none-authservice/Makefile +++ /dev/null @@ -1 +0,0 @@ -include ../Makefile.java.mk diff --git a/none-authservice/pom.xml b/none-authservice/pom.xml deleted file mode 100644 index cbafa230893..00000000000 --- a/none-authservice/pom.xml +++ /dev/null @@ -1,63 +0,0 @@ - - - - io.enmasse - enmasse - 0.32-SNAPSHOT - - 4.0.0 - none-authservice - - - io.vertx - vertx-proton - compile - - - org.slf4j - slf4j-api - compile - - - ch.qos.logback - logback-classic - runtime - - - - - - - org.apache.maven.plugins - maven-shade-plugin - - - package - - shade - - - - - - io.enmasse.authservice.none.NoneAuthService - - - - - *:* - - META-INF/*.SF - META-INF/*.DSA - META-INF/*.RSA - - - - - - - - - - diff --git a/none-authservice/src/main/java/io/enmasse/authservice/none/HealthServer.java b/none-authservice/src/main/java/io/enmasse/authservice/none/HealthServer.java deleted file mode 100644 index c893532e090..00000000000 --- a/none-authservice/src/main/java/io/enmasse/authservice/none/HealthServer.java +++ /dev/null @@ -1,37 +0,0 @@ -/* - * Copyright 2019, EnMasse authors. - * License: Apache License 2.0 (see the file LICENSE or http://apache.org/licenses/LICENSE-2.0.html). - */ -package io.enmasse.authservice.none; - -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import io.vertx.core.AbstractVerticle; -import io.vertx.core.Promise; -import io.vertx.core.http.HttpServer; -import io.vertx.core.http.HttpServerOptions; - -public class HealthServer extends AbstractVerticle { - private static final Logger log = LoggerFactory.getLogger(HealthServer.class); - private final HttpServerOptions serverOptions; - public HealthServer(HttpServerOptions serverOptions) { - this.serverOptions = serverOptions; - } - - @Override - public void start(Promise startPromise) { - HttpServer httpServer = vertx.createHttpServer(serverOptions); - httpServer.requestHandler(httpServerRequest -> httpServerRequest.response().end("OK")); - httpServer.listen(result -> { - if (result.succeeded()) { - log.info("HealthServer started"); - startPromise.complete(); - } else { - log.error("Error starting HealthServer"); - startPromise.fail(result.cause()); - } - }); - - } -} diff --git a/none-authservice/src/main/java/io/enmasse/authservice/none/NoneAuthService.java b/none-authservice/src/main/java/io/enmasse/authservice/none/NoneAuthService.java deleted file mode 100644 index 51ea365c766..00000000000 --- a/none-authservice/src/main/java/io/enmasse/authservice/none/NoneAuthService.java +++ /dev/null @@ -1,47 +0,0 @@ -/* - * Copyright 2019, EnMasse authors. - * License: Apache License 2.0 (see the file LICENSE or http://apache.org/licenses/LICENSE-2.0.html). - */ -package io.enmasse.authservice.none; - -import java.io.File; -import java.util.Map; - -import io.vertx.core.DeploymentOptions; -import io.vertx.core.Vertx; -import io.vertx.core.VertxOptions; -import io.vertx.core.http.HttpServerOptions; -import io.vertx.core.net.PemKeyCertOptions; -import io.vertx.proton.ProtonServerOptions; - -public class NoneAuthService { - public static void main(String[] args) { - Map env = System.getenv(); - String certDir = env.getOrDefault("CERT_DIR", "/opt/none-authservice/cert"); - int listenPort = Integer.parseInt(env.getOrDefault("LISTENPORT", "5671")); - int healthPort = Integer.parseInt(env.getOrDefault("HEALTHPORT", "8080")); - int serverInstances = Integer.parseInt(env.getOrDefault("ENMASSE_NUM_AMQPS_SERVER_INSTANCES", String.format("%s", Runtime.getRuntime().availableProcessors()))); - - VertxOptions vertxOptions = new VertxOptions(); - if (serverInstances > vertxOptions.getWorkerPoolSize()) { - vertxOptions.setWorkerPoolSize(serverInstances); - } - Vertx vertx = Vertx.vertx(vertxOptions); - - ProtonServerOptions protonServerOptions = new ProtonServerOptions() - .setSsl(true) - .setPort(listenPort) - .setHost("0.0.0.0") - .setPemKeyCertOptions(new PemKeyCertOptions() - .setCertPath(new File(certDir, "tls.crt").getAbsolutePath()) - .setKeyPath(new File(certDir, "tls.key").getAbsolutePath())); - - HttpServerOptions httpServerOptions = new HttpServerOptions() - .setHost("0.0.0.0") - .setPort(healthPort); - HealthServer healthServer = new HealthServer(httpServerOptions); - - vertx.deployVerticle(() -> new SaslServer(protonServerOptions), new DeploymentOptions().setWorker(true).setInstances(serverInstances)); - vertx.deployVerticle(healthServer); - } -} diff --git a/none-authservice/src/main/java/io/enmasse/authservice/none/NoneAuthServiceAuthenticator.java b/none-authservice/src/main/java/io/enmasse/authservice/none/NoneAuthServiceAuthenticator.java deleted file mode 100644 index 08d7efbb42a..00000000000 --- a/none-authservice/src/main/java/io/enmasse/authservice/none/NoneAuthServiceAuthenticator.java +++ /dev/null @@ -1,55 +0,0 @@ -/* - * Copyright 2019, EnMasse authors. - * License: Apache License 2.0 (see the file LICENSE or http://apache.org/licenses/LICENSE-2.0.html). - */ -package io.enmasse.authservice.none; - -import io.vertx.core.Handler; -import io.vertx.core.net.NetSocket; -import io.vertx.proton.ProtonConnection; -import io.vertx.proton.sasl.ProtonSaslAuthenticator; -import org.apache.qpid.proton.engine.Sasl; -import org.apache.qpid.proton.engine.Transport; - -public class NoneAuthServiceAuthenticator implements ProtonSaslAuthenticator { - private static final String MECH_ANONYMOUS = "ANONYMOUS"; - private static final String MECH_PLAIN = "PLAIN"; - - private Sasl sasl; - - @Override - public void init(NetSocket netSocket, ProtonConnection protonConnection, Transport transport) { - - transport.setInitialRemoteMaxFrameSize(1024*1024); - this.sasl = transport.sasl(); - sasl.server(); - sasl.allowSkip(false); - sasl.setMechanisms(MECH_ANONYMOUS, MECH_PLAIN); - } - - @Override - public void process(Handler handler) { - String[] remoteMechanisms = sasl.getRemoteMechanisms(); - boolean done = false; - if (!done && remoteMechanisms.length > 0) { - String chosen = remoteMechanisms[0]; - byte[] response; - if (sasl.pending() > 0) { - response = new byte[sasl.pending()]; - sasl.recv(response, 0, response.length); - } - done = true; - if (MECH_ANONYMOUS.equals(chosen) || MECH_PLAIN.equals(chosen)) { - sasl.done(Sasl.SaslOutcome.PN_SASL_OK); - } else { - sasl.done(Sasl.SaslOutcome.PN_SASL_SYS); - } - } - handler.handle(done); - } - - @Override - public boolean succeeded() { - return true; - } -} diff --git a/none-authservice/src/main/java/io/enmasse/authservice/none/SaslServer.java b/none-authservice/src/main/java/io/enmasse/authservice/none/SaslServer.java deleted file mode 100644 index d43fb4fb9d5..00000000000 --- a/none-authservice/src/main/java/io/enmasse/authservice/none/SaslServer.java +++ /dev/null @@ -1,66 +0,0 @@ -/* - * Copyright 2019, EnMasse authors. - * License: Apache License 2.0 (see the file LICENSE or http://apache.org/licenses/LICENSE-2.0.html). - */ -package io.enmasse.authservice.none; - -import java.util.Collections; -import java.util.HashMap; -import java.util.Map; - -import org.apache.qpid.proton.amqp.Symbol; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import io.vertx.core.AbstractVerticle; -import io.vertx.core.Promise; -import io.vertx.proton.ProtonConnection; -import io.vertx.proton.ProtonServer; -import io.vertx.proton.ProtonServerOptions; - -public class SaslServer extends AbstractVerticle { - private static final Logger log = LoggerFactory.getLogger(SaslServer.class); - private static final Symbol AUTHENTICATED_IDENTITY = Symbol.getSymbol("authenticated-identity"); - private static final Symbol GROUPS = Symbol.getSymbol("groups"); - private final ProtonServerOptions options; - public SaslServer(ProtonServerOptions options) { - this.options = options; - } - - @Override - public void start(Promise startPromise) { - - ProtonServer server = ProtonServer.create(vertx, options); - server.connectHandler(SaslServer::connectHandler); - server.saslAuthenticatorFactory(NoneAuthServiceAuthenticator::new); - server.listen(result -> { - if (result.succeeded()) { - log.info("SaslServer started"); - startPromise.complete(); - } else { - log.error("Error starting SaslServer"); - startPromise.fail(result.cause()); - } - }); - } - - private static void connectHandler(ProtonConnection connection) { - connection.setContainer("none-authservice"); - connection.openHandler(conn -> { - Map properties = new HashMap<>(); - - properties.put(AUTHENTICATED_IDENTITY, Collections.singletonMap("sub", "anonymous")); - properties.put(GROUPS, Collections.singletonList("manage")); - - connection.setProperties(properties); - connection.open(); - connection.close(); - - }).closeHandler(conn -> { - connection.close(); - connection.disconnect(); - }).disconnectHandler(protonConnection -> { - connection.disconnect(); - }); - } -} diff --git a/none-authservice/src/main/resources/logback.xml b/none-authservice/src/main/resources/logback.xml deleted file mode 100644 index 50d0862925b..00000000000 --- a/none-authservice/src/main/resources/logback.xml +++ /dev/null @@ -1,17 +0,0 @@ - - - - - - - %d{yyyy-MM-dd'T'HH:mm:ss.SSS'Z',GMT} %-5p [%c{0}] %m%n - - - - - - - - - - diff --git a/olm-manifest/pom.xml b/olm-manifest/pom.xml index 22f4957660f..7886a189c6b 100644 --- a/olm-manifest/pom.xml +++ b/olm-manifest/pom.xml @@ -4,7 +4,7 @@ io.enmasse enmasse - 0.32-SNAPSHOT + 1.0-SNAPSHOT 4.0.0 olm-manifest diff --git a/pkg/apis/enmasse/v1beta2/funcs_addressplan.go b/pkg/apis/enmasse/v1beta2/funcs_addressplan.go new file mode 100644 index 00000000000..a06ee745068 --- /dev/null +++ b/pkg/apis/enmasse/v1beta2/funcs_addressplan.go @@ -0,0 +1,39 @@ +/* + * Copyright 2020, EnMasse authors. + * License: Apache License 2.0 (see the file LICENSE or http://apache.org/licenses/LICENSE-2.0.html). + */ +package v1beta2 + +import ( + corev1 "k8s.io/api/core/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" +) + +func (m *MessagingAddressPlanStatus) GetMessagingAddressPlanCondition(t MessagingAddressPlanConditionType) *MessagingAddressPlanCondition { + for i, c := range m.Conditions { + if c.Type == t { + return &m.Conditions[i] + } + } + + mc := MessagingAddressPlanCondition{ + Type: t, + Status: corev1.ConditionUnknown, + LastTransitionTime: metav1.Now(), + } + + m.Conditions = append(m.Conditions, mc) + + return &m.Conditions[len(m.Conditions)-1] +} + +func (c *MessagingAddressPlanCondition) SetStatus(status corev1.ConditionStatus, reason string, message string) { + + if c.Status != status { + c.Status = status + c.LastTransitionTime = metav1.Now() + } + + c.Reason = reason + c.Message = message +} diff --git a/pkg/apis/enmasse/v1beta2/funcs_plan.go b/pkg/apis/enmasse/v1beta2/funcs_plan.go new file mode 100644 index 00000000000..575850bcb3a --- /dev/null +++ b/pkg/apis/enmasse/v1beta2/funcs_plan.go @@ -0,0 +1,39 @@ +/* + * Copyright 2020, EnMasse authors. + * License: Apache License 2.0 (see the file LICENSE or http://apache.org/licenses/LICENSE-2.0.html). + */ +package v1beta2 + +import ( + corev1 "k8s.io/api/core/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" +) + +func (m *MessagingPlanStatus) GetMessagingPlanCondition(t MessagingPlanConditionType) *MessagingPlanCondition { + for i, c := range m.Conditions { + if c.Type == t { + return &m.Conditions[i] + } + } + + mc := MessagingPlanCondition{ + Type: t, + Status: corev1.ConditionUnknown, + LastTransitionTime: metav1.Now(), + } + + m.Conditions = append(m.Conditions, mc) + + return &m.Conditions[len(m.Conditions)-1] +} + +func (c *MessagingPlanCondition) SetStatus(status corev1.ConditionStatus, reason string, message string) { + + if c.Status != status { + c.Status = status + c.LastTransitionTime = metav1.Now() + } + + c.Reason = reason + c.Message = message +} diff --git a/pkg/apis/enmasse/v1beta2/register.go b/pkg/apis/enmasse/v1beta2/register.go index 9553fe741b0..08ef2b47ef0 100644 --- a/pkg/apis/enmasse/v1beta2/register.go +++ b/pkg/apis/enmasse/v1beta2/register.go @@ -39,6 +39,10 @@ func addKnownTypes(scheme *runtime.Scheme) error { &MessagingAddressList{}, &MessagingEndpoint{}, &MessagingEndpointList{}, + &MessagingPlan{}, + &MessagingPlanList{}, + &MessagingAddressPlan{}, + &MessagingAddressPlanList{}, ) metav1.AddToGroupVersion(scheme, SchemeGroupVersion) diff --git a/pkg/apis/enmasse/v1beta2/types_addressplan.go b/pkg/apis/enmasse/v1beta2/types_addressplan.go new file mode 100644 index 00000000000..2da1fa207b4 --- /dev/null +++ b/pkg/apis/enmasse/v1beta2/types_addressplan.go @@ -0,0 +1,71 @@ +/* + * Copyright 2020, EnMasse authors. + * License: Apache License 2.0 (see the file LICENSE or http://apache.org/licenses/LICENSE-2.0.html). + */ +package v1beta2 + +import ( + corev1 "k8s.io/api/core/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" +) + +/* + * Be careful with the comments in this file. The prefix "+" indicates that this is being processed + * by the client generator. The location, empty lines, and other comments in this file may confuse + * the generator, and produce a non-version version. + */ + +// +genclient +// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object +// +kubebuilder:resource:shortName=msgp;msgplan;msgplans,categories=enmasse +// +kubebuilder:subresource:status +// +kubebuilder:printcolumn:name="Phase",type="string",JSONPath=".status.phase",description="The current phase." +// +kubebuilder:printcolumn:name="Message",type="string",JSONPath=".status.message",priority=1,description="Message describing the reason for the current Phase." +// +kubebuilder:printcolumn:name="Age",type="date",JSONPath=".metadata.creationTimestamp" +type MessagingAddressPlan struct { + metav1.TypeMeta `json:",inline"` + metav1.ObjectMeta `json:"metadata,omitempty"` + Spec MessagingAddressPlanSpec `json:"spec,omitempty"` + Status MessagingAddressPlanStatus `json:"status,omitempty"` +} + +type MessagingAddressPlanSpec struct { +} + +type MessagingAddressPlanStatus struct { + // +kubebuilder:printcolumn + Phase MessagingAddressPlanPhase `json:"phase,omitempty"` + Message string `json:"message,omitempty"` + Conditions []MessagingAddressPlanCondition `json:"conditions,omitempty"` +} + +type MessagingAddressPlanCondition struct { + Type MessagingAddressPlanConditionType `json:"type"` + Status corev1.ConditionStatus `json:"status"` + LastTransitionTime metav1.Time `json:"lastTransitionTime,omitempty"` + Reason string `json:"reason,omitempty"` + Message string `json:"message,omitempty"` +} + +type MessagingAddressPlanConditionType string + +const ( + MessagingAddressPlanReady MessagingAddressPlanConditionType = "Ready" +) + +type MessagingAddressPlanPhase string + +const ( + MessagingAddressPlanConfiguring MessagingAddressPlanPhase = "Configuring" + MessagingAddressPlanActive MessagingAddressPlanPhase = "Active" + MessagingAddressPlanTerminating MessagingAddressPlanPhase = "Terminating" +) + +// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object + +type MessagingAddressPlanList struct { + metav1.TypeMeta `json:",inline"` + metav1.ListMeta `json:"metadata,omitempty"` + + Items []MessagingAddressPlan `json:"items"` +} diff --git a/pkg/apis/enmasse/v1beta2/types_common.go b/pkg/apis/enmasse/v1beta2/types_common.go index e731d73c5e8..bfe422630ee 100644 --- a/pkg/apis/enmasse/v1beta2/types_common.go +++ b/pkg/apis/enmasse/v1beta2/types_common.go @@ -22,6 +22,12 @@ type NamespaceSelector struct { MatchNames []string `json:"matchNames,omitempty"` } +type MessagingCapability string + +const ( + MessagingCapabilityTransactional MessagingCapability = "transactional" +) + type MessagingInfrastructureReference struct { // Name of referenced MessagingInfra. Name string `json:"name"` diff --git a/pkg/apis/enmasse/v1beta2/types_endpoint.go b/pkg/apis/enmasse/v1beta2/types_endpoint.go index a67ace38004..aea9b13d449 100644 --- a/pkg/apis/enmasse/v1beta2/types_endpoint.go +++ b/pkg/apis/enmasse/v1beta2/types_endpoint.go @@ -26,7 +26,7 @@ import ( // +kubebuilder:printcolumn:name="Host",type="string",JSONPath=".status.host",description="The hostname." // +kubebuilder:printcolumn:name="Message",type="string",JSONPath=".status.message",priority=1,description="Message describing the reason for the current Phase." // +kubebuilder:printcolumn:name="Protocols",type="string",JSONPath=".spec.protocols",priority=1,description="Supported protocols." -// +kubebuilder:printcolumn:name="CertficateExpiry",type="string",JSONPath=".status.tls.certificateInfo.notAfter",priority=1,description="Certificate expiry." +// +kubebuilder:printcolumn:name="CertficateExpiry",type="string",JSONPath=".status.tls.certificateValidity.notAfter",priority=1,description="Certificate expiry." // +kubebuilder:printcolumn:name="Age",type="date",JSONPath=".metadata.creationTimestamp" type MessagingEndpoint struct { metav1.TypeMeta `json:",inline"` diff --git a/pkg/apis/enmasse/v1beta2/types_plan.go b/pkg/apis/enmasse/v1beta2/types_plan.go new file mode 100644 index 00000000000..d3f4d03ca1e --- /dev/null +++ b/pkg/apis/enmasse/v1beta2/types_plan.go @@ -0,0 +1,71 @@ +/* + * Copyright 2020, EnMasse authors. + * License: Apache License 2.0 (see the file LICENSE or http://apache.org/licenses/LICENSE-2.0.html). + */ +package v1beta2 + +import ( + corev1 "k8s.io/api/core/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" +) + +/* + * Be careful with the comments in this file. The prefix "+" indicates that this is being processed + * by the client generator. The location, empty lines, and other comments in this file may confuse + * the generator, and produce a non-version version. + */ + +// +genclient +// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object +// +kubebuilder:resource:shortName=msgp;msgplan;msgplans,categories=enmasse +// +kubebuilder:subresource:status +// +kubebuilder:printcolumn:name="Phase",type="string",JSONPath=".status.phase",description="The current phase." +// +kubebuilder:printcolumn:name="Message",type="string",JSONPath=".status.message",priority=1,description="Message describing the reason for the current Phase." +// +kubebuilder:printcolumn:name="Age",type="date",JSONPath=".metadata.creationTimestamp" +type MessagingPlan struct { + metav1.TypeMeta `json:",inline"` + metav1.ObjectMeta `json:"metadata,omitempty"` + Spec MessagingPlanSpec `json:"spec,omitempty"` + Status MessagingPlanStatus `json:"status,omitempty"` +} + +type MessagingPlanSpec struct { +} + +type MessagingPlanStatus struct { + // +kubebuilder:printcolumn + Phase MessagingPlanPhase `json:"phase,omitempty"` + Message string `json:"message,omitempty"` + Conditions []MessagingPlanCondition `json:"conditions,omitempty"` +} + +type MessagingPlanCondition struct { + Type MessagingPlanConditionType `json:"type"` + Status corev1.ConditionStatus `json:"status"` + LastTransitionTime metav1.Time `json:"lastTransitionTime,omitempty"` + Reason string `json:"reason,omitempty"` + Message string `json:"message,omitempty"` +} + +type MessagingPlanConditionType string + +const ( + MessagingPlanReady MessagingPlanConditionType = "Ready" +) + +type MessagingPlanPhase string + +const ( + MessagingPlanConfiguring MessagingPlanPhase = "Configuring" + MessagingPlanActive MessagingPlanPhase = "Active" + MessagingPlanTerminating MessagingPlanPhase = "Terminating" +) + +// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object + +type MessagingPlanList struct { + metav1.TypeMeta `json:",inline"` + metav1.ListMeta `json:"metadata,omitempty"` + + Items []MessagingPlan `json:"items"` +} diff --git a/pkg/apis/enmasse/v1beta2/types_tenant.go b/pkg/apis/enmasse/v1beta2/types_tenant.go index e917ddf700b..3ccc70b5291 100644 --- a/pkg/apis/enmasse/v1beta2/types_tenant.go +++ b/pkg/apis/enmasse/v1beta2/types_tenant.go @@ -32,6 +32,8 @@ type MessagingTenant struct { type MessagingTenantSpec struct { // Reference to a specific MessagingInfra to use (must be available for this tenant). MessagingInfrastructureRef *MessagingInfrastructureReference `json:"messagingInfrastructureRef,omitempty"` + // The desired capabilities common to all addresses for this tenant. + Capabilities []MessagingCapability `json:"capabilities,omitempty"` } type MessagingTenantStatus struct { @@ -41,6 +43,10 @@ type MessagingTenantStatus struct { // MessagingInfra this tenant is bound to. MessagingInfrastructureRef MessagingInfrastructureReference `json:"messagingInfrastructureRef,omitempty"` Conditions []MessagingTenantCondition `json:"conditions,omitempty"` + // The actual capabilities common to all addresses for this tenant. + Capabilities []MessagingCapability `json:"capabilities,omitempty"` + // For transactional tenants, the broker addresses should be scheduled todo + Broker *MessagingAddressBroker `json:"broker,omitempty"` } type MessagingTenantCondition struct { @@ -56,6 +62,8 @@ type MessagingTenantConditionType string const ( MessagingTenantBound MessagingTenantConditionType = "Bound" MessagingTenantCaCreated MessagingTenantConditionType = "CaCreated" + MessagingTenantScheduled MessagingTenantConditionType = "Scheduled" + MessagingTenantCreated MessagingTenantConditionType = "Created" MessagingTenantReady MessagingTenantConditionType = "Ready" ) diff --git a/pkg/apis/enmasse/v1beta2/zz_generated.deepcopy.go b/pkg/apis/enmasse/v1beta2/zz_generated.deepcopy.go index 8954b8598d5..db383b533c9 100644 --- a/pkg/apis/enmasse/v1beta2/zz_generated.deepcopy.go +++ b/pkg/apis/enmasse/v1beta2/zz_generated.deepcopy.go @@ -132,6 +132,123 @@ func (in *MessagingAddressList) DeepCopyObject() runtime.Object { return nil } +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *MessagingAddressPlan) DeepCopyInto(out *MessagingAddressPlan) { + *out = *in + out.TypeMeta = in.TypeMeta + in.ObjectMeta.DeepCopyInto(&out.ObjectMeta) + out.Spec = in.Spec + in.Status.DeepCopyInto(&out.Status) + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new MessagingAddressPlan. +func (in *MessagingAddressPlan) DeepCopy() *MessagingAddressPlan { + if in == nil { + return nil + } + out := new(MessagingAddressPlan) + in.DeepCopyInto(out) + return out +} + +// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. +func (in *MessagingAddressPlan) DeepCopyObject() runtime.Object { + if c := in.DeepCopy(); c != nil { + return c + } + return nil +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *MessagingAddressPlanCondition) DeepCopyInto(out *MessagingAddressPlanCondition) { + *out = *in + in.LastTransitionTime.DeepCopyInto(&out.LastTransitionTime) + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new MessagingAddressPlanCondition. +func (in *MessagingAddressPlanCondition) DeepCopy() *MessagingAddressPlanCondition { + if in == nil { + return nil + } + out := new(MessagingAddressPlanCondition) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *MessagingAddressPlanList) DeepCopyInto(out *MessagingAddressPlanList) { + *out = *in + out.TypeMeta = in.TypeMeta + in.ListMeta.DeepCopyInto(&out.ListMeta) + if in.Items != nil { + in, out := &in.Items, &out.Items + *out = make([]MessagingAddressPlan, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new MessagingAddressPlanList. +func (in *MessagingAddressPlanList) DeepCopy() *MessagingAddressPlanList { + if in == nil { + return nil + } + out := new(MessagingAddressPlanList) + in.DeepCopyInto(out) + return out +} + +// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. +func (in *MessagingAddressPlanList) DeepCopyObject() runtime.Object { + if c := in.DeepCopy(); c != nil { + return c + } + return nil +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *MessagingAddressPlanSpec) DeepCopyInto(out *MessagingAddressPlanSpec) { + *out = *in + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new MessagingAddressPlanSpec. +func (in *MessagingAddressPlanSpec) DeepCopy() *MessagingAddressPlanSpec { + if in == nil { + return nil + } + out := new(MessagingAddressPlanSpec) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *MessagingAddressPlanStatus) DeepCopyInto(out *MessagingAddressPlanStatus) { + *out = *in + if in.Conditions != nil { + in, out := &in.Conditions, &out.Conditions + *out = make([]MessagingAddressPlanCondition, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new MessagingAddressPlanStatus. +func (in *MessagingAddressPlanStatus) DeepCopy() *MessagingAddressPlanStatus { + if in == nil { + return nil + } + out := new(MessagingAddressPlanStatus) + in.DeepCopyInto(out) + return out +} + // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *MessagingAddressSpec) DeepCopyInto(out *MessagingAddressSpec) { *out = *in @@ -1030,6 +1147,123 @@ func (in *MessagingInfrastructureStatusRouter) DeepCopy() *MessagingInfrastructu return out } +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *MessagingPlan) DeepCopyInto(out *MessagingPlan) { + *out = *in + out.TypeMeta = in.TypeMeta + in.ObjectMeta.DeepCopyInto(&out.ObjectMeta) + out.Spec = in.Spec + in.Status.DeepCopyInto(&out.Status) + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new MessagingPlan. +func (in *MessagingPlan) DeepCopy() *MessagingPlan { + if in == nil { + return nil + } + out := new(MessagingPlan) + in.DeepCopyInto(out) + return out +} + +// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. +func (in *MessagingPlan) DeepCopyObject() runtime.Object { + if c := in.DeepCopy(); c != nil { + return c + } + return nil +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *MessagingPlanCondition) DeepCopyInto(out *MessagingPlanCondition) { + *out = *in + in.LastTransitionTime.DeepCopyInto(&out.LastTransitionTime) + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new MessagingPlanCondition. +func (in *MessagingPlanCondition) DeepCopy() *MessagingPlanCondition { + if in == nil { + return nil + } + out := new(MessagingPlanCondition) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *MessagingPlanList) DeepCopyInto(out *MessagingPlanList) { + *out = *in + out.TypeMeta = in.TypeMeta + in.ListMeta.DeepCopyInto(&out.ListMeta) + if in.Items != nil { + in, out := &in.Items, &out.Items + *out = make([]MessagingPlan, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new MessagingPlanList. +func (in *MessagingPlanList) DeepCopy() *MessagingPlanList { + if in == nil { + return nil + } + out := new(MessagingPlanList) + in.DeepCopyInto(out) + return out +} + +// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. +func (in *MessagingPlanList) DeepCopyObject() runtime.Object { + if c := in.DeepCopy(); c != nil { + return c + } + return nil +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *MessagingPlanSpec) DeepCopyInto(out *MessagingPlanSpec) { + *out = *in + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new MessagingPlanSpec. +func (in *MessagingPlanSpec) DeepCopy() *MessagingPlanSpec { + if in == nil { + return nil + } + out := new(MessagingPlanSpec) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *MessagingPlanStatus) DeepCopyInto(out *MessagingPlanStatus) { + *out = *in + if in.Conditions != nil { + in, out := &in.Conditions, &out.Conditions + *out = make([]MessagingPlanCondition, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new MessagingPlanStatus. +func (in *MessagingPlanStatus) DeepCopy() *MessagingPlanStatus { + if in == nil { + return nil + } + out := new(MessagingPlanStatus) + in.DeepCopyInto(out) + return out +} + // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *MessagingTenant) DeepCopyInto(out *MessagingTenant) { *out = *in @@ -1116,6 +1350,11 @@ func (in *MessagingTenantSpec) DeepCopyInto(out *MessagingTenantSpec) { *out = new(MessagingInfrastructureReference) **out = **in } + if in.Capabilities != nil { + in, out := &in.Capabilities, &out.Capabilities + *out = make([]MessagingCapability, len(*in)) + copy(*out, *in) + } return } @@ -1140,6 +1379,16 @@ func (in *MessagingTenantStatus) DeepCopyInto(out *MessagingTenantStatus) { (*in)[i].DeepCopyInto(&(*out)[i]) } } + if in.Capabilities != nil { + in, out := &in.Capabilities, &out.Capabilities + *out = make([]MessagingCapability, len(*in)) + copy(*out, *in) + } + if in.Broker != nil { + in, out := &in.Broker, &out.Broker + *out = new(MessagingAddressBroker) + **out = **in + } return } diff --git a/pkg/apis/iot/v1alpha1/funcs.go b/pkg/apis/iot/v1alpha1/funcs.go index c9dcd5e349e..d4e3d0036b5 100644 --- a/pkg/apis/iot/v1alpha1/funcs.go +++ b/pkg/apis/iot/v1alpha1/funcs.go @@ -9,6 +9,8 @@ import ( "github.com/enmasseproject/enmasse/pkg/apis/enmasse/v1beta1" "github.com/enmasseproject/enmasse/pkg/util" "github.com/pkg/errors" + "sort" + "strings" corev1 "k8s.io/api/core/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" @@ -120,6 +122,58 @@ func (c *CommonServiceConfig) TlsVersions(config *IoTConfig) []string { // endregion +// region QuarkusServiceConfig + +func (c *QuarkusServiceConfig) IsNativeTlsRequired(config *IoTConfig) bool { + if c.Container.RequireNativeTls == nil { + return config.Spec.DefaultNativeTlsRequired() + } + return *c.Container.RequireNativeTls +} + +func (c *QuarkusServiceConfig) TlsVersions(config *IoTConfig) []string { + if len(c.Tls.Versions) > 0 { + return c.Tls.Versions + } else { + return config.Spec.TlsDefaults.Versions + } +} + +func (c QuarkusContainerConfig) ApplyLoggingToContainer(config *IoTConfig, opts []string) []string { + + level, loggers := c.Logging.GetEffectiveConfiguration(config) + + opts = append(opts, "-Dquarkus.log.level="+strings.ToUpper(string(level))) + + // extract keys, and sort + keys := make([]string, 0, len(loggers)) + for k, _ := range loggers { + keys = append(keys, k) + } + sort.Strings(keys) + + // iterate with sorted keys to have a stable env-var value + for _, k := range keys { + v := strings.ToUpper(string(loggers[k])) + if v == "" { + v = "INFO" + } + opts = append(opts, "-Dquarkus.log.category.\""+k+"\".level="+v) + } + + return opts +} + +// check if the native image should be used +func (c QuarkusContainerConfig) UseNativeImage(_ *IoTConfig) bool { + if c.NativeImage != nil { + return *c.NativeImage + } + return false +} + +// endregion + func (p *IoTProjectStatus) GetProjectCondition(t ProjectConditionType) *ProjectCondition { for i, c := range p.Conditions { if c.Type == t { @@ -328,44 +382,49 @@ func (r JdbcDeviceRegistry) IsSplitRegistry() (bool, error) { // region Logging -type DefaultLoggingRenderer func(rootLogger LogLevel, loggers map[string]LogLevel) string - -func (log LogbackConfig) RenderConfiguration(config *IoTConfig, defaultRenderer DefaultLoggingRenderer, override string) string { - - // did we get overridden - if override != "" { - return override - } - - // if we have an explicit configuration - if log.Logback != "" { - // use it - return log.Logback - } +func (log CommonLoggingConfig) GetEffectiveConfiguration(config *IoTConfig) (rootLevel LogLevel, loggers map[string]LogLevel) { // if we have a local level or loggers if log.Level != "" || log.Loggers != nil { + + rootLevel = log.Level + loggers = log.Loggers + // if the local root level is empty - if log.Level == "" { + if rootLevel == "" { // use the global root level - log.Level = config.Spec.Logging.Level + rootLevel = config.Spec.Logging.Level } - return defaultRenderer(log.Level, log.Loggers) + + } else { + + rootLevel = config.Spec.Logging.Level + loggers = config.Spec.Logging.Loggers + } - // if we have a global explicit logback config - if config.Spec.Logging.Defaults.Logback != "" { - // use it - return config.Spec.Logging.Defaults.Logback + if rootLevel == "" { + rootLevel = LogLevelInfo + } + + return +} + +type DefaultLoggingRenderer func(rootLogger LogLevel, loggers map[string]LogLevel) string + +func (log CommonLoggingConfig) RenderConfiguration(config *IoTConfig, defaultRenderer DefaultLoggingRenderer, override string) string { + + // did we get overridden + if override != "" { + return override } - // generate some reasonable defaults - return defaultRenderer(config.Spec.Logging.Level, config.Spec.Logging.Loggers) + return defaultRenderer(log.GetEffectiveConfiguration(config)) } func (csc CommonServiceConfig) RenderConfiguration(config *IoTConfig, defaultRenderer DefaultLoggingRenderer, override string) string { - return csc.Container.Logback.RenderConfiguration(config, defaultRenderer, override) + return csc.Container.Logging.RenderConfiguration(config, defaultRenderer, override) } // endregion diff --git a/pkg/apis/iot/v1alpha1/funcs_test.go b/pkg/apis/iot/v1alpha1/funcs_test.go new file mode 100644 index 00000000000..b77bd366530 --- /dev/null +++ b/pkg/apis/iot/v1alpha1/funcs_test.go @@ -0,0 +1,147 @@ +/* + * Copyright 2020, EnMasse authors. + * License: Apache License 2.0 (see the file LICENSE or http://apache.org/licenses/LICENSE-2.0.html). + */ + +package v1alpha1 + +import ( + "github.com/stretchr/testify/assert" + "testing" +) + +func TestLoggingPureDefault(t *testing.T) { + + config := &IoTConfig{} + + root, loggers := CommonLoggingConfig{}.GetEffectiveConfiguration(config) + assert.Equal(t, LogLevelInfo, root) + assert.Len(t, loggers, 0) + +} + +func TestLoggingConfigDefault(t *testing.T) { + + config := &IoTConfig{ + Spec: IoTConfigSpec{ + Logging: LoggingConfig{ + Level: LogLevelWarning, + }, + }, + } + + root, loggers := CommonLoggingConfig{}.GetEffectiveConfiguration(config) + assert.Equal(t, LogLevelWarning, root) + assert.Len(t, loggers, 0) + +} + +func TestLoggingConfigDefaultWithLoggers(t *testing.T) { + + config := &IoTConfig{ + Spec: IoTConfigSpec{ + Logging: LoggingConfig{ + Level: LogLevelWarning, + Loggers: map[string]LogLevel{ + "foo": LogLevelTrace, + }, + }, + }, + } + + root, loggers := CommonLoggingConfig{}.GetEffectiveConfiguration(config) + assert.Equal(t, LogLevelWarning, root) + assert.Len(t, loggers, 1) + assert.Equal(t, LogLevelTrace, loggers["foo"]) + +} + +func TestLoggingConfigOverrideRoot(t *testing.T) { + + config := &IoTConfig{ + Spec: IoTConfigSpec{ + Logging: LoggingConfig{ + Level: LogLevelWarning, + Loggers: map[string]LogLevel{ + "foo": LogLevelTrace, + }, + }, + }, + } + + root, loggers := CommonLoggingConfig{ + Level: LogLevelDebug, + }.GetEffectiveConfiguration(config) + assert.Equal(t, LogLevelDebug, root) + assert.Len(t, loggers, 0) + +} + +func TestLoggingConfigOverrideRootWithLoggers(t *testing.T) { + + config := &IoTConfig{ + Spec: IoTConfigSpec{ + Logging: LoggingConfig{ + Level: LogLevelWarning, + Loggers: map[string]LogLevel{ + "foo": LogLevelTrace, + }, + }, + }, + } + + root, loggers := CommonLoggingConfig{ + Level: LogLevelDebug, + Loggers: map[string]LogLevel{ + "bar": LogLevelError, + }, + }.GetEffectiveConfiguration(config) + assert.Equal(t, LogLevelDebug, root) + assert.Len(t, loggers, 1) + assert.Equal(t, LogLevelError, loggers["bar"]) + +} + +func TestLoggingConfigOverrideRootWithLoggersOnly(t *testing.T) { + + config := &IoTConfig{ + Spec: IoTConfigSpec{ + Logging: LoggingConfig{ + Level: LogLevelWarning, + Loggers: map[string]LogLevel{ + "foo": LogLevelTrace, + }, + }, + }, + } + + root, loggers := CommonLoggingConfig{ + Loggers: map[string]LogLevel{ + "bar": LogLevelError, + }, + }.GetEffectiveConfiguration(config) + assert.Equal(t, LogLevelWarning, root) + assert.Len(t, loggers, 1) + assert.Equal(t, LogLevelError, loggers["bar"]) + +} + +func TestQuarkusApplyLoggingToContainer(t *testing.T) { + config := &IoTConfig{} + opts := QuarkusContainerConfig{ + JavaContainerConfig: JavaContainerConfig{ + Logging: CommonLoggingConfig{ + Level: LogLevelInfo, + Loggers: map[string]LogLevel{ + "foo": LogLevelTrace, + "bar": LogLevelWarning, + }, + }, + }, + }.ApplyLoggingToContainer(config, nil) + + assert.Len(t, opts, 3) + assert.Equal(t, "-Dquarkus.log.level=info", opts[0]) + assert.Equal(t, "-Dquarkus.log.category.\"bar\".level=warn", opts[1]) + assert.Equal(t, "-Dquarkus.log.category.\"foo\".level=trace", opts[2]) +} diff --git a/pkg/apis/iot/v1alpha1/interfaces.go b/pkg/apis/iot/v1alpha1/interfaces.go index eabb1c491c4..a1d48649415 100644 --- a/pkg/apis/iot/v1alpha1/interfaces.go +++ b/pkg/apis/iot/v1alpha1/interfaces.go @@ -14,3 +14,4 @@ type CommonJavaContainerOptions interface { var _ CommonJavaContainerOptions = &CommonAdapterConfig{} var _ CommonJavaContainerOptions = &CommonServiceConfig{} +var _ CommonJavaContainerOptions = &QuarkusServiceConfig{} diff --git a/pkg/apis/iot/v1alpha1/types_config.go b/pkg/apis/iot/v1alpha1/types_config.go index 27810e4282b..efa01067cc8 100644 --- a/pkg/apis/iot/v1alpha1/types_config.go +++ b/pkg/apis/iot/v1alpha1/types_config.go @@ -67,11 +67,6 @@ type CommonLoggingConfig struct { Loggers map[string]LogLevel `json:"loggers,omitempty"` } -type LogbackConfig struct { - CommonLoggingConfig `json:",inline"` - Logback string `json:"logback,omitempty"` -} - // endregion //region Mesh @@ -169,7 +164,13 @@ type JavaContainerConfig struct { RequireNativeTls *bool `json:"requireNativeTls,omitempty"` - Logback LogbackConfig `json:"logback,omitempty"` + Logging CommonLoggingConfig `json:"logging,omitempty"` +} + +type QuarkusContainerConfig struct { + JavaContainerConfig `json:",inline"` + + NativeImage *bool `json:"native,omitempty"` } type EndpointConfig struct { @@ -352,16 +353,24 @@ type JdbcRegistryManagement struct { //endregion // Common options for a single container Java service + type CommonServiceConfig struct { Container JavaContainerConfig `json:"container,omitempty"` Tls TlsOptions `json:"tls,omitempty"` } +// Options for single container Quarkus service + +type QuarkusServiceConfig struct { + Container QuarkusContainerConfig `json:"container,omitempty"` + Tls TlsOptions `json:"tls,omitempty"` +} + //region TenantService type TenantServiceConfig struct { - ServiceConfig `json:",inline"` - CommonServiceConfig `json:",inline"` + ServiceConfig `json:",inline"` + QuarkusServiceConfig `json:",inline"` } //endregion diff --git a/pkg/apis/iot/v1alpha1/zz_generated.deepcopy.go b/pkg/apis/iot/v1alpha1/zz_generated.deepcopy.go index 9e7e6377cc6..c3762ed2d11 100644 --- a/pkg/apis/iot/v1alpha1/zz_generated.deepcopy.go +++ b/pkg/apis/iot/v1alpha1/zz_generated.deepcopy.go @@ -1333,7 +1333,7 @@ func (in *JavaContainerConfig) DeepCopyInto(out *JavaContainerConfig) { *out = new(bool) **out = **in } - in.Logback.DeepCopyInto(&out.Logback) + in.Logging.DeepCopyInto(&out.Logging) return } @@ -1481,23 +1481,6 @@ func (in *JdbcRegistryServer) DeepCopy() *JdbcRegistryServer { return out } -// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *LogbackConfig) DeepCopyInto(out *LogbackConfig) { - *out = *in - in.CommonLoggingConfig.DeepCopyInto(&out.CommonLoggingConfig) - return -} - -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new LogbackConfig. -func (in *LogbackConfig) DeepCopy() *LogbackConfig { - if in == nil { - return nil - } - out := new(LogbackConfig) - in.DeepCopyInto(out) - return out -} - // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *LoggingConfig) DeepCopyInto(out *LoggingConfig) { *out = *in @@ -1710,6 +1693,46 @@ func (in *ProvidedDownstreamStrategy) DeepCopy() *ProvidedDownstreamStrategy { return out } +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *QuarkusContainerConfig) DeepCopyInto(out *QuarkusContainerConfig) { + *out = *in + in.JavaContainerConfig.DeepCopyInto(&out.JavaContainerConfig) + if in.NativeImage != nil { + in, out := &in.NativeImage, &out.NativeImage + *out = new(bool) + **out = **in + } + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new QuarkusContainerConfig. +func (in *QuarkusContainerConfig) DeepCopy() *QuarkusContainerConfig { + if in == nil { + return nil + } + out := new(QuarkusContainerConfig) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *QuarkusServiceConfig) DeepCopyInto(out *QuarkusServiceConfig) { + *out = *in + in.Container.DeepCopyInto(&out.Container) + in.Tls.DeepCopyInto(&out.Tls) + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new QuarkusServiceConfig. +func (in *QuarkusServiceConfig) DeepCopy() *QuarkusServiceConfig { + if in == nil { + return nil + } + out := new(QuarkusServiceConfig) + in.DeepCopyInto(out) + return out +} + // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *ResourceLimits) DeepCopyInto(out *ResourceLimits) { *out = *in @@ -1947,7 +1970,7 @@ func (in *TenantConfiguration) DeepCopy() *TenantConfiguration { func (in *TenantServiceConfig) DeepCopyInto(out *TenantServiceConfig) { *out = *in in.ServiceConfig.DeepCopyInto(&out.ServiceConfig) - in.CommonServiceConfig.DeepCopyInto(&out.CommonServiceConfig) + in.QuarkusServiceConfig.DeepCopyInto(&out.QuarkusServiceConfig) return } diff --git a/pkg/client/clientset/versioned/typed/enmasse/v1beta2/enmasse_client.go b/pkg/client/clientset/versioned/typed/enmasse/v1beta2/enmasse_client.go index 945ec6bbd42..284a853de21 100644 --- a/pkg/client/clientset/versioned/typed/enmasse/v1beta2/enmasse_client.go +++ b/pkg/client/clientset/versioned/typed/enmasse/v1beta2/enmasse_client.go @@ -16,8 +16,10 @@ import ( type EnmasseV1beta2Interface interface { RESTClient() rest.Interface MessagingAddressesGetter + MessagingAddressPlansGetter MessagingEndpointsGetter MessagingInfrastructuresGetter + MessagingPlansGetter MessagingTenantsGetter } @@ -30,6 +32,10 @@ func (c *EnmasseV1beta2Client) MessagingAddresses(namespace string) MessagingAdd return newMessagingAddresses(c, namespace) } +func (c *EnmasseV1beta2Client) MessagingAddressPlans(namespace string) MessagingAddressPlanInterface { + return newMessagingAddressPlans(c, namespace) +} + func (c *EnmasseV1beta2Client) MessagingEndpoints(namespace string) MessagingEndpointInterface { return newMessagingEndpoints(c, namespace) } @@ -38,6 +44,10 @@ func (c *EnmasseV1beta2Client) MessagingInfrastructures(namespace string) Messag return newMessagingInfrastructures(c, namespace) } +func (c *EnmasseV1beta2Client) MessagingPlans(namespace string) MessagingPlanInterface { + return newMessagingPlans(c, namespace) +} + func (c *EnmasseV1beta2Client) MessagingTenants(namespace string) MessagingTenantInterface { return newMessagingTenants(c, namespace) } diff --git a/pkg/client/clientset/versioned/typed/enmasse/v1beta2/fake/fake_enmasse_client.go b/pkg/client/clientset/versioned/typed/enmasse/v1beta2/fake/fake_enmasse_client.go index 042b7b16d4f..c0da3018cdc 100644 --- a/pkg/client/clientset/versioned/typed/enmasse/v1beta2/fake/fake_enmasse_client.go +++ b/pkg/client/clientset/versioned/typed/enmasse/v1beta2/fake/fake_enmasse_client.go @@ -21,6 +21,10 @@ func (c *FakeEnmasseV1beta2) MessagingAddresses(namespace string) v1beta2.Messag return &FakeMessagingAddresses{c, namespace} } +func (c *FakeEnmasseV1beta2) MessagingAddressPlans(namespace string) v1beta2.MessagingAddressPlanInterface { + return &FakeMessagingAddressPlans{c, namespace} +} + func (c *FakeEnmasseV1beta2) MessagingEndpoints(namespace string) v1beta2.MessagingEndpointInterface { return &FakeMessagingEndpoints{c, namespace} } @@ -29,6 +33,10 @@ func (c *FakeEnmasseV1beta2) MessagingInfrastructures(namespace string) v1beta2. return &FakeMessagingInfrastructures{c, namespace} } +func (c *FakeEnmasseV1beta2) MessagingPlans(namespace string) v1beta2.MessagingPlanInterface { + return &FakeMessagingPlans{c, namespace} +} + func (c *FakeEnmasseV1beta2) MessagingTenants(namespace string) v1beta2.MessagingTenantInterface { return &FakeMessagingTenants{c, namespace} } diff --git a/pkg/client/clientset/versioned/typed/enmasse/v1beta2/fake/fake_messagingaddressplan.go b/pkg/client/clientset/versioned/typed/enmasse/v1beta2/fake/fake_messagingaddressplan.go new file mode 100644 index 00000000000..8a537f62b1a --- /dev/null +++ b/pkg/client/clientset/versioned/typed/enmasse/v1beta2/fake/fake_messagingaddressplan.go @@ -0,0 +1,129 @@ +/* + * Copyright 2018-2019, EnMasse authors. + * License: Apache License 2.0 (see the file LICENSE or http://apache.org/licenses/LICENSE-2.0.html). + */ + +// Code generated by client-gen. DO NOT EDIT. + +package fake + +import ( + v1beta2 "github.com/enmasseproject/enmasse/pkg/apis/enmasse/v1beta2" + v1 "k8s.io/apimachinery/pkg/apis/meta/v1" + labels "k8s.io/apimachinery/pkg/labels" + schema "k8s.io/apimachinery/pkg/runtime/schema" + types "k8s.io/apimachinery/pkg/types" + watch "k8s.io/apimachinery/pkg/watch" + testing "k8s.io/client-go/testing" +) + +// FakeMessagingAddressPlans implements MessagingAddressPlanInterface +type FakeMessagingAddressPlans struct { + Fake *FakeEnmasseV1beta2 + ns string +} + +var messagingaddressplansResource = schema.GroupVersionResource{Group: "enmasse.io", Version: "v1beta2", Resource: "messagingaddressplans"} + +var messagingaddressplansKind = schema.GroupVersionKind{Group: "enmasse.io", Version: "v1beta2", Kind: "MessagingAddressPlan"} + +// Get takes name of the messagingAddressPlan, and returns the corresponding messagingAddressPlan object, and an error if there is any. +func (c *FakeMessagingAddressPlans) Get(name string, options v1.GetOptions) (result *v1beta2.MessagingAddressPlan, err error) { + obj, err := c.Fake. + Invokes(testing.NewGetAction(messagingaddressplansResource, c.ns, name), &v1beta2.MessagingAddressPlan{}) + + if obj == nil { + return nil, err + } + return obj.(*v1beta2.MessagingAddressPlan), err +} + +// List takes label and field selectors, and returns the list of MessagingAddressPlans that match those selectors. +func (c *FakeMessagingAddressPlans) List(opts v1.ListOptions) (result *v1beta2.MessagingAddressPlanList, err error) { + obj, err := c.Fake. + Invokes(testing.NewListAction(messagingaddressplansResource, messagingaddressplansKind, c.ns, opts), &v1beta2.MessagingAddressPlanList{}) + + if obj == nil { + return nil, err + } + + label, _, _ := testing.ExtractFromListOptions(opts) + if label == nil { + label = labels.Everything() + } + list := &v1beta2.MessagingAddressPlanList{ListMeta: obj.(*v1beta2.MessagingAddressPlanList).ListMeta} + for _, item := range obj.(*v1beta2.MessagingAddressPlanList).Items { + if label.Matches(labels.Set(item.Labels)) { + list.Items = append(list.Items, item) + } + } + return list, err +} + +// Watch returns a watch.Interface that watches the requested messagingAddressPlans. +func (c *FakeMessagingAddressPlans) Watch(opts v1.ListOptions) (watch.Interface, error) { + return c.Fake. + InvokesWatch(testing.NewWatchAction(messagingaddressplansResource, c.ns, opts)) + +} + +// Create takes the representation of a messagingAddressPlan and creates it. Returns the server's representation of the messagingAddressPlan, and an error, if there is any. +func (c *FakeMessagingAddressPlans) Create(messagingAddressPlan *v1beta2.MessagingAddressPlan) (result *v1beta2.MessagingAddressPlan, err error) { + obj, err := c.Fake. + Invokes(testing.NewCreateAction(messagingaddressplansResource, c.ns, messagingAddressPlan), &v1beta2.MessagingAddressPlan{}) + + if obj == nil { + return nil, err + } + return obj.(*v1beta2.MessagingAddressPlan), err +} + +// Update takes the representation of a messagingAddressPlan and updates it. Returns the server's representation of the messagingAddressPlan, and an error, if there is any. +func (c *FakeMessagingAddressPlans) Update(messagingAddressPlan *v1beta2.MessagingAddressPlan) (result *v1beta2.MessagingAddressPlan, err error) { + obj, err := c.Fake. + Invokes(testing.NewUpdateAction(messagingaddressplansResource, c.ns, messagingAddressPlan), &v1beta2.MessagingAddressPlan{}) + + if obj == nil { + return nil, err + } + return obj.(*v1beta2.MessagingAddressPlan), err +} + +// UpdateStatus was generated because the type contains a Status member. +// Add a +genclient:noStatus comment above the type to avoid generating UpdateStatus(). +func (c *FakeMessagingAddressPlans) UpdateStatus(messagingAddressPlan *v1beta2.MessagingAddressPlan) (*v1beta2.MessagingAddressPlan, error) { + obj, err := c.Fake. + Invokes(testing.NewUpdateSubresourceAction(messagingaddressplansResource, "status", c.ns, messagingAddressPlan), &v1beta2.MessagingAddressPlan{}) + + if obj == nil { + return nil, err + } + return obj.(*v1beta2.MessagingAddressPlan), err +} + +// Delete takes name of the messagingAddressPlan and deletes it. Returns an error if one occurs. +func (c *FakeMessagingAddressPlans) Delete(name string, options *v1.DeleteOptions) error { + _, err := c.Fake. + Invokes(testing.NewDeleteAction(messagingaddressplansResource, c.ns, name), &v1beta2.MessagingAddressPlan{}) + + return err +} + +// DeleteCollection deletes a collection of objects. +func (c *FakeMessagingAddressPlans) DeleteCollection(options *v1.DeleteOptions, listOptions v1.ListOptions) error { + action := testing.NewDeleteCollectionAction(messagingaddressplansResource, c.ns, listOptions) + + _, err := c.Fake.Invokes(action, &v1beta2.MessagingAddressPlanList{}) + return err +} + +// Patch applies the patch and returns the patched messagingAddressPlan. +func (c *FakeMessagingAddressPlans) Patch(name string, pt types.PatchType, data []byte, subresources ...string) (result *v1beta2.MessagingAddressPlan, err error) { + obj, err := c.Fake. + Invokes(testing.NewPatchSubresourceAction(messagingaddressplansResource, c.ns, name, pt, data, subresources...), &v1beta2.MessagingAddressPlan{}) + + if obj == nil { + return nil, err + } + return obj.(*v1beta2.MessagingAddressPlan), err +} diff --git a/pkg/client/clientset/versioned/typed/enmasse/v1beta2/fake/fake_messagingplan.go b/pkg/client/clientset/versioned/typed/enmasse/v1beta2/fake/fake_messagingplan.go new file mode 100644 index 00000000000..f6375db866a --- /dev/null +++ b/pkg/client/clientset/versioned/typed/enmasse/v1beta2/fake/fake_messagingplan.go @@ -0,0 +1,129 @@ +/* + * Copyright 2018-2019, EnMasse authors. + * License: Apache License 2.0 (see the file LICENSE or http://apache.org/licenses/LICENSE-2.0.html). + */ + +// Code generated by client-gen. DO NOT EDIT. + +package fake + +import ( + v1beta2 "github.com/enmasseproject/enmasse/pkg/apis/enmasse/v1beta2" + v1 "k8s.io/apimachinery/pkg/apis/meta/v1" + labels "k8s.io/apimachinery/pkg/labels" + schema "k8s.io/apimachinery/pkg/runtime/schema" + types "k8s.io/apimachinery/pkg/types" + watch "k8s.io/apimachinery/pkg/watch" + testing "k8s.io/client-go/testing" +) + +// FakeMessagingPlans implements MessagingPlanInterface +type FakeMessagingPlans struct { + Fake *FakeEnmasseV1beta2 + ns string +} + +var messagingplansResource = schema.GroupVersionResource{Group: "enmasse.io", Version: "v1beta2", Resource: "messagingplans"} + +var messagingplansKind = schema.GroupVersionKind{Group: "enmasse.io", Version: "v1beta2", Kind: "MessagingPlan"} + +// Get takes name of the messagingPlan, and returns the corresponding messagingPlan object, and an error if there is any. +func (c *FakeMessagingPlans) Get(name string, options v1.GetOptions) (result *v1beta2.MessagingPlan, err error) { + obj, err := c.Fake. + Invokes(testing.NewGetAction(messagingplansResource, c.ns, name), &v1beta2.MessagingPlan{}) + + if obj == nil { + return nil, err + } + return obj.(*v1beta2.MessagingPlan), err +} + +// List takes label and field selectors, and returns the list of MessagingPlans that match those selectors. +func (c *FakeMessagingPlans) List(opts v1.ListOptions) (result *v1beta2.MessagingPlanList, err error) { + obj, err := c.Fake. + Invokes(testing.NewListAction(messagingplansResource, messagingplansKind, c.ns, opts), &v1beta2.MessagingPlanList{}) + + if obj == nil { + return nil, err + } + + label, _, _ := testing.ExtractFromListOptions(opts) + if label == nil { + label = labels.Everything() + } + list := &v1beta2.MessagingPlanList{ListMeta: obj.(*v1beta2.MessagingPlanList).ListMeta} + for _, item := range obj.(*v1beta2.MessagingPlanList).Items { + if label.Matches(labels.Set(item.Labels)) { + list.Items = append(list.Items, item) + } + } + return list, err +} + +// Watch returns a watch.Interface that watches the requested messagingPlans. +func (c *FakeMessagingPlans) Watch(opts v1.ListOptions) (watch.Interface, error) { + return c.Fake. + InvokesWatch(testing.NewWatchAction(messagingplansResource, c.ns, opts)) + +} + +// Create takes the representation of a messagingPlan and creates it. Returns the server's representation of the messagingPlan, and an error, if there is any. +func (c *FakeMessagingPlans) Create(messagingPlan *v1beta2.MessagingPlan) (result *v1beta2.MessagingPlan, err error) { + obj, err := c.Fake. + Invokes(testing.NewCreateAction(messagingplansResource, c.ns, messagingPlan), &v1beta2.MessagingPlan{}) + + if obj == nil { + return nil, err + } + return obj.(*v1beta2.MessagingPlan), err +} + +// Update takes the representation of a messagingPlan and updates it. Returns the server's representation of the messagingPlan, and an error, if there is any. +func (c *FakeMessagingPlans) Update(messagingPlan *v1beta2.MessagingPlan) (result *v1beta2.MessagingPlan, err error) { + obj, err := c.Fake. + Invokes(testing.NewUpdateAction(messagingplansResource, c.ns, messagingPlan), &v1beta2.MessagingPlan{}) + + if obj == nil { + return nil, err + } + return obj.(*v1beta2.MessagingPlan), err +} + +// UpdateStatus was generated because the type contains a Status member. +// Add a +genclient:noStatus comment above the type to avoid generating UpdateStatus(). +func (c *FakeMessagingPlans) UpdateStatus(messagingPlan *v1beta2.MessagingPlan) (*v1beta2.MessagingPlan, error) { + obj, err := c.Fake. + Invokes(testing.NewUpdateSubresourceAction(messagingplansResource, "status", c.ns, messagingPlan), &v1beta2.MessagingPlan{}) + + if obj == nil { + return nil, err + } + return obj.(*v1beta2.MessagingPlan), err +} + +// Delete takes name of the messagingPlan and deletes it. Returns an error if one occurs. +func (c *FakeMessagingPlans) Delete(name string, options *v1.DeleteOptions) error { + _, err := c.Fake. + Invokes(testing.NewDeleteAction(messagingplansResource, c.ns, name), &v1beta2.MessagingPlan{}) + + return err +} + +// DeleteCollection deletes a collection of objects. +func (c *FakeMessagingPlans) DeleteCollection(options *v1.DeleteOptions, listOptions v1.ListOptions) error { + action := testing.NewDeleteCollectionAction(messagingplansResource, c.ns, listOptions) + + _, err := c.Fake.Invokes(action, &v1beta2.MessagingPlanList{}) + return err +} + +// Patch applies the patch and returns the patched messagingPlan. +func (c *FakeMessagingPlans) Patch(name string, pt types.PatchType, data []byte, subresources ...string) (result *v1beta2.MessagingPlan, err error) { + obj, err := c.Fake. + Invokes(testing.NewPatchSubresourceAction(messagingplansResource, c.ns, name, pt, data, subresources...), &v1beta2.MessagingPlan{}) + + if obj == nil { + return nil, err + } + return obj.(*v1beta2.MessagingPlan), err +} diff --git a/pkg/client/clientset/versioned/typed/enmasse/v1beta2/generated_expansion.go b/pkg/client/clientset/versioned/typed/enmasse/v1beta2/generated_expansion.go index e5212fe0610..cda98befe6e 100644 --- a/pkg/client/clientset/versioned/typed/enmasse/v1beta2/generated_expansion.go +++ b/pkg/client/clientset/versioned/typed/enmasse/v1beta2/generated_expansion.go @@ -9,8 +9,12 @@ package v1beta2 type MessagingAddressExpansion interface{} +type MessagingAddressPlanExpansion interface{} + type MessagingEndpointExpansion interface{} type MessagingInfrastructureExpansion interface{} +type MessagingPlanExpansion interface{} + type MessagingTenantExpansion interface{} diff --git a/pkg/client/clientset/versioned/typed/enmasse/v1beta2/messagingaddressplan.go b/pkg/client/clientset/versioned/typed/enmasse/v1beta2/messagingaddressplan.go new file mode 100644 index 00000000000..ef96ef9b20f --- /dev/null +++ b/pkg/client/clientset/versioned/typed/enmasse/v1beta2/messagingaddressplan.go @@ -0,0 +1,180 @@ +/* + * Copyright 2018-2019, EnMasse authors. + * License: Apache License 2.0 (see the file LICENSE or http://apache.org/licenses/LICENSE-2.0.html). + */ + +// Code generated by client-gen. DO NOT EDIT. + +package v1beta2 + +import ( + "time" + + v1beta2 "github.com/enmasseproject/enmasse/pkg/apis/enmasse/v1beta2" + scheme "github.com/enmasseproject/enmasse/pkg/client/clientset/versioned/scheme" + v1 "k8s.io/apimachinery/pkg/apis/meta/v1" + types "k8s.io/apimachinery/pkg/types" + watch "k8s.io/apimachinery/pkg/watch" + rest "k8s.io/client-go/rest" +) + +// MessagingAddressPlansGetter has a method to return a MessagingAddressPlanInterface. +// A group's client should implement this interface. +type MessagingAddressPlansGetter interface { + MessagingAddressPlans(namespace string) MessagingAddressPlanInterface +} + +// MessagingAddressPlanInterface has methods to work with MessagingAddressPlan resources. +type MessagingAddressPlanInterface interface { + Create(*v1beta2.MessagingAddressPlan) (*v1beta2.MessagingAddressPlan, error) + Update(*v1beta2.MessagingAddressPlan) (*v1beta2.MessagingAddressPlan, error) + UpdateStatus(*v1beta2.MessagingAddressPlan) (*v1beta2.MessagingAddressPlan, error) + Delete(name string, options *v1.DeleteOptions) error + DeleteCollection(options *v1.DeleteOptions, listOptions v1.ListOptions) error + Get(name string, options v1.GetOptions) (*v1beta2.MessagingAddressPlan, error) + List(opts v1.ListOptions) (*v1beta2.MessagingAddressPlanList, error) + Watch(opts v1.ListOptions) (watch.Interface, error) + Patch(name string, pt types.PatchType, data []byte, subresources ...string) (result *v1beta2.MessagingAddressPlan, err error) + MessagingAddressPlanExpansion +} + +// messagingAddressPlans implements MessagingAddressPlanInterface +type messagingAddressPlans struct { + client rest.Interface + ns string +} + +// newMessagingAddressPlans returns a MessagingAddressPlans +func newMessagingAddressPlans(c *EnmasseV1beta2Client, namespace string) *messagingAddressPlans { + return &messagingAddressPlans{ + client: c.RESTClient(), + ns: namespace, + } +} + +// Get takes name of the messagingAddressPlan, and returns the corresponding messagingAddressPlan object, and an error if there is any. +func (c *messagingAddressPlans) Get(name string, options v1.GetOptions) (result *v1beta2.MessagingAddressPlan, err error) { + result = &v1beta2.MessagingAddressPlan{} + err = c.client.Get(). + Namespace(c.ns). + Resource("messagingaddressplans"). + Name(name). + VersionedParams(&options, scheme.ParameterCodec). + Do(). + Into(result) + return +} + +// List takes label and field selectors, and returns the list of MessagingAddressPlans that match those selectors. +func (c *messagingAddressPlans) List(opts v1.ListOptions) (result *v1beta2.MessagingAddressPlanList, err error) { + var timeout time.Duration + if opts.TimeoutSeconds != nil { + timeout = time.Duration(*opts.TimeoutSeconds) * time.Second + } + result = &v1beta2.MessagingAddressPlanList{} + err = c.client.Get(). + Namespace(c.ns). + Resource("messagingaddressplans"). + VersionedParams(&opts, scheme.ParameterCodec). + Timeout(timeout). + Do(). + Into(result) + return +} + +// Watch returns a watch.Interface that watches the requested messagingAddressPlans. +func (c *messagingAddressPlans) Watch(opts v1.ListOptions) (watch.Interface, error) { + var timeout time.Duration + if opts.TimeoutSeconds != nil { + timeout = time.Duration(*opts.TimeoutSeconds) * time.Second + } + opts.Watch = true + return c.client.Get(). + Namespace(c.ns). + Resource("messagingaddressplans"). + VersionedParams(&opts, scheme.ParameterCodec). + Timeout(timeout). + Watch() +} + +// Create takes the representation of a messagingAddressPlan and creates it. Returns the server's representation of the messagingAddressPlan, and an error, if there is any. +func (c *messagingAddressPlans) Create(messagingAddressPlan *v1beta2.MessagingAddressPlan) (result *v1beta2.MessagingAddressPlan, err error) { + result = &v1beta2.MessagingAddressPlan{} + err = c.client.Post(). + Namespace(c.ns). + Resource("messagingaddressplans"). + Body(messagingAddressPlan). + Do(). + Into(result) + return +} + +// Update takes the representation of a messagingAddressPlan and updates it. Returns the server's representation of the messagingAddressPlan, and an error, if there is any. +func (c *messagingAddressPlans) Update(messagingAddressPlan *v1beta2.MessagingAddressPlan) (result *v1beta2.MessagingAddressPlan, err error) { + result = &v1beta2.MessagingAddressPlan{} + err = c.client.Put(). + Namespace(c.ns). + Resource("messagingaddressplans"). + Name(messagingAddressPlan.Name). + Body(messagingAddressPlan). + Do(). + Into(result) + return +} + +// UpdateStatus was generated because the type contains a Status member. +// Add a +genclient:noStatus comment above the type to avoid generating UpdateStatus(). + +func (c *messagingAddressPlans) UpdateStatus(messagingAddressPlan *v1beta2.MessagingAddressPlan) (result *v1beta2.MessagingAddressPlan, err error) { + result = &v1beta2.MessagingAddressPlan{} + err = c.client.Put(). + Namespace(c.ns). + Resource("messagingaddressplans"). + Name(messagingAddressPlan.Name). + SubResource("status"). + Body(messagingAddressPlan). + Do(). + Into(result) + return +} + +// Delete takes name of the messagingAddressPlan and deletes it. Returns an error if one occurs. +func (c *messagingAddressPlans) Delete(name string, options *v1.DeleteOptions) error { + return c.client.Delete(). + Namespace(c.ns). + Resource("messagingaddressplans"). + Name(name). + Body(options). + Do(). + Error() +} + +// DeleteCollection deletes a collection of objects. +func (c *messagingAddressPlans) DeleteCollection(options *v1.DeleteOptions, listOptions v1.ListOptions) error { + var timeout time.Duration + if listOptions.TimeoutSeconds != nil { + timeout = time.Duration(*listOptions.TimeoutSeconds) * time.Second + } + return c.client.Delete(). + Namespace(c.ns). + Resource("messagingaddressplans"). + VersionedParams(&listOptions, scheme.ParameterCodec). + Timeout(timeout). + Body(options). + Do(). + Error() +} + +// Patch applies the patch and returns the patched messagingAddressPlan. +func (c *messagingAddressPlans) Patch(name string, pt types.PatchType, data []byte, subresources ...string) (result *v1beta2.MessagingAddressPlan, err error) { + result = &v1beta2.MessagingAddressPlan{} + err = c.client.Patch(pt). + Namespace(c.ns). + Resource("messagingaddressplans"). + SubResource(subresources...). + Name(name). + Body(data). + Do(). + Into(result) + return +} diff --git a/pkg/client/clientset/versioned/typed/enmasse/v1beta2/messagingplan.go b/pkg/client/clientset/versioned/typed/enmasse/v1beta2/messagingplan.go new file mode 100644 index 00000000000..082da1a86f5 --- /dev/null +++ b/pkg/client/clientset/versioned/typed/enmasse/v1beta2/messagingplan.go @@ -0,0 +1,180 @@ +/* + * Copyright 2018-2019, EnMasse authors. + * License: Apache License 2.0 (see the file LICENSE or http://apache.org/licenses/LICENSE-2.0.html). + */ + +// Code generated by client-gen. DO NOT EDIT. + +package v1beta2 + +import ( + "time" + + v1beta2 "github.com/enmasseproject/enmasse/pkg/apis/enmasse/v1beta2" + scheme "github.com/enmasseproject/enmasse/pkg/client/clientset/versioned/scheme" + v1 "k8s.io/apimachinery/pkg/apis/meta/v1" + types "k8s.io/apimachinery/pkg/types" + watch "k8s.io/apimachinery/pkg/watch" + rest "k8s.io/client-go/rest" +) + +// MessagingPlansGetter has a method to return a MessagingPlanInterface. +// A group's client should implement this interface. +type MessagingPlansGetter interface { + MessagingPlans(namespace string) MessagingPlanInterface +} + +// MessagingPlanInterface has methods to work with MessagingPlan resources. +type MessagingPlanInterface interface { + Create(*v1beta2.MessagingPlan) (*v1beta2.MessagingPlan, error) + Update(*v1beta2.MessagingPlan) (*v1beta2.MessagingPlan, error) + UpdateStatus(*v1beta2.MessagingPlan) (*v1beta2.MessagingPlan, error) + Delete(name string, options *v1.DeleteOptions) error + DeleteCollection(options *v1.DeleteOptions, listOptions v1.ListOptions) error + Get(name string, options v1.GetOptions) (*v1beta2.MessagingPlan, error) + List(opts v1.ListOptions) (*v1beta2.MessagingPlanList, error) + Watch(opts v1.ListOptions) (watch.Interface, error) + Patch(name string, pt types.PatchType, data []byte, subresources ...string) (result *v1beta2.MessagingPlan, err error) + MessagingPlanExpansion +} + +// messagingPlans implements MessagingPlanInterface +type messagingPlans struct { + client rest.Interface + ns string +} + +// newMessagingPlans returns a MessagingPlans +func newMessagingPlans(c *EnmasseV1beta2Client, namespace string) *messagingPlans { + return &messagingPlans{ + client: c.RESTClient(), + ns: namespace, + } +} + +// Get takes name of the messagingPlan, and returns the corresponding messagingPlan object, and an error if there is any. +func (c *messagingPlans) Get(name string, options v1.GetOptions) (result *v1beta2.MessagingPlan, err error) { + result = &v1beta2.MessagingPlan{} + err = c.client.Get(). + Namespace(c.ns). + Resource("messagingplans"). + Name(name). + VersionedParams(&options, scheme.ParameterCodec). + Do(). + Into(result) + return +} + +// List takes label and field selectors, and returns the list of MessagingPlans that match those selectors. +func (c *messagingPlans) List(opts v1.ListOptions) (result *v1beta2.MessagingPlanList, err error) { + var timeout time.Duration + if opts.TimeoutSeconds != nil { + timeout = time.Duration(*opts.TimeoutSeconds) * time.Second + } + result = &v1beta2.MessagingPlanList{} + err = c.client.Get(). + Namespace(c.ns). + Resource("messagingplans"). + VersionedParams(&opts, scheme.ParameterCodec). + Timeout(timeout). + Do(). + Into(result) + return +} + +// Watch returns a watch.Interface that watches the requested messagingPlans. +func (c *messagingPlans) Watch(opts v1.ListOptions) (watch.Interface, error) { + var timeout time.Duration + if opts.TimeoutSeconds != nil { + timeout = time.Duration(*opts.TimeoutSeconds) * time.Second + } + opts.Watch = true + return c.client.Get(). + Namespace(c.ns). + Resource("messagingplans"). + VersionedParams(&opts, scheme.ParameterCodec). + Timeout(timeout). + Watch() +} + +// Create takes the representation of a messagingPlan and creates it. Returns the server's representation of the messagingPlan, and an error, if there is any. +func (c *messagingPlans) Create(messagingPlan *v1beta2.MessagingPlan) (result *v1beta2.MessagingPlan, err error) { + result = &v1beta2.MessagingPlan{} + err = c.client.Post(). + Namespace(c.ns). + Resource("messagingplans"). + Body(messagingPlan). + Do(). + Into(result) + return +} + +// Update takes the representation of a messagingPlan and updates it. Returns the server's representation of the messagingPlan, and an error, if there is any. +func (c *messagingPlans) Update(messagingPlan *v1beta2.MessagingPlan) (result *v1beta2.MessagingPlan, err error) { + result = &v1beta2.MessagingPlan{} + err = c.client.Put(). + Namespace(c.ns). + Resource("messagingplans"). + Name(messagingPlan.Name). + Body(messagingPlan). + Do(). + Into(result) + return +} + +// UpdateStatus was generated because the type contains a Status member. +// Add a +genclient:noStatus comment above the type to avoid generating UpdateStatus(). + +func (c *messagingPlans) UpdateStatus(messagingPlan *v1beta2.MessagingPlan) (result *v1beta2.MessagingPlan, err error) { + result = &v1beta2.MessagingPlan{} + err = c.client.Put(). + Namespace(c.ns). + Resource("messagingplans"). + Name(messagingPlan.Name). + SubResource("status"). + Body(messagingPlan). + Do(). + Into(result) + return +} + +// Delete takes name of the messagingPlan and deletes it. Returns an error if one occurs. +func (c *messagingPlans) Delete(name string, options *v1.DeleteOptions) error { + return c.client.Delete(). + Namespace(c.ns). + Resource("messagingplans"). + Name(name). + Body(options). + Do(). + Error() +} + +// DeleteCollection deletes a collection of objects. +func (c *messagingPlans) DeleteCollection(options *v1.DeleteOptions, listOptions v1.ListOptions) error { + var timeout time.Duration + if listOptions.TimeoutSeconds != nil { + timeout = time.Duration(*listOptions.TimeoutSeconds) * time.Second + } + return c.client.Delete(). + Namespace(c.ns). + Resource("messagingplans"). + VersionedParams(&listOptions, scheme.ParameterCodec). + Timeout(timeout). + Body(options). + Do(). + Error() +} + +// Patch applies the patch and returns the patched messagingPlan. +func (c *messagingPlans) Patch(name string, pt types.PatchType, data []byte, subresources ...string) (result *v1beta2.MessagingPlan, err error) { + result = &v1beta2.MessagingPlan{} + err = c.client.Patch(pt). + Namespace(c.ns). + Resource("messagingplans"). + SubResource(subresources...). + Name(name). + Body(data). + Do(). + Into(result) + return +} diff --git a/pkg/client/informers/externalversions/enmasse/v1beta2/interface.go b/pkg/client/informers/externalversions/enmasse/v1beta2/interface.go index 6808e8dbb64..dcba7431f13 100644 --- a/pkg/client/informers/externalversions/enmasse/v1beta2/interface.go +++ b/pkg/client/informers/externalversions/enmasse/v1beta2/interface.go @@ -15,10 +15,14 @@ import ( type Interface interface { // MessagingAddresses returns a MessagingAddressInformer. MessagingAddresses() MessagingAddressInformer + // MessagingAddressPlans returns a MessagingAddressPlanInformer. + MessagingAddressPlans() MessagingAddressPlanInformer // MessagingEndpoints returns a MessagingEndpointInformer. MessagingEndpoints() MessagingEndpointInformer // MessagingInfrastructures returns a MessagingInfrastructureInformer. MessagingInfrastructures() MessagingInfrastructureInformer + // MessagingPlans returns a MessagingPlanInformer. + MessagingPlans() MessagingPlanInformer // MessagingTenants returns a MessagingTenantInformer. MessagingTenants() MessagingTenantInformer } @@ -39,6 +43,11 @@ func (v *version) MessagingAddresses() MessagingAddressInformer { return &messagingAddressInformer{factory: v.factory, namespace: v.namespace, tweakListOptions: v.tweakListOptions} } +// MessagingAddressPlans returns a MessagingAddressPlanInformer. +func (v *version) MessagingAddressPlans() MessagingAddressPlanInformer { + return &messagingAddressPlanInformer{factory: v.factory, namespace: v.namespace, tweakListOptions: v.tweakListOptions} +} + // MessagingEndpoints returns a MessagingEndpointInformer. func (v *version) MessagingEndpoints() MessagingEndpointInformer { return &messagingEndpointInformer{factory: v.factory, namespace: v.namespace, tweakListOptions: v.tweakListOptions} @@ -49,6 +58,11 @@ func (v *version) MessagingInfrastructures() MessagingInfrastructureInformer { return &messagingInfrastructureInformer{factory: v.factory, namespace: v.namespace, tweakListOptions: v.tweakListOptions} } +// MessagingPlans returns a MessagingPlanInformer. +func (v *version) MessagingPlans() MessagingPlanInformer { + return &messagingPlanInformer{factory: v.factory, namespace: v.namespace, tweakListOptions: v.tweakListOptions} +} + // MessagingTenants returns a MessagingTenantInformer. func (v *version) MessagingTenants() MessagingTenantInformer { return &messagingTenantInformer{factory: v.factory, namespace: v.namespace, tweakListOptions: v.tweakListOptions} diff --git a/pkg/client/informers/externalversions/enmasse/v1beta2/messagingaddressplan.go b/pkg/client/informers/externalversions/enmasse/v1beta2/messagingaddressplan.go new file mode 100644 index 00000000000..b3c3389042d --- /dev/null +++ b/pkg/client/informers/externalversions/enmasse/v1beta2/messagingaddressplan.go @@ -0,0 +1,78 @@ +/* + * Copyright 2018-2019, EnMasse authors. + * License: Apache License 2.0 (see the file LICENSE or http://apache.org/licenses/LICENSE-2.0.html). + */ + +// Code generated by informer-gen. DO NOT EDIT. + +package v1beta2 + +import ( + time "time" + + enmassev1beta2 "github.com/enmasseproject/enmasse/pkg/apis/enmasse/v1beta2" + versioned "github.com/enmasseproject/enmasse/pkg/client/clientset/versioned" + internalinterfaces "github.com/enmasseproject/enmasse/pkg/client/informers/externalversions/internalinterfaces" + v1beta2 "github.com/enmasseproject/enmasse/pkg/client/listers/enmasse/v1beta2" + v1 "k8s.io/apimachinery/pkg/apis/meta/v1" + runtime "k8s.io/apimachinery/pkg/runtime" + watch "k8s.io/apimachinery/pkg/watch" + cache "k8s.io/client-go/tools/cache" +) + +// MessagingAddressPlanInformer provides access to a shared informer and lister for +// MessagingAddressPlans. +type MessagingAddressPlanInformer interface { + Informer() cache.SharedIndexInformer + Lister() v1beta2.MessagingAddressPlanLister +} + +type messagingAddressPlanInformer struct { + factory internalinterfaces.SharedInformerFactory + tweakListOptions internalinterfaces.TweakListOptionsFunc + namespace string +} + +// NewMessagingAddressPlanInformer constructs a new informer for MessagingAddressPlan type. +// Always prefer using an informer factory to get a shared informer instead of getting an independent +// one. This reduces memory footprint and number of connections to the server. +func NewMessagingAddressPlanInformer(client versioned.Interface, namespace string, resyncPeriod time.Duration, indexers cache.Indexers) cache.SharedIndexInformer { + return NewFilteredMessagingAddressPlanInformer(client, namespace, resyncPeriod, indexers, nil) +} + +// NewFilteredMessagingAddressPlanInformer constructs a new informer for MessagingAddressPlan type. +// Always prefer using an informer factory to get a shared informer instead of getting an independent +// one. This reduces memory footprint and number of connections to the server. +func NewFilteredMessagingAddressPlanInformer(client versioned.Interface, namespace string, resyncPeriod time.Duration, indexers cache.Indexers, tweakListOptions internalinterfaces.TweakListOptionsFunc) cache.SharedIndexInformer { + return cache.NewSharedIndexInformer( + &cache.ListWatch{ + ListFunc: func(options v1.ListOptions) (runtime.Object, error) { + if tweakListOptions != nil { + tweakListOptions(&options) + } + return client.EnmasseV1beta2().MessagingAddressPlans(namespace).List(options) + }, + WatchFunc: func(options v1.ListOptions) (watch.Interface, error) { + if tweakListOptions != nil { + tweakListOptions(&options) + } + return client.EnmasseV1beta2().MessagingAddressPlans(namespace).Watch(options) + }, + }, + &enmassev1beta2.MessagingAddressPlan{}, + resyncPeriod, + indexers, + ) +} + +func (f *messagingAddressPlanInformer) defaultInformer(client versioned.Interface, resyncPeriod time.Duration) cache.SharedIndexInformer { + return NewFilteredMessagingAddressPlanInformer(client, f.namespace, resyncPeriod, cache.Indexers{cache.NamespaceIndex: cache.MetaNamespaceIndexFunc}, f.tweakListOptions) +} + +func (f *messagingAddressPlanInformer) Informer() cache.SharedIndexInformer { + return f.factory.InformerFor(&enmassev1beta2.MessagingAddressPlan{}, f.defaultInformer) +} + +func (f *messagingAddressPlanInformer) Lister() v1beta2.MessagingAddressPlanLister { + return v1beta2.NewMessagingAddressPlanLister(f.Informer().GetIndexer()) +} diff --git a/pkg/client/informers/externalversions/enmasse/v1beta2/messagingplan.go b/pkg/client/informers/externalversions/enmasse/v1beta2/messagingplan.go new file mode 100644 index 00000000000..f716f90aec0 --- /dev/null +++ b/pkg/client/informers/externalversions/enmasse/v1beta2/messagingplan.go @@ -0,0 +1,78 @@ +/* + * Copyright 2018-2019, EnMasse authors. + * License: Apache License 2.0 (see the file LICENSE or http://apache.org/licenses/LICENSE-2.0.html). + */ + +// Code generated by informer-gen. DO NOT EDIT. + +package v1beta2 + +import ( + time "time" + + enmassev1beta2 "github.com/enmasseproject/enmasse/pkg/apis/enmasse/v1beta2" + versioned "github.com/enmasseproject/enmasse/pkg/client/clientset/versioned" + internalinterfaces "github.com/enmasseproject/enmasse/pkg/client/informers/externalversions/internalinterfaces" + v1beta2 "github.com/enmasseproject/enmasse/pkg/client/listers/enmasse/v1beta2" + v1 "k8s.io/apimachinery/pkg/apis/meta/v1" + runtime "k8s.io/apimachinery/pkg/runtime" + watch "k8s.io/apimachinery/pkg/watch" + cache "k8s.io/client-go/tools/cache" +) + +// MessagingPlanInformer provides access to a shared informer and lister for +// MessagingPlans. +type MessagingPlanInformer interface { + Informer() cache.SharedIndexInformer + Lister() v1beta2.MessagingPlanLister +} + +type messagingPlanInformer struct { + factory internalinterfaces.SharedInformerFactory + tweakListOptions internalinterfaces.TweakListOptionsFunc + namespace string +} + +// NewMessagingPlanInformer constructs a new informer for MessagingPlan type. +// Always prefer using an informer factory to get a shared informer instead of getting an independent +// one. This reduces memory footprint and number of connections to the server. +func NewMessagingPlanInformer(client versioned.Interface, namespace string, resyncPeriod time.Duration, indexers cache.Indexers) cache.SharedIndexInformer { + return NewFilteredMessagingPlanInformer(client, namespace, resyncPeriod, indexers, nil) +} + +// NewFilteredMessagingPlanInformer constructs a new informer for MessagingPlan type. +// Always prefer using an informer factory to get a shared informer instead of getting an independent +// one. This reduces memory footprint and number of connections to the server. +func NewFilteredMessagingPlanInformer(client versioned.Interface, namespace string, resyncPeriod time.Duration, indexers cache.Indexers, tweakListOptions internalinterfaces.TweakListOptionsFunc) cache.SharedIndexInformer { + return cache.NewSharedIndexInformer( + &cache.ListWatch{ + ListFunc: func(options v1.ListOptions) (runtime.Object, error) { + if tweakListOptions != nil { + tweakListOptions(&options) + } + return client.EnmasseV1beta2().MessagingPlans(namespace).List(options) + }, + WatchFunc: func(options v1.ListOptions) (watch.Interface, error) { + if tweakListOptions != nil { + tweakListOptions(&options) + } + return client.EnmasseV1beta2().MessagingPlans(namespace).Watch(options) + }, + }, + &enmassev1beta2.MessagingPlan{}, + resyncPeriod, + indexers, + ) +} + +func (f *messagingPlanInformer) defaultInformer(client versioned.Interface, resyncPeriod time.Duration) cache.SharedIndexInformer { + return NewFilteredMessagingPlanInformer(client, f.namespace, resyncPeriod, cache.Indexers{cache.NamespaceIndex: cache.MetaNamespaceIndexFunc}, f.tweakListOptions) +} + +func (f *messagingPlanInformer) Informer() cache.SharedIndexInformer { + return f.factory.InformerFor(&enmassev1beta2.MessagingPlan{}, f.defaultInformer) +} + +func (f *messagingPlanInformer) Lister() v1beta2.MessagingPlanLister { + return v1beta2.NewMessagingPlanLister(f.Informer().GetIndexer()) +} diff --git a/pkg/client/informers/externalversions/generic.go b/pkg/client/informers/externalversions/generic.go index f9cda7832d9..3736e0cb91a 100644 --- a/pkg/client/informers/externalversions/generic.go +++ b/pkg/client/informers/externalversions/generic.go @@ -71,10 +71,14 @@ func (f *sharedInformerFactory) ForResource(resource schema.GroupVersionResource // Group=enmasse.io, Version=v1beta2 case enmassev1beta2.SchemeGroupVersion.WithResource("messagingaddresses"): return &genericInformer{resource: resource.GroupResource(), informer: f.Enmasse().V1beta2().MessagingAddresses().Informer()}, nil + case enmassev1beta2.SchemeGroupVersion.WithResource("messagingaddressplans"): + return &genericInformer{resource: resource.GroupResource(), informer: f.Enmasse().V1beta2().MessagingAddressPlans().Informer()}, nil case enmassev1beta2.SchemeGroupVersion.WithResource("messagingendpoints"): return &genericInformer{resource: resource.GroupResource(), informer: f.Enmasse().V1beta2().MessagingEndpoints().Informer()}, nil case enmassev1beta2.SchemeGroupVersion.WithResource("messaginginfrastructures"): return &genericInformer{resource: resource.GroupResource(), informer: f.Enmasse().V1beta2().MessagingInfrastructures().Informer()}, nil + case enmassev1beta2.SchemeGroupVersion.WithResource("messagingplans"): + return &genericInformer{resource: resource.GroupResource(), informer: f.Enmasse().V1beta2().MessagingPlans().Informer()}, nil case enmassev1beta2.SchemeGroupVersion.WithResource("messagingtenants"): return &genericInformer{resource: resource.GroupResource(), informer: f.Enmasse().V1beta2().MessagingTenants().Informer()}, nil diff --git a/pkg/client/listers/enmasse/v1beta2/expansion_generated.go b/pkg/client/listers/enmasse/v1beta2/expansion_generated.go index cfdd10746c3..7008db74a56 100644 --- a/pkg/client/listers/enmasse/v1beta2/expansion_generated.go +++ b/pkg/client/listers/enmasse/v1beta2/expansion_generated.go @@ -15,6 +15,14 @@ type MessagingAddressListerExpansion interface{} // MessagingAddressNamespaceLister. type MessagingAddressNamespaceListerExpansion interface{} +// MessagingAddressPlanListerExpansion allows custom methods to be added to +// MessagingAddressPlanLister. +type MessagingAddressPlanListerExpansion interface{} + +// MessagingAddressPlanNamespaceListerExpansion allows custom methods to be added to +// MessagingAddressPlanNamespaceLister. +type MessagingAddressPlanNamespaceListerExpansion interface{} + // MessagingEndpointListerExpansion allows custom methods to be added to // MessagingEndpointLister. type MessagingEndpointListerExpansion interface{} @@ -31,6 +39,14 @@ type MessagingInfrastructureListerExpansion interface{} // MessagingInfrastructureNamespaceLister. type MessagingInfrastructureNamespaceListerExpansion interface{} +// MessagingPlanListerExpansion allows custom methods to be added to +// MessagingPlanLister. +type MessagingPlanListerExpansion interface{} + +// MessagingPlanNamespaceListerExpansion allows custom methods to be added to +// MessagingPlanNamespaceLister. +type MessagingPlanNamespaceListerExpansion interface{} + // MessagingTenantListerExpansion allows custom methods to be added to // MessagingTenantLister. type MessagingTenantListerExpansion interface{} diff --git a/pkg/client/listers/enmasse/v1beta2/messagingaddressplan.go b/pkg/client/listers/enmasse/v1beta2/messagingaddressplan.go new file mode 100644 index 00000000000..392d98dd64f --- /dev/null +++ b/pkg/client/listers/enmasse/v1beta2/messagingaddressplan.go @@ -0,0 +1,83 @@ +/* + * Copyright 2018-2019, EnMasse authors. + * License: Apache License 2.0 (see the file LICENSE or http://apache.org/licenses/LICENSE-2.0.html). + */ + +// Code generated by lister-gen. DO NOT EDIT. + +package v1beta2 + +import ( + v1beta2 "github.com/enmasseproject/enmasse/pkg/apis/enmasse/v1beta2" + "k8s.io/apimachinery/pkg/api/errors" + "k8s.io/apimachinery/pkg/labels" + "k8s.io/client-go/tools/cache" +) + +// MessagingAddressPlanLister helps list MessagingAddressPlans. +type MessagingAddressPlanLister interface { + // List lists all MessagingAddressPlans in the indexer. + List(selector labels.Selector) (ret []*v1beta2.MessagingAddressPlan, err error) + // MessagingAddressPlans returns an object that can list and get MessagingAddressPlans. + MessagingAddressPlans(namespace string) MessagingAddressPlanNamespaceLister + MessagingAddressPlanListerExpansion +} + +// messagingAddressPlanLister implements the MessagingAddressPlanLister interface. +type messagingAddressPlanLister struct { + indexer cache.Indexer +} + +// NewMessagingAddressPlanLister returns a new MessagingAddressPlanLister. +func NewMessagingAddressPlanLister(indexer cache.Indexer) MessagingAddressPlanLister { + return &messagingAddressPlanLister{indexer: indexer} +} + +// List lists all MessagingAddressPlans in the indexer. +func (s *messagingAddressPlanLister) List(selector labels.Selector) (ret []*v1beta2.MessagingAddressPlan, err error) { + err = cache.ListAll(s.indexer, selector, func(m interface{}) { + ret = append(ret, m.(*v1beta2.MessagingAddressPlan)) + }) + return ret, err +} + +// MessagingAddressPlans returns an object that can list and get MessagingAddressPlans. +func (s *messagingAddressPlanLister) MessagingAddressPlans(namespace string) MessagingAddressPlanNamespaceLister { + return messagingAddressPlanNamespaceLister{indexer: s.indexer, namespace: namespace} +} + +// MessagingAddressPlanNamespaceLister helps list and get MessagingAddressPlans. +type MessagingAddressPlanNamespaceLister interface { + // List lists all MessagingAddressPlans in the indexer for a given namespace. + List(selector labels.Selector) (ret []*v1beta2.MessagingAddressPlan, err error) + // Get retrieves the MessagingAddressPlan from the indexer for a given namespace and name. + Get(name string) (*v1beta2.MessagingAddressPlan, error) + MessagingAddressPlanNamespaceListerExpansion +} + +// messagingAddressPlanNamespaceLister implements the MessagingAddressPlanNamespaceLister +// interface. +type messagingAddressPlanNamespaceLister struct { + indexer cache.Indexer + namespace string +} + +// List lists all MessagingAddressPlans in the indexer for a given namespace. +func (s messagingAddressPlanNamespaceLister) List(selector labels.Selector) (ret []*v1beta2.MessagingAddressPlan, err error) { + err = cache.ListAllByNamespace(s.indexer, s.namespace, selector, func(m interface{}) { + ret = append(ret, m.(*v1beta2.MessagingAddressPlan)) + }) + return ret, err +} + +// Get retrieves the MessagingAddressPlan from the indexer for a given namespace and name. +func (s messagingAddressPlanNamespaceLister) Get(name string) (*v1beta2.MessagingAddressPlan, error) { + obj, exists, err := s.indexer.GetByKey(s.namespace + "/" + name) + if err != nil { + return nil, err + } + if !exists { + return nil, errors.NewNotFound(v1beta2.Resource("messagingaddressplan"), name) + } + return obj.(*v1beta2.MessagingAddressPlan), nil +} diff --git a/pkg/client/listers/enmasse/v1beta2/messagingplan.go b/pkg/client/listers/enmasse/v1beta2/messagingplan.go new file mode 100644 index 00000000000..cf4de74eea6 --- /dev/null +++ b/pkg/client/listers/enmasse/v1beta2/messagingplan.go @@ -0,0 +1,83 @@ +/* + * Copyright 2018-2019, EnMasse authors. + * License: Apache License 2.0 (see the file LICENSE or http://apache.org/licenses/LICENSE-2.0.html). + */ + +// Code generated by lister-gen. DO NOT EDIT. + +package v1beta2 + +import ( + v1beta2 "github.com/enmasseproject/enmasse/pkg/apis/enmasse/v1beta2" + "k8s.io/apimachinery/pkg/api/errors" + "k8s.io/apimachinery/pkg/labels" + "k8s.io/client-go/tools/cache" +) + +// MessagingPlanLister helps list MessagingPlans. +type MessagingPlanLister interface { + // List lists all MessagingPlans in the indexer. + List(selector labels.Selector) (ret []*v1beta2.MessagingPlan, err error) + // MessagingPlans returns an object that can list and get MessagingPlans. + MessagingPlans(namespace string) MessagingPlanNamespaceLister + MessagingPlanListerExpansion +} + +// messagingPlanLister implements the MessagingPlanLister interface. +type messagingPlanLister struct { + indexer cache.Indexer +} + +// NewMessagingPlanLister returns a new MessagingPlanLister. +func NewMessagingPlanLister(indexer cache.Indexer) MessagingPlanLister { + return &messagingPlanLister{indexer: indexer} +} + +// List lists all MessagingPlans in the indexer. +func (s *messagingPlanLister) List(selector labels.Selector) (ret []*v1beta2.MessagingPlan, err error) { + err = cache.ListAll(s.indexer, selector, func(m interface{}) { + ret = append(ret, m.(*v1beta2.MessagingPlan)) + }) + return ret, err +} + +// MessagingPlans returns an object that can list and get MessagingPlans. +func (s *messagingPlanLister) MessagingPlans(namespace string) MessagingPlanNamespaceLister { + return messagingPlanNamespaceLister{indexer: s.indexer, namespace: namespace} +} + +// MessagingPlanNamespaceLister helps list and get MessagingPlans. +type MessagingPlanNamespaceLister interface { + // List lists all MessagingPlans in the indexer for a given namespace. + List(selector labels.Selector) (ret []*v1beta2.MessagingPlan, err error) + // Get retrieves the MessagingPlan from the indexer for a given namespace and name. + Get(name string) (*v1beta2.MessagingPlan, error) + MessagingPlanNamespaceListerExpansion +} + +// messagingPlanNamespaceLister implements the MessagingPlanNamespaceLister +// interface. +type messagingPlanNamespaceLister struct { + indexer cache.Indexer + namespace string +} + +// List lists all MessagingPlans in the indexer for a given namespace. +func (s messagingPlanNamespaceLister) List(selector labels.Selector) (ret []*v1beta2.MessagingPlan, err error) { + err = cache.ListAllByNamespace(s.indexer, s.namespace, selector, func(m interface{}) { + ret = append(ret, m.(*v1beta2.MessagingPlan)) + }) + return ret, err +} + +// Get retrieves the MessagingPlan from the indexer for a given namespace and name. +func (s messagingPlanNamespaceLister) Get(name string) (*v1beta2.MessagingPlan, error) { + obj, exists, err := s.indexer.GetByKey(s.namespace + "/" + name) + if err != nil { + return nil, err + } + if !exists { + return nil, errors.NewNotFound(v1beta2.Resource("messagingplan"), name) + } + return obj.(*v1beta2.MessagingPlan), nil +} diff --git a/pkg/controller/add_address_space_controller.go b/pkg/controller/add_address_space_controller.go deleted file mode 100644 index eee7c7369a7..00000000000 --- a/pkg/controller/add_address_space_controller.go +++ /dev/null @@ -1,20 +0,0 @@ -/* - * Copyright 2019, EnMasse authors. - * License: Apache License 2.0 (see the file LICENSE or http://apache.org/licenses/LICENSE-2.0.html). - */ - -package controller - -import ( - address_space_controller "github.com/enmasseproject/enmasse/pkg/controller/address_space_controller" - "github.com/enmasseproject/enmasse/pkg/util" -) - -func init() { - - // add ourselves to the list of controllers - - if util.IsModuleEnabled("ADDRESS_SPACE_CONTROLLER") { - AddToManagerFuncs = append(AddToManagerFuncs, address_space_controller.Add) - } -} diff --git a/pkg/controller/add_authenticationservice.go b/pkg/controller/add_authenticationservice.go deleted file mode 100644 index b61911583db..00000000000 --- a/pkg/controller/add_authenticationservice.go +++ /dev/null @@ -1,25 +0,0 @@ -/* - * Copyright 2018, EnMasse authors. - * License: Apache License 2.0 (see the file LICENSE or http://apache.org/licenses/LICENSE-2.0.html). - */ - -package controller - -import ( - "github.com/enmasseproject/enmasse/pkg/controller/authenticationservice" - "github.com/enmasseproject/enmasse/pkg/controller/authenticationservice/upgrader" - "github.com/enmasseproject/enmasse/pkg/util" -) - -func init() { - - // add ourselves to the list of controllers - - if util.IsModuleEnabled("AUTHENTICATION_SERVICE") { - AddToManagerFuncs = append(AddToManagerFuncs, authenticationservice.Add) - if util.GetBooleanEnvOrDefault("ENMASSE_AUTHENTICATION_SERVICE_UPGRADE", true) { - AddToManagerFuncs = append(AddToManagerFuncs, upgrader.Add) - } - - } -} diff --git a/pkg/controller/add_messaginguser.go b/pkg/controller/add_messaginguser.go deleted file mode 100644 index 3821fa74db2..00000000000 --- a/pkg/controller/add_messaginguser.go +++ /dev/null @@ -1,20 +0,0 @@ -/* - * Copyright 2018, EnMasse authors. - * License: Apache License 2.0 (see the file LICENSE or http://apache.org/licenses/LICENSE-2.0.html). - */ - -package controller - -import ( - "github.com/enmasseproject/enmasse/pkg/controller/messaginguser" - "github.com/enmasseproject/enmasse/pkg/util" -) - -func init() { - - // add ourselves to the list of controllers - - if util.IsModuleEnabled("MESSAGING_USER") { - AddToManagerFuncs = append(AddToManagerFuncs, messaginguser.Add) - } -} diff --git a/pkg/controller/address_space_controller/controller.go b/pkg/controller/address_space_controller/controller.go deleted file mode 100644 index ec0da52998f..00000000000 --- a/pkg/controller/address_space_controller/controller.go +++ /dev/null @@ -1,307 +0,0 @@ -/* - * Copyright 2019, EnMasse authors. - * License: Apache License 2.0 (see the file LICENSE or http://apache.org/licenses/LICENSE-2.0.html). - */ - -package address_space_controller - -import ( - "context" - "os" - "time" - - "github.com/enmasseproject/enmasse/pkg/util" - "github.com/enmasseproject/enmasse/pkg/util/images" - "github.com/enmasseproject/enmasse/pkg/util/install" - - "github.com/go-logr/logr" - - "sigs.k8s.io/controller-runtime/pkg/client" - "sigs.k8s.io/controller-runtime/pkg/controller" - "sigs.k8s.io/controller-runtime/pkg/controller/controllerutil" - "sigs.k8s.io/controller-runtime/pkg/handler" - "sigs.k8s.io/controller-runtime/pkg/manager" - "sigs.k8s.io/controller-runtime/pkg/reconcile" - logf "sigs.k8s.io/controller-runtime/pkg/runtime/log" - "sigs.k8s.io/controller-runtime/pkg/source" - - appsv1 "k8s.io/api/apps/v1" - corev1 "k8s.io/api/core/v1" - k8errors "k8s.io/apimachinery/pkg/api/errors" - resource "k8s.io/apimachinery/pkg/api/resource" - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - runtime "k8s.io/apimachinery/pkg/runtime" - controllertypes "k8s.io/apimachinery/pkg/types" - intstr "k8s.io/apimachinery/pkg/util/intstr" -) - -var log = logf.Log.WithName("controller_address_space_controller") - -const ADDRESS_SPACE_CONTROLLER_NAME = "address-space-controller" -const ANNOTATION_VERSION = "enmasse.io/version" -const ENV_VERSION = "VERSION" - -var _ reconcile.Reconciler = &ReconcileAddressSpaceController{} - -type ReconcileAddressSpaceController struct { - client client.Client - reader client.Reader - scheme *runtime.Scheme - namespace string -} - -// Gets called by parent "init", adding as to the manager -func Add(mgr manager.Manager) error { - reconciler, err := newReconciler(mgr) - if err != nil { - return err - } - return add(mgr, reconciler) -} - -func newReconciler(mgr manager.Manager) (*ReconcileAddressSpaceController, error) { - return &ReconcileAddressSpaceController{ - client: mgr.GetClient(), - reader: mgr.GetAPIReader(), - scheme: mgr.GetScheme(), - namespace: util.GetEnvOrDefault("NAMESPACE", "enmasse-infra"), - }, nil -} - -func add(mgr manager.Manager, r *ReconcileAddressSpaceController) error { - - // Create initial deployment if it does not exist. We cannot yet rely on - // the controller-runtime client cache (CreateOrUpdate), as the runtime has not yet started. - deployment := &appsv1.Deployment{} - err := r.reader.Get(context.TODO(), controllertypes.NamespacedName{Namespace: r.namespace, Name: ADDRESS_SPACE_CONTROLLER_NAME}, deployment) - if err != nil { - if k8errors.IsNotFound(err) { - deployment = &appsv1.Deployment{ - ObjectMeta: metav1.ObjectMeta{ - Namespace: r.namespace, - Name: ADDRESS_SPACE_CONTROLLER_NAME, - }, - } - err = ApplyDeployment(deployment) - if err != nil { - return err - } - err = r.client.Create(context.TODO(), deployment) - if err != nil { - return err - } - } else { - return err - } - } - - service := &corev1.Service{} - err = r.reader.Get(context.TODO(), controllertypes.NamespacedName{Namespace: r.namespace, Name: ADDRESS_SPACE_CONTROLLER_NAME}, service) - if err != nil { - if k8errors.IsNotFound(err) { - service = &corev1.Service{ - ObjectMeta: metav1.ObjectMeta{ - Namespace: r.namespace, - Name: ADDRESS_SPACE_CONTROLLER_NAME, - }, - } - err = applyService(service) - if err != nil { - return err - } - err = r.client.Create(context.TODO(), service) - if err != nil { - return err - } - } else { - return err - } - } - - // Start reconciler for address-space-controller deployment - c, err := controller.New("address-space-controller-controller", mgr, controller.Options{Reconciler: r}) - if err != nil { - return err - } - - return c.Watch(&source.Kind{Type: &appsv1.Deployment{}}, &handler.EnqueueRequestForObject{}) -} - -func (r *ReconcileAddressSpaceController) Reconcile(request reconcile.Request) (reconcile.Result, error) { - - expectedName := controllertypes.NamespacedName{ - Namespace: r.namespace, - Name: ADDRESS_SPACE_CONTROLLER_NAME, - } - - if expectedName == request.NamespacedName { - reqLogger := log.WithValues("Request.Namespace", request.Namespace, "Request.Name", request.Name) - reqLogger.Info("Reconciling Address Space Controller") - - ctx := context.TODO() - - _, err := r.ensureDeployment(ctx, request, reqLogger) - if err != nil { - reqLogger.Error(err, "Error creating address space controller deployment") - return reconcile.Result{}, err - } - - _, err = r.ensureService(ctx, request, reqLogger) - if err != nil { - reqLogger.Error(err, "Error creating address space controller service") - return reconcile.Result{}, err - } - } - return reconcile.Result{RequeueAfter: 30 * time.Second}, nil -} - -func (r *ReconcileAddressSpaceController) ensureDeployment(ctx context.Context, request reconcile.Request, reqLogger logr.Logger) (reconcile.Result, error) { - - deployment := &appsv1.Deployment{ - ObjectMeta: metav1.ObjectMeta{ - Name: request.NamespacedName.Name, - Namespace: request.NamespacedName.Namespace, - }, - } - - _, err := controllerutil.CreateOrUpdate(ctx, r.client, deployment, func() error { - return ApplyDeployment(deployment) - }) - - return reconcile.Result{}, err -} - -func ApplyDeployment(deployment *appsv1.Deployment) error { - install.ApplyDeploymentDefaults(deployment, "address-space-controller", deployment.Name) - err := install.ApplyDeploymentContainerWithError(deployment, "address-space-controller", func(container *corev1.Container) error { - install.ApplyContainerImage(container, "address-space-controller", nil) - container.LivenessProbe = install.ApplyHttpProbe(container.LivenessProbe, 30, "/healthz", 8080) - container.ReadinessProbe = install.ApplyHttpProbe(container.ReadinessProbe, 30, "/healthz", 8080) - container.Ports = []corev1.ContainerPort{ - {Name: "http", ContainerPort: 8080, Protocol: corev1.ProtocolTCP}, - } - install.ApplyEnvSimple(container, "JAVA_OPTS", "-verbose:gc") - install.ApplyEnvSimple(container, "ENABLE_EVENT_LOGGER", "true") - install.ApplyEnvSimple(container, "TEMPLATE_DIR", "/opt/templates") - install.ApplyEnvSimple(container, "RESOURCES_DIR", "/opt") - if value, ok := os.LookupEnv("FS_GROUP_FALLBACK_MAP"); ok { - install.ApplyOrRemoveEnvSimple(container, "FS_GROUP_FALLBACK_MAP", value) - } - - t := true - install.ApplyEnvConfigMap(container, "WILDCARD_ENDPOINT_CERT_SECRET", "wildcardEndpointCertSecret", "address-space-controller-config", &t) - install.ApplyEnvConfigMap(container, "RESYNC_INTERVAL", "resyncInterval", "address-space-controller-config", &t) - install.ApplyEnvConfigMap(container, "RECHECK_INTERVAL", "recheckInterval", "address-space-controller-config", &t) - install.ApplyEnvConfigMap(container, "ROUTER_STATUS_CHECK_INTERVAL", "routerStatusCheckInterval", "address-space-controller-config", &t) - install.ApplyEnvConfigMap(container, "EXPOSE_ENDPOINTS_BY_DEFAULT", "exposeEndpointsByDefault", "address-space-controller-config", &t) - install.ApplyEnvConfigMap(container, "DISABLE_EXTERNAL_CERT_PROVISIONING", "disableExternalCertProvisioning", "address-space-controller-config", &t) - - install.ApplyEnvSimple(container, "IMAGE_PULL_POLICY", string(images.PullPolicyFromImageName(container.Image))) - applyImageEnv(container, "ROUTER_IMAGE", "router") - applyImageEnv(container, "STANDARD_CONTROLLER_IMAGE", "standard-controller") - applyImageEnv(container, "AGENT_IMAGE", "agent") - applyImageEnv(container, "BROKER_IMAGE", "broker") - applyImageEnv(container, "BROKER_PLUGIN_IMAGE", "broker-plugin") - applyImageEnv(container, "TOPIC_FORWARDER_IMAGE", "topic-forwarder") - applyImageEnv(container, "MQTT_GATEWAY_IMAGE", "mqtt-gateway") - applyImageEnv(container, "MQTT_LWT_IMAGE", "mqtt-lwt") - if util.IsOpenshift() { - install.ApplyEnvSimple(container, util.EnMasseOpenshiftEnvVar, "true") - } - - if value, ok := os.LookupEnv("ENABLE_MONITORING_ANNOTATIONS"); ok && value == "true" { - deployment.ObjectMeta.Annotations["prometheus.io/scrape"] = "true" - deployment.ObjectMeta.Annotations["prometheus.io/path"] = "/metrics" - deployment.ObjectMeta.Annotations["prometheus.io/port"] = "8080" - } - - if container.Resources.Requests == nil { - container.Resources.Requests = make(map[corev1.ResourceName]resource.Quantity, 0) - } - - if container.Resources.Limits == nil { - container.Resources.Limits = make(map[corev1.ResourceName]resource.Quantity, 0) - } - - cpuEnv, ok := os.LookupEnv("ADDRESS_SPACE_CONTROLLER_CPU_LIMIT") - if ok { - cpuLimit, err := resource.ParseQuantity(cpuEnv) - if err != nil { - return err - } - container.Resources.Requests[corev1.ResourceCPU] = cpuLimit - container.Resources.Limits[corev1.ResourceCPU] = cpuLimit - } else { - delete(container.Resources.Requests, corev1.ResourceCPU) - delete(container.Resources.Limits, corev1.ResourceCPU) - } - - memoryEnv, ok := os.LookupEnv("ADDRESS_SPACE_CONTROLLER_MEMORY_LIMIT") - if ok { - memoryLimit, err := resource.ParseQuantity(memoryEnv) - if err != nil { - return err - } - container.Resources.Requests[corev1.ResourceMemory] = memoryLimit - container.Resources.Limits[corev1.ResourceMemory] = memoryLimit - } else { - delete(container.Resources.Requests, corev1.ResourceMemory) - delete(container.Resources.Limits, corev1.ResourceMemory) - } - return nil - }) - if err != nil { - return err - } - // This means we are running - version, ok := os.LookupEnv(ENV_VERSION) - if ok { - deployment.Annotations[ANNOTATION_VERSION] = version - } - - var one int32 = 1 - deployment.Spec.Template.Spec.ServiceAccountName = "address-space-controller" - deployment.Spec.Replicas = &one - install.ApplyNodeAffinity(&deployment.Spec.Template, "node-role.enmasse.io/operator-infra") - - return nil -} - -func applyImageEnv(container *corev1.Container, env string, imageName string) error { - image, err := images.GetImage(imageName) - if err != nil { - return err - } - install.ApplyEnvSimple(container, env, image) - return nil -} - -func (r *ReconcileAddressSpaceController) ensureService(ctx context.Context, request reconcile.Request, reqLogger logr.Logger) (reconcile.Result, error) { - - service := &corev1.Service{ - ObjectMeta: metav1.ObjectMeta{ - Name: request.NamespacedName.Name, - Namespace: request.NamespacedName.Namespace, - }, - } - - _, err := controllerutil.CreateOrUpdate(ctx, r.client, service, func() error { - return applyService(service) - }) - - return reconcile.Result{}, err -} - -func applyService(service *corev1.Service) error { - install.ApplyServiceDefaults(service, service.Name, service.Name) - install.ApplyCustomLabel(&service.ObjectMeta, "monitoring-key", "enmasse-tenants") - service.Spec.Ports = []corev1.ServicePort{ - { - Port: 8080, - Protocol: corev1.ProtocolTCP, - TargetPort: intstr.FromString("http"), - Name: "health", - }, - } - return nil -} diff --git a/pkg/controller/authenticationservice/authenticationservice_controller.go b/pkg/controller/authenticationservice/authenticationservice_controller.go deleted file mode 100644 index f349e9df0d3..00000000000 --- a/pkg/controller/authenticationservice/authenticationservice_controller.go +++ /dev/null @@ -1,425 +0,0 @@ -/* - * Copyright 2019, EnMasse authors. - * License: Apache License 2.0 (see the file LICENSE or http://apache.org/licenses/LICENSE-2.0.html). - */ - -package authenticationservice - -import ( - "context" - "fmt" - "reflect" - - adminv1beta1 "github.com/enmasseproject/enmasse/pkg/apis/admin/v1beta1" - "github.com/enmasseproject/enmasse/pkg/util" - "github.com/enmasseproject/enmasse/pkg/util/recon" - - routev1 "github.com/openshift/api/route/v1" - "github.com/prometheus/client_golang/prometheus" - appsv1 "k8s.io/api/apps/v1" - corev1 "k8s.io/api/core/v1" - k8errors "k8s.io/apimachinery/pkg/api/errors" - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - "k8s.io/apimachinery/pkg/runtime" - "k8s.io/apimachinery/pkg/types" - "sigs.k8s.io/controller-runtime/pkg/client" - "sigs.k8s.io/controller-runtime/pkg/controller" - "sigs.k8s.io/controller-runtime/pkg/controller/controllerutil" - "sigs.k8s.io/controller-runtime/pkg/handler" - "sigs.k8s.io/controller-runtime/pkg/manager" - "sigs.k8s.io/controller-runtime/pkg/metrics" - "sigs.k8s.io/controller-runtime/pkg/reconcile" - logf "sigs.k8s.io/controller-runtime/pkg/runtime/log" - "sigs.k8s.io/controller-runtime/pkg/source" -) - -var ( - authinfo = prometheus.NewGaugeVec( - prometheus.GaugeOpts{ - Name: "authentication_service_ready", - Help: "EnMasse authentication services in the ready state", - }, - []string{"authservice_name", "authservice_type"}, - ) -) - -var log = logf.Log.WithName("controller_authenticationservice") - -// Gets called by parent "init", adding as to the manager -func Add(mgr manager.Manager) error { - return add(mgr, newReconciler(mgr)) -} - -func newReconciler(mgr manager.Manager) *ReconcileAuthenticationService { - return &ReconcileAuthenticationService{client: mgr.GetClient(), scheme: mgr.GetScheme(), namespace: util.GetEnvOrDefault("NAMESPACE", "enmasse-infra")} -} - -func add(mgr manager.Manager, r *ReconcileAuthenticationService) error { - - // Create a new controller - c, err := controller.New("authenticationservice-controller", mgr, controller.Options{Reconciler: r}) - if err != nil { - return err - } - - // Watch for changes to primary resource AuthenticationService - err = c.Watch(&source.Kind{Type: &adminv1beta1.AuthenticationService{}}, &handler.EnqueueRequestForObject{}) - if err != nil { - return err - } - - err = c.Watch(&source.Kind{Type: &appsv1.Deployment{}}, &handler.EnqueueRequestForOwner{ - IsController: true, - OwnerType: &adminv1beta1.AuthenticationService{}, - }) - if err != nil { - return err - } - - err = c.Watch(&source.Kind{Type: &corev1.Service{}}, &handler.EnqueueRequestForOwner{ - IsController: true, - OwnerType: &adminv1beta1.AuthenticationService{}, - }) - if err != nil { - return err - } - - metrics.Registry.MustRegister(authinfo) - - return nil -} - -var _ reconcile.Reconciler = &ReconcileAuthenticationService{} - -type ReconcileAuthenticationService struct { - // This client, initialized using mgr.Client() above, is a split client - // that reads objects from the cache and writes to the apiserver - client client.Client - scheme *runtime.Scheme - namespace string -} - -// Reconcile by reading the authentication service spec and making required changes -// -// returning an error will get the request re-queued -func (r *ReconcileAuthenticationService) Reconcile(request reconcile.Request) (reconcile.Result, error) { - reqLogger := log.WithValues("Request.Namespace", request.Namespace, "Request.Name", request.Name) - reqLogger.Info("Reconciling AuthenticationService") - - ctx := context.TODO() - authservice := &adminv1beta1.AuthenticationService{} - err := r.client.Get(ctx, request.NamespacedName, authservice) - if err != nil { - if k8errors.IsNotFound(err) { - reqLogger.Info("AuthenticationService resource not found. Ignoring since object must be deleted") - return reconcile.Result{}, nil - } - // Error reading the object - requeue the request - reqLogger.Error(err, "Failed to get AuthenticationService") - return reconcile.Result{}, err - } - - if authservice.Spec.Type == adminv1beta1.None { - return r.reconcileNoneAuthService(ctx, authservice) - } else if authservice.Spec.Type == adminv1beta1.Standard { - return r.reconcileStandardAuthService(ctx, authservice) - } else if authservice.Spec.Type == adminv1beta1.External { - return r.reconcileExternalAuthService(ctx, authservice) - } - return reconcile.Result{}, nil -} - -func (r *ReconcileAuthenticationService) reconcileNoneAuthService(ctx context.Context, authservice *adminv1beta1.AuthenticationService) (reconcile.Result, error) { - currentStatus := authservice.Status - applyNoneAuthServiceDefaults(authservice) - - rc := &recon.ReconcileContext{} - if !util.IsOpenshift() { - rc.ProcessSimple(func() error { - return r.reconcileCertificate(ctx, authservice, authservice.Spec.None.CertificateSecret.Name, applyNoneAuthServiceCert) - }) - } - - rc.Process(func() (reconcile.Result, error) { - return r.reconcileDeployment(ctx, authservice, authservice.Name, applyNoneAuthServiceDeployment) - }) - - rc.Process(func() (reconcile.Result, error) { - return r.reconcileService(ctx, authservice, authservice.Name, applyNoneAuthServiceService) - }) - - if rc.Error() != nil || rc.NeedRequeue() { - return rc.Result() - } - - rc.Process(func() (reconcile.Result, error) { - return r.updateStatus(ctx, authservice, currentStatus, func(status *adminv1beta1.AuthenticationServiceStatus) error { - status.Phase = authservice.Status.Phase - status.Message = authservice.Status.Message - status.Host = fmt.Sprintf("%s.%s.svc", authservice.Name, authservice.Namespace) - status.Port = 5671 - status.CaCertSecret = authservice.Spec.None.CertificateSecret - return nil - }) - }) - - if authservice.Status.Phase == adminv1beta1.AuthenticationServiceActive { - authinfo.WithLabelValues(authservice.Name, fmt.Sprintf("%v", authservice.Spec.Type)).Set(1) - } else { - authinfo.WithLabelValues(authservice.Name, fmt.Sprintf("%v", authservice.Spec.Type)).Set(0) - } - - return rc.Result() -} - -func (r *ReconcileAuthenticationService) reconcileStandardAuthService(ctx context.Context, authservice *adminv1beta1.AuthenticationService) (reconcile.Result, error) { - - currentStatus := authservice.Status - rc := &recon.ReconcileContext{} - applyStandardAuthServiceDefaults(authservice) - - rc.ProcessSimple(func() error { - return r.reconcileStandardCredentials(ctx, authservice) - }) - - if !util.IsOpenshift() { - rc.ProcessSimple(func() error { - return r.reconcileCertificate(ctx, authservice, authservice.Spec.Standard.CertificateSecret.Name, applyStandardAuthServiceCert) - }) - } - - rc.Process(func() (reconcile.Result, error) { - return r.reconcileService(ctx, authservice, *authservice.Spec.Standard.ServiceName, applyStandardAuthServiceService) - }) - - rc.Process(func() (reconcile.Result, error) { - return r.reconcileStandardVolume(ctx, *authservice.Spec.Standard.Storage.ClaimName, authservice) - }) - - rc.Process(func() (reconcile.Result, error) { - return r.reconcileStandardRoute(ctx, *authservice.Spec.Standard.RouteName, authservice) - }) - - rc.Process(func() (reconcile.Result, error) { - return r.reconcileDeployment(ctx, authservice, *authservice.Spec.Standard.DeploymentName, applyStandardAuthServiceDeployment) - }) - - if rc.Error() != nil || rc.NeedRequeue() { - return rc.Result() - } - - rc.Process(func() (reconcile.Result, error) { - return r.updateStatus(ctx, authservice, currentStatus, func(status *adminv1beta1.AuthenticationServiceStatus) error { - status.Phase = authservice.Status.Phase - status.Message = authservice.Status.Message - status.Host = fmt.Sprintf("%s.%s.svc", authservice.Name, authservice.Namespace) - status.Port = 5671 - status.CaCertSecret = authservice.Spec.Standard.CertificateSecret - return nil - }) - }) - - if authservice.Status.Phase == adminv1beta1.AuthenticationServiceActive { - authinfo.WithLabelValues(authservice.Name, fmt.Sprintf("%v", authservice.Spec.Type)).Set(1) - } else { - authinfo.WithLabelValues(authservice.Name, fmt.Sprintf("%v", authservice.Spec.Type)).Set(0) - } - - rc.Process(func() (reconcile.Result, error) { - return r.removeKeycloakController(ctx, authservice) - }) - - return rc.Result() -} - -func (r *ReconcileAuthenticationService) reconcileExternalAuthService(ctx context.Context, authservice *adminv1beta1.AuthenticationService) (reconcile.Result, error) { - currentStatus := authservice.Status - authinfo.WithLabelValues(authservice.Name, fmt.Sprintf("%v", authservice.Spec.Type)).Set(1) - return r.updateStatus(ctx, authservice, currentStatus, func(status *adminv1beta1.AuthenticationServiceStatus) error { - status.Phase = adminv1beta1.AuthenticationServiceActive - status.Host = authservice.Spec.External.Host - status.Port = authservice.Spec.External.Port - status.CaCertSecret = authservice.Spec.External.CaCertSecret - status.ClientCertSecret = authservice.Spec.External.ClientCertSecret - return nil - }) -} - -type UpdateStatusFn func(status *adminv1beta1.AuthenticationServiceStatus) error - -func (r *ReconcileAuthenticationService) updateStatus(ctx context.Context, authservice *adminv1beta1.AuthenticationService, currentStatus adminv1beta1.AuthenticationServiceStatus, updateFn UpdateStatusFn) (reconcile.Result, error) { - - newStatus := adminv1beta1.AuthenticationServiceStatus{} - if err := updateFn(&newStatus); err != nil { - return reconcile.Result{}, err - } - - if currentStatus.Host != newStatus.Host || - currentStatus.Port != newStatus.Port || - currentStatus.Phase != newStatus.Phase || - currentStatus.Message != newStatus.Message || - !reflect.DeepEqual(currentStatus.CaCertSecret, newStatus.CaCertSecret) || - !reflect.DeepEqual(currentStatus.ClientCertSecret, newStatus.ClientCertSecret) { - - authservice.Status = newStatus - err := r.client.Update(ctx, authservice) - if err != nil { - return reconcile.Result{}, err - } - return reconcile.Result{Requeue: true}, nil - } - return reconcile.Result{}, nil -} - -type ApplyCertificateFn func(authservice *adminv1beta1.AuthenticationService, existingSecret *corev1.Secret) error - -func (r *ReconcileAuthenticationService) reconcileCertificate(ctx context.Context, authservice *adminv1beta1.AuthenticationService, name string, applyFn ApplyCertificateFn) error { - secret := &corev1.Secret{ - ObjectMeta: metav1.ObjectMeta{Namespace: authservice.Namespace, Name: name}, - } - _, err := controllerutil.CreateOrUpdate(ctx, r.client, secret, func() error { - if err := controllerutil.SetControllerReference(authservice, secret, r.scheme); err != nil { - return err - } - return applyFn(authservice, secret) - }) - return err -} - -type ApplyDeploymentFn func(authservice *adminv1beta1.AuthenticationService, existingDeployment *appsv1.Deployment) error - -func (r *ReconcileAuthenticationService) reconcileDeployment(ctx context.Context, authservice *adminv1beta1.AuthenticationService, name string, fn ApplyDeploymentFn) (reconcile.Result, error) { - deployment := &appsv1.Deployment{ - ObjectMeta: metav1.ObjectMeta{Namespace: authservice.Namespace, Name: name}, - } - _, err := controllerutil.CreateOrUpdate(ctx, r.client, deployment, func() error { - if err := controllerutil.SetControllerReference(authservice, deployment, r.scheme); err != nil { - return err - } - return fn(authservice, deployment) - }) - - if deployment.Status.AvailableReplicas < 1 { - authservice.Status.Phase = adminv1beta1.AuthenticationServiceConfiguring - authservice.Status.Message = "Waiting for deployment: " + deployment.Name - } else { - authservice.Status.Phase = adminv1beta1.AuthenticationServiceActive - authservice.Status.Message = "" - } - - if err != nil { - log.Error(err, "Failed reconciling Deployment") - return reconcile.Result{}, err - } - return reconcile.Result{}, nil -} - -type ApplyServiceFn func(authservice *adminv1beta1.AuthenticationService, existingService *corev1.Service) error - -func (r *ReconcileAuthenticationService) reconcileService(ctx context.Context, authservice *adminv1beta1.AuthenticationService, name string, applyFn ApplyServiceFn) (reconcile.Result, error) { - service := &corev1.Service{ - ObjectMeta: metav1.ObjectMeta{Namespace: authservice.Namespace, Name: name}, - } - _, err := controllerutil.CreateOrUpdate(ctx, r.client, service, func() error { - if err := controllerutil.SetControllerReference(authservice, service, r.scheme); err != nil { - return err - } - - return applyFn(authservice, service) - }) - - if err != nil { - log.Error(err, "Failed reconciling Service") - return reconcile.Result{}, err - } - return reconcile.Result{}, nil -} - -func (r *ReconcileAuthenticationService) reconcileStandardVolume(ctx context.Context, name string, authservice *adminv1beta1.AuthenticationService) (reconcile.Result, error) { - if authservice.Spec.Type == adminv1beta1.Standard && - authservice.Spec.Standard.Storage != nil && - authservice.Spec.Standard.Storage.Type == adminv1beta1.PersistentClaim { - pvc := &corev1.PersistentVolumeClaim{ - ObjectMeta: metav1.ObjectMeta{Namespace: authservice.Namespace, Name: name}, - } - _, err := controllerutil.CreateOrUpdate(ctx, r.client, pvc, func() error { - if *authservice.Spec.Standard.Storage.DeleteClaim { - if err := controllerutil.SetControllerReference(authservice, pvc, r.scheme); err != nil { - return err - } - } - return applyStandardAuthServiceVolume(authservice, pvc) - }) - - if err != nil { - log.Error(err, "Failed reconciling PersistentVolumeClaim") - return reconcile.Result{}, err - } - } - return reconcile.Result{}, nil -} - -func (r *ReconcileAuthenticationService) reconcileStandardCredentials(ctx context.Context, authservice *adminv1beta1.AuthenticationService) error { - secret := &corev1.Secret{ - ObjectMeta: metav1.ObjectMeta{Namespace: authservice.Namespace, Name: authservice.Spec.Standard.CredentialsSecret.Name}, - } - _, err := controllerutil.CreateOrUpdate(ctx, r.client, secret, func() error { - if err := controllerutil.SetControllerReference(authservice, secret, r.scheme); err != nil { - return err - } - return applyStandardAuthServiceCredentials(authservice, secret) - }) - return err -} - -func (r *ReconcileAuthenticationService) reconcileStandardRoute(ctx context.Context, name string, authservice *adminv1beta1.AuthenticationService) (reconcile.Result, error) { - if authservice.Spec.Type == adminv1beta1.Standard && util.IsOpenshift() { - route := &routev1.Route{ - ObjectMeta: metav1.ObjectMeta{Namespace: authservice.Namespace, Name: name}, - } - _, err := controllerutil.CreateOrUpdate(ctx, r.client, route, func() error { - secretName := types.NamespacedName{ - Name: authservice.Spec.Standard.CertificateSecret.Name, - Namespace: authservice.Namespace, - } - certsecret := &corev1.Secret{} - err := r.client.Get(ctx, secretName, certsecret) - if err != nil { - return err - } - cert := certsecret.Data["tls.crt"] - if err := controllerutil.SetControllerReference(authservice, route, r.scheme); err != nil { - return err - } - return applyRoute(authservice, route, string(cert[:])) - }) - - if err != nil { - log.Error(err, "Failed reconciling Route") - return reconcile.Result{}, err - } - } - return reconcile.Result{}, nil -} - -/* - * This function removes the keycloak controller if it exists. This process is no longer needed if this - * operator is running. - */ -func (r *ReconcileAuthenticationService) removeKeycloakController(ctx context.Context, authservice *adminv1beta1.AuthenticationService) (reconcile.Result, error) { - dep := &appsv1.Deployment{} - name := types.NamespacedName{Namespace: authservice.Namespace, Name: "keycloak-controller"} - err := r.client.Get(ctx, name, dep) - if err != nil { - if k8errors.IsNotFound(err) { - return reconcile.Result{}, nil - } else { - return reconcile.Result{Requeue: true}, err - } - } - err = r.client.Delete(ctx, dep) - if err != nil { - return reconcile.Result{Requeue: true}, err - } - return reconcile.Result{}, nil -} diff --git a/pkg/controller/authenticationservice/authenticationservice_controller_test.go b/pkg/controller/authenticationservice/authenticationservice_controller_test.go deleted file mode 100644 index 033df295124..00000000000 --- a/pkg/controller/authenticationservice/authenticationservice_controller_test.go +++ /dev/null @@ -1,91 +0,0 @@ -/* - * Copyright 2019, EnMasse authors. - * License: Apache License 2.0 (see the file LICENSE or http://apache.org/licenses/LICENSE-2.0.html). - */ -package authenticationservice - -import ( - "context" - "os" - "testing" - - adminv1beta1 "github.com/enmasseproject/enmasse/pkg/apis/admin/v1beta1" - - "fmt" - - appsv1 "k8s.io/api/apps/v1" - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - "k8s.io/apimachinery/pkg/runtime" - "k8s.io/apimachinery/pkg/types" - "k8s.io/client-go/kubernetes/scheme" - "sigs.k8s.io/controller-runtime/pkg/client/fake" - "sigs.k8s.io/controller-runtime/pkg/reconcile" -) - -func setup(t *testing.T, authservice *adminv1beta1.AuthenticationService) *ReconcileAuthenticationService { - s := scheme.Scheme - s.AddKnownTypes(adminv1beta1.SchemeGroupVersion, authservice) - objs := []runtime.Object{ - authservice, - } - cl := fake.NewFakeClientWithScheme(s, objs...) - r := &ReconcileAuthenticationService{client: cl, scheme: s} - return r -} - -func TestNoneAuthService(t *testing.T) { - authservice := &adminv1beta1.AuthenticationService{ - ObjectMeta: metav1.ObjectMeta{Namespace: "infra", Name: "none"}, - Spec: adminv1beta1.AuthenticationServiceSpec{ - Type: adminv1beta1.None, - }, - } - - r := setup(t, authservice) - - if err := os.Setenv("RELATED_IMAGE_NONE_AUTHSERVICE", "none-authservice"); err != nil { - t.Fatalf("Failed to set image version for test: %v", err) - } - - req := reconcile.Request{ - NamespacedName: types.NamespacedName{ - Name: authservice.Name, - Namespace: authservice.Namespace, - }, - } - _, err := r.Reconcile(req) - - if err != nil { - t.Fatalf("reconcile: (%v)", err) - } - - dep := &appsv1.Deployment{} - err = r.client.Get(context.TODO(), req.NamespacedName, dep) - if err != nil { - t.Fatalf("get deployment: (%v)", err) - } - - if dep.Labels["name"] != "none" { - t.Error("wrong label 'name'") - } - - if dep.Labels["component"] != "none-authservice" { - t.Error("wrong label 'component': " + dep.Labels["component"]) - } - - if dep.Spec.Template.Spec.Volumes[0].Name != "none-authservice-cert" { - t.Error("deployment volume for cert not set") - } - - if dep.Spec.Template.Spec.Containers[0].Name != "none-authservice" { - t.Error("deployment container not set") - } - - if dep.Spec.Selector == nil { - t.Error("null label selector") - } - - fmt.Printf("") //%#v", dep) - - _ = r.client.Create(context.TODO(), dep) -} diff --git a/pkg/controller/authenticationservice/none.go b/pkg/controller/authenticationservice/none.go deleted file mode 100644 index c1731001af6..00000000000 --- a/pkg/controller/authenticationservice/none.go +++ /dev/null @@ -1,117 +0,0 @@ -/* - * Copyright 2019, EnMasse authors. - * License: Apache License 2.0 (see the file LICENSE or http://apache.org/licenses/LICENSE-2.0.html). - */ -package authenticationservice - -import ( - "fmt" - - adminv1beta1 "github.com/enmasseproject/enmasse/pkg/apis/admin/v1beta1" - "github.com/enmasseproject/enmasse/pkg/util" - "github.com/enmasseproject/enmasse/pkg/util/install" - appsv1 "k8s.io/api/apps/v1" - corev1 "k8s.io/api/core/v1" - intstr "k8s.io/apimachinery/pkg/util/intstr" -) - -func applyNoneAuthServiceDefaults(authservice *adminv1beta1.AuthenticationService) { - if authservice.Spec.None == nil { - authservice.Spec.None = &adminv1beta1.AuthenticationServiceSpecNone{} - } - if authservice.Spec.None.CertificateSecret == nil { - secretName := fmt.Sprintf("%s-cert", authservice.Name) - authservice.Spec.None.CertificateSecret = &corev1.SecretReference{ - Name: secretName, - } - } -} - -func applyNoneAuthServiceCert(authservice *adminv1beta1.AuthenticationService, secret *corev1.Secret) error { - // On OpenShift we use the automatic cluster certificate provider - install.ApplyDefaultLabels(&secret.ObjectMeta, "none-authservice", secret.Name) - if !hasEntry(secret, "tls.key") || !hasEntry(secret, "tls.crt") { - cn := util.ServiceToCommonName(authservice.Namespace, authservice.Name) - return util.GenerateSelfSignedCertSecret(cn, nil, nil, secret) - } - return nil -} - -func applyNoneAuthServiceDeployment(authservice *adminv1beta1.AuthenticationService, deployment *appsv1.Deployment) error { - install.ApplyDeploymentDefaults(deployment, "none-authservice", authservice.Name) - if err := install.ApplyDeploymentContainerWithError(deployment, "none-authservice", func(container *corev1.Container) error { - if err := install.ApplyContainerImage(container, "none-authservice", authservice.Spec.None.Image); err != nil { - return err - } - - container.Env = []corev1.EnvVar{ - { - Name: "LISTENPORT", - Value: "5671", - }, - { - Name: "HEALTHPORT", - Value: "8080", - }, - } - - container.LivenessProbe = &corev1.Probe{ - InitialDelaySeconds: 30, - Handler: corev1.Handler{ - HTTPGet: &corev1.HTTPGetAction{ - Port: intstr.FromString("http"), - Path: "/healthz", - Scheme: "HTTP", - }, - }, - } - - container.Ports = []corev1.ContainerPort{ - { - ContainerPort: 5671, - Name: "amqps", - }, - { - ContainerPort: 8080, - Name: "http", - }, - } - - if authservice.Spec.None.Resources != nil { - container.Resources = *authservice.Spec.None.Resources - } else { - container.Resources = corev1.ResourceRequirements{} - } - - install.ApplyVolumeMountSimple(container, "none-authservice-cert", "/opt/none-authservice/cert", true) - - return nil - }); err != nil { - return err - } - - if authservice.Spec.None.Replicas != nil { - deployment.Spec.Replicas = authservice.Spec.None.Replicas - } - - install.ApplySecretVolume(&deployment.Spec.Template.Spec, "none-authservice-cert", authservice.Spec.None.CertificateSecret.Name) - - return nil -} - -func applyNoneAuthServiceService(authservice *adminv1beta1.AuthenticationService, service *corev1.Service) error { - install.ApplyServiceDefaults(service, "none-authservice", authservice.Name) - if service.Annotations == nil { - service.Annotations = make(map[string]string) - } - service.Annotations["service.alpha.openshift.io/serving-cert-secret-name"] = authservice.Spec.None.CertificateSecret.Name - service.Spec.Ports = []corev1.ServicePort{ - { - Port: 5671, - Protocol: corev1.ProtocolTCP, - TargetPort: intstr.FromString("amqps"), - Name: "amqps", - }, - } - return nil -} diff --git a/pkg/controller/authenticationservice/operatorImageMap.yaml b/pkg/controller/authenticationservice/operatorImageMap.yaml deleted file mode 100644 index e69de29bb2d..00000000000 diff --git a/pkg/controller/authenticationservice/standard.go b/pkg/controller/authenticationservice/standard.go deleted file mode 100644 index 3b644f608ee..00000000000 --- a/pkg/controller/authenticationservice/standard.go +++ /dev/null @@ -1,309 +0,0 @@ -/* - * Copyright 2019, EnMasse authors. - * License: Apache License 2.0 (see the file LICENSE or http://apache.org/licenses/LICENSE-2.0.html). - */ -package authenticationservice - -import ( - "fmt" - - adminv1beta1 "github.com/enmasseproject/enmasse/pkg/apis/admin/v1beta1" - "github.com/enmasseproject/enmasse/pkg/util" - "github.com/enmasseproject/enmasse/pkg/util/install" - oauthv1 "github.com/openshift/api/oauth/v1" - routev1 "github.com/openshift/api/route/v1" - appsv1 "k8s.io/api/apps/v1" - corev1 "k8s.io/api/core/v1" - resource "k8s.io/apimachinery/pkg/api/resource" - intstr "k8s.io/apimachinery/pkg/util/intstr" -) - -func applyStandardAuthServiceDefaults(authservice *adminv1beta1.AuthenticationService) { - if authservice.Spec.Standard == nil { - authservice.Spec.Standard = &adminv1beta1.AuthenticationServiceSpecStandard{} - } - if authservice.Spec.Standard.Storage == nil { - authservice.Spec.Standard.Storage = &adminv1beta1.AuthenticationServiceSpecStandardStorage{ - Type: adminv1beta1.Ephemeral, - } - } - if authservice.Spec.Standard.DeploymentName == nil { - authservice.Spec.Standard.DeploymentName = &authservice.Name - } - if authservice.Spec.Standard.ServiceName == nil { - authservice.Spec.Standard.ServiceName = &authservice.Name - } - if authservice.Spec.Standard.RouteName == nil { - authservice.Spec.Standard.RouteName = &authservice.Name - } - if authservice.Spec.Standard.Storage.ClaimName == nil { - authservice.Spec.Standard.Storage.ClaimName = &authservice.Name - } - if authservice.Spec.Standard.Storage.DeleteClaim == nil { - authservice.Spec.Standard.Storage.DeleteClaim = new(bool) - *authservice.Spec.Standard.Storage.DeleteClaim = true - } - if authservice.Spec.Standard.Resources == nil { - authservice.Spec.Standard.Resources = &corev1.ResourceRequirements{ - Requests: map[corev1.ResourceName]resource.Quantity{"memory": *resource.NewScaledQuantity(2, resource.Giga)}, - Limits: map[corev1.ResourceName]resource.Quantity{"memory": *resource.NewScaledQuantity(2, resource.Giga)}, - } - } - if authservice.Spec.Standard.Datasource == nil { - authservice.Spec.Standard.Datasource = &adminv1beta1.AuthenticationServiceSpecStandardDatasource{ - Type: adminv1beta1.H2Datasource, - } - } - if authservice.Spec.Standard.CredentialsSecret == nil { - secretName := *authservice.Spec.Standard.ServiceName + "-credentials" - authservice.Spec.Standard.CredentialsSecret = &corev1.SecretReference{ - Name: secretName, - } - } - - if authservice.Spec.Standard.CertificateSecret == nil { - secretName := *authservice.Spec.Standard.ServiceName + "-cert" - authservice.Spec.Standard.CertificateSecret = &corev1.SecretReference{ - Name: secretName, - } - } -} - -func applyStandardAuthServiceCredentials(authservice *adminv1beta1.AuthenticationService, secret *corev1.Secret) error { - install.ApplyDefaultLabels(&secret.ObjectMeta, "standard-authservice", secret.Name) - - if !hasEntry(secret, "admin.username") || !hasEntry(secret, "admin.password") { - secret.StringData = make(map[string]string) - - if !hasEntry(secret, "admin.username") { - secret.StringData["admin.username"] = "admin" - } - - if !hasEntry(secret, "admin.password") { - adminPassword, err := util.GeneratePassword(32) - if err != nil { - return err - } - secret.StringData["admin.password"] = adminPassword - } - } - return nil -} - -func hasEntry(secret *corev1.Secret, key string) bool { - if secret == nil { - return false - } - if secret.Data == nil { - return false - } - _, ok := secret.Data[key] - return ok -} - -func applyStandardAuthServiceCert(authservice *adminv1beta1.AuthenticationService, secret *corev1.Secret) error { - // On OpenShift we use the automatic cluster certificate provider - install.ApplyDefaultLabels(&secret.ObjectMeta, "standard-authservice", secret.Name) - if !hasEntry(secret, "tls.key") || !hasEntry(secret, "tls.crt") { - cn := util.ServiceToCommonName(authservice.Namespace, *authservice.Spec.Standard.ServiceName) - return util.GenerateSelfSignedCertSecret(cn, nil, nil, secret) - } - return nil -} - -func applyStandardAuthServiceDeployment(authservice *adminv1beta1.AuthenticationService, deployment *appsv1.Deployment) error { - - install.ApplyDeploymentDefaults(deployment, "standard-authservice", *authservice.Spec.Standard.DeploymentName) - - install.OverrideSecurityContextFsGroup("standard-authservice", authservice.Spec.Standard.SecurityContext, deployment) - - if err := install.ApplyInitContainerWithError(deployment, "keycloak-plugin", func(container *corev1.Container) error { - if err := install.ApplyContainerImage(container, "keycloak-plugin", authservice.Spec.Standard.InitImage); err != nil { - return err - } - install.ApplyEnvSimple(container, "KEYCLOAK_DIR", "/opt/jboss/keycloak") - install.ApplyVolumeMountSimple(container, "keycloak-providers", "/opt/jboss/keycloak/providers", false) - install.ApplyVolumeMountSimple(container, "keycloak-configuration", "/opt/jboss/keycloak/standalone/configuration", false) - install.ApplyVolumeMountSimple(container, "standard-authservice-cert", "/opt/enmasse/cert", false) - install.ApplyEnvSimple(container, "KEYCLOAK_CONFIG_FILE", "standalone-"+string(authservice.Spec.Standard.Datasource.Type)+".xml") - - return nil - }); err != nil { - return err - } - - if err := install.ApplyDeploymentContainerWithError(deployment, "keycloak", func(container *corev1.Container) error { - if err := install.ApplyContainerImage(container, "keycloak", authservice.Spec.Standard.Image); err != nil { - return err - } - jvmOptions := "-Dvertx.cacheDirBase=/tmp -Djboss.bind.address=0.0.0.0 -Djava.net.preferIPv4Stack=true -Duser.timezone=UTC" - if authservice.Spec.Standard.JvmOptions != nil { - jvmOptions += " " + *authservice.Spec.Standard.JvmOptions - } else if qty, ok := authservice.Spec.Standard.Resources.Requests["memory"]; ok { - containerMemoryRequest := qty.ScaledValue(resource.Mega) - jvmOptions += fmt.Sprintf(" -Xms%dm -Xmx%dm", containerMemoryRequest/2, containerMemoryRequest/2) - } - install.ApplyEnvSimple(container, "JAVA_OPTS", jvmOptions) - install.ApplyEnvSecret(container, "KEYCLOAK_USER", "admin.username", authservice.Spec.Standard.CredentialsSecret.Name) - install.ApplyEnvSecret(container, "KEYCLOAK_PASSWORD", "admin.password", authservice.Spec.Standard.CredentialsSecret.Name) - - if authservice.Spec.Standard.Datasource.Type == adminv1beta1.PostgresqlDatasource { - install.ApplyEnvSimple(container, "DB_HOST", authservice.Spec.Standard.Datasource.Host) - install.ApplyEnvSimple(container, "DB_PORT", fmt.Sprintf("%d", authservice.Spec.Standard.Datasource.Port)) - install.ApplyEnvSimple(container, "DB_DATABASE", authservice.Spec.Standard.Datasource.Database) - install.ApplyEnvSecret(container, "DB_USERNAME", "database-user", authservice.Spec.Standard.Datasource.CredentialsSecret.Name) - install.ApplyEnvSecret(container, "DB_PASSWORD", "database-password", authservice.Spec.Standard.Datasource.CredentialsSecret.Name) - } - - container.Args = []string{"start-keycloak.sh", "-b", "0.0.0.0", "-c", "standalone-openshift.xml"} - - container.Ports = []corev1.ContainerPort{{ - ContainerPort: 5671, - Name: "amqps", - }, { - ContainerPort: 8443, - Name: "https", - }} - container.ReadinessProbe = &corev1.Probe{ - InitialDelaySeconds: 30, - Handler: corev1.Handler{ - TCPSocket: &corev1.TCPSocketAction{ - Port: intstr.FromString("amqps"), - }, - }, - } - container.LivenessProbe = &corev1.Probe{ - InitialDelaySeconds: 120, - Handler: corev1.Handler{ - TCPSocket: &corev1.TCPSocketAction{ - Port: intstr.FromString("amqps"), - }, - }, - } - if authservice.Spec.Standard.Resources != nil { - container.Resources = *authservice.Spec.Standard.Resources - } else { - container.Resources = corev1.ResourceRequirements{} - } - install.ApplyVolumeMountSimple(container, "keycloak-providers", "/opt/jboss/keycloak/providers", false) - install.ApplyVolumeMountSimple(container, "keycloak-configuration", "/opt/jboss/keycloak/standalone/configuration", false) - install.ApplyVolumeMountSimple(container, "keycloak-persistence", "/opt/jboss/keycloak/standalone/data", false) - install.ApplyVolumeMountSimple(container, "standard-authservice-cert", "/opt/enmasse/cert", true) - - return nil - }); err != nil { - return err - } - - install.ApplySecretVolume(&deployment.Spec.Template.Spec, "standard-authservice-cert", authservice.Spec.Standard.CertificateSecret.Name) - install.ApplyEmptyDirVolume(&deployment.Spec.Template.Spec, "keycloak-providers") - install.ApplyEmptyDirVolume(&deployment.Spec.Template.Spec, "keycloak-configuration") - install.ApplyEmptyDirVolume(&deployment.Spec.Template.Spec, "keycloak-configuration") - - // Only allow setting volume on initial creation - if util.IsNewObject(deployment) { - if authservice.Spec.Standard.Storage.Type == adminv1beta1.PersistentClaim { - install.ApplyPersistentVolume(&deployment.Spec.Template.Spec, "keycloak-persistence", *authservice.Spec.Standard.Storage.ClaimName) - } else { - install.ApplyEmptyDirVolume(&deployment.Spec.Template.Spec, "keycloak-persistence") - } - } - - if authservice.Spec.Standard.Replicas != nil && authservice.Spec.Standard.Datasource.Type == adminv1beta1.PostgresqlDatasource { - deployment.Spec.Replicas = authservice.Spec.Standard.Replicas - } - - if authservice.Spec.Standard.ServiceAccountName != nil { - deployment.Spec.Template.Spec.ServiceAccountName = *authservice.Spec.Standard.ServiceAccountName - } else { - deployment.Spec.Template.Spec.ServiceAccountName = "standard-authservice" - } - deployment.Spec.Strategy = appsv1.DeploymentStrategy{ - Type: appsv1.RecreateDeploymentStrategyType, - } - return nil -} - -func applyStandardAuthServiceService(authservice *adminv1beta1.AuthenticationService, service *corev1.Service) error { - - install.ApplyServiceDefaults(service, "standard-authservice", *authservice.Spec.Standard.ServiceName) - service.Spec.Selector = install.CreateDefaultLabels(nil, "standard-authservice", *authservice.Spec.Standard.DeploymentName) - - if service.Annotations == nil { - service.Annotations = make(map[string]string) - } - service.Annotations["service.alpha.openshift.io/serving-cert-secret-name"] = authservice.Spec.Standard.CertificateSecret.Name - service.Spec.Ports = []corev1.ServicePort{ - { - Port: 8443, - Protocol: corev1.ProtocolTCP, - TargetPort: intstr.FromString("https"), - Name: "https", - }, - { - Port: 5671, - Protocol: corev1.ProtocolTCP, - TargetPort: intstr.FromString("amqps"), - Name: "amqps", - }, - } - return nil -} - -func applyStandardAuthServiceVolume(authservice *adminv1beta1.AuthenticationService, pvc *corev1.PersistentVolumeClaim) error { - - install.ApplyDefaultLabels(&pvc.ObjectMeta, "standard-authservice", *authservice.Spec.Standard.Storage.ClaimName) - - // Only allow setting volume on initial creation - if util.IsNewObject(pvc) { - selector := authservice.Spec.Standard.Storage.Selector - storageClassName := authservice.Spec.Standard.Storage.Class - resources := corev1.ResourceRequirements{ - Requests: map[corev1.ResourceName]resource.Quantity{"storage": authservice.Spec.Standard.Storage.Size}, - } - - pvc.Spec.AccessModes = []corev1.PersistentVolumeAccessMode{ - corev1.ReadWriteOnce, - } - pvc.Spec.Selector = selector - pvc.Spec.StorageClassName = storageClassName - pvc.Spec.Resources = resources - } - return nil -} - -func applyRoute(authservice *adminv1beta1.AuthenticationService, route *routev1.Route, caCertificate string) error { - - install.ApplyDefaultLabels(&route.ObjectMeta, "standard-authservice", *authservice.Spec.Standard.RouteName) - - route.Spec = routev1.RouteSpec{ - To: routev1.RouteTargetReference{ - Kind: "Service", - Name: authservice.Name, - }, - TLS: &routev1.TLSConfig{ - Termination: routev1.TLSTerminationReencrypt, - CACertificate: caCertificate, - }, - Port: &routev1.RoutePort{ - TargetPort: intstr.FromString("https"), - }, - } - return nil -} - -func applyOauthClient(authservice *adminv1beta1.AuthenticationService, oauth *oauthv1.OAuthClient, redirectUri string) error { - install.ApplyDefaultLabels(&oauth.ObjectMeta, "oauthclient", oauth.Name) - if oauth.Secret == "" { - password, err := util.GeneratePassword(32) - if err != nil { - return err - } - oauth.Secret = password - } - oauth.GrantMethod = oauthv1.GrantHandlerAuto - oauth.RedirectURIs = []string{ - redirectUri, - } - return nil -} diff --git a/pkg/controller/authenticationservice/upgrader/authenticationservice_upgrade_controller.go b/pkg/controller/authenticationservice/upgrader/authenticationservice_upgrade_controller.go deleted file mode 100644 index f42cb88edf6..00000000000 --- a/pkg/controller/authenticationservice/upgrader/authenticationservice_upgrade_controller.go +++ /dev/null @@ -1,113 +0,0 @@ -/* - * Copyright 2019, EnMasse authors. - * License: Apache License 2.0 (see the file LICENSE or http://apache.org/licenses/LICENSE-2.0.html). - */ - -package upgrader - -import ( - "context" - - adminv1beta1 "github.com/enmasseproject/enmasse/pkg/apis/admin/v1beta1" - "github.com/enmasseproject/enmasse/pkg/util" - appsv1 "k8s.io/api/apps/v1" - "k8s.io/apimachinery/pkg/apis/meta/v1" - "k8s.io/apimachinery/pkg/runtime" - "k8s.io/apimachinery/pkg/types" - "sigs.k8s.io/controller-runtime/pkg/client" - "sigs.k8s.io/controller-runtime/pkg/controller" - "sigs.k8s.io/controller-runtime/pkg/event" - "sigs.k8s.io/controller-runtime/pkg/handler" - "sigs.k8s.io/controller-runtime/pkg/manager" - "sigs.k8s.io/controller-runtime/pkg/predicate" - "sigs.k8s.io/controller-runtime/pkg/reconcile" - logf "sigs.k8s.io/controller-runtime/pkg/runtime/log" - "sigs.k8s.io/controller-runtime/pkg/source" -) - -var log = logf.Log.WithName("controller_authenticationservice_upgrader") - -func Add(mgr manager.Manager) error { - return add(mgr, newUpgrader(mgr)) -} - -func newUpgrader(mgr manager.Manager) *UpgradeAuthenticationService { - return &UpgradeAuthenticationService{client: mgr.GetClient(), scheme: mgr.GetScheme(), namespace: util.GetEnvOrDefault("NAMESPACE", "enmasse-infra")} -} - -func add(mgr manager.Manager, r *UpgradeAuthenticationService) error { - - // Create a new controller - c, err := controller.New("authenticationservice-upgrade-controller", mgr, controller.Options{Reconciler: r}) - if err != nil { - return err - } - - // Watch for existing unowned keycloak deployment - isUnownedKeycloakDeployment := func(meta v1.Object) bool { - return meta.GetName() == "keycloak" && - (len(meta.GetOwnerReferences()) == 0 || meta.GetOwnerReferences()[0].Controller == nil || *(meta.GetOwnerReferences()[0].Controller) == false) - } - - err = c.Watch(&source.Kind{Type: &appsv1.Deployment{}}, - &handler.EnqueueRequestsFromMapFunc{ - ToRequests: handler.ToRequestsFunc(func(a handler.MapObject) []reconcile.Request { - return []reconcile.Request{ - { - NamespacedName: types.NamespacedName{Namespace: r.namespace, Name: "authservice"}, - }, - } - }), - }, predicate.Funcs{ - CreateFunc: func(e event.CreateEvent) bool { - return isUnownedKeycloakDeployment(e.Meta) - }, - UpdateFunc: func(e event.UpdateEvent) bool { - return isUnownedKeycloakDeployment(e.MetaNew) - }, - DeleteFunc: func(e event.DeleteEvent) bool { - return false - }, - GenericFunc: func(e event.GenericEvent) bool { - return isUnownedKeycloakDeployment(e.Meta) - }, - }) - if err != nil { - return err - } - - return nil -} - -func (r *UpgradeAuthenticationService) Reconcile(request reconcile.Request) (reconcile.Result, error) { - reqLogger := log.WithValues("Request.Namespace", request.Namespace, "Request.Name", request.Name) - reqLogger.Info("UpgradingAuthenticationService") - - ctx := context.TODO() - - list := &adminv1beta1.AuthenticationServiceList{} - opts := &client.ListOptions{} - err := r.client.List(ctx, list, opts) - if err != nil { - return reconcile.Result{}, err - } - - if len(list.Items) > 0 { - log.Info("Authentication services CR defined already") - return reconcile.Result{}, nil - } - - err = tryUpgradeExistingStandardAuthService(ctx, r) - - return reconcile.Result{}, err -} - -var _ reconcile.Reconciler = &UpgradeAuthenticationService{} - -type UpgradeAuthenticationService struct { - // This client, initialized using mgr.Client() above, is a split client - // that reads objects from the cache and writes to the apiserver - client client.Client - scheme *runtime.Scheme - namespace string -} diff --git a/pkg/controller/authenticationservice/upgrader/standard.go b/pkg/controller/authenticationservice/upgrader/standard.go deleted file mode 100644 index 814f25d5ee0..00000000000 --- a/pkg/controller/authenticationservice/upgrader/standard.go +++ /dev/null @@ -1,295 +0,0 @@ -/* - * Copyright 2019, EnMasse authors. - * License: Apache License 2.0 (see the file LICENSE or http://apache.org/licenses/LICENSE-2.0.html). - */ -package upgrader - -import ( - "context" - - adminv1beta1 "github.com/enmasseproject/enmasse/pkg/apis/admin/v1beta1" - "github.com/enmasseproject/enmasse/pkg/util" - v12 "github.com/openshift/api/apps/v1" - appsv1 "k8s.io/api/apps/v1" - corev1 "k8s.io/api/core/v1" - "k8s.io/apimachinery/pkg/api/errors" - "k8s.io/apimachinery/pkg/apis/meta/v1" - "k8s.io/apimachinery/pkg/types" - "sigs.k8s.io/controller-runtime/pkg/client" - "sigs.k8s.io/controller-runtime/pkg/controller/controllerutil" -) - -func tryUpgradeExistingStandardAuthService(ctx context.Context, r *UpgradeAuthenticationService) error { - - deployment := &appsv1.Deployment{} - deploymentName := types.NamespacedName{Name: "keycloak", Namespace: r.namespace} - err := r.client.Get(ctx, deploymentName, deployment) - - if err != nil { - if errors.IsNotFound(err) { - log.Error(err, "No existing keycloak deployment found", "NamespacedName", deploymentName.String()) - return nil - } else { - return err - } - } - - deploymentTemplateSpec := deployment.Spec.Template.Spec - pvcClaimName := findFirstPVCClaimName(deploymentTemplateSpec) - - pvc := &corev1.PersistentVolumeClaim{} - if pvcClaimName != nil { - pvcName := types.NamespacedName{Name: *pvcClaimName, Namespace: r.namespace} - err = r.client.Get(ctx, pvcName, pvc) - if err != nil { - if errors.IsNotFound(err) { - log.Info("No existing keycloak pvc found", "NamespacedName", pvcName.String()) - pvc = nil - } else { - return err - } - } else { - log.Info("Found existing keycloak pvc", "NamespacedName", pvcName.String()) - } - - } else { - pvc = nil - } - - standardauthService := &corev1.Service{} - standardauthServiceName := types.NamespacedName{Name: "standard-authservice", Namespace: r.namespace} - err = r.client.Get(ctx, standardauthServiceName, standardauthService) - - if err != nil { - if errors.IsNotFound(err) { - log.Info("No existing standardauth service found", "NamespacedName", standardauthServiceName.String()) - standardauthService = nil - } else { - return err - } - } else { - log.Info("Found existing standardauth service", "NamespacedName", standardauthServiceName.String()) - } - - postgresqlSecret := &corev1.Secret{} - postgresService := &corev1.Service{} - postgresDeploymentConfig := &v12.DeploymentConfig{} - postgresqlContainer := &corev1.Container{} - if util.IsOpenshift() { - postgresqlSecretName := types.NamespacedName{Name: "postgresql", Namespace: r.namespace} - err = r.client.Get(ctx, postgresqlSecretName, postgresqlSecret) - if err != nil { - if errors.IsNotFound(err) { - log.Info("No existing postgresql secret found", "NamespacedName", postgresqlSecretName.String()) - postgresqlSecret = nil - } else { - return err - } - } else { - log.Info("Found existing postgresql secret", "NamespacedName", postgresqlSecretName.String()) - } - - postgresServiceName := types.NamespacedName{Name: "postgresql", Namespace: r.namespace} - err = r.client.Get(ctx, postgresServiceName, postgresService) - - if err != nil { - if errors.IsNotFound(err) { - log.Info("No existing postgresql service found", "NamespacedName", postgresServiceName.String()) - postgresService = nil - } else { - return err - } - } else { - log.Info("Found existing postgresql service", "NamespacedName", postgresServiceName.String()) - } - - postgresDeploymentConfigName := types.NamespacedName{Name: "postgresql", Namespace: r.namespace} - err = r.client.Get(ctx, postgresDeploymentConfigName, postgresDeploymentConfig) - - if err != nil { - if errors.IsNotFound(err) { - log.Info("No existing postgresql deploymentconfig found", "NamespacedName", postgresServiceName.String()) - postgresDeploymentConfig = nil - } else { - return err - } - } else { - log.Info("Found existing postgresql deploymentconfig", "NamespacedName", postgresServiceName.String()) - if len(postgresDeploymentConfig.Spec.Template.Spec.Containers) > 0 { - postgresqlContainer = &postgresDeploymentConfig.Spec.Template.Spec.Containers[0] - } - } - - } else { - postgresqlSecret = nil - postgresService = nil - postgresDeploymentConfig = nil - postgresqlContainer = nil - } - - var keycloakContainer *corev1.Container - if len(deploymentTemplateSpec.Containers) > 0 { - for _, container := range deploymentTemplateSpec.Containers { - if container.Name == "keycloak" { - keycloakContainer = &container - break - } - } - } - - if keycloakContainer == nil { - log.Info("Couldn't find keycloak container within existing deployment", "deployment", deployment) - return nil - } - - meta := v1.ObjectMeta{Namespace: r.namespace, Name: "standard-authservice"} - authservice := &adminv1beta1.AuthenticationService{ - ObjectMeta: meta, - } - - meta.Labels = make(map[string]string) - meta.Labels["app"] = "enmasse" - - authservice.Spec.Type = adminv1beta1.Standard - authservice.Spec.Standard = &adminv1beta1.AuthenticationServiceSpecStandard{} - - authservice.Spec.Standard.DeploymentName = &deployment.Name - authservice.Spec.Standard.CredentialsSecret = &corev1.SecretReference{Name: "keycloak-credentials"} - - if standardauthService != nil { - authservice.Spec.Standard.ServiceName = &standardauthService.Name - } - - if pvc != nil { - if claimQuantity, ok := pvc.Spec.Resources.Requests["storage"]; ok { - authservice.Spec.Standard.Storage = &adminv1beta1.AuthenticationServiceSpecStandardStorage{ - Type: adminv1beta1.PersistentClaim, - ClaimName: &pvc.Name, - Size: claimQuantity, - } - } - } - - if keycloakContainer.Resources.Requests != nil || keycloakContainer.Resources.Limits != nil { - authservice.Spec.Standard.Resources = &keycloakContainer.Resources - } - - authservice.Spec.Standard.Datasource = &adminv1beta1.AuthenticationServiceSpecStandardDatasource{ - Type: adminv1beta1.H2Datasource, - } - - if postgresService != nil && postgresqlContainer != nil { - if err := configurePostgresqlDatasource(ctx, postgresService, authservice.Spec.Standard.Datasource, keycloakContainer, postgresqlContainer, r); err != nil { - log.Error(err, "Failed to create configure postgres datasource", "authenticationservice", authservice) - } - } - - _, err = controllerutil.CreateOrUpdate(ctx, r.client, authservice, func() error { - return nil - }) - - if err != nil { - log.Error(err, "Failed to create authenticationservice record", "authenticationservice", authservice) - } else { - log.Info("Successfully upgraded existing authentication service", "authenticationservice", authservice) - } - - return nil -} - -func configurePostgresqlDatasource(ctx context.Context, postgresService *corev1.Service, datasource *adminv1beta1.AuthenticationServiceSpecStandardDatasource, keycloakContainer *corev1.Container, postgresqlContainer *corev1.Container, r *UpgradeAuthenticationService) error { - - isSecretKey := func(s corev1.EnvVar) bool { - return s.ValueFrom != nil && s.ValueFrom.SecretKeyRef != nil - } - all := func(s corev1.EnvVar) bool { return true } - - dbUserName := findEnvVar(keycloakContainer, "DB_USERNAME", isSecretKey) - dbPassword := findEnvVar(keycloakContainer, "DB_PASSWORD", isSecretKey) - dbDatabase := findEnvVar(keycloakContainer, "DB_DATABASE", all) - - if dbUserName == nil || dbPassword == nil || dbDatabase == nil { - log.Info("Could not extract existing database details from keycloak deployment") - return nil - } - - pdbUserName := findEnvVar(postgresqlContainer, "POSTGRESQL_USER", isSecretKey) - pdbPassword := findEnvVar(postgresqlContainer, "POSTGRESQL_PASSWORD", isSecretKey) - pdbDatabase := findEnvVar(postgresqlContainer, "POSTGRESQL_DATABASE", all) - - if pdbUserName == nil || pdbPassword == nil || pdbDatabase == nil { - log.Info("Could not extract existing database details from postgresql deploymentconfig") - return nil - } - - if dbUserName.ValueFrom.SecretKeyRef.Name == pdbUserName.ValueFrom.SecretKeyRef.Name && - dbPassword.ValueFrom.SecretKeyRef.Name == pdbPassword.ValueFrom.SecretKeyRef.Name && - dbUserName.ValueFrom.SecretKeyRef.Name == dbPassword.ValueFrom.SecretKeyRef.Name { - datasource.CredentialsSecret = corev1.SecretReference{ - Name: dbUserName.ValueFrom.SecretKeyRef.Name, - } - } else { - // TODO could handle the cases where the environment variables refer to different secrets (or don't refer to secrets at all) by creating a new secret. - // probably not going to need these cases. - log.Info("Existing database username/password details on postgresql deploymentconfig and keycloak deployment refer to different secrets, " + - "can't set datasource.CredentialsSecret automatically") - } - - if dbDatabase.Value != "" && dbDatabase.Value == pdbDatabase.Value { - datasource.Database = dbDatabase.Value - } else if dbDatabase.ValueFrom.SecretKeyRef != nil && - dbDatabase.ValueFrom.SecretKeyRef.Name == pdbDatabase.ValueFrom.SecretKeyRef.Name { - secretKeyRef := dbDatabase.ValueFrom.SecretKeyRef - key := client.ObjectKey{Namespace: r.namespace, Name: secretKeyRef.Name} - secret := &corev1.Secret{} - err := r.client.Get(ctx, key, secret) - if err != nil { - if errors.IsNotFound(err) { - log.Info("Existing secret carrying database-name not found,"+ - "can't set datasource.Database automatically", "NamespacedName", secretKeyRef.String()) - } else { - return err - } - } else { - if db, ok := secret.Data["database-name"]; ok { - datasource.Database = string(db) - } else { - log.Info("Can't find key 'database-name' within secret, "+ - "can't set datasource.Database automatically", "NamespacedName", secretKeyRef.String()) - } - } - } else { - log.Info("Existing database database-name details on postgresql deploymentconfig and keycloak deployment refer to different secrets, " + - "can't set datasource.Database automatically") - } - - datasource.Host = postgresService.Name - if len(postgresService.Spec.Ports) > 0 { - datasource.Port = int(postgresService.Spec.Ports[0].Port) - } - - datasource.Type = adminv1beta1.PostgresqlDatasource - return nil -} - -func findFirstPVCClaimName(deploymentTemplateSpec corev1.PodSpec) *string { - for _, item := range deploymentTemplateSpec.Volumes { - if item.PersistentVolumeClaim != nil && item.PersistentVolumeClaim.ClaimName != "" { - return &item.PersistentVolumeClaim.ClaimName - } - } - return nil -} - -type envPredicate func(endVar corev1.EnvVar) bool - -func findEnvVar(c *corev1.Container, name string, pred envPredicate) *corev1.EnvVar { - - for _, env := range c.Env { - if env.Name == name && pred(env) { - return &env - } - } - - return nil -} diff --git a/pkg/controller/controller.go b/pkg/controller/controller.go index cb6d3abad90..01e081d2bd7 100644 --- a/pkg/controller/controller.go +++ b/pkg/controller/controller.go @@ -24,7 +24,7 @@ func AddToManager(m manager.Manager) error { } func CheckUpgrade(m manager.Manager) error { - if util.IsModuleEnabled("UPGRADER") && !util.IsModuleEnabled("MESSAGING_INFRASTRUCTURE") { + if util.IsModuleEnabled("UPGRADER") { upgrader, err := upgrader.New(m) if err != nil { return err diff --git a/pkg/controller/iotconfig/adapter.go b/pkg/controller/iotconfig/adapter.go index 754815497a6..e247266dee0 100644 --- a/pkg/controller/iotconfig/adapter.go +++ b/pkg/controller/iotconfig/adapter.go @@ -79,7 +79,7 @@ var adapters = []adapter{ // render the logback configuration func (a adapter) RenderLoggingConfig(config *iotv1alpha1.IoTConfig, override string) string { - return a.AdapterConfigProvider(config).Containers.Adapter.Logback.RenderConfiguration(config, logbackDefault, override) + return a.AdapterConfigProvider(config).Containers.Adapter.Logging.RenderConfiguration(config, logbackDefault, override) } func (a adapter) IsEnabled(config *iotv1alpha1.IoTConfig) bool { diff --git a/pkg/controller/iotconfig/auth.go b/pkg/controller/iotconfig/auth.go index 4fbee3fccf7..5322e513866 100644 --- a/pkg/controller/iotconfig/auth.go +++ b/pkg/controller/iotconfig/auth.go @@ -7,7 +7,11 @@ package iotconfig import ( "context" + "fmt" + iotv1alpha1 "github.com/enmasseproject/enmasse/pkg/apis/iot/v1alpha1" + "github.com/enmasseproject/enmasse/pkg/controller/messaginginfra" + "github.com/enmasseproject/enmasse/pkg/controller/messaginginfra/cert" "github.com/enmasseproject/enmasse/pkg/util" "github.com/enmasseproject/enmasse/pkg/util/cchange" "github.com/enmasseproject/enmasse/pkg/util/recon" @@ -80,6 +84,33 @@ func (r *ReconcileIoTConfig) processAdapterPskCredentials(ctx context.Context, c } +func (r *ReconcileIoTConfig) processAdapterInfraCert(ctx context.Context, config *iotv1alpha1.IoTConfig, configTracker *configTracker) (reconcile.Result, error) { + + rc := &recon.ReconcileContext{} + + // for all adapters + + _, infra, err := messaginginfra.LookupInfra(ctx, r.client, config.Namespace) + if err != nil { + return reconcile.Result{}, err + } + + // TODO: Set desired DNS names + _, err = r.certController.ReconcileCert(ctx, nil, infra, config, "iot-adapters") + if err != nil { + return reconcile.Result{}, err + } + + // TODO: Inject the following into adapter deployments + certSecretName := cert.GetCertSecretName(config.Name) + host := fmt.Sprintf("%s-cluster", infra.Name) + port := 55667 + log.Info("Reconcile adapter cert", "secret", certSecretName, "host", host, "port", port) + + return rc.Result() + +} + func (r *ReconcileIoTConfig) processAuthServicePskSecret(ctx context.Context, config *iotv1alpha1.IoTConfig, authServiceConfigCtx *cchange.ConfigChangeRecorder) error { return r.processSecret(ctx, nameAuthServicePskSecret, config, false, func(config *iotv1alpha1.IoTConfig, secret *corev1.Secret) error { diff --git a/pkg/controller/iotconfig/iotconfig_controller.go b/pkg/controller/iotconfig/iotconfig_controller.go index 601b254c6c9..be0c043c4b0 100644 --- a/pkg/controller/iotconfig/iotconfig_controller.go +++ b/pkg/controller/iotconfig/iotconfig_controller.go @@ -7,15 +7,19 @@ package iotconfig import ( "context" + "time" + + "github.com/enmasseproject/enmasse/pkg/controller/messaginginfra/cert" "github.com/enmasseproject/enmasse/pkg/util/iot" "github.com/enmasseproject/enmasse/pkg/util/loghandler" "github.com/pkg/errors" promv1 "github.com/coreos/prometheus-operator/pkg/apis/monitoring/v1" - "k8s.io/client-go/tools/record" "reflect" + "k8s.io/client-go/tools/record" + "github.com/enmasseproject/enmasse/pkg/util/cchange" "github.com/enmasseproject/enmasse/pkg/util/install" @@ -30,7 +34,7 @@ import ( appsv1 "k8s.io/api/apps/v1" corev1 "k8s.io/api/core/v1" - "k8s.io/apimachinery/pkg/apis/meta/v1" + v1 "k8s.io/apimachinery/pkg/apis/meta/v1" iotv1alpha1 "github.com/enmasseproject/enmasse/pkg/apis/iot/v1alpha1" apierrors "k8s.io/apimachinery/pkg/api/errors" @@ -75,12 +79,14 @@ func Add(mgr manager.Manager) error { } func newReconciler(mgr manager.Manager, infraNamespace string, configName string) *ReconcileIoTConfig { + certController := cert.NewCertController(mgr.GetClient(), mgr.GetScheme(), 24*30*time.Hour, 24*time.Hour) return &ReconcileIoTConfig{ - client: mgr.GetClient(), - scheme: mgr.GetScheme(), - namespace: infraNamespace, - configName: configName, - recorder: mgr.GetEventRecorderFor(ControllerName), + client: mgr.GetClient(), + scheme: mgr.GetScheme(), + namespace: infraNamespace, + configName: configName, + recorder: mgr.GetEventRecorderFor(ControllerName), + certController: certController, } } @@ -154,6 +160,9 @@ type ReconcileIoTConfig struct { scheme *runtime.Scheme recorder record.EventRecorder + // Manages certificates for messaging infrastructure. + certController *cert.CertController + // The name of the configuration we are watching // we are watching only one config, in our own namespace configName string @@ -256,6 +265,9 @@ func (r *ReconcileIoTConfig) Reconcile(request reconcile.Request) (reconcile.Res rc.Process(func() (reconcile.Result, error) { return r.processAdapterPskCredentials(ctx, config, configTracker) }) + rc.Process(func() (reconcile.Result, error) { + return r.processAdapterInfraCert(ctx, config, configTracker) + }) // start normal reconcile diff --git a/pkg/controller/iotconfig/tenant_service.go b/pkg/controller/iotconfig/tenant_service.go index 00b8232fe8b..22ef0b9ae08 100644 --- a/pkg/controller/iotconfig/tenant_service.go +++ b/pkg/controller/iotconfig/tenant_service.go @@ -19,6 +19,7 @@ import ( appsv1 "k8s.io/api/apps/v1" corev1 "k8s.io/api/core/v1" "k8s.io/apimachinery/pkg/api/resource" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" iotv1alpha1 "github.com/enmasseproject/enmasse/pkg/apis/iot/v1alpha1" ) @@ -30,13 +31,6 @@ func (r *ReconcileIoTConfig) processTenantService(ctx context.Context, config *i rc := &recon.ReconcileContext{} change := cchange.NewRecorder() - service := config.Spec.ServicesConfig.Tenant - - rc.ProcessSimple(func() error { - return r.processConfigMap(ctx, nameTenantService+"-config", config, false, func(config *iotv1alpha1.IoTConfig, configMap *corev1.ConfigMap) error { - return r.reconcileTenantServiceConfigMap(config, service, configMap, change) - }) - }) rc.ProcessSimple(func() error { return r.processDeployment(ctx, nameTenantService, config, false, func(config *iotv1alpha1.IoTConfig, deployment *appsv1.Deployment) error { return r.reconcileTenantServiceDeployment(config, deployment, change, authServicePsk) @@ -49,6 +43,12 @@ func (r *ReconcileIoTConfig) processTenantService(ctx context.Context, config *i return r.processService(ctx, nameTenantService+"-metrics", config, false, r.reconcileMetricsService(nameTenantService)) }) + // delete legacy configmap + + rc.Delete(ctx, r.client, &corev1.ConfigMap{ObjectMeta: metav1.ObjectMeta{Namespace: config.Namespace, Name: nameTenantService}}) + + // done + return rc.Result() } @@ -66,13 +66,19 @@ func (r *ReconcileIoTConfig) reconcileTenantServiceDeployment(config *iotv1alpha err := install.ApplyDeploymentContainerWithError(deployment, "tenant-service", func(container *corev1.Container) error { tracingContainer = container + var javaOptions []string if err := install.SetContainerImage(container, "iot-tenant-service", config); err != nil { return err } - container.Args = nil - container.Command = nil + // set command + + if service.Container.UseNativeImage(config) { + container.Command = []string{"/iot-tenant-service"} + } else { + container.Command = nil + } // set default resource limits @@ -92,20 +98,20 @@ func (r *ReconcileIoTConfig) reconcileTenantServiceDeployment(config *iotv1alpha // environment container.Env = []corev1.EnvVar{ - {Name: "SPRING_PROFILES_ACTIVE", Value: "prod"}, - {Name: "LOGGING_CONFIG", Value: "file:///etc/config/logback-spring.xml"}, {Name: "KUBERNETES_NAMESPACE", ValueFrom: install.FromFieldNamespace()}, {Name: "ENMASSE_IOT_AUTH_HOST", Value: FullHostNameForEnvVar("iot-auth-service")}, - {Name: "ENMASSE_IOT_AUTH_VALIDATION_SHARED_SECRET", ValueFrom: install.FromSecret(nameAuthServicePskSecret, keyInterServicePsk)}, + {Name: "ENMASSE_IOT_AUTH_VALIDATION_SHAREDSECRET", ValueFrom: install.FromSecret(nameAuthServicePskSecret, keyInterServicePsk)}, } - appendCommonHonoJavaEnv(container, "ENMASSE_IOT_AMQP_", config, &service.CommonServiceConfig) + javaOptions = service.QuarkusServiceConfig.Container.ApplyLoggingToContainer(config, javaOptions) + + appendCommonHonoJavaEnv(container, "ENMASSE_IOT_AMQP_", config, &service.QuarkusServiceConfig) SetupTracing(config, deployment, container) - AppendStandardHonoJavaOptions(container) + javaOptions = AppendQuarkusHonoJavaOptions(javaOptions) - if err := AppendTrustStores(config, container, []string{"ENMASSE_IOT_AUTH_TRUST_STORE_PATH"}); err != nil { + if err := AppendTrustStores(config, container, []string{"ENMASSE_IOT_AUTH_TRUSTSTOREPATH"}); err != nil { return err } @@ -118,6 +124,16 @@ func (r *ReconcileIoTConfig) reconcileTenantServiceDeployment(config *iotv1alpha applyContainerConfig(container, service.Container.ContainerConfig) + // apply java options + + if service.Container.UseNativeImage(config) { + container.Args = javaOptions + install.RemoveEnv(container, install.JavaOptsEnvVarName) + } else { + container.Args = nil + install.AppendEnvVarValue(container, install.JavaOptsEnvVarName, javaOptions...) + } + // return return nil @@ -173,18 +189,3 @@ func (r *ReconcileIoTConfig) reconcileTenantServiceService(config *iotv1alpha1.I return nil } - -func (r *ReconcileIoTConfig) reconcileTenantServiceConfigMap(config *iotv1alpha1.IoTConfig, service iotv1alpha1.TenantServiceConfig, configMap *corev1.ConfigMap, change *cchange.ConfigChangeRecorder) error { - - install.ApplyDefaultLabels(&configMap.ObjectMeta, "iot", configMap.Name) - - if configMap.Data == nil { - configMap.Data = make(map[string]string) - } - - configMap.Data["logback-spring.xml"] = service.RenderConfiguration(config, logbackDefault, configMap.Data["logback-custom.xml"]) - - change.AddStringsFromMap(configMap.Data, "logback-spring.xml") - - return nil -} diff --git a/pkg/controller/iotconfig/utils.go b/pkg/controller/iotconfig/utils.go index 0e4996d0117..ae7349446e2 100644 --- a/pkg/controller/iotconfig/utils.go +++ b/pkg/controller/iotconfig/utils.go @@ -127,6 +127,11 @@ func AppendStandardHonoJavaOptions(container *corev1.Container) { } +func AppendQuarkusHonoJavaOptions(opts []string) []string { + opts = append(opts, "-Djava.net.preferIPv4Stack=true") + return opts +} + func applyDefaultStatefulSetConfig(statefulSet *appsv1.StatefulSet, serviceConfig iotv1alpha1.ServiceConfig, config *cchange.ConfigChangeRecorder) { statefulSet.Spec.Replicas = serviceConfig.Replicas @@ -245,10 +250,10 @@ func appendCommonHonoJavaEnv(container *corev1.Container, envVarPrefix string, c // add native tls flag - install.ApplyOrRemoveEnvSimple(container, envVarPrefix+"NATIVE_TLS_REQUIRED", strconv.FormatBool(commonJavaService.IsNativeTlsRequired(config))) + install.ApplyOrRemoveEnvSimple(container, envVarPrefix+"NATIVETLSREQUIRED", strconv.FormatBool(commonJavaService.IsNativeTlsRequired(config))) // configure tls versions - install.ApplyOrRemoveEnvSimple(container, envVarPrefix+"SECURE_PROTOCOLS", strings.Join(commonJavaService.TlsVersions(config), ",")) + install.ApplyOrRemoveEnvSimple(container, envVarPrefix+"SECUREPROTOCOLS", strings.Join(commonJavaService.TlsVersions(config), ",")) } diff --git a/pkg/controller/messagingaddress/messagingaddress_controller.go b/pkg/controller/messagingaddress/messagingaddress_controller.go index db37a1f0ee9..327b1bb95dd 100644 --- a/pkg/controller/messagingaddress/messagingaddress_controller.go +++ b/pkg/controller/messagingaddress/messagingaddress_controller.go @@ -17,7 +17,6 @@ import ( v1beta2 "github.com/enmasseproject/enmasse/pkg/apis/enmasse/v1beta2" "github.com/enmasseproject/enmasse/pkg/controller/messaginginfra" "github.com/enmasseproject/enmasse/pkg/state" - "github.com/enmasseproject/enmasse/pkg/state/broker" stateerrors "github.com/enmasseproject/enmasse/pkg/state/errors" "github.com/enmasseproject/enmasse/pkg/util" utilerrors "github.com/enmasseproject/enmasse/pkg/util/errors" @@ -100,27 +99,6 @@ func add(mgr manager.Manager, r *ReconcileMessagingAddress) error { return err } -/* - * Very dumb scheduler that doesn't look at broker capacity. - */ -type DummyScheduler struct { -} - -var _ state.Scheduler = &DummyScheduler{} - -func (s *DummyScheduler) ScheduleAddress(address *v1beta2.MessagingAddress, brokers []*broker.BrokerState) error { - if len(brokers) > 0 { - broker := brokers[0] - address.Status.Brokers = append(address.Status.Brokers, v1beta2.MessagingAddressBroker{ - State: v1beta2.MessagingAddressBrokerScheduled, - Host: broker.Host().Hostname, - }) - } else { - return fmt.Errorf("no available broker") - } - return nil -} - func (r *ReconcileMessagingAddress) Reconcile(request reconcile.Request) (reconcile.Result, error) { logger := log.WithValues("Request.Namespace", request.Namespace, "Request.Name", request.Name) @@ -198,7 +176,7 @@ func (r *ReconcileMessagingAddress) Reconcile(request reconcile.Request) (reconc return reconcile.Result{}, fmt.Errorf("provided wrong object type to finalizer, only supports MessagingAddress") } - infra, err := messaginginfra.LookupInfra(ctx, r.client, address.Namespace) + _, infra, err := messaginginfra.LookupInfra(ctx, r.client, address.Namespace) if err != nil { // Not bound - allow dropping finalizer if utilerrors.IsNotBound(err) || utilerrors.IsNotFound(err) { @@ -271,7 +249,7 @@ func (r *ReconcileMessagingAddress) Reconcile(request reconcile.Request) (reconc var infra *v1beta2.MessagingInfrastructure // Retrieve the MessagingInfra for this MessagingAddress result, err = rc.Process(func(address *v1beta2.MessagingAddress) (processorResult, error) { - i, err := messaginginfra.LookupInfra(ctx, r.client, found.Namespace) + _, i, err := messaginginfra.LookupInfra(ctx, r.client, found.Namespace) if err != nil && (k8errors.IsNotFound(err) || utilerrors.IsNotBound(err)) { foundTenant.SetStatus(corev1.ConditionFalse, "", err.Error()) address.Status.Message = err.Error() @@ -356,22 +334,20 @@ func (r *ReconcileMessagingAddress) Reconcile(request reconcile.Request) (reconc result, err = rc.Process(func(address *v1beta2.MessagingAddress) (processorResult, error) { // TODO: Handle changes to partitions etc. + // We're already scheduled so just make sure scheduler is synced if len(address.Status.Brokers) > 0 { - // We're already scheduled so don't change scheduled.SetStatus(corev1.ConditionTrue, "", "") return processorResult{}, nil } // These addresses don't require scheduling if address.Spec.Anycast != nil || address.Spec.Multicast != nil { + scheduled.SetStatus(corev1.ConditionTrue, "", "") return processorResult{}, nil } - // TODO: Make configurable and a better scheduler - scheduler := &DummyScheduler{} - client := r.clientManager.GetClient(infra) - err := client.ScheduleAddress(address, scheduler) + err := client.ScheduleAddress(address) if err != nil { scheduled.SetStatus(corev1.ConditionFalse, "", err.Error()) address.Status.Message = err.Error() diff --git a/pkg/controller/messagingendpoint/messagingendpoint_controller.go b/pkg/controller/messagingendpoint/messagingendpoint_controller.go index 1079bec7a44..97caf25c4a8 100644 --- a/pkg/controller/messagingendpoint/messagingendpoint_controller.go +++ b/pkg/controller/messagingendpoint/messagingendpoint_controller.go @@ -180,7 +180,7 @@ func (r *ReconcileMessagingEndpoint) Reconcile(request reconcile.Request) (recon // Retrieve the MessagingInfra for this MessagingEndpoint var infra *v1beta2.MessagingInfrastructure result, err = rc.Process(func(endpoint *v1beta2.MessagingEndpoint) (processorResult, error) { - i, err := messaginginfra.LookupInfra(ctx, r.client, found.Namespace) + _, i, err := messaginginfra.LookupInfra(ctx, r.client, found.Namespace) if err != nil && (k8errors.IsNotFound(err) || utilerrors.IsNotBound(err)) { endpoint.Status.GetMessagingEndpointCondition(v1beta2.MessagingEndpointFoundTenant).SetStatus(corev1.ConditionFalse, "", err.Error()) endpoint.Status.Message = err.Error() @@ -330,7 +330,7 @@ func (r *ReconcileMessagingEndpoint) reconcileFinalizer(ctx context.Context, log return reconcile.Result{}, fmt.Errorf("provided wrong object type to finalizer, only supports MessagingEndpoint") } - infra, err := messaginginfra.LookupInfra(ctx, r.client, endpoint.Namespace) + _, infra, err := messaginginfra.LookupInfra(ctx, r.client, endpoint.Namespace) if err != nil { // Not bound - allow dropping finalizer if utilerrors.IsNotBound(err) || utilerrors.IsNotFound(err) { diff --git a/pkg/controller/messaginginfra/broker/controller.go b/pkg/controller/messaginginfra/broker/controller.go index 9dbf1a2bd2c..81829435317 100644 --- a/pkg/controller/messaginginfra/broker/controller.go +++ b/pkg/controller/messaginginfra/broker/controller.go @@ -7,6 +7,7 @@ package broker import ( "context" + "errors" "fmt" "strings" @@ -15,7 +16,9 @@ import ( v1beta2 "github.com/enmasseproject/enmasse/pkg/apis/enmasse/v1beta2" "github.com/enmasseproject/enmasse/pkg/controller/messaginginfra/cert" "github.com/enmasseproject/enmasse/pkg/controller/messaginginfra/common" + "github.com/enmasseproject/enmasse/pkg/state" . "github.com/enmasseproject/enmasse/pkg/state/common" + stateerrors "github.com/enmasseproject/enmasse/pkg/state/errors" "github.com/enmasseproject/enmasse/pkg/util" "github.com/enmasseproject/enmasse/pkg/util/install" @@ -34,13 +37,16 @@ type BrokerController struct { client client.Client scheme *runtime.Scheme certController *cert.CertController + clientManager state.ClientManager } func NewBrokerController(client client.Client, scheme *runtime.Scheme, certController *cert.CertController) *BrokerController { + clientManager := state.GetClientManager() return &BrokerController{ client: client, scheme: scheme, certController: certController, + clientManager: clientManager, } } @@ -96,11 +102,20 @@ func (b *BrokerController) ReconcileBrokers(ctx context.Context, logger logr.Log } + infraClient := b.clientManager.GetClient(infra) toDelete := numBrokersToDelete(infra.Spec.Broker.ScalingStrategy, brokers.Items) + newSize := len(brokers.Items) - toDelete if toDelete > 0 { logger.Info("Removing brokers", "toDelete", toDelete) - for i := len(brokers.Items) - 1; toDelete > 0; i-- { - err := b.client.Delete(ctx, &brokers.Items[i]) + for i := len(brokers.Items) - 1; toDelete > 0 && i > 0; i-- { + err := infraClient.DeleteBroker(toHost(&brokers.Items[i])) + if err != nil { + if errors.Is(err, stateerrors.BrokerInUseError) { + continue + } + return nil, err + } + err = b.client.Delete(ctx, &brokers.Items[i]) if err != nil { return nil, err } @@ -108,6 +123,10 @@ func (b *BrokerController) ReconcileBrokers(ctx context.Context, logger logr.Log toDelete-- } } + // TODO: Depending on scaling strategy, support migrating queues + if toDelete > 0 { + return nil, fmt.Errorf("unable to scale down to %d brokers: %d brokers are still needed", newSize, newSize+toDelete) + } // Update discoverable brokers brokerPods := corev1.PodList{} @@ -250,7 +269,7 @@ func (b *BrokerController) reconcileBroker(ctx context.Context, logger logr.Logg }, }, TimeoutSeconds: 3, - InitialDelaySeconds: 30, + InitialDelaySeconds: 10, } return nil diff --git a/pkg/controller/messaginginfra/messaginginfra_controller.go b/pkg/controller/messaginginfra/messaginginfra_controller.go index 3b920a41930..ae19fe9f455 100644 --- a/pkg/controller/messaginginfra/messaginginfra_controller.go +++ b/pkg/controller/messaginginfra/messaginginfra_controller.go @@ -631,26 +631,26 @@ func (r *processorResult) Result() reconcile.Result { } // Find the MessagingInfra servicing a given namespace -func LookupInfra(ctx context.Context, c client.Client, namespace string) (*v1beta2.MessagingInfrastructure, error) { +func LookupInfra(ctx context.Context, c client.Client, namespace string) (*v1beta2.MessagingTenant, *v1beta2.MessagingInfrastructure, error) { // Retrieve the MessagingTenant for this namespace tenant := &v1beta2.MessagingTenant{} err := c.Get(ctx, types.NamespacedName{Name: messagingtenant.TENANT_RESOURCE_NAME, Namespace: namespace}, tenant) if err != nil { if k8errors.IsNotFound(err) { - return nil, utilerrors.NewNotFoundError("MessagingTenant", messagingtenant.TENANT_RESOURCE_NAME, namespace) + return nil, nil, utilerrors.NewNotFoundError("MessagingTenant", messagingtenant.TENANT_RESOURCE_NAME, namespace) } - return nil, err + return nil, nil, err } if !tenant.IsBound() { - return nil, utilerrors.NewNotBoundError(namespace) + return tenant, nil, utilerrors.NewNotBoundError(namespace) } // Retrieve the MessagingInfra for this MessagingTenant infra := &v1beta2.MessagingInfrastructure{} err = c.Get(ctx, types.NamespacedName{Name: tenant.Status.MessagingInfrastructureRef.Name, Namespace: tenant.Status.MessagingInfrastructureRef.Namespace}, infra) if err != nil { - return nil, err + return tenant, nil, err } - return infra, nil + return tenant, infra, nil } diff --git a/pkg/controller/messaginginfra/router/controller.go b/pkg/controller/messaginginfra/router/controller.go index 52c5a860107..f0dadba2d54 100644 --- a/pkg/controller/messaginginfra/router/controller.go +++ b/pkg/controller/messaginginfra/router/controller.go @@ -123,6 +123,8 @@ func (r *RouterController) ReconcileRouters(ctx context.Context, logger logr.Log certShaSum = certSha.Sum(certShaSum) routerCertSha := hex.EncodeToString(certShaSum[:]) + meshServiceName := fmt.Sprintf("%s", routerInfraName) + // Reconcile statefulset of the router statefulset := &appsv1.StatefulSet{ ObjectMeta: metav1.ObjectMeta{Namespace: infra.Namespace, Name: routerInfraName}, @@ -158,13 +160,21 @@ func (r *RouterController) ReconcileRouters(ctx context.Context, logger logr.Log install.ApplyEnvSimple(container, "QDROUTERD_CONF", "/etc/qpid-dispatch/config/qdrouterd.json") install.ApplyEnvSimple(container, "QDROUTERD_CONF_TYPE", "json") install.ApplyEnvSimple(container, "QDROUTERD_AUTO_MESH_DISCOVERY", "INFER") - install.ApplyEnvSimple(container, "QDROUTERD_AUTO_MESH_SERVICE_NAME", routerInfraName) + install.ApplyEnvSimple(container, "QDROUTERD_AUTO_MESH_SERVICE_NAME", meshServiceName) container.Ports = []corev1.ContainerPort{ { ContainerPort: 55672, Name: "inter-router", }, + { + ContainerPort: 55671, + Name: "operator", + }, + { + ContainerPort: 55667, + Name: "cluster", + }, { ContainerPort: 7777, Name: "management", @@ -198,7 +208,7 @@ func (r *RouterController) ReconcileRouters(ctx context.Context, logger logr.Log Port: intstr.FromString("readiness"), }, }, - InitialDelaySeconds: 30, + InitialDelaySeconds: 10, } for i := 40000; i < 40100; i++ { @@ -214,7 +224,7 @@ func (r *RouterController) ReconcileRouters(ctx context.Context, logger logr.Log return err } statefulset.Spec.Template.Spec.Containers = containers - statefulset.Spec.ServiceName = routerInfraName + statefulset.Spec.ServiceName = meshServiceName install.ApplyConfigMapVolume(&statefulset.Spec.Template.Spec, "config", routerInfraName) install.ApplySecretVolume(&statefulset.Spec.Template.Spec, "certs", certSecretName) @@ -227,17 +237,17 @@ func (r *RouterController) ReconcileRouters(ctx context.Context, logger logr.Log } // Reconcile router service - service := &corev1.Service{ - ObjectMeta: metav1.ObjectMeta{Namespace: infra.Namespace, Name: routerInfraName}, + meshService := &corev1.Service{ + ObjectMeta: metav1.ObjectMeta{Namespace: infra.Namespace, Name: meshServiceName}, } - _, err = controllerutil.CreateOrUpdate(ctx, r.client, service, func() error { - if err := controllerutil.SetControllerReference(infra, service, r.scheme); err != nil { + _, err = controllerutil.CreateOrUpdate(ctx, r.client, meshService, func() error { + if err := controllerutil.SetControllerReference(infra, meshService, r.scheme); err != nil { return err } - install.ApplyServiceDefaults(service, "router", infra.Name) - service.Spec.ClusterIP = "None" - service.Spec.Selector = statefulset.Spec.Template.Labels - service.Spec.Ports = []corev1.ServicePort{ + install.ApplyServiceDefaults(meshService, "router", infra.Name) + meshService.Spec.ClusterIP = "None" + meshService.Spec.Selector = statefulset.Spec.Template.Labels + meshService.Spec.Ports = []corev1.ServicePort{ { Port: 55672, Protocol: corev1.ProtocolTCP, @@ -252,8 +262,41 @@ func (r *RouterController) ReconcileRouters(ctx context.Context, logger logr.Log return nil, err } + // Reconcile internal cluster router service + internalClusterServiceName := fmt.Sprintf("%s-cluster", routerInfraName) + internalClusterService := &corev1.Service{ + ObjectMeta: metav1.ObjectMeta{Namespace: infra.Namespace, Name: internalClusterServiceName}, + } + _, err = controllerutil.CreateOrUpdate(ctx, r.client, internalClusterService, func() error { + if err := controllerutil.SetControllerReference(infra, internalClusterService, r.scheme); err != nil { + return err + } + install.ApplyServiceDefaults(internalClusterService, "router", infra.Name) + internalClusterService.Spec.Type = corev1.ServiceTypeClusterIP + internalClusterService.Spec.Selector = statefulset.Spec.Template.Labels + internalClusterService.Spec.Ports = []corev1.ServicePort{ + { + Port: 55667, + Protocol: corev1.ProtocolTCP, + TargetPort: intstr.FromString("cluster"), + Name: "cluster", + }, + } + + return nil + }) + if err != nil { + return nil, err + } + // Reconcile router certificate - _, err = r.certController.ReconcileCert(ctx, logger, infra, statefulset, fmt.Sprintf("%s", service.Name), fmt.Sprintf("%s.%s.svc", service.Name, service.Namespace), fmt.Sprintf("*.%s.%s.svc", service.Name, service.Namespace)) + _, err = r.certController.ReconcileCert(ctx, logger, infra, statefulset, + fmt.Sprintf("%s", meshService.Name), + fmt.Sprintf("%s.%s.svc", meshService.Name, meshService.Namespace), + fmt.Sprintf("*.%s.%s.svc", meshService.Name, meshService.Namespace), + fmt.Sprintf("%s", internalClusterService.Name), + fmt.Sprintf("%s.%s.svc", internalClusterService.Name, internalClusterService.Namespace), + fmt.Sprintf("*.%s.%s.svc", internalClusterService.Name, internalClusterService.Namespace)) if err != nil { return nil, err } diff --git a/pkg/controller/messaginginfra/router/router_config.go b/pkg/controller/messaginginfra/router/router_config.go index f84498ae4e5..fa825872e36 100644 --- a/pkg/controller/messaginginfra/router/router_config.go +++ b/pkg/controller/messaginginfra/router/router_config.go @@ -92,6 +92,19 @@ func generateConfig(router *v1beta2.MessagingInfrastructureSpecRouter) routerCon "httpRootDir": "invalid", }, }, + []interface{}{ + // Listener for cluster-internal components + "listener", + map[string]interface{}{ + "name": "cluster-internal", + "host": "0.0.0.0", + "port": 55667, + "requireSsl": true, + "saslMechanisms": "EXTERNAL", + "sslProfile": "infra_tls", + "authenticatePeer": true, + }, + }, }, } } diff --git a/pkg/controller/messagingtenant/messagingtenant_controller.go b/pkg/controller/messagingtenant/messagingtenant_controller.go index 7575673fe61..96772150a9e 100644 --- a/pkg/controller/messagingtenant/messagingtenant_controller.go +++ b/pkg/controller/messagingtenant/messagingtenant_controller.go @@ -7,12 +7,16 @@ package messagingtenant import ( "context" + "errors" "fmt" "reflect" "time" + amqp "github.com/enmasseproject/enmasse/pkg/amqpcommand" v1beta2 "github.com/enmasseproject/enmasse/pkg/apis/enmasse/v1beta2" "github.com/enmasseproject/enmasse/pkg/controller/messaginginfra/cert" + "github.com/enmasseproject/enmasse/pkg/state" + stateerrors "github.com/enmasseproject/enmasse/pkg/state/errors" "github.com/enmasseproject/enmasse/pkg/util" "github.com/enmasseproject/enmasse/pkg/util/finalizer" @@ -41,6 +45,7 @@ type ReconcileMessagingTenant struct { reader client.Reader recorder record.EventRecorder certController *cert.CertController + clientManager state.ClientManager namespace string } @@ -62,12 +67,14 @@ const ( func newReconciler(mgr manager.Manager) *ReconcileMessagingTenant { namespace := util.GetEnvOrDefault("NAMESPACE", "enmasse-infra") + clientManager := state.GetClientManager() return &ReconcileMessagingTenant{ client: mgr.GetClient(), reader: mgr.GetAPIReader(), recorder: mgr.GetEventRecorderFor("messagingtenant"), certController: cert.NewCertController(mgr.GetClient(), mgr.GetScheme(), 24*30*time.Hour, 24*time.Hour), namespace: namespace, + clientManager: clientManager, } } @@ -122,8 +129,13 @@ func (r *ReconcileMessagingTenant) Reconcile(request reconcile.Request) (reconci if tenant.Status.Phase == "" { tenant.Status.Phase = v1beta2.MessagingTenantConfiguring } + // TODO: Set based on plans + tenant.Status.Capabilities = tenant.Spec.Capabilities + tenant.Status.GetMessagingTenantCondition(v1beta2.MessagingTenantBound) tenant.Status.GetMessagingTenantCondition(v1beta2.MessagingTenantCaCreated) + tenant.Status.GetMessagingTenantCondition(v1beta2.MessagingTenantScheduled) + tenant.Status.GetMessagingTenantCondition(v1beta2.MessagingTenantCreated) tenant.Status.GetMessagingTenantCondition(v1beta2.MessagingTenantReady) return processorResult{}, nil }) @@ -165,6 +177,18 @@ func (r *ReconcileMessagingTenant) Reconcile(request reconcile.Request) (reconci } if tenant.IsBound() { + infra := &v1beta2.MessagingInfrastructure{} + err = r.client.Get(ctx, types.NamespacedName{Name: tenant.Status.MessagingInfrastructureRef.Name, Namespace: tenant.Status.MessagingInfrastructureRef.Namespace}, infra) + if err != nil { + return reconcile.Result{}, err + } + + client := r.clientManager.GetClient(infra) + err = client.DeleteTenant(tenant) + if err != nil { + return reconcile.Result{}, err + } + secret := &corev1.Secret{ ObjectMeta: metav1.ObjectMeta{Namespace: tenant.Status.MessagingInfrastructureRef.Namespace, Name: cert.GetTenantCaSecretName(tenant.Namespace)}, } @@ -247,6 +271,60 @@ func (r *ReconcileMessagingTenant) Reconcile(request reconcile.Request) (reconci return result.Result(), err } + // Schedule tenant with infra + result, err = rc.Process(func(tenant *v1beta2.MessagingTenant) (processorResult, error) { + transactional := false + for _, capability := range tenant.Status.Capabilities { + if capability == v1beta2.MessagingCapabilityTransactional { + transactional = true + break + } + } + + if !transactional { + tenant.Status.GetMessagingTenantCondition(v1beta2.MessagingTenantScheduled).SetStatus(corev1.ConditionTrue, "", "") + return processorResult{}, nil + } + + // Already scheduled + if tenant.Status.Broker != nil { + tenant.Status.GetMessagingTenantCondition(v1beta2.MessagingTenantScheduled).SetStatus(corev1.ConditionTrue, "", "") + return processorResult{}, nil + } + + client := r.clientManager.GetClient(infra) + err := client.ScheduleTenant(tenant) + if err != nil { + tenant.Status.GetMessagingTenantCondition(v1beta2.MessagingTenantScheduled).SetStatus(corev1.ConditionFalse, "", err.Error()) + tenant.Status.Message = err.Error() + if errors.Is(err, stateerrors.NotInitializedError) || errors.Is(err, amqp.RequestTimeoutError) || errors.Is(err, stateerrors.NotSyncedError) { + return processorResult{RequeueAfter: 10 * time.Second}, nil + } + return processorResult{}, err + } + tenant.Status.GetMessagingTenantCondition(v1beta2.MessagingTenantScheduled).SetStatus(corev1.ConditionTrue, "", "") + return processorResult{Requeue: true}, nil + }) + if result.ShouldReturn(err) { + return result.Result(), err + } + + // Sync tenant with infra + result, err = rc.Process(func(tenant *v1beta2.MessagingTenant) (processorResult, error) { + client := r.clientManager.GetClient(infra) + err := client.SyncTenant(tenant) + if err != nil { + tenant.Status.GetMessagingTenantCondition(v1beta2.MessagingTenantCreated).SetStatus(corev1.ConditionFalse, "", err.Error()) + tenant.Status.Message = err.Error() + return processorResult{}, err + } + tenant.Status.GetMessagingTenantCondition(v1beta2.MessagingTenantCreated).SetStatus(corev1.ConditionTrue, "", "") + return processorResult{}, nil + }) + if result.ShouldReturn(err) { + return result.Result(), err + } + // Reconcile Tenant CA result, err = rc.Process(func(tenant *v1beta2.MessagingTenant) (processorResult, error) { err := r.certController.ReconcileTenantCa(ctx, logger, infra, tenant.Namespace) diff --git a/pkg/controller/messaginguser/keycloak_cache.go b/pkg/controller/messaginguser/keycloak_cache.go deleted file mode 100644 index 80510c0c7f5..00000000000 --- a/pkg/controller/messaginguser/keycloak_cache.go +++ /dev/null @@ -1,92 +0,0 @@ -/* - * Copyright 2019, EnMasse authors. - * License: Apache License 2.0 (see the file LICENSE or http://apache.org/licenses/LICENSE-2.0.html). - */ - -package messaginguser - -import ( - userv1beta1 "github.com/enmasseproject/enmasse/pkg/apis/user/v1beta1" - "github.com/enmasseproject/enmasse/pkg/keycloak" -) - -type keycloakCache struct { - clients map[string]keycloak.KeycloakClient -} - -type cachedClient struct { - wrapped keycloak.KeycloakClient - invalidate func() -} - -var ( - _ keycloak.KeycloakClient = &cachedClient{} -) - -func NewKeycloakCache() keycloakCache { - return keycloakCache{ - clients: make(map[string]keycloak.KeycloakClient, 0), - } -} - -func (c *keycloakCache) get(key string) keycloak.KeycloakClient { - return c.clients[key] -} - -func (c *keycloakCache) put(key string, client keycloak.KeycloakClient) keycloak.KeycloakClient { - cached := &cachedClient{ - wrapped: client, - invalidate: func() { - delete(c.clients, key) - }, - } - c.clients[key] = cached - return cached -} - -func (c *cachedClient) CreateUser(realm string, user *userv1beta1.MessagingUser) error { - err := c.wrapped.CreateUser(realm, user) - if err != nil { - c.invalidate() - return err - } - return err -} - -func (c *cachedClient) UpdateUser(realm string, existing *userv1beta1.MessagingUser, updated *userv1beta1.MessagingUser) error { - err := c.wrapped.UpdateUser(realm, existing, updated) - if err != nil { - c.invalidate() - } - return err -} - -func (c *cachedClient) GetUser(realm string, username string) (*userv1beta1.MessagingUser, error) { - user, err := c.wrapped.GetUser(realm, username) - if err != nil { - c.invalidate() - } - return user, err -} -func (c *cachedClient) DeleteUser(realm string, user *userv1beta1.MessagingUser) error { - err := c.wrapped.DeleteUser(realm, user) - if err != nil { - c.invalidate() - } - return err -} -func (c *cachedClient) GetUsers(realm string, filters ...keycloak.AttributeFilter) ([]*userv1beta1.MessagingUser, error) { - users, err := c.wrapped.GetUsers(realm, filters...) - if err != nil { - c.invalidate() - } - return users, err -} - -func (c *cachedClient) GetRealms() ([]string, error) { - realms, err := c.wrapped.GetRealms() - if err != nil { - c.invalidate() - } - return realms, err -} diff --git a/pkg/controller/messaginguser/messaginguser_controller.go b/pkg/controller/messaginguser/messaginguser_controller.go deleted file mode 100644 index b76b01fc1a1..00000000000 --- a/pkg/controller/messaginguser/messaginguser_controller.go +++ /dev/null @@ -1,395 +0,0 @@ -/* - * Copyright 2019, EnMasse authors. - * License: Apache License 2.0 (see the file LICENSE or http://apache.org/licenses/LICENSE-2.0.html). - */ - -package messaginguser - -import ( - "context" - "fmt" - "github.com/pkg/errors" - "k8s.io/client-go/tools/record" - "reflect" - "strings" - "time" - - adminv1beta1 "github.com/enmasseproject/enmasse/pkg/apis/admin/v1beta1" - enmassev1beta1 "github.com/enmasseproject/enmasse/pkg/apis/enmasse/v1beta1" - userv1beta1 "github.com/enmasseproject/enmasse/pkg/apis/user/v1beta1" - "github.com/enmasseproject/enmasse/pkg/keycloak" - "github.com/enmasseproject/enmasse/pkg/util" - "github.com/enmasseproject/enmasse/pkg/util/finalizer" - "github.com/enmasseproject/enmasse/pkg/util/recon" - - logr "github.com/go-logr/logr" - corev1 "k8s.io/api/core/v1" - - k8errors "k8s.io/apimachinery/pkg/api/errors" - "k8s.io/apimachinery/pkg/runtime" - "k8s.io/apimachinery/pkg/types" - - "sigs.k8s.io/controller-runtime/pkg/client" - "sigs.k8s.io/controller-runtime/pkg/controller" - "sigs.k8s.io/controller-runtime/pkg/handler" - logf "sigs.k8s.io/controller-runtime/pkg/log" - "sigs.k8s.io/controller-runtime/pkg/manager" - "sigs.k8s.io/controller-runtime/pkg/reconcile" - "sigs.k8s.io/controller-runtime/pkg/source" - - apierrors "k8s.io/apimachinery/pkg/api/errors" -) - -var log = logf.Log.WithName("controller_messaginguser") -var _ reconcile.Reconciler = &ReconcileMessagingUser{} - -const ( - ANNOTATION_REALM_NAME = "enmasse.io/realm-name" - FINALIZER_NAME = "enmasse.io/standard-authservice" -) - -type ReconcileMessagingUser struct { - client client.Client - reader client.Reader - recorder record.EventRecorder - scheme *runtime.Scheme - namespace string - keycloakCache keycloakCache - newKeycloakClientFunc keycloak.NewKeycloakClientFunc -} - -// Gets called by parent "init", adding as to the manager -func Add(mgr manager.Manager) error { - return add(mgr, newReconciler(mgr)) -} - -func newReconciler(mgr manager.Manager) *ReconcileMessagingUser { - return &ReconcileMessagingUser{ - client: mgr.GetClient(), - reader: mgr.GetAPIReader(), - scheme: mgr.GetScheme(), - namespace: util.GetEnvOrDefault("NAMESPACE", "enmasse-infra"), - keycloakCache: NewKeycloakCache(), - newKeycloakClientFunc: keycloak.NewClient, - } -} - -func add(mgr manager.Manager, r *ReconcileMessagingUser) error { - - // Create a new controller - c, err := controller.New("messaginguser-controller", mgr, controller.Options{Reconciler: r}) - if err != nil { - return err - } - - // Watch for changes to primary resource MessagingUser - err = c.Watch(&source.Kind{Type: &userv1beta1.MessagingUser{}}, &handler.EnqueueRequestForObject{}) - if err != nil { - return err - } - - return nil -} - -func (r *ReconcileMessagingUser) Reconcile(request reconcile.Request) (reconcile.Result, error) { - - reqLogger := log.WithValues("Request.Namespace", request.Namespace, "Request.Name", request.Name) - reqLogger.Info("Reconciling MessagingUser") - - ctx := context.TODO() - - user := &userv1beta1.MessagingUser{} - err := r.client.Get(ctx, request.NamespacedName, user) - if err != nil { - if k8errors.IsNotFound(err) { - reqLogger.Info("MessagingUser resource not found. Ignoring since object must be deleted") - return reconcile.Result{}, nil - } - reqLogger.Error(err, "Failed to get MessagingUser") - return reconcile.Result{}, err - } - - // make copy for change detection - - original := user.DeepCopy() - - rc := &recon.ReconcileContext{} - - reqLogger.V(1).Info("checkFinalizer", "user", user.ObjectMeta) - rc.Process(func() (reconcile.Result, error) { - return r.checkFinalizer(ctx, reqLogger, user) - }) - - if rc.Error() != nil { - // processing finalizers failed - return rc.Result() - } - - if rc.NeedRequeue() { - // persist possible changes from finalizers, this is signaled to use via "need requeue" - if !reflect.DeepEqual(original, user) { - err := r.client.Update(ctx, user) - // processing the finalizers required a persist step, and so we stop early - // the call to Update will re-trigger us, and we don't need to set "requeue" explicitly - return reconcile.Result{}, errors.Wrap(err, "Failed to update after finalizers") - } else { - return rc.Result() - } - } - - lastMessage := user.Status.Message - user.Status.Message = "" - - if user.Status.Phase == "" { - // set and persist the current phase - user.Status.Phase = userv1beta1.UserPending - if err := r.client.Status().Update(ctx, user); err != nil { - return reconcile.Result{}, errors.Wrap(err, "Failed to set status to Pending") - } - } - - reqLogger.V(1).Info("createOrUpdate", "user", user.ObjectMeta) - if user.Status.Phase != userv1beta1.UserTerminating { - rc.Process(func() (reconcile.Result, error) { - return r.createOrUpdateUser(ctx, reqLogger, user) - }) - } - - if rc.Error() != nil { - user.Status.Message = rc.Error().Error() - } - - reqLogger.V(1).Info("updateGeneration", "user", user.ObjectMeta) - rc.ProcessSimple(func() error { - if user.Status.Message != lastMessage || user.Generation != user.Status.Generation { - user.Status.Generation = user.Generation - err := r.client.Status().Update(ctx, user) - if err != nil { - return errors.Wrap(err, "Failed to update status after reconcile") - } - } - return nil - }) - - if rc.NeedRequeue() { - reqLogger.Info("Need requeue", "result", rc.PlainResult()) - } - - return rc.Result() -} - -func (r *ReconcileMessagingUser) createOrUpdateUser(ctx context.Context, logger logr.Logger, user *userv1beta1.MessagingUser) (reconcile.Result, error) { - - addressSpace, err := r.lookupAddressSpace(ctx, logger, user) - if err != nil { - if apierrors.IsNotFound(err) { - logger.Info(fmt.Sprintf("Addresspace not (yet) found for user %s/%s", strings.Split(user.Name, ".")[0], user.Namespace)) - return reconcile.Result{RequeueAfter: time.Second * 10}, nil - } - return reconcile.Result{}, err - } - - if addressSpace.Spec.AuthenticationService == nil { - // authentication service is not set yet, we need to wait - logger.Info(fmt.Sprintf("Authentication service not (yet) set for address space %s/%s", addressSpace.Namespace, addressSpace.Name)) - return reconcile.Result{RequeueAfter: time.Second * 10}, nil - } - authenticationService, err := r.lookupAuthenticationService(ctx, logger, addressSpace) - if err != nil { - if apierrors.IsNotFound(err) { - return reconcile.Result{RequeueAfter: time.Second * 10}, nil - } - return reconcile.Result{}, err - } - - if authenticationService.Spec.Type == adminv1beta1.Standard { - if !isAuthserviceAvailable(authenticationService) { - logger.Info(fmt.Sprintf("Authentication service %s is not yet available", authenticationService.Name)) - return reconcile.Result{RequeueAfter: time.Second * 10}, nil - } - if user.Status.Phase == userv1beta1.UserPending { - user.Status.Phase = userv1beta1.UserConfiguring - err = r.client.Status().Update(ctx, user) - if err != nil { - return reconcile.Result{}, err - } - } - kcClient, err := r.getKeycloakClient(ctx, authenticationService) - if err != nil { - return reconcile.Result{}, err - } - - realm := addressSpace.Annotations[ANNOTATION_REALM_NAME] - - existingUser, err := kcClient.GetUser(realm, user.Spec.Username) - if err != nil { - return reconcile.Result{}, err - } - - if existingUser == nil { - err := kcClient.CreateUser(realm, user) - if err != nil { - return reconcile.Result{}, err - } - - } else { - err := kcClient.UpdateUser(realm, existingUser, user) - if err != nil { - return reconcile.Result{}, err - } - } - } - - if user.Status.Phase == userv1beta1.UserConfiguring { - user.Status.Phase = userv1beta1.UserActive - err = r.client.Status().Update(ctx, user) - if err != nil { - return reconcile.Result{}, err - } - } - return reconcile.Result{}, nil -} - -func (r *ReconcileMessagingUser) checkFinalizer(ctx context.Context, logger logr.Logger, user *userv1beta1.MessagingUser) (reconcile.Result, error) { - return finalizer.ProcessFinalizers(ctx, r.client, r.reader, r.recorder, user, []finalizer.Finalizer{ - finalizer.Finalizer{ - Name: FINALIZER_NAME, - Deconstruct: func(c finalizer.DeconstructorContext) (reconcile.Result, error) { - user, ok := c.Object.(*userv1beta1.MessagingUser) - if !ok { - return reconcile.Result{}, fmt.Errorf("provided wrong object type to finalizer, only supports MessagingUser") - } - user.Status.Phase = userv1beta1.UserTerminating - err := r.client.Status().Update(ctx, user) - if err != nil { - return reconcile.Result{}, err - } - - addressSpace, err := r.lookupAddressSpace(ctx, logger, user) - if err != nil { - // Do not block finalizer if address space is gone - if k8errors.IsNotFound(err) { - return reconcile.Result{}, nil - } - return reconcile.Result{}, err - } - - authenticationService, err := r.lookupAuthenticationService(ctx, logger, addressSpace) - if err != nil { - // Do not block finalizer if authentication service is gone - if k8errors.IsNotFound(err) { - return reconcile.Result{}, nil - } - return reconcile.Result{}, err - } - if authenticationService.Spec.Type == adminv1beta1.Standard && isAuthserviceAvailable(authenticationService) { - kcClient, err := r.getKeycloakClient(ctx, authenticationService) - if err != nil { - return reconcile.Result{}, nil - } - - realm := addressSpace.Annotations[ANNOTATION_REALM_NAME] - - user, err := kcClient.GetUser(realm, user.Spec.Username) - if err != nil { - return reconcile.Result{}, nil - } - if user != nil { - err = kcClient.DeleteUser(realm, user) - return reconcile.Result{}, err - } - return reconcile.Result{}, nil - } else { - logger.Info("Unable to finalize MessagingUser: authentication service not ready") - return reconcile.Result{Requeue: true}, nil - } - }, - }, - }) -} - -func (r *ReconcileMessagingUser) lookupAddressSpace(ctx context.Context, logger logr.Logger, user *userv1beta1.MessagingUser) (*enmassev1beta1.AddressSpace, error) { - addressSpace := &enmassev1beta1.AddressSpace{} - addressSpaceName := types.NamespacedName{ - Name: strings.Split(user.Name, ".")[0], - Namespace: user.Namespace, - } - err := r.client.Get(ctx, addressSpaceName, addressSpace) - if err != nil { - logger.Error(err, "Failed to get AddressSpace for MessagingUser") - return nil, err - } - return addressSpace, nil -} - -func (r *ReconcileMessagingUser) lookupAuthenticationService(ctx context.Context, logger logr.Logger, addressSpace *enmassev1beta1.AddressSpace) (*adminv1beta1.AuthenticationService, error) { - if addressSpace.Spec.AuthenticationService == nil { - return nil, fmt.Errorf("Authentication service not (yet) set for address space %s/%s", addressSpace.Namespace, addressSpace.Name) - } - authNameRef := addressSpace.Spec.AuthenticationService.Name - if authNameRef == "" { - authNameRef = addressSpace.Spec.AuthenticationService.Type - } - authenticationService := &adminv1beta1.AuthenticationService{} - authServiceName := types.NamespacedName{ - Name: authNameRef, - Namespace: r.namespace, - } - if authServiceName.Name == "" { - authServiceName.Name = addressSpace.Spec.AuthenticationService.Type - } - err := r.client.Get(ctx, authServiceName, authenticationService) - if err != nil { - logger.Error(err, "Failed to get AuthenticationService for MessagingUser") - return nil, err - } - return authenticationService, nil -} - -func (r *ReconcileMessagingUser) getKeycloakClient(ctx context.Context, authservice *adminv1beta1.AuthenticationService) (keycloak.KeycloakClient, error) { - existing := r.keycloakCache.get(authservice.Name) - if existing != nil { - return existing, nil - } else { - var ca []byte - if authservice.Status.CaCertSecret != nil { - caCertSecret := &corev1.Secret{} - err := r.client.Get(ctx, types.NamespacedName{ - Name: authservice.Status.CaCertSecret.Name, - Namespace: r.namespace, - }, caCertSecret) - if err != nil { - return nil, err - } - - ca = caCertSecret.Data["tls.crt"] - } - - credentials := &corev1.Secret{} - err := r.client.Get(ctx, types.NamespacedName{ - Name: authservice.Spec.Standard.CredentialsSecret.Name, - Namespace: r.namespace, - }, credentials) - if err != nil { - return nil, err - } - - // Handle wrong host used in older versions - host := authservice.Status.Host - if !strings.HasSuffix(host, fmt.Sprintf("%s.svc", authservice.Namespace)) { - host += "." + authservice.Namespace + ".svc" - } - - adminUser := credentials.Data["admin.username"] - adminPassword := credentials.Data["admin.password"] - kcClient, err := r.newKeycloakClientFunc(host, 8443, string(adminUser), string(adminPassword), ca) - if err != nil { - return nil, err - } - return r.keycloakCache.put(authservice.Name, kcClient), nil - } -} - -func isAuthserviceAvailable(authservice *adminv1beta1.AuthenticationService) bool { - return authservice.Status.Host != "" -} diff --git a/pkg/controller/messaginguser/messaginguser_controller_test.go b/pkg/controller/messaginguser/messaginguser_controller_test.go deleted file mode 100644 index ccb24df99d8..00000000000 --- a/pkg/controller/messaginguser/messaginguser_controller_test.go +++ /dev/null @@ -1,196 +0,0 @@ -/* - * Copyright 2019, EnMasse authors. - * License: Apache License 2.0 (see the file LICENSE or http://apache.org/licenses/LICENSE-2.0.html). - */ - -package messaginguser - -import ( - "context" - "testing" - - adminv1beta1 "github.com/enmasseproject/enmasse/pkg/apis/admin/v1beta1" - enmassev1beta1 "github.com/enmasseproject/enmasse/pkg/apis/enmasse/v1beta1" - userv1beta1 "github.com/enmasseproject/enmasse/pkg/apis/user/v1beta1" - keycloak "github.com/enmasseproject/enmasse/pkg/keycloak" - - "github.com/stretchr/testify/assert" - - corev1 "k8s.io/api/core/v1" - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - "k8s.io/apimachinery/pkg/runtime" - "k8s.io/apimachinery/pkg/types" - "k8s.io/client-go/kubernetes/scheme" - "sigs.k8s.io/controller-runtime/pkg/client/fake" - "sigs.k8s.io/controller-runtime/pkg/reconcile" -) - -func setup(t *testing.T) *ReconcileMessagingUser { - s := scheme.Scheme - - s.AddKnownTypes(adminv1beta1.SchemeGroupVersion, &adminv1beta1.AuthenticationService{}) - s.AddKnownTypes(adminv1beta1.SchemeGroupVersion, &enmassev1beta1.AddressSpace{}) - s.AddKnownTypes(adminv1beta1.SchemeGroupVersion, &userv1beta1.MessagingUser{}) - objs := []runtime.Object{ - &adminv1beta1.AuthenticationService{ - ObjectMeta: metav1.ObjectMeta{Namespace: "test", Name: "standard"}, - Spec: adminv1beta1.AuthenticationServiceSpec{ - Type: adminv1beta1.Standard, - Standard: &adminv1beta1.AuthenticationServiceSpecStandard{ - CredentialsSecret: &corev1.SecretReference{ - Name: "creds", - }, - }, - }, - Status: adminv1beta1.AuthenticationServiceStatus{ - Host: "example.com", - Port: 5671, - }, - }, - - &enmassev1beta1.AddressSpace{ - ObjectMeta: metav1.ObjectMeta{ - Namespace: "ns", - Name: "myspace", - Annotations: map[string]string{ - "enmasse.io/realm-name": "realm1", - }, - }, - Spec: enmassev1beta1.AddressSpaceSpec{ - Type: "standard", - Plan: "standard-small", - AuthenticationService: &enmassev1beta1.AuthenticationService{ - Name: "standard", - }, - }, - }, - &corev1.Secret{ - ObjectMeta: metav1.ObjectMeta{ - Name: "creds", - Namespace: "test", - }, - Data: map[string][]byte{ - "admin.username": []byte("admin"), - "admin.password": []byte("admin"), - }, - }, - } - - cl := fake.NewFakeClientWithScheme(s, objs...) - r := &ReconcileMessagingUser{ - client: cl, - reader: cl, - newKeycloakClientFunc: func(host string, port int, user string, password string, cert []byte) (keycloak.KeycloakClient, error) { - return &keycloak.FakeClient{ - Users: map[string][]*userv1beta1.MessagingUser{ - "realm1": make([]*userv1beta1.MessagingUser, 0), - }, - }, nil - - }, - keycloakCache: NewKeycloakCache(), - namespace: "test", - - scheme: s, - } - - return r -} - -func buildTestUser(name string, username string) *userv1beta1.MessagingUser { - return &userv1beta1.MessagingUser{ - ObjectMeta: metav1.ObjectMeta{Namespace: "ns", Name: name}, - Spec: userv1beta1.MessagingUserSpec{ - Username: username, - Authentication: userv1beta1.AuthenticationSpec{ - Type: userv1beta1.Password, - Password: []byte("secret!"), - }, - Authorization: []userv1beta1.AuthorizationSpec{ - userv1beta1.AuthorizationSpec{ - Operations: []userv1beta1.AuthorizationOperation{ - userv1beta1.Send, - userv1beta1.Recv, - userv1beta1.Manage, - }, - Addresses: []string{ - "*", - }, - }, - }, - }, - } -} - -func getFake(client keycloak.KeycloakClient) *keycloak.FakeClient { - return client.(*cachedClient).wrapped.(*keycloak.FakeClient) -} - -func TestReconcile(t *testing.T) { - r := setup(t) - - user := buildTestUser("myspace.test", "test") - - err := r.client.Create(context.TODO(), user) - assert.Nil(t, err) - - userType := types.NamespacedName{ - Name: user.Name, - Namespace: user.Namespace, - } - - // First iteration should add the finalizer - result, err := r.Reconcile(reconcile.Request{NamespacedName: userType}) - assert.Nil(t, err, "Unexpected reconcile error") - assert.False(t, result.Requeue) - - // Refetch - err = r.client.Get(context.TODO(), userType, user) - assert.Nil(t, err) - assert.Contains(t, user.ObjectMeta.Finalizers, FINALIZER_NAME) - - result, err = r.Reconcile(reconcile.Request{NamespacedName: userType}) - assert.Nil(t, err, "Unexpected reconcile error") - assert.False(t, result.Requeue) - - // Refetch - err = r.client.Get(context.TODO(), userType, user) - assert.Nil(t, err) - - client := r.keycloakCache.get("standard") - if assert.NotNil(t, client, "Unable to find expected keycloak client") { - - usermap := getFake(client).Users - userlist, ok := usermap["realm1"] - - if assert.True(t, ok, "Unable to find expected realm in fake client") { - assert.Equal(t, 1, len(userlist), "Unexpected length of user list") - assert.Equal(t, *user, *userlist[0], "Stored used does not equal reconciled user") - } - } - - // Update user - user2 := *user - user2.Spec.Authentication.Password = []byte("other") - assert.NotEqual(t, user2, *user) - err = r.client.Update(context.TODO(), &user2) - assert.Nil(t, err) - assert.Equal(t, *getFake(r.keycloakCache.get("standard")).Users["realm1"][0], *user) - - result, err = r.Reconcile(reconcile.Request{NamespacedName: userType}) - assert.Nil(t, err, "Unexpected reconcile error") - assert.False(t, result.Requeue) - assert.NotEqual(t, *getFake(r.keycloakCache.get("standard")).Users["realm1"][0], *user) - assert.Equal(t, *getFake(r.keycloakCache.get("standard")).Users["realm1"][0], user2) - - // Delete - now := metav1.Now() - user2.ObjectMeta.SetDeletionTimestamp(&now) - err = r.client.Update(context.TODO(), &user2) - assert.Nil(t, err) - - result, err = r.Reconcile(reconcile.Request{NamespacedName: userType}) - assert.Nil(t, err, "Unexpected reconcile error") - assert.False(t, result.Requeue) - assert.Equal(t, 0, len(getFake(r.keycloakCache.get("standard")).Users["realm1"])) -} diff --git a/pkg/controller/upgrader/upgrader.go b/pkg/controller/upgrader/upgrader.go index 0db8a475b55..6e1b0e62962 100644 --- a/pkg/controller/upgrader/upgrader.go +++ b/pkg/controller/upgrader/upgrader.go @@ -6,41 +6,16 @@ package upgrader import ( - "errors" - "fmt" - "strconv" - "strings" - "time" - - adminv1beta1 "github.com/enmasseproject/enmasse/pkg/apis/admin/v1beta1" - adminv1beta1_client "github.com/enmasseproject/enmasse/pkg/client/clientset/versioned/typed/admin/v1beta1" - userv1beta1_client "github.com/enmasseproject/enmasse/pkg/client/clientset/versioned/typed/user/v1beta1" - deployer "github.com/enmasseproject/enmasse/pkg/controller/address_space_controller" - "github.com/enmasseproject/enmasse/pkg/keycloak" "github.com/enmasseproject/enmasse/pkg/util" - - appsv1 "k8s.io/api/apps/v1" - corev1 "k8s.io/api/core/v1" - k8errors "k8s.io/apimachinery/pkg/api/errors" - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - labels "k8s.io/apimachinery/pkg/labels" "k8s.io/apimachinery/pkg/runtime" kubernetes "k8s.io/client-go/kubernetes" - corev1client "k8s.io/client-go/kubernetes/typed/core/v1" rest "k8s.io/client-go/rest" - "k8s.io/client-go/util/retry" - aggregatorclient "k8s.io/kube-aggregator/pkg/client/clientset_generated/clientset" - apiserviceclient "k8s.io/kube-aggregator/pkg/client/clientset_generated/clientset/typed/apiregistration/v1" "sigs.k8s.io/controller-runtime/pkg/manager" logf "sigs.k8s.io/controller-runtime/pkg/runtime/log" ) var log = logf.Log.WithName("upgrader") -const ADDRESS_SPACE_CONTROLLER_NAME = "address-space-controller" -const ANNOTATION_VERSION = "enmasse.io/version" -const ENV_VERSION = "VERSION" - type Upgrader struct { client *kubernetes.Clientset config *rest.Config @@ -63,375 +38,5 @@ func New(mgr manager.Manager) (*Upgrader, error) { } func (u *Upgrader) Upgrade() error { - - deploymentClient := u.client.AppsV1().Deployments(u.namespace) - - var deployment *appsv1.Deployment - deployment, err := deploymentClient.Get(ADDRESS_SPACE_CONTROLLER_NAME, metav1.GetOptions{}) - if err != nil && k8errors.IsNotFound(err) { - return nil - } else if err != nil { - return err - } else { - if _, ok := deployment.Annotations[ANNOTATION_VERSION]; !ok { - log.Info("address-space-controller is missing annotation, initiating upgrade") - err = u.performUpgrade(deployment) - if err != nil { - return err - } - } - } - return nil -} - -func (u *Upgrader) performUpgrade(addressSpaceControllerDeployment *appsv1.Deployment) error { - deploymentClient := u.client.AppsV1().Deployments(u.namespace) - - // Scale down iot-operator - err := u.scale("iot-operator", 0) - if err != nil { - return err - } - - // Scale down api-server - err = u.scale("api-server", 0) - if err != nil { - return err - } - - // Scale down address-space-controller - err = u.scaleDeployment(addressSpaceControllerDeployment, 0) - if err != nil { - return err - } - - // Scale down admin pods - admins, err := deploymentClient.List(metav1.ListOptions{ - LabelSelector: "name=admin", - }) - if err != nil { - return err - } - for _, adminDeployment := range admins.Items { - err = u.scaleDeployment(&adminDeployment, 0) - if err != nil { - return err - } - } - - // Scale down agent pods - agents, err := deploymentClient.List(metav1.ListOptions{ - LabelSelector: "role=agent", - }) - if err != nil { - return err - } - for _, agentDeployment := range agents.Items { - err = u.scaleDeployment(&agentDeployment, 0) - if err != nil { - return err - } - } - - // Get rid of API service so that it doesn't prevent upgrade - aggClient, err := aggregatorclient.NewForConfig(u.config) - if err != nil { - return err - } - apiServiceClient := aggClient.ApiregistrationV1().APIServices() - - log.Info("Deleting v1beta1.enmasse.io api service") - err = deleteApiServiceIfPresent(apiServiceClient, "v1beta1.enmasse.io") - if err != nil { - return err - } - - log.Info("Deleting v1alpha1.enmasse.io api service") - err = deleteApiServiceIfPresent(apiServiceClient, "v1alpha1.enmasse.io") - if err != nil { - return err - } - - log.Info("Deleting v1beta1.user.enmasse.io api service") - err = deleteApiServiceIfPresent(apiServiceClient, "v1beta1.user.enmasse.io") - if err != nil { - return err - } - - log.Info("Deleting v1alpha1.user.enmasse.io api service") - err = deleteApiServiceIfPresent(apiServiceClient, "v1alpha1.user.enmasse.io") - if err != nil { - return err - } - - // Create MessagingUser CRs for all users in all realms - err = u.convertMessagingUsers() - if err != nil { - return err - } - - // Delete api-server service - err = u.deleteServiceIfExists("api-server") - if err != nil { - return err - } - - // Delete api-server deployment - err = u.deleteDeploymentIfExists("api-server") - if err != nil { - return err - } - - // Scale address-space-controller back up. Retry in case the deployment got modified while we were scaling down - err = retry.RetryOnConflict(retry.DefaultRetry, func() error { - // Update address-space-controller deployment object to new version - err = deployer.ApplyDeployment(addressSpaceControllerDeployment) - if err != nil { - return err - } - - // Apply new configuration and wait for it to scale up - return u.scaleDeployment(addressSpaceControllerDeployment, 1) - }) - if err != nil { - return err - } - - // Delete iot-operator deployment - return u.deleteDeploymentIfExists("iot-operator") -} - -func (u *Upgrader) convertMessagingUsers() error { - adminClient, err := adminv1beta1_client.NewForConfig(u.config) - if err != nil { - return err - } - userClient, err := userv1beta1_client.NewForConfig(u.config) - if err != nil { - return err - } - secretClient := u.client.CoreV1().Secrets(u.namespace) - if err != nil { - return err - } - list, err := adminClient.AuthenticationServices(u.namespace).List(metav1.ListOptions{}) - if err != nil { - return err - } - for _, authenticationService := range list.Items { - if authenticationService.Spec.Type == adminv1beta1.Standard { - var ca []byte - if authenticationService.Status.CaCertSecret != nil { - caCertSecret, err := secretClient.Get(authenticationService.Status.CaCertSecret.Name, metav1.GetOptions{}) - if err != nil { - log.Error(err, "Getting authentication service CA") - return err - } - ca = caCertSecret.Data["tls.crt"] - } - - credentials, err := secretClient.Get(authenticationService.Spec.Standard.CredentialsSecret.Name, metav1.GetOptions{}) - if err != nil { - log.Error(err, "Getting authentication service credentials") - return err - } - - adminUser := credentials.Data["admin.username"] - adminPassword := credentials.Data["admin.password"] - // Handle wrong host used in older versions - host := authenticationService.Status.Host - if !strings.HasSuffix(host, fmt.Sprintf("%s.svc", authenticationService.Namespace)) { - host += "." + authenticationService.Namespace + ".svc" - } - kcClient, err := keycloak.NewClient(host, 8443, string(adminUser), string(adminPassword), ca) - if err != nil { - log.Error(err, "Creating keycloak client") - return err - } - - realms, err := kcClient.GetRealms() - if err != nil { - log.Error(err, "Getting realms from keycloak") - return err - } - - for _, realm := range realms { - if realm != "master" { - log.Info("Migrating users in authentication service", "realm", realm) - users, err := kcClient.GetUsers(realm, func(name string, values []string) bool { - return name != keycloak.ATTR_FROM_CRD - }) - if err != nil { - log.Error(err, "Getting users from keycloak", "realm", realm) - return err - } - for _, user := range users { - log.Info("Migrating user", "name", user.Name, "namespace", user.Namespace) - _, err = userClient.MessagingUsers(user.Namespace).Get(user.Name, metav1.GetOptions{}) - if err != nil { - if k8errors.IsNotFound(err) { - _, err = userClient.MessagingUsers(user.Namespace).Create(user) - if err != nil { - log.Error(err, "Error creating messaginguser") - return err - } - log.Info("User migrated successfully", "name", user.Name, "namespace", user.Namespace) - } else { - log.Error(err, "Error getting messaginguser") - return err - } - } else { - log.Info("User already migrated! Skipping...", "name", user.Name, "namespace", user.Namespace) - } - } - } - } - - } - } - return nil -} - -func (u *Upgrader) deleteServiceIfExists(name string) error { - propagationPolicy := metav1.DeletePropagationBackground - serviceClient := u.client.CoreV1().Services(u.namespace) - _, err := serviceClient.Get(name, metav1.GetOptions{}) - if err != nil && k8errors.IsNotFound(err) { - return nil - } else if err != nil { - return err - } else { - return serviceClient.Delete(name, &metav1.DeleteOptions{ - PropagationPolicy: &propagationPolicy, - }) - } -} - -func (u *Upgrader) deleteDeployment(name string) error { - deploymentClient := u.client.AppsV1().Deployments(u.namespace) - propagationPolicy := metav1.DeletePropagationBackground - return deploymentClient.Delete(name, &metav1.DeleteOptions{ - PropagationPolicy: &propagationPolicy, - }) -} - -func (u *Upgrader) deleteDeploymentIfExists(deploymentName string) error { - deploymentClient := u.client.AppsV1().Deployments(u.namespace) - - _, err := deploymentClient.Get(deploymentName, metav1.GetOptions{}) - if err != nil && k8errors.IsNotFound(err) { - return nil - } else if err != nil { - return err - } else { - return u.deleteDeployment(deploymentName) - } -} - -func (u *Upgrader) scale(deploymentName string, replicas int32) error { - deploymentClient := u.client.AppsV1().Deployments(u.namespace) - - deployment, err := deploymentClient.Get(deploymentName, metav1.GetOptions{}) - if err != nil && k8errors.IsNotFound(err) { - return nil - } else if err != nil { - return err - } else { - return u.scaleDeployment(deployment, replicas) - } -} - -func (u *Upgrader) scaleDeployment(deployment *appsv1.Deployment, replicas int32) error { - podClient := u.client.CoreV1().Pods(u.namespace) - deploymentClient := u.client.AppsV1().Deployments(u.namespace) - - log.Info("Downscaling", "deployment", deployment.Name) - deployment.Spec.Replicas = &replicas - _, err := deploymentClient.Update(deployment) - if err != nil { - return err - } - err = waitForReplicas(podClient, deployment, int(replicas)) - if err != nil { - return err - } - log.Info("Scaled down", "deployment", deployment.Name) - return nil -} - -func deleteApiServiceIfPresent(client apiserviceclient.APIServiceInterface, name string) error { - _, err := client.Get(name, metav1.GetOptions{}) - if err != nil { - if k8errors.IsNotFound(err) { - return nil - } - return err - } - - dopts := metav1.DeleteOptions{} - return client.Delete(name, &dopts) -} - -func getBooleanAnnotationValue(Annotations map[string]string, name string) bool { - if Annotations != nil { - if sval, ok := Annotations[name]; ok { - if bval, ok := strconv.ParseBool(sval); ok == nil && bval { - return bval - } - } - } - return false -} - -func waitForReplicas(podClient corev1client.PodInterface, deployment *appsv1.Deployment, expectedReplicas int) error { - - labelMap, err := metav1.LabelSelectorAsMap(deployment.Spec.Selector) - if err != nil { - return err - } - selector := labels.SelectorFromSet(labelMap).String() - - log.Info(fmt.Sprintf("Looking for pods with label selector %s", selector)) - opts := metav1.ListOptions{ - LabelSelector: selector, - } - - // Set a long timeout like 15 minutes to prevent from looping forever in case of errors - var timeout time.Duration = 15 * time.Minute - now := time.Now() - end := now.Add(timeout) - - for now.Before(end) { - list, err := podClient.List(opts) - numReady := 0 - if err == nil && len(list.Items) == expectedReplicas { - numReady := 0 - for _, pod := range list.Items { - if pod.Status.Phase == corev1.PodRunning { - for _, condition := range pod.Status.Conditions { - if condition.Type == corev1.PodReady && - condition.Status == corev1.ConditionTrue { - numReady += 1 - break - } - } - } - } - if numReady == expectedReplicas { - return nil - } - } - log.Info(fmt.Sprintf("Found %d ready pods (expected %d)", numReady, expectedReplicas)) - time.Sleep(5 * time.Second) - now = time.Now() - } - - list, err := podClient.List(opts) - if err != nil { - return err - } - if len(list.Items) != expectedReplicas { - return errors.New(fmt.Sprintf("Deployment %s has %d replicas. Expected: %d", deployment.Name, len(list.Items), expectedReplicas)) - } return nil } diff --git a/pkg/state/errors/errors.go b/pkg/state/errors/errors.go index ca0b8c387ea..957855260d6 100644 --- a/pkg/state/errors/errors.go +++ b/pkg/state/errors/errors.go @@ -9,6 +9,8 @@ import ( ) var ( + BrokerInUseError error = fmt.Errorf("broker in use") + TenantNotFoundError error = fmt.Errorf("tenant not found") NotInitializedError error = fmt.Errorf("not initialized") NotSyncedError error = fmt.Errorf("not synchronized") NoEndpointsError error = fmt.Errorf("no endpoints") diff --git a/pkg/state/infra.go b/pkg/state/infra.go index 6b99e2b7d23..2e552cc9791 100644 --- a/pkg/state/infra.go +++ b/pkg/state/infra.go @@ -19,7 +19,9 @@ import ( . "github.com/enmasseproject/enmasse/pkg/state/common" . "github.com/enmasseproject/enmasse/pkg/state/errors" . "github.com/enmasseproject/enmasse/pkg/state/router" - "k8s.io/apimachinery/pkg/runtime" + sched "github.com/enmasseproject/enmasse/pkg/state/scheduler" + + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "golang.org/x/sync/errgroup" ) @@ -38,9 +40,11 @@ const maxBatchSize int = 50 // TODO - Unit test of address and endpoint management type request struct { done chan error - resource runtime.Object + resource metav1.Object } +var defaultScheduler = sched.NewDummyScheduler() + type infraClient struct { // The known routers and brokers. All configuration is synchronized with these. If their connections get reset, // state is re-synced. If new routers and brokers are created, their configuration will be synced as well. @@ -65,10 +69,11 @@ type infraClient struct { deleterStop chan bool deleterStopped chan bool - // Endpoints and addresses known to this client. These provide a consistent view of addresses and endpoints + // Tenants, endpoints and addresses known to this client. These provide a consistent view of addresses and endpoints // between infra, endpoint and address controllers that may attempt to modify the state at the same time. addresses map[resourceKey]*v1beta2.MessagingAddress endpoints map[resourceKey]*v1beta2.MessagingEndpoint + tenants map[resourceKey]*v1beta2.MessagingTenant // Factory classes to make it possible to inject alternative clients for configuring routers and brokers. routerStateFactory routerStateFunc @@ -94,6 +99,7 @@ func NewInfra(routerFactory routerStateFunc, brokerFactory brokerStateFunc, cloc hostMap: make(map[string]Host, 0), addresses: make(map[resourceKey]*v1beta2.MessagingAddress, 0), endpoints: make(map[resourceKey]*v1beta2.MessagingEndpoint, 0), + tenants: make(map[resourceKey]*v1beta2.MessagingTenant, 0), syncRequests: make(chan *request, syncBufferSize), syncerStop: make(chan bool), syncerStopped: make(chan bool), @@ -207,7 +213,7 @@ func (i *infraClient) updateRouters(hosts []Host) { } } -func (i *infraClient) updateBrokers(ctx context.Context, hosts []Host) error { +func (i *infraClient) updateBrokers(ctx context.Context, hosts []Host, removeRouterConnectors bool) error { toAdd := make(map[string]Host, 0) for _, host := range hosts { toAdd[host.Hostname] = host @@ -235,7 +241,7 @@ func (i *infraClient) updateBrokers(ctx context.Context, hosts []Host) error { log.Printf("Removing broker %+v", host) _, foundAdded := toAdd[hostname] // If it was found, it means the ip changed, but the host remains stable so there is no need to delete the connector. - if !foundAdded { + if !foundAdded && removeRouterConnectors { err := i.applyRouters(ctx, func(r *RouterState) error { router := r return router.DeleteEntities(ctx, []RouterEntity{&NamedEntity{EntityType: RouterConnectorEntity, Name: connectorName(i.brokers[host])}}) @@ -285,6 +291,42 @@ func (i *infraClient) initialize(ctx context.Context) error { }) } +func (i *infraClient) DeleteBroker(host string) error { + i.lock.Lock() + defer i.lock.Unlock() + + var state *BrokerState + newBrokers := make([]Host, 0) + for _, brokerState := range i.brokers { + if brokerState.Host().Hostname == host { + state = brokerState + } else { + newBrokers = append(newBrokers, brokerState.Host()) + } + } + if state == nil { + return nil + } + + for _, tenant := range i.tenants { + if tenant.Status.Broker != nil && tenant.Status.Broker.Host == host { + return BrokerInUseError + } + } + + for _, address := range i.addresses { + for _, broker := range address.Status.Brokers { + if broker.Host == host { + return BrokerInUseError + } + } + } + + // Not in use, so we can safely delete the broker + ctx := context.Background() + return i.updateBrokers(ctx, newBrokers, false) +} + func (i *infraClient) SyncAll(routers []Host, brokers []Host, tlsConfig *tls.Config) ([]ConnectorStatus, error) { log.Printf("Syncing with routers: %+v, and brokers: %+v", routers, brokers) i.lock.Lock() @@ -295,7 +337,7 @@ func (i *infraClient) SyncAll(routers []Host, brokers []Host, tlsConfig *tls.Con i.updateRouters(routers) - err := i.updateBrokers(ctx, brokers) + err := i.updateBrokers(ctx, brokers, true) if err != nil { return nil, err } @@ -458,7 +500,27 @@ func (i *infraClient) syncConfiguration(ctx context.Context) error { return i.syncEntities(ctx, routerEntities, brokerEntities) } -func (i *infraClient) ScheduleAddress(address *v1beta2.MessagingAddress, scheduler Scheduler) error { +func (i *infraClient) ScheduleTenant(tenant *v1beta2.MessagingTenant) error { + + i.lock.Lock() + defer i.lock.Unlock() + + if !i.initialized { + return NotInitializedError + } + + brokers := make([]*BrokerState, 0) + for _, broker := range i.brokers { + brokers = append(brokers, broker) + } + + scheduler := defaultScheduler + // Use the assigned tenant broker if set + return scheduler.ScheduleTenant(tenant, brokers) +} + +func (i *infraClient) ScheduleAddress(address *v1beta2.MessagingAddress) error { + i.lock.Lock() defer i.lock.Unlock() @@ -466,12 +528,24 @@ func (i *infraClient) ScheduleAddress(address *v1beta2.MessagingAddress, schedul return NotInitializedError } + tenant, ok := i.tenants[resourceKey{Name: "default", Namespace: address.GetNamespace()}] + if !ok { + return TenantNotFoundError + } + brokers := make([]*BrokerState, 0) for _, broker := range i.brokers { brokers = append(brokers, broker) } - return scheduler.ScheduleAddress(address, brokers) + // Use the assigned tenant broker if set + if tenant.Status.Broker != nil { + address.Status.Brokers = []v1beta2.MessagingAddressBroker{*tenant.Status.Broker} + return nil + } else { + scheduler := defaultScheduler + return scheduler.ScheduleAddress(address, brokers) + } } func (i *infraClient) applyRouters(ctx context.Context, fn func(router *RouterState) error) error { @@ -514,12 +588,10 @@ func (i *infraClient) getActiveEndpoints() map[string][]*v1beta2.MessagingEndpoi return activeEndpoints } -func (i *infraClient) SyncAddress(address *v1beta2.MessagingAddress) error { - +func (i *infraClient) requestSync(object metav1.Object) error { // Request sync - log.Printf("Syncing address %s/%s", address.Namespace, address.Name) req := &request{ - resource: address, + resource: object, done: make(chan error, 1), } select { @@ -531,19 +603,21 @@ func (i *infraClient) SyncAddress(address *v1beta2.MessagingAddress) error { } } +func (i *infraClient) SyncAddress(address *v1beta2.MessagingAddress) error { + + // Request sync + log.Printf("Syncing address %s/%s", address.Namespace, address.Name) + return i.requestSync(address) +} + func (i *infraClient) SyncEndpoint(endpoint *v1beta2.MessagingEndpoint) error { log.Printf("Syncing endpoint %s/%s", endpoint.Namespace, endpoint.Name) - req := &request{ - resource: endpoint, - done: make(chan error, 1), - } - select { - case i.syncRequests <- req: - return <-req.done - default: - close(req.done) - return NotSyncedError - } + return i.requestSync(endpoint) +} + +func (i *infraClient) SyncTenant(tenant *v1beta2.MessagingTenant) error { + log.Printf("Syncing tenant %s/%s", tenant.Namespace, tenant.Name) + return i.requestSync(tenant) } func (i *infraClient) AllocatePorts(endpoint *v1beta2.MessagingEndpoint, protocols []v1beta2.MessagingEndpointProtocol) error { @@ -647,26 +721,47 @@ func (i *infraClient) doSync() { return } - builtRequests, routerEntities, brokerEntities := i.buildEntities(toSync) + valid := make([]*request, 0, len(toSync)) + for _, req := range toSync { + switch req.resource.(type) { + case *v1beta2.MessagingAddress: + if _, ok := i.tenants[resourceKey{Name: "default", Namespace: req.resource.GetNamespace()}]; !ok { + req.done <- fmt.Errorf("tenant not synced for %s/%s", req.resource.GetNamespace(), req.resource.GetName()) + continue + } + valid = append(valid, req) + case *v1beta2.MessagingEndpoint: + if _, ok := i.tenants[resourceKey{Name: "default", Namespace: req.resource.GetNamespace()}]; !ok { + req.done <- fmt.Errorf("tenant not synced for %s/%s", req.resource.GetNamespace(), req.resource.GetName()) + continue + } + valid = append(valid, req) + default: + valid = append(valid, req) + } + } + + builtRequests, routerEntities, brokerEntities := i.buildEntities(valid) log.Printf("Syncing %d router entities", len(routerEntities)) ctx := context.Background() err := i.syncEntities(ctx, routerEntities, brokerEntities) for _, req := range builtRequests { - var key resourceKey + key := resourceKey{Name: req.resource.GetName(), Namespace: req.resource.GetNamespace()} switch v := req.resource.(type) { case *v1beta2.MessagingAddress: - address := req.resource.(*v1beta2.MessagingAddress) - key = resourceKey{Name: address.Name, Namespace: address.Namespace} if err == nil { - i.addresses[key] = address + i.addresses[key] = v } req.done <- err case *v1beta2.MessagingEndpoint: - endpoint := req.resource.(*v1beta2.MessagingEndpoint) - key = resourceKey{Name: endpoint.Name, Namespace: endpoint.Namespace} if err == nil { - i.endpoints[key] = endpoint + i.endpoints[key] = v + } + req.done <- err + case *v1beta2.MessagingTenant: + if err == nil { + i.tenants[key] = v } req.done <- err default: @@ -717,7 +812,7 @@ func (i *infraClient) buildEntities(requests []*request) (built []*request, rout for _, req := range requests { switch v := req.resource.(type) { case *v1beta2.MessagingAddress: - address := req.resource.(*v1beta2.MessagingAddress) + address := v endpoints := activeEndpoints[address.Namespace] if endpoints == nil { req.done <- NoEndpointsError @@ -755,7 +850,7 @@ func (i *infraClient) buildEntities(requests []*request) (built []*request, rout } case *v1beta2.MessagingEndpoint: - endpoint := req.resource.(*v1beta2.MessagingEndpoint) + endpoint := v result, err := i.buildRouterEndpointEntities(endpoint) if err != nil { req.done <- err @@ -763,6 +858,15 @@ func (i *infraClient) buildEntities(requests []*request) (built []*request, rout } routerEntities = append(routerEntities, result...) built = append(built, req) + case *v1beta2.MessagingTenant: + tenant := v + result, err := i.buildRouterTenantEntities(tenant) + if err != nil { + req.done <- err + continue + } + routerEntities = append(routerEntities, result...) + built = append(built, req) default: req.done <- fmt.Errorf("unknown resource type %T", v) } @@ -776,6 +880,12 @@ func isEndpointActive(endpoint *v1beta2.MessagingEndpoint) bool { return endpoint.Status.Phase == v1beta2.MessagingEndpointActive && endpoint.Status.Host != "" } +func (i *infraClient) buildRouterTenantEntities(tenant *v1beta2.MessagingTenant) ([]RouterEntity, error) { + routerEntities := make([]RouterEntity, 0) + // TODO: Create vhost policy based on status (plan settings) + return routerEntities, nil +} + func (i *infraClient) buildRouterEndpointEntities(endpoint *v1beta2.MessagingEndpoint) ([]RouterEntity, error) { // TODO: Make configurable // linkCapacity := 20 @@ -842,8 +952,38 @@ func (i *infraClient) buildRouterAddressEntities(endpoint *v1beta2.MessagingEndp routerEntities := make([]RouterEntity, 0) + tenant := i.tenants[resourceKey{Name: "default", Namespace: address.Namespace}] tenantId := endpoint.Status.Host + // If transactional, rely on link route to be created + // TODO: Move this to buildRouterTenantEntities once endpoint is not needed. + if tenant.Status.Broker != nil { + // Transactional means that we create a link route based on the tenant prefix, + // and all broker addresses go through that route. + log.Println("Tenant borkre set, using ") + broker := *tenant.Status.Broker + host := i.hostMap[broker.Host] + brokerState := i.brokers[host] + if brokerState == nil { + return nil, fmt.Errorf("unable to configure transactional linkroute (tenant %s) for unknown broker %s", tenantId, broker.Host) + } + connector := connectorName(brokerState) + routerEntities = append(routerEntities, &RouterLinkRoute{ + Name: globalLinkRouteName(tenantId, host.Hostname, "out"), + Prefix: tenantId, + Direction: "out", + Connection: connector, + }) + + routerEntities = append(routerEntities, &RouterLinkRoute{ + Name: globalLinkRouteName(tenantId, host.Hostname, "in"), + Prefix: tenantId, + Direction: "in", + Connection: connector, + }) + return routerEntities, nil + } + // Build desired state addressName := addressName(tenantId, address) fullAddress := qualifiedAddress(tenantId, address.GetAddress()) @@ -1130,6 +1270,22 @@ func (i *infraClient) DeleteAddress(address *v1beta2.MessagingAddress) error { return NotDeletedError } } + +func (i *infraClient) DeleteTenant(tenant *v1beta2.MessagingTenant) error { + log.Printf("Deleting tenant %s/%s", tenant.Namespace, tenant.Name) + req := &request{ + resource: tenant, + done: make(chan error, 1), + } + select { + case i.deleteRequests <- req: + return <-req.done + default: + close(req.done) + return NotDeletedError + } +} + func (i *infraClient) doDelete() { toDelete := i.collectRequests(i.deleteRequests) if len(toDelete) == 0 { @@ -1146,18 +1302,28 @@ func (i *infraClient) doDelete() { return } + checkDelete := func(resource metav1.Object) error { + var err error + for key, _ := range i.addresses { + if resource.GetNamespace() == key.Namespace { + err = fmt.Errorf("resource still in use by addresses") + break + } + } + return err + } valid := make([]*request, 0, len(toDelete)) for _, req := range toDelete { switch req.resource.(type) { - case *v1beta2.MessagingEndpoint: - endpoint := req.resource.(*v1beta2.MessagingEndpoint) - var err error - for key, _ := range i.addresses { - if endpoint.Namespace == key.Namespace { - err = fmt.Errorf("endpoint still in use by addresses") - break - } + case *v1beta2.MessagingTenant: + err := checkDelete(req.resource) + if err != nil { + req.done <- err + continue } + valid = append(valid, req) + case *v1beta2.MessagingEndpoint: + err := checkDelete(req.resource) if err != nil { req.done <- err continue @@ -1173,26 +1339,28 @@ func (i *infraClient) doDelete() { ctx := context.Background() err := i.deleteEntities(ctx, routerEntities, brokerEntities) for _, req := range builtRequests { - var key resourceKey + key := resourceKey{Name: req.resource.GetName(), Namespace: req.resource.GetNamespace()} switch v := req.resource.(type) { case *v1beta2.MessagingAddress: - address := req.resource.(*v1beta2.MessagingAddress) - key = resourceKey{Name: address.Name, Namespace: address.Namespace} if err == nil { delete(i.addresses, key) - log.Printf("Deleted address %s/%s", address.Namespace, address.Name) + log.Printf("Deleted address %s/%s", key.Namespace, key.Name) } req.done <- err case *v1beta2.MessagingEndpoint: - endpoint := req.resource.(*v1beta2.MessagingEndpoint) - key = resourceKey{Name: endpoint.Name, Namespace: endpoint.Namespace} - + endpoint := v if err == nil { i.freePortsInternal(endpoint) delete(i.endpoints, key) log.Printf("Deleted endpoint %s/%s", endpoint.Namespace, endpoint.Name) } req.done <- err + case *v1beta2.MessagingTenant: + if err == nil { + delete(i.tenants, key) + log.Printf("Deleted tenant %s/%s", key.Namespace, key.Name) + } + req.done <- err default: req.done <- fmt.Errorf("unknown resource type %T", v) } @@ -1267,8 +1435,12 @@ func autoLinkName(tenantId string, address *v1beta2.MessagingAddress, host strin return fmt.Sprintf("autoLink-%s-%s-%s-%s", tenantId, address.Name, host, direction) } +func globalLinkRouteName(tenantId string, host string, direction string) string { + return fmt.Sprintf("linkRoute-%s-%s-%s", tenantId, host, direction) +} + func linkRouteName(tenantId string, address *v1beta2.MessagingAddress, host string, direction string) string { - return fmt.Sprintf("autoLink-%s-%s-%s-%s", tenantId, address.Name, host, direction) + return fmt.Sprintf("linkRoute-%s-%s-%s-%s", tenantId, address.Name, host, direction) } func addressName(tenantId string, address *v1beta2.MessagingAddress) string { diff --git a/pkg/state/infra_test.go b/pkg/state/infra_test.go index 7732fa78d54..e0933b4e939 100644 --- a/pkg/state/infra_test.go +++ b/pkg/state/infra_test.go @@ -57,14 +57,14 @@ func TestUpdateBrokers(t *testing.T) { }, &testClock{}) assert.NotNil(t, i) - err := i.updateBrokers(context.TODO(), []Host{{Hostname: "b1.example.com", Ip: "10.0.0.1"}, {Hostname: "b2.example.com", Ip: "10.0.0.2"}}) + err := i.updateBrokers(context.TODO(), []Host{{Hostname: "b1.example.com", Ip: "10.0.0.1"}, {Hostname: "b2.example.com", Ip: "10.0.0.2"}}, true) assert.Nil(t, err) assert.Equal(t, 2, len(i.brokers)) assert.Equal(t, 2, len(i.hostMap)) assertBroker(t, i, "b1.example.com", "10.0.0.1") assertBroker(t, i, "b2.example.com", "10.0.0.2") - err = i.updateBrokers(context.TODO(), []Host{{Hostname: "b1.example.com", Ip: "10.0.0.3"}, {Hostname: "b2.example.com", Ip: "10.0.0.2"}}) + err = i.updateBrokers(context.TODO(), []Host{{Hostname: "b1.example.com", Ip: "10.0.0.3"}, {Hostname: "b2.example.com", Ip: "10.0.0.2"}}, true) assert.Nil(t, err) assertBroker(t, i, "b1.example.com", "10.0.0.3") assertBroker(t, i, "b2.example.com", "10.0.0.2") diff --git a/pkg/state/scheduler/dummy.go b/pkg/state/scheduler/dummy.go new file mode 100644 index 00000000000..1069851b972 --- /dev/null +++ b/pkg/state/scheduler/dummy.go @@ -0,0 +1,51 @@ +/* + * Copyright 2020, EnMasse authors. + * License: Apache License 2.0 (see the file LICENSE or http://apache.org/licenses/LICENSE-2.0.html). + */ + +package scheduler + +import ( + "fmt" + + "github.com/enmasseproject/enmasse/pkg/apis/enmasse/v1beta2" + "github.com/enmasseproject/enmasse/pkg/state/broker" +) + +/* + * Very dumb scheduler that doesn't look at broker capacity. + */ +type dummyScheduler struct { +} + +var _ Scheduler = &dummyScheduler{} + +func NewDummyScheduler() Scheduler { + return &dummyScheduler{} +} + +func (s *dummyScheduler) ScheduleTenant(tenant *v1beta2.MessagingTenant, brokers []*broker.BrokerState) error { + if len(brokers) > 0 { + broker := brokers[0] + tenant.Status.Broker = &v1beta2.MessagingAddressBroker{ + State: v1beta2.MessagingAddressBrokerScheduled, + Host: broker.Host().Hostname, + } + } else { + return fmt.Errorf("no brokers available") + } + return nil +} + +func (s *dummyScheduler) ScheduleAddress(address *v1beta2.MessagingAddress, brokers []*broker.BrokerState) error { + if len(brokers) > 0 { + broker := brokers[0] + address.Status.Brokers = append(address.Status.Brokers, v1beta2.MessagingAddressBroker{ + State: v1beta2.MessagingAddressBrokerScheduled, + Host: broker.Host().Hostname, + }) + } else { + return fmt.Errorf("no brokers available") + } + return nil +} diff --git a/pkg/state/scheduler.go b/pkg/state/scheduler/scheduler.go similarity index 61% rename from pkg/state/scheduler.go rename to pkg/state/scheduler/scheduler.go index 47613b37a21..1d21f47c11e 100644 --- a/pkg/state/scheduler.go +++ b/pkg/state/scheduler/scheduler.go @@ -3,7 +3,7 @@ * License: Apache License 2.0 (see the file LICENSE or http://apache.org/licenses/LICENSE-2.0.html). */ -package state +package scheduler import ( v1beta2 "github.com/enmasseproject/enmasse/pkg/apis/enmasse/v1beta2" @@ -11,5 +11,8 @@ import ( ) type Scheduler interface { + // Schedule an address to a broker. The implication is that all addresses will be configured on the same broker. + ScheduleTenant(tenant *v1beta2.MessagingTenant, brokers []*broker.BrokerState) error + // Schedule an address to a broker. ScheduleAddress(address *v1beta2.MessagingAddress, brokers []*broker.BrokerState) error } diff --git a/pkg/state/test/fakeinfra.go b/pkg/state/test/fakeinfra.go index e351280fd05..f1e2be8688d 100644 --- a/pkg/state/test/fakeinfra.go +++ b/pkg/state/test/fakeinfra.go @@ -52,6 +52,10 @@ func (m *FakeManager) DeleteClient(infra *v1beta2.MessagingInfrastructure) error func (i *FakeClient) Start() { } +func (i *FakeClient) DeleteBroker(host string) error { + return nil +} + func (i *FakeClient) SyncAll(routers []Host, brokers []Host, tlsConfig *tls.Config) ([]state.ConnectorStatus, error) { i.Routers = routers i.Brokers = brokers @@ -73,7 +77,11 @@ func (i *FakeClient) DeleteEndpoint(endpoint *v1beta2.MessagingEndpoint) error { return nil } -func (i *FakeClient) ScheduleAddress(address *v1beta2.MessagingAddress, scheduler state.Scheduler) error { +func (i *FakeClient) ScheduleTenant(tenant *v1beta2.MessagingTenant) error { + return nil +} + +func (i *FakeClient) ScheduleAddress(address *v1beta2.MessagingAddress) error { return nil } @@ -85,6 +93,14 @@ func (i *FakeClient) DeleteAddress(address *v1beta2.MessagingAddress) error { return nil } +func (i *FakeClient) SyncTenant(tenant *v1beta2.MessagingTenant) error { + return nil +} + +func (i *FakeClient) DeleteTenant(tenant *v1beta2.MessagingTenant) error { + return nil +} + func (i *FakeClient) Shutdown() error { return nil } diff --git a/pkg/state/types.go b/pkg/state/types.go index 4557bd825b1..7a9fc009f75 100644 --- a/pkg/state/types.go +++ b/pkg/state/types.go @@ -35,12 +35,20 @@ type InfraClient interface { SyncAll(routers []common.Host, brokers []common.Host, tlsConfig *tls.Config) ([]ConnectorStatus, error) // Stop and cleanup client resources Shutdown() error + // Check if broker is in use by any address or tenant and delete it + DeleteBroker(host string) error + // Schedule tenant + ScheduleTenant(tenant *v1beta2.MessagingTenant) error // Schedule durable address for tenant - ScheduleAddress(address *v1beta2.MessagingAddress, scheduler Scheduler) error + ScheduleAddress(address *v1beta2.MessagingAddress) error // Synchronize address SyncAddress(address *v1beta2.MessagingAddress) error // Delete address DeleteAddress(address *v1beta2.MessagingAddress) error + // Synchronize tenant + SyncTenant(tenant *v1beta2.MessagingTenant) error + // Delete tenant + DeleteTenant(tenant *v1beta2.MessagingTenant) error // Allocate endpoint ports AllocatePorts(endpoint *v1beta2.MessagingEndpoint, protocols []v1beta2.MessagingEndpointProtocol) error // Free endpoint ports diff --git a/pkg/util/cchange/change.go b/pkg/util/cchange/change.go index e6c1539b6eb..84aca578f9e 100644 --- a/pkg/util/cchange/change.go +++ b/pkg/util/cchange/change.go @@ -26,7 +26,7 @@ func NewRecorder() *ConfigChangeRecorder { func (c *ConfigChangeRecorder) AddBytesFromMap(data map[string][]byte, onlyKeys ...string) { // extract keys, and sort - keys := make([]string, len(data)) + keys := make([]string, 0, len(data)) for k, _ := range data { keys = append(keys, k) } @@ -53,7 +53,7 @@ func (c *ConfigChangeRecorder) AddBytesFromMap(data map[string][]byte, onlyKeys func (c *ConfigChangeRecorder) AddStringsFromMap(data map[string]string, onlyKeys ...string) { // extract keys, and sort - keys := make([]string, len(data)) + keys := make([]string, 0, len(data)) for k, _ := range data { keys = append(keys, k) } diff --git a/pkg/util/install/utils.go b/pkg/util/install/utils.go index 876b0289315..2b7f2f43017 100644 --- a/pkg/util/install/utils.go +++ b/pkg/util/install/utils.go @@ -570,7 +570,7 @@ func ApplyEnvConfigMap(container *corev1.Container, name string, configMapKey st // Append a string to the value of an env-var. If the env-var doesn't exist, it will be created with the provided value. // A whitespace is added between the existing value and the new value. -func AppendEnvVarValue(container *corev1.Container, name string, value string) { +func AppendEnvVarValue(container *corev1.Container, name string, values ...string) { if container.Env == nil { container.Env = make([]corev1.EnvVar, 0) } @@ -583,7 +583,7 @@ func AppendEnvVarValue(container *corev1.Container, name string, value string) { opts += " " } - opts += value + opts += strings.Join(values, " ") env.Value = opts container.Env[i] = env @@ -594,7 +594,7 @@ func AppendEnvVarValue(container *corev1.Container, name string, value string) { container.Env = append(container.Env, corev1.EnvVar{ Name: name, - Value: value, + Value: strings.Join(values, " "), }) } diff --git a/pkg/util/install/utils_test.go b/pkg/util/install/utils_test.go index b3e9aaf54dd..10198676025 100644 --- a/pkg/util/install/utils_test.go +++ b/pkg/util/install/utils_test.go @@ -235,11 +235,11 @@ func TestAppendEnvVar(t *testing.T) { // append second element - AppendEnvVarValue(&container, JavaOptsEnvVarName, "bar") + AppendEnvVarValue(&container, JavaOptsEnvVarName, "bar", "baz") assert.NotNil(t, container.Env) assert.Len(t, container.Env, 1) assert.Equal(t, JavaOptsEnvVarName, container.Env[0].Name) - assert.Equal(t, "foo bar", container.Env[0].Value) + assert.Equal(t, "foo bar baz", container.Env[0].Value) } diff --git a/pom.properties b/pom.properties index 0bea9593109..124a37e5129 100644 --- a/pom.properties +++ b/pom.properties @@ -22,6 +22,6 @@ olm.csv.logo.base64=PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iVVRGLTgiPz4KPHN2ZyB3 olm.csv.logo.mediatype=image/svg+xml license.advice.text=EnMasse comprises the following components: olm.version=999.999.999 -release.version=0.32-SNAPSHOT -maven.version=0.32-SNAPSHOT +release.version=1.0-SNAPSHOT +maven.version=1.0-SNAPSHOT application.docs=https://enmasse.io/documentation/ diff --git a/pom.xml b/pom.xml index 9f45a7ceac0..4b7563c5c19 100644 --- a/pom.xml +++ b/pom.xml @@ -6,7 +6,7 @@ io.enmasse enmasse pom - 0.32-SNAPSHOT + 1.0-SNAPSHOT EnMasse http://enmasse.io/ @@ -42,13 +42,14 @@ 11 1.9.0 - 4.6.4 + + 4.9.1 0.17.2 3.9.0 1.7.21 1.2.3 7.7.1 - 6.0.20.Final + 6.1.5.Final 2.10.0 2.10.4 2.10.4 @@ -63,13 +64,12 @@ 2.21.0 25.0-jre 2.2 - 5.5.2 - 1.5.2 - 1.5.2 - 2.1.3 + 5.6.2 + 1.6.2 + 1.6.2 2.13.0 1.2.1 - 4.1.48.Final + 4.1.49.Final 4.8.3.Final 3.3.0.Final 2.1.0.Final @@ -87,9 +87,12 @@ 1.0.2 1.18.10 - + 1.5.2.Final + 1.0.7 + + 1.1.0 - + 0.33.0 1.6.0 @@ -103,7 +106,7 @@ 3.8.0 3.1.1 3.1.0 - 3.0.0-M4 + 3.0.0-M5 3.5.1 2.9 3.3.0 @@ -118,25 +121,11 @@ api-model-annotator api-model - metrics-api - keycloak-user-api - documentation - agent - k8s-api - k8s-api-testutil - amqp-utils api-common - discovery-lib - topic-forwarder - mqtt-gateway - none-authservice - mqtt-lwt - standard-controller - address-space-controller - keycloak-plugin + amqp-utils + documentation systemtests broker-plugin - service-broker bundle iot controller-manager @@ -145,6 +134,20 @@ templates + + + hono.snapshots + Eclipse Hono Development Snapshot Repository + https://repo.eclipse.org/content/repositories/hono-snapshots/ + + false + + + true + + + + @@ -160,11 +163,6 @@ api-model ${project.version} - - io.enmasse - user-model-lib - ${project.version} - io.enmasse amqp-utils @@ -172,45 +170,7 @@ io.enmasse - discovery-lib - ${project.version} - - - io.enmasse - k8s-api - ${project.version} - - - io.enmasse - metrics-api - ${project.version} - - - io.enmasse - amqp-connector - ${project.version} - - - io.enmasse - sasl-delegation - ${project.version} - - - io.enmasse - broker-cli - ${project.version} - - - io.enmasse - keycloak-user-api - ${project.version} - - - - - - io.enmasse - k8s-api-testutil + broker-cli ${project.version} @@ -640,12 +600,6 @@ ${junit.platform.launcher.version} - - io.github.artsok - rerunner-jupiter - ${rerunner-jupiter.version} - - org.mockito mockito-core @@ -687,6 +641,51 @@ ${snakeyaml.version} + + io.quarkus + quarkus-core + ${quarkus.version} + + + io.quarkus + quarkus-config-yaml + ${quarkus.version} + + + io.quarkus + quarkus-hibernate-validator + ${quarkus.version} + + + io.quarkus + quarkus-kubernetes-client + ${quarkus.version} + + + io.quarkus + quarkus-jaeger + ${quarkus.version} + + + io.quarkus + quarkus-vertx + ${quarkus.version} + + + io.jaegertracing + jaeger-core + ${jaeger-client.version} + + + io.jaegertracing + jaeger-client + ${jaeger-client.version} + + + io.jaegertracing + jaeger-thrift + ${jaeger-client.version} + @@ -798,6 +797,16 @@ jsonschema2pojo-maven-plugin ${jsonschema2pojo.version} + + io.quarkus + quarkus-maven-plugin + ${quarkus.version} + + + org.jboss.jandex + jandex-maven-plugin + ${jandex-plugin.version} + @@ -909,6 +918,8 @@ maven-surefire-plugin true + 10 + true value true diff --git a/service-broker/Dockerfile b/service-broker/Dockerfile deleted file mode 100644 index b881688977d..00000000000 --- a/service-broker/Dockerfile +++ /dev/null @@ -1,9 +0,0 @@ -FROM quay.io/enmasse/java-base:11-5 - -ARG version -ARG maven_version -ARG revision -ENV VERSION=${version} REVISION=${revision} MAVEN_VERSION=${maven_version} -ADD target/service-broker-${maven_version}.jar /service-broker.jar - -CMD ["/opt/run-java/launch_java.sh", "-jar", "/service-broker.jar"] diff --git a/service-broker/Makefile b/service-broker/Makefile deleted file mode 100644 index 27e8bca799a..00000000000 --- a/service-broker/Makefile +++ /dev/null @@ -1 +0,0 @@ -include ../Makefile.java.mk diff --git a/service-broker/pom.xml b/service-broker/pom.xml deleted file mode 100644 index 7590ba1c0cd..00000000000 --- a/service-broker/pom.xml +++ /dev/null @@ -1,153 +0,0 @@ - - - - io.enmasse - enmasse - 0.32-SNAPSHOT - - 4.0.0 - service-broker - - - io.enmasse - api-model - - - io.enmasse - keycloak-user-api - compile - - - io.enmasse - api-common - compile - - - io.enmasse - amqp-utils - compile - - - io.enmasse - k8s-api - compile - - - io.fabric8 - openshift-client - compile - - - io.vertx - vertx-core - compile - - - io.vertx - vertx-proton - compile - - - org.slf4j - slf4j-api - compile - - - ch.qos.logback - logback-classic - runtime - - - com.fasterxml.jackson.core - jackson-core - compile - - - com.fasterxml.jackson.core - jackson-databind - compile - - - com.fasterxml.jackson.module - jackson-module-jsonSchema - compile - - - org.jboss.resteasy - resteasy-jackson2-provider - compile - - - org.jboss.resteasy - resteasy-vertx - compile - - - io.vertx - vertx-junit5 - test - - - org.mockito - mockito-core - test - - - io.enmasse - k8s-api-testutil - test - - - io.fabric8 - kubernetes-server-mock - test - - - org.junit.jupiter - junit-jupiter-api - test - - - org.junit.jupiter - junit-jupiter-engine - test - - - - - - - org.apache.maven.plugins - maven-shade-plugin - - - package - - shade - - - - - - - io.enmasse.osb.ServiceBroker - - - - - *:* - - META-INF/*.SF - META-INF/*.DSA - META-INF/*.RSA - - - - - - - - - - diff --git a/service-broker/src/main/java/io/enmasse/osb/HTTPServer.java b/service-broker/src/main/java/io/enmasse/osb/HTTPServer.java deleted file mode 100644 index b9f0a1aba71..00000000000 --- a/service-broker/src/main/java/io/enmasse/osb/HTTPServer.java +++ /dev/null @@ -1,137 +0,0 @@ -/* - * Copyright 2016-2018, EnMasse authors. - * License: Apache License 2.0 (see the file LICENSE or http://apache.org/licenses/LICENSE-2.0.html). - */ - -package io.enmasse.osb; - -import java.io.File; - -import org.jboss.resteasy.plugins.server.vertx.VertxRequestHandler; -import org.jboss.resteasy.plugins.server.vertx.VertxResteasyDeployment; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import io.enmasse.api.auth.AllowAllAuthInterceptor; -import io.enmasse.api.auth.ApiHeaderConfig; -import io.enmasse.api.auth.AuthApi; -import io.enmasse.api.auth.AuthInterceptor; -import io.enmasse.api.common.DefaultExceptionMapper; -import io.enmasse.k8s.api.AddressSpaceApi; -import io.enmasse.k8s.api.SchemaProvider; -import io.enmasse.osb.api.bind.OSBBindingService; -import io.enmasse.osb.api.catalog.OSBCatalogService; -import io.enmasse.osb.api.lastoperation.OSBLastOperationService; -import io.enmasse.osb.api.provision.ConsoleProxy; -import io.enmasse.osb.api.provision.OSBProvisioningService; -import io.vertx.core.AbstractVerticle; -import io.vertx.core.Promise; -import io.vertx.core.http.HttpServer; -import io.vertx.core.http.HttpServerOptions; -import io.vertx.core.net.PemKeyCertOptions; - -public class HTTPServer extends AbstractVerticle { - private static final Logger log = LoggerFactory.getLogger(HTTPServer.class.getName()); - private final AddressSpaceApi addressSpaceApi; - private final AuthApi authApi; - private final String certDir; - private final boolean enableRbac; - private final SchemaProvider schemaProvider; - private final UserApi userApi; - private final int listenPort; - private final ConsoleProxy consoleProxy; - - private HttpServer httpServer; - - public HTTPServer(AddressSpaceApi addressSpaceApi, SchemaProvider schemaProvider, - AuthApi authApi, String certDir, boolean enableRbac, - UserApi userApi, int listenPort, ConsoleProxy consoleProxy) { - this.addressSpaceApi = addressSpaceApi; - this.schemaProvider = schemaProvider; - this.certDir = certDir; - this.authApi = authApi; - this.enableRbac = enableRbac; - this.userApi = userApi; - this.listenPort = listenPort; - this.consoleProxy = consoleProxy; - } - - @Override - public void start(Promise startPromise) { - VertxResteasyDeployment deployment = new VertxResteasyDeployment(); - deployment.start(); - - deployment.getProviderFactory().registerProvider(DefaultExceptionMapper.class); - - if (enableRbac) { - log.info("Enabling RBAC for REST API"); - deployment.getProviderFactory().registerProviderInstance(new AuthInterceptor(authApi, ApiHeaderConfig.DEFAULT_HEADERS_CONFIG, path -> - path.startsWith(HttpHealthService.BASE_URI))); - } else { - log.info("Disabling authentication and authorization for REST API"); - deployment.getProviderFactory().registerProviderInstance(new AllowAllAuthInterceptor()); - } - - deployment.getRegistry().addSingletonResource(new HttpHealthService()); - deployment.getRegistry().addSingletonResource(new OSBCatalogService(addressSpaceApi, authApi, schemaProvider)); - deployment.getRegistry().addSingletonResource(new OSBProvisioningService(addressSpaceApi, authApi, schemaProvider, consoleProxy)); - deployment.getRegistry().addSingletonResource(new OSBBindingService(addressSpaceApi, authApi, schemaProvider, userApi)); - deployment.getRegistry().addSingletonResource(new OSBLastOperationService(addressSpaceApi, authApi, schemaProvider)); - - VertxRequestHandler requestHandler = new VertxRequestHandler(vertx, deployment); - - httpServer = createServer(requestHandler, startPromise); - } - - @Override - public void stop() { - if (httpServer != null) { - httpServer.close(); - } - } - - int getActualPort() { - if (httpServer != null) { - return httpServer.actualPort(); - } else { - return listenPort; - } - } - - private HttpServer createServer(VertxRequestHandler requestHandler, Promise startPromise) { - if (certDir != null && new File(certDir).exists()) { - HttpServerOptions options = new HttpServerOptions(); - File keyFile = new File(certDir, "tls.key"); - File certFile = new File(certDir, "tls.crt"); - log.info("Loading key from " + keyFile.getAbsolutePath() + ", cert from " + certFile.getAbsolutePath()); - options.setKeyCertOptions(new PemKeyCertOptions() - .setKeyPath(keyFile.getAbsolutePath()) - .setCertPath(certFile.getAbsolutePath())); - options.setSsl(true); - - return vertx.createHttpServer(options) - .requestHandler(requestHandler) - .listen(listenPort, ar -> { - if (ar.succeeded()) { - log.info("Started HTTPS server. Listening on port " + listenPort); - startPromise.complete(); - } else { - log.info("Error starting HTTPS server"); - startPromise.fail(ar.cause()); - } - }); - } else { - return vertx.createHttpServer() - .requestHandler(requestHandler) - .listen(listenPort, ar -> { - if (ar.succeeded()) { - log.info("Started HTTP server. Listening on port " + listenPort); - startPromise.complete(); - } else { - log.info("Error starting HTTP server"); - startPromise.fail(ar.cause()); - } - }); - } - } -} diff --git a/service-broker/src/main/java/io/enmasse/osb/HttpHealthService.java b/service-broker/src/main/java/io/enmasse/osb/HttpHealthService.java deleted file mode 100644 index 1584ce726ff..00000000000 --- a/service-broker/src/main/java/io/enmasse/osb/HttpHealthService.java +++ /dev/null @@ -1,31 +0,0 @@ -/* - * Copyright 2017-2018, EnMasse authors. - * License: Apache License 2.0 (see the file LICENSE or http://apache.org/licenses/LICENSE-2.0.html). - */ -package io.enmasse.osb; - -import javax.ws.rs.GET; -import javax.ws.rs.Path; -import javax.ws.rs.Produces; -import javax.ws.rs.core.MediaType; -import javax.ws.rs.core.Response; - -@Path(HttpHealthService.BASE_URI) -public class HttpHealthService { - public static final String BASE_URI = "/healthz"; - @GET - @Produces({MediaType.APPLICATION_JSON}) - public Response getHealth() { - return Response.status(200).entity(new HealthResponse(200, "OK")).build(); - } - - public class HealthResponse { - public int status; - public String message; - - public HealthResponse(int status, String message) { - this.status = status; - this.message = message; - } - } -} diff --git a/service-broker/src/main/java/io/enmasse/osb/ServiceBroker.java b/service-broker/src/main/java/io/enmasse/osb/ServiceBroker.java deleted file mode 100644 index 3da9dc697f0..00000000000 --- a/service-broker/src/main/java/io/enmasse/osb/ServiceBroker.java +++ /dev/null @@ -1,130 +0,0 @@ -/* - * Copyright 2016-2018, EnMasse authors. - * License: Apache License 2.0 (see the file LICENSE or http://apache.org/licenses/LICENSE-2.0.html). - */ - -package io.enmasse.osb; - -import java.nio.charset.StandardCharsets; -import java.util.Base64; - -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import io.enmasse.address.model.CoreCrd; -import io.enmasse.admin.model.v1.AdminCrd; -import io.enmasse.api.auth.AuthApi; -import io.enmasse.api.auth.KubeAuthApi; -import io.enmasse.k8s.api.AddressSpaceApi; -import io.enmasse.k8s.api.CachingSchemaProvider; -import io.enmasse.k8s.api.KubeAddressSpaceApi; -import io.enmasse.k8s.api.KubeSchemaApi; -import io.enmasse.k8s.api.SchemaApi; -import io.enmasse.osb.api.provision.ConsoleProxy; -import io.enmasse.user.model.v1.DoneableUser; -import io.enmasse.user.model.v1.User; -import io.enmasse.user.model.v1.UserCrd; -import io.enmasse.user.model.v1.UserList; -import io.fabric8.kubernetes.api.model.Secret; -import io.fabric8.kubernetes.client.DefaultKubernetesClient; -import io.fabric8.kubernetes.client.NamespacedKubernetesClient; -import io.fabric8.openshift.api.model.Route; -import io.fabric8.openshift.client.OpenShiftClient; -import io.vertx.core.AbstractVerticle; -import io.vertx.core.Promise; -import io.vertx.core.Vertx; - -public class ServiceBroker extends AbstractVerticle { - private static final Logger log = LoggerFactory.getLogger(ServiceBroker.class.getName()); - private final NamespacedKubernetesClient client; - private final ServiceBrokerOptions options; - - static { - try { - CoreCrd.registerCustomCrds(); - AdminCrd.registerCustomCrds(); - } catch (RuntimeException t) { - t.printStackTrace(); - throw new ExceptionInInitializerError(t); - } - } - - private ServiceBroker(ServiceBrokerOptions options) { - this.client = new DefaultKubernetesClient(); - this.options = options; - } - - @Override - public void start(Promise startPromise) throws Exception { - SchemaApi schemaApi = KubeSchemaApi.create(client, client.getNamespace(), options.getVersion(), true, false); - CachingSchemaProvider schemaProvider = new CachingSchemaProvider(); - schemaApi.watchSchema(schemaProvider, options.getResyncInterval()); - - ensureCredentialsExist(client, options); - - AddressSpaceApi addressSpaceApi = KubeAddressSpaceApi.create(client, null, options.getVersion()); - AuthApi authApi = new KubeAuthApi(client, client.getConfiguration().getOauthToken()); - - UserApi userApi = createUserApi(); - - ConsoleProxy consoleProxy = addressSpace -> { - Route route = client.adapt(OpenShiftClient.class).routes().inNamespace(client.getNamespace()).withName(options.getConsoleRouteName()).get(); - if (route == null) { - return null; - } - - return String.format("https://%s/#/address-spaces/%s/%s/%s/addresses", route.getSpec().getHost(), addressSpace.getMetadata().getNamespace(), addressSpace.getMetadata().getName(), addressSpace.getSpec().getType()); - }; - - vertx.deployVerticle(new HTTPServer(addressSpaceApi, schemaProvider, authApi, options.getCertDir(), options.getEnableRbac(), userApi, options.getListenPort(), consoleProxy), - result -> { - if (result.succeeded()) { - log.info("EnMasse Service Broker started"); - startPromise.complete(); - } else { - startPromise.fail(result.cause()); - } - }); - } - - private void ensureCredentialsExist(NamespacedKubernetesClient client, ServiceBrokerOptions options) { - Secret secret = client.secrets().withName(options.getServiceCatalogCredentialsSecretName()).get(); - if (secret == null) { - client.secrets().createNew() - .editOrNewMetadata() - .withName(options.getServiceCatalogCredentialsSecretName()) - .addToLabels("app", "enmasse") - .endMetadata() - .addToData("token", Base64.getEncoder().encodeToString(client.getConfiguration().getOauthToken().getBytes(StandardCharsets.UTF_8))) - .done(); - } - } - - private UserApi createUserApi() { - var userClient = client.customResources(UserCrd.messagingUser(), User.class, UserList.class, DoneableUser.class); - return new UserApi() { - @Override - public void createOrReplace(User user) { - userClient.inNamespace(user.getMetadata().getNamespace()).create(user); - } - - @Override - public boolean deleteUser(String namespace, String name) { - return userClient.inNamespace(namespace).withName(name).delete(); - } - }; - } - - public static void main(String args[]) { - try { - Vertx vertx = Vertx.vertx(); - vertx.deployVerticle(new ServiceBroker(ServiceBrokerOptions.fromEnv(System.getenv()))); - } catch (IllegalArgumentException e) { - System.out.println(String.format("Unable to parse arguments: %s", e.getMessage())); - System.exit(1); - } catch (Exception e) { - System.out.println("Error starting service broker: " + e.getMessage()); - System.exit(1); - } - } -} diff --git a/service-broker/src/main/java/io/enmasse/osb/ServiceBrokerOptions.java b/service-broker/src/main/java/io/enmasse/osb/ServiceBrokerOptions.java deleted file mode 100644 index 90e3f3271cd..00000000000 --- a/service-broker/src/main/java/io/enmasse/osb/ServiceBrokerOptions.java +++ /dev/null @@ -1,152 +0,0 @@ -/* - * Copyright 2016-2018, EnMasse authors. - * License: Apache License 2.0 (see the file LICENSE or http://apache.org/licenses/LICENSE-2.0.html). - */ -package io.enmasse.osb; - -import java.time.Duration; -import java.util.Map; -import java.util.Optional; - -public class ServiceBrokerOptions { - private Duration resyncInterval = Duration.ofMinutes(10); - private String certDir = null; - private boolean enableRbac = false; - private String standardAuthserviceConfigName; - private String standardAuthserviceCredentialsSecretName; - private String standardAuthserviceCertSecretName; - private String serviceCatalogCredentialsSecretName; - private String consoleRouteName; - private int listenPort = 8080; - private String version; - - public Duration getResyncInterval() { - return resyncInterval; - } - - public ServiceBrokerOptions setResyncInterval(Duration resyncInterval) { - this.resyncInterval = resyncInterval; - return this; - } - - public String getCertDir() { - return certDir; - } - - public boolean getEnableRbac() { - return enableRbac; - } - - public int getListenPort() { - return listenPort; - } - - private ServiceBrokerOptions setCertDir(String certDir) { - this.certDir = certDir; - return this; - } - - private ServiceBrokerOptions setEnableRbac(boolean enableRbac) { - this.enableRbac = enableRbac; - return this; - } - - private ServiceBrokerOptions setListenPort(int listenPort) { - this.listenPort = listenPort; - return this; - } - - public String getStandardAuthserviceConfigName() { - return standardAuthserviceConfigName; - } - - public ServiceBrokerOptions setStandardAuthserviceConfigName(String standardAuthserviceConfigName) { - this.standardAuthserviceConfigName = standardAuthserviceConfigName; - return this; - } - - public String getStandardAuthserviceCredentialsSecretName() { - return standardAuthserviceCredentialsSecretName; - } - - public ServiceBrokerOptions setStandardAuthserviceCredentialsSecretName(String standardAuthserviceCredentialsSecretName) { - this.standardAuthserviceCredentialsSecretName = standardAuthserviceCredentialsSecretName; - return this; - } - - public String getStandardAuthserviceCertSecretName() { - return standardAuthserviceCertSecretName; - } - - public ServiceBrokerOptions setStandardAuthserviceCertSecretName(String standardAuthserviceCertSecretName) { - this.standardAuthserviceCertSecretName = standardAuthserviceCertSecretName; - return this; - } - - public static ServiceBrokerOptions fromEnv(Map env) { - ServiceBrokerOptions options = new ServiceBrokerOptions(); - - options.setStandardAuthserviceConfigName(getEnvOrThrow(env, "STANDARD_AUTHSERVICE_CONFIG_NAME")); - options.setStandardAuthserviceCredentialsSecretName(getEnvOrThrow(env, "STANDARD_AUTHSERVICE_CREDENTIALS_SECRET_NAME")); - options.setStandardAuthserviceCertSecretName(getEnvOrThrow(env, "STANDARD_AUTHSERVICE_CERT_SECRET_NAME")); - options.setConsoleRouteName(getEnv(env, "CONSOLE_ROUTE_NAME").orElse("console")); - options.setServiceCatalogCredentialsSecretName(getEnvOrThrow(env, "SERVICE_CATALOG_CREDENTIALS_SECRET_NAME")); - - String resyncInterval = env.get("RESYNC_INTERVAL"); - if (resyncInterval != null) { - options.setResyncInterval(Duration.ofSeconds(Long.parseLong(resyncInterval))); - } - - String enableRbac = env.get("ENABLE_RBAC"); - if (enableRbac != null) { - options.setEnableRbac(Boolean.parseBoolean(enableRbac)); - } - - String certDir = env.get("CERT_DIR"); - if (certDir != null) { - options.setCertDir(certDir); - } - - String listenPort = env.get("LISTEN_PORT"); - if (listenPort != null) { - options.setListenPort(Integer.parseInt(listenPort)); - } - - options.setVersion(getEnvOrThrow(env, "VERSION")); - - return options; - } - - private static Optional getEnv(Map env, String envVar) { - return Optional.ofNullable(env.get(envVar)); - } - - private static String getEnvOrThrow(Map env, String envVar) { - return getEnv(env, envVar).orElseThrow(() -> new IllegalArgumentException(String.format("Unable to find value for required environment var '%s'", envVar))); - } - - public String getConsoleRouteName() { - return consoleRouteName; - } - - public void setConsoleRouteName(String consoleRouteName) { - this.consoleRouteName = consoleRouteName; - } - - public String getServiceCatalogCredentialsSecretName() { - return serviceCatalogCredentialsSecretName; - } - - public ServiceBrokerOptions setServiceCatalogCredentialsSecretName(String serviceCatalogCredentialsSecretName) { - this.serviceCatalogCredentialsSecretName = serviceCatalogCredentialsSecretName; - return this; - } - - public String getVersion() { - return version; - } - - public void setVersion(String version) { - this.version = version; - } -} diff --git a/service-broker/src/main/java/io/enmasse/osb/UserApi.java b/service-broker/src/main/java/io/enmasse/osb/UserApi.java deleted file mode 100644 index b390c21b515..00000000000 --- a/service-broker/src/main/java/io/enmasse/osb/UserApi.java +++ /dev/null @@ -1,13 +0,0 @@ -/* - * Copyright 2020, EnMasse authors. - * License: Apache License 2.0 (see the file LICENSE or http://apache.org/licenses/LICENSE-2.0.html). - */ - -package io.enmasse.osb; - -import io.enmasse.user.model.v1.User; - -public interface UserApi { - void createOrReplace(User user); - boolean deleteUser(String namespace, String name); -} diff --git a/service-broker/src/main/java/io/enmasse/osb/api/EmptyResponse.java b/service-broker/src/main/java/io/enmasse/osb/api/EmptyResponse.java deleted file mode 100644 index bb6bc3bf8af..00000000000 --- a/service-broker/src/main/java/io/enmasse/osb/api/EmptyResponse.java +++ /dev/null @@ -1,30 +0,0 @@ -/* - * Copyright 2018, EnMasse authors. - * License: Apache License 2.0 (see the file LICENSE or http://apache.org/licenses/LICENSE-2.0.html). - */ -package io.enmasse.osb.api; - -import java.io.IOException; - -import com.fasterxml.jackson.core.JsonGenerator; -import com.fasterxml.jackson.databind.JsonSerializer; -import com.fasterxml.jackson.databind.ObjectMapper; -import com.fasterxml.jackson.databind.SerializerProvider; -import com.fasterxml.jackson.databind.annotation.JsonSerialize; -import com.fasterxml.jackson.databind.node.ObjectNode; - -@JsonSerialize(using = EmptyResponse.Serializer.class) -public class EmptyResponse { - private static final ObjectMapper mapper = new ObjectMapper(); - - public EmptyResponse() { - } - - protected static class Serializer extends JsonSerializer { - @Override - public void serialize(EmptyResponse value, JsonGenerator gen, SerializerProvider serializers) throws IOException { - ObjectNode node = mapper.createObjectNode(); - mapper.writeValue(gen, node); - } - } -} diff --git a/service-broker/src/main/java/io/enmasse/osb/api/JsonSerializationUtils.java b/service-broker/src/main/java/io/enmasse/osb/api/JsonSerializationUtils.java deleted file mode 100644 index ed2b74399ff..00000000000 --- a/service-broker/src/main/java/io/enmasse/osb/api/JsonSerializationUtils.java +++ /dev/null @@ -1,31 +0,0 @@ -/* - * Copyright 2018, EnMasse authors. - * License: Apache License 2.0 (see the file LICENSE or http://apache.org/licenses/LICENSE-2.0.html). - */ -package io.enmasse.osb.api; - -import java.util.UUID; -import javax.ws.rs.BadRequestException; - -import com.fasterxml.jackson.databind.JsonNode; -import com.fasterxml.jackson.databind.node.ObjectNode; - -public class JsonSerializationUtils { - - public static UUID getUuid(ObjectNode node, String field) { - JsonNode jsonNode = getRequiredField(node, field); - try { - return UUID.fromString(jsonNode.asText()); - } catch (IllegalArgumentException e) { - throw new BadRequestException("Invalid UUID " + jsonNode.asText() + " in field " + field); - } - } - - public static JsonNode getRequiredField(ObjectNode node, String name) { - JsonNode organizationGuid = node.get(name); - if (organizationGuid == null) { - throw new BadRequestException("Field " + name + " is required"); - } - return organizationGuid; - } -} diff --git a/service-broker/src/main/java/io/enmasse/osb/api/OSBServiceBase.java b/service-broker/src/main/java/io/enmasse/osb/api/OSBServiceBase.java deleted file mode 100644 index 58d8fcd9f5c..00000000000 --- a/service-broker/src/main/java/io/enmasse/osb/api/OSBServiceBase.java +++ /dev/null @@ -1,124 +0,0 @@ -/* - * Copyright 2018, EnMasse authors. - * License: Apache License 2.0 (see the file LICENSE or http://apache.org/licenses/LICENSE-2.0.html). - */ -package io.enmasse.osb.api; - -import java.util.*; - -import io.enmasse.address.model.*; -import io.enmasse.api.auth.AuthApi; -import io.enmasse.api.auth.RbacSecurityContext; -import io.enmasse.api.auth.ResourceVerb; -import io.enmasse.api.common.Exceptions; -import io.enmasse.k8s.api.SchemaProvider; -import io.enmasse.api.common.UuidGenerator; -import io.enmasse.config.AnnotationKeys; -import io.enmasse.config.LabelKeys; -import io.enmasse.k8s.api.AddressSpaceApi; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import javax.ws.rs.core.SecurityContext; - -public abstract class OSBServiceBase { - - public static final String BASE_URI = "/osbapi/v2"; - - protected final Logger log = LoggerFactory.getLogger(getClass().getName()); - - private final AddressSpaceApi addressSpaceApi; - private final AuthApi authApi; - private final SchemaProvider schemaProvider; - private final String namespace; - private final UuidGenerator uuidGenerator = new UuidGenerator(); - - public OSBServiceBase(AddressSpaceApi addressSpaceApi, AuthApi authApi, SchemaProvider schemaProvider) { - this.addressSpaceApi = addressSpaceApi; - this.namespace = authApi.getNamespace(); - this.authApi = authApi; - this.schemaProvider = schemaProvider; - - } - - protected ServiceMapping getServiceMapping() { - return new ServiceMapping(schemaProvider.getSchema()); - } - - protected AuthApi getAuthApi() { - return authApi; - } - - protected void verifyAuthorized(SecurityContext securityContext, ResourceVerb verb) { - if (!securityContext.isUserInRole(RbacSecurityContext.rbacToRole(namespace, verb, "addressspaces", "enmasse.io"))) { - throw Exceptions.notAuthorizedException(); - } - } - - - protected Optional findAddressSpaceByInstanceId(String serviceInstanceId) { - return addressSpaceApi.listAllAddressSpacesWithLabels(Collections.singletonMap(LabelKeys.SERVICE_INSTANCE_ID, serviceInstanceId)).stream().findAny(); - } - - protected Optional findAddressSpace(String name, String namespace) { - return addressSpaceApi.listAddressSpaces(namespace).stream().filter(a -> a.getMetadata().getName().equals(name)).findAny(); - } - - protected AddressSpace createAddressSpace(String instanceId, String name, String namespace, String type, String plan, String userId, String userName) throws Exception { - AuthenticationService authService = new AuthenticationServiceBuilder() - .withType(AuthenticationServiceType.STANDARD) - .build(); - AddressSpace addressSpace = new AddressSpaceBuilder() - .withNewMetadata() - .withName(name) - .withNamespace(namespace) - .addToAnnotations(AnnotationKeys.CREATED_BY, userName) - .addToAnnotations(AnnotationKeys.CREATED_BY_UID, userId) - .addToLabels(LabelKeys.SERVICE_INSTANCE_ID, instanceId) - .endMetadata() - - .withNewSpec() - .withType(type) - .withPlan(plan) - .withAuthenticationService(authService) - .endSpec() - .editOrNewStatus() - .withReady(false) - .withPhase(Phase.Pending) - .endStatus() - - .build(); - addressSpace = setDefaults(addressSpace, namespace); - addressSpaceApi.createAddressSpace(addressSpace); - log.info("Created MaaS addressspace {}", addressSpace.getMetadata().getName()); - return addressSpace; - } - - private AddressSpace setDefaults(AddressSpace addressSpace, String namespace) { - - if (addressSpace.getMetadata().getNamespace() == null) { - addressSpace = new AddressSpaceBuilder(addressSpace) - .editOrNewMetadata() - .withNamespace(namespace) - .endMetadata() - .build(); - } - - final Map annotations = addressSpace.getMetadata().getAnnotations(); - final Map labels = addressSpace.getMetadata().getLabels(); - - annotations.putIfAbsent(AnnotationKeys.REALM_NAME, KubeUtil.sanitizeName(addressSpace.getMetadata().getNamespace() + "-" + addressSpace.getMetadata().getName())); - annotations.putIfAbsent(AnnotationKeys.INFRA_UUID, uuidGenerator.generateInfraUuid()); - - labels.putIfAbsent(LabelKeys.ADDRESS_SPACE_TYPE, addressSpace.getSpec().getType()); - labels.putIfAbsent(LabelKeys.NAMESPACE, addressSpace.getMetadata().getNamespace()); - - return addressSpace; - } - - protected boolean deleteAddressSpace(AddressSpace addressSpace) { - log.info("Deleting address space : {}", addressSpace.getMetadata().getName()); - addressSpaceApi.deleteAddressSpace(addressSpace); - return true; - } -} diff --git a/service-broker/src/main/java/io/enmasse/osb/api/ServiceMapping.java b/service-broker/src/main/java/io/enmasse/osb/api/ServiceMapping.java deleted file mode 100644 index 2010c200d0b..00000000000 --- a/service-broker/src/main/java/io/enmasse/osb/api/ServiceMapping.java +++ /dev/null @@ -1,197 +0,0 @@ -/* - * Copyright 2018, EnMasse authors. - * License: Apache License 2.0 (see the file LICENSE or http://apache.org/licenses/LICENSE-2.0.html). - */ -package io.enmasse.osb.api; - -import com.fasterxml.jackson.module.jsonSchema.types.ObjectSchema; -import com.fasterxml.jackson.module.jsonSchema.types.StringSchema; -import io.enmasse.address.model.AddressSpaceType; -import io.enmasse.address.model.Schema; -import io.enmasse.admin.model.AddressSpacePlan; -import io.enmasse.osb.api.catalog.InputParameters; -import io.enmasse.osb.api.catalog.Plan; -import io.enmasse.osb.api.catalog.Schemas; -import io.enmasse.osb.api.catalog.Service; -import io.enmasse.osb.api.catalog.ServiceBindingSchema; -import io.enmasse.osb.api.catalog.ServiceInstanceSchema; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import java.io.IOException; -import java.io.InputStream; -import java.nio.charset.StandardCharsets; -import java.text.MessageFormat; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Collections; -import java.util.LinkedHashMap; -import java.util.Properties; -import java.util.List; -import java.util.Map; -import java.util.Optional; -import java.util.UUID; - -public class ServiceMapping { - private static final Logger LOG = LoggerFactory.getLogger(ServiceMapping.class); - - private static final Properties SERVICE_PROPERTIES = new Properties(); - - private final Map services; - private final Schema schema; - - { - String servicePropsResource = "/service.properties"; - try { - try (InputStream resourceAsStream = ServiceMapping.class.getResourceAsStream(servicePropsResource)) { - if (resourceAsStream != null) { - SERVICE_PROPERTIES.load(resourceAsStream); - } else { - LOG.warn("Could not load {} from classpath, ignoring", servicePropsResource); - } - } - } catch (IOException e) { - LOG.warn("Could not load {} from classpath, ignoring", servicePropsResource, e); - } - } - - private final String SERVICE_NAME_PATTERN = getStringFromEnv("SERVICE_BROKER_SERVICE_NAME_PATTERN", "enmasse-{0}"); - private final String[] TAGS = getStringsFromEnv("SERVICE_BROKER_SERVICE_TAGS", new String[] {"middleware", "messaging", "amqp", "mqtt", "enmasse"}); - private final String SERVICE_DISPLAY_NAME_PATTERN = getStringFromEnv("SERVICE_BROKER_SERVICE_DISPLAY_NAME_PATTERN", "EnMasse ({0})"); - - private final String SERVICE_PROVIDER_NAME = getStringFromEnv("SERVICE_BROKER_SERVICE_PROVIDER_NAME", "EnMasse"); - private final String IMAGE_URL = getStringFromEnv("SERVICE_BROKER_SERVICE_IMAGE_URL", "https://raw.githubusercontent.com/EnMasseProject/enmasse/master/documentation/_images/logo/enmasse_icon.png"); - private final String DOCUMENTATION_URL = getStringFromEnv("SERVICE_BROKER_DOCUMENTATION_URL", "https://github.com/EnMasseProject/enmasse"); - static final String addressRegexp = "^\\s*(([a-zA-Z0-9_-]+(([/.])[a-zA-Z0-9_-]+)*(([/.])?[#*])?)|[#*])(\\s*,\\s*(([a-zA-Z0-9_-]+([/.][a-zA-Z0-9_-]+)*([/.]?[#*])?)|[#*]))*\\s*$"; - - private String getStringFromEnv(String varName, String defaultValue) { - - return System.getenv().getOrDefault(varName, SERVICE_PROPERTIES.getProperty(varName, defaultValue)); - } - - private String[] getStringsFromEnv(String varName, String[] defaultValue) { - String value = getStringFromEnv(varName, null); - if(value != null) { - return value.split(","); - } else { - return defaultValue; - } - } - - public ServiceMapping(Schema schema) { - this.schema = schema; - this.services = populateServices(schema); - } - - private Map populateServices(Schema schema) { - Map services = new LinkedHashMap<>(); - - for(AddressSpaceType addressSpaceType : schema.getAddressSpaceTypes()) { - Service service = new Service(getUuidForAddressSpaceType(addressSpaceType), - MessageFormat.format(SERVICE_NAME_PATTERN,addressSpaceType.getName()), - addressSpaceType.getDescription(), - true); - - service.getTags().addAll(Arrays.asList(TAGS)); - service.getMetadata().put("displayName", MessageFormat.format(SERVICE_DISPLAY_NAME_PATTERN, addressSpaceType.getName())); - service.getMetadata().put("providerDisplayName", SERVICE_PROVIDER_NAME); - //service.getMetadata().put("longDescription", addressSpaceType.getDescription()); - service.getMetadata().put("imageUrl", IMAGE_URL); - service.getMetadata().put("documentationUrl", DOCUMENTATION_URL); - - service.setPlans(populatePlans(addressSpaceType)); - // TODO - should come from config data - service.setPlanUpdatable(false); - - services.put(addressSpaceType, service); - } - return Collections.unmodifiableMap(services); - } - - private UUID getUuidForAddressSpaceType(AddressSpaceType addressSpaceType) { - return UUID.nameUUIDFromBytes(("service-enmasse-"+addressSpaceType.getName()).getBytes(StandardCharsets.US_ASCII)); - } - - - private List populatePlans(AddressSpaceType addressSpaceType) { - List plans = new ArrayList<>(); - for(AddressSpacePlan addressSpacePlan : addressSpaceType.getPlans()) { - // TODO - boolean isFree = true; - Plan plan = new Plan(UUID.nameUUIDFromBytes(("plan-"+getUuidForAddressSpaceType(addressSpaceType)+"-"+addressSpacePlan.getMetadata().getName()).getBytes(StandardCharsets.US_ASCII)), - addressSpacePlan.getMetadata().getName(), addressSpacePlan.getShortDescription(), isFree, true); - - ObjectSchema bindParameters = new ObjectSchema(); - StringSchema sendAddressProperty = new StringSchema(); - sendAddressProperty.setDescription("Addresses which the bound application will have permission to send to"); - sendAddressProperty.setRequired(false); - sendAddressProperty.setPattern(addressRegexp); - sendAddressProperty.setDefault("*"); - - StringSchema receiveAddressProperty = new StringSchema(); - receiveAddressProperty.setDescription("Addresses which the bound application will have permission to receive from"); - receiveAddressProperty.setRequired(false); - receiveAddressProperty.setPattern(addressRegexp); - receiveAddressProperty.setDefault("*"); - - - bindParameters.putProperty("sendAddresses", sendAddressProperty); - bindParameters.putProperty("receiveAddresses", receiveAddressProperty); - InputParameters bindParametersSchema = new InputParameters(bindParameters); - ServiceBindingSchema bindSchema = new ServiceBindingSchema(bindParametersSchema); - ObjectSchema serviceCreateParameters = new ObjectSchema(); - StringSchema instanceNameProperty = new StringSchema(); - instanceNameProperty.setDescription("The name of the address space to create"); - instanceNameProperty.setRequired(true); - instanceNameProperty.setMinLength(1); - instanceNameProperty.setMaxLength(64); - instanceNameProperty.setPattern("^[a-z][a-z0-9-]{0,63}$"); - - StringSchema instanceNamespaceProperty = new StringSchema(); - instanceNamespaceProperty.setDescription("The namespace of the address space to create"); - instanceNamespaceProperty.setRequired(true); - instanceNamespaceProperty.setMinLength(1); - instanceNamespaceProperty.setMaxLength(64); - instanceNamespaceProperty.setPattern("^[a-z][a-z0-9-]{0,63}$"); - serviceCreateParameters.putProperty("name", instanceNameProperty); - serviceCreateParameters.putProperty("namespace", instanceNamespaceProperty); - InputParameters createParametersSchema = new InputParameters(serviceCreateParameters); - InputParameters updateParametersSchema = null; - ServiceInstanceSchema instanceSchema = new ServiceInstanceSchema(createParametersSchema, updateParametersSchema); - Schemas schemas = new Schemas(instanceSchema, bindSchema); - plan.setSchemas(schemas); - plans.add(plan); - } - return plans; - } - - public List getServices() { - return new ArrayList<>(services.values()); - } - - public Service getService(UUID serviceId) { - for(Service service : services.values()) { - if(service.getUuid().equals(serviceId)) { - return service; - } - } - return null; - } - - private Optional getServiceForAddressSpaceType(AddressSpaceType type) { - return Optional.ofNullable(services.get(type)); - } - - public Optional getServiceForAddressSpaceType(String type) { - return schema.findAddressSpaceType(type).flatMap(this::getServiceForAddressSpaceType); - } - - public AddressSpaceType getAddressSpaceTypeForService(Service service) { - for(Map.Entry entry : services.entrySet()) { - if(entry.getValue().equals(service)) { - return entry.getKey(); - } - } - return null; - } -} diff --git a/service-broker/src/main/java/io/enmasse/osb/api/ServiceType.java b/service-broker/src/main/java/io/enmasse/osb/api/ServiceType.java deleted file mode 100644 index 3c187d4cc5c..00000000000 --- a/service-broker/src/main/java/io/enmasse/osb/api/ServiceType.java +++ /dev/null @@ -1,47 +0,0 @@ -/* - * Copyright 2018, EnMasse authors. - * License: Apache License 2.0 (see the file LICENSE or http://apache.org/licenses/LICENSE-2.0.html). - */ -package io.enmasse.osb.api; - -import java.util.Optional; -import java.util.UUID; - -public enum ServiceType { - // TODO: These are tied to the 'standard' address space - ANYCAST("ac6348d6-eeea-43e5-9b97-5ed18da5dcaf", "enmasse-anycast", "anycast"), - MULTICAST("7739ea7d-8de4-4fe8-8297-90f703904587", "enmasse-multicast", "multicast"), - QUEUE("7739ea7d-8de4-4fe8-8297-90f703904589", "enmasse-queue", "queue"), - TOPIC("7739ea7d-8de4-4fe8-8297-90f703904590", "enmasse-topic", "topic"); - - private UUID uuid; - private String serviceName; - private String addressType; - - ServiceType(String uuid, String serviceName, String addressType) { - this.uuid = UUID.fromString(uuid); - this.serviceName = serviceName; - this.addressType = addressType; - } - - public static Optional valueOf(UUID uuid) { - for (ServiceType serviceType : values()) { - if (serviceType.uuid().equals(uuid)) { - return Optional.of(serviceType); - } - } - return Optional.empty(); - } - - public UUID uuid() { - return uuid; - } - - public String serviceName() { - return serviceName; - } - - public String addressType() { - return addressType; - } -} diff --git a/service-broker/src/main/java/io/enmasse/osb/api/bind/BindRequest.java b/service-broker/src/main/java/io/enmasse/osb/api/bind/BindRequest.java deleted file mode 100644 index 61316f51e40..00000000000 --- a/service-broker/src/main/java/io/enmasse/osb/api/bind/BindRequest.java +++ /dev/null @@ -1,128 +0,0 @@ -/* - * Copyright 2018, EnMasse authors. - * License: Apache License 2.0 (see the file LICENSE or http://apache.org/licenses/LICENSE-2.0.html). - */ -package io.enmasse.osb.api.bind; - -import java.io.IOException; -import java.util.HashMap; -import java.util.Iterator; -import java.util.Map; -import java.util.UUID; - -import com.fasterxml.jackson.core.JsonParser; -import com.fasterxml.jackson.databind.DeserializationContext; -import com.fasterxml.jackson.databind.JsonDeserializer; -import com.fasterxml.jackson.databind.JsonNode; -import com.fasterxml.jackson.databind.ObjectMapper; -import com.fasterxml.jackson.databind.annotation.JsonDeserialize; -import com.fasterxml.jackson.databind.node.ObjectNode; - -import static io.enmasse.osb.api.JsonSerializationUtils.getUuid; - -@JsonDeserialize(using = BindRequest.Deserializer.class) -public class BindRequest { - private static final ObjectMapper mapper = new ObjectMapper(); - - private UUID serviceId; - private UUID planId; - private BindResource bindResource; - private Map parameters = new HashMap<>(); - - public BindRequest(UUID serviceId, UUID planId) { - this.serviceId = serviceId; - this.planId = planId; - } - - public UUID getServiceId() { - return serviceId; - } - - public void setServiceId(UUID serviceId) { - this.serviceId = serviceId; - } - - public UUID getPlanId() { - return planId; - } - - public void setPlanId(UUID planId) { - this.planId = planId; - } - - public BindResource getBindResource() { - return bindResource; - } - - public void setBindResource(BindResource bindResource) { - this.bindResource = bindResource; - } - - public Map getParameters() { - return parameters; - } - - public void setParameters(Map parameters) { - this.parameters = parameters; - } - - protected static class Deserializer extends JsonDeserializer { - - @Override - public BindRequest deserialize(JsonParser p, DeserializationContext ctxt) throws IOException { - ObjectNode node = mapper.readValue(p, ObjectNode.class); - - BindRequest bindRequest = new BindRequest( - getUuid(node, "service_id"), - getUuid(node, "plan_id") - ); - - ObjectNode bindResourceNode = (ObjectNode) node.get("bind_resource"); - if (bindResourceNode != null) { - JsonNode appGuid = bindResourceNode.get("app_guid"); - JsonNode route = bindResourceNode.get("route"); - bindRequest.setBindResource(new BindResource( - appGuid == null ? null : appGuid.asText(), - route == null ? null : route.asText() - )); - } - - ObjectNode parametersNode = (ObjectNode) node.get("parameters"); - if (parametersNode != null) { - Iterator> iterator = parametersNode.fields(); - while (iterator.hasNext()) { - Map.Entry entry = iterator.next(); - bindRequest.getParameters().put(entry.getKey(), entry.getValue().asText()); - } - } - return bindRequest; - } - } - - public static class BindResource { - private String appId; - private String route; - - public BindResource(String appId, String route) { - this.appId = appId; - this.route = route; - } - - public String getAppId() { - return appId; - } - - public void setAppId(String appId) { - this.appId = appId; - } - - public String getRoute() { - return route; - } - - public void setRoute(String route) { - this.route = route; - } - } - -} diff --git a/service-broker/src/main/java/io/enmasse/osb/api/bind/BindResponse.java b/service-broker/src/main/java/io/enmasse/osb/api/bind/BindResponse.java deleted file mode 100644 index e764c9f2881..00000000000 --- a/service-broker/src/main/java/io/enmasse/osb/api/bind/BindResponse.java +++ /dev/null @@ -1,85 +0,0 @@ -/* - * Copyright 2018, EnMasse authors. - * License: Apache License 2.0 (see the file LICENSE or http://apache.org/licenses/LICENSE-2.0.html). - */ -package io.enmasse.osb.api.bind; - -import java.io.IOException; -import java.util.ArrayList; -import java.util.List; -import java.util.Map; - -import com.fasterxml.jackson.core.JsonGenerator; -import com.fasterxml.jackson.databind.JsonSerializer; -import com.fasterxml.jackson.databind.ObjectMapper; -import com.fasterxml.jackson.databind.SerializerProvider; -import com.fasterxml.jackson.databind.annotation.JsonSerialize; -import com.fasterxml.jackson.databind.node.ObjectNode; - -@JsonSerialize(using = BindResponse.Serializer.class) -public class BindResponse { - private static final ObjectMapper mapper = new ObjectMapper(); - - private Map credentials; - private String syslogDrainUrl; - private String routeServiceUrl; - private List volumeMounts = new ArrayList<>(); - - public BindResponse(Map credentials) { - this.credentials = credentials; - } - - public Map getCredentials() { - return credentials; - } - - public void setCredentials(Map credentials) { - this.credentials = credentials; - } - - public String getSyslogDrainUrl() { - return syslogDrainUrl; - } - - public void setSyslogDrainUrl(String syslogDrainUrl) { - this.syslogDrainUrl = syslogDrainUrl; - } - - public String getRouteServiceUrl() { - return routeServiceUrl; - } - - public void setRouteServiceUrl(String routeServiceUrl) { - this.routeServiceUrl = routeServiceUrl; - } - - public List getVolumeMounts() { - return volumeMounts; - } - - public void setVolumeMounts(List volumeMounts) { - this.volumeMounts = volumeMounts; - } - - protected static class Serializer extends JsonSerializer { - @Override - public void serialize(BindResponse value, JsonGenerator gen, SerializerProvider serializers) throws IOException { - ObjectNode node = mapper.createObjectNode(); - - if (value.getSyslogDrainUrl() != null) { - node.put("syslog_drain_url", value.getSyslogDrainUrl()); - } - - if (value.getRouteServiceUrl() != null) { - node.put("route_service_url", value.getRouteServiceUrl()); - } - - // TODO volumeMounts - - ObjectNode credentialsNode = node.putObject("credentials"); - value.getCredentials().forEach(credentialsNode::put); - - mapper.writeValue(gen, node); - } - } -} diff --git a/service-broker/src/main/java/io/enmasse/osb/api/bind/OSBBindingService.java b/service-broker/src/main/java/io/enmasse/osb/api/bind/OSBBindingService.java deleted file mode 100644 index ae99f810a8d..00000000000 --- a/service-broker/src/main/java/io/enmasse/osb/api/bind/OSBBindingService.java +++ /dev/null @@ -1,214 +0,0 @@ -/* - * Copyright 2018, EnMasse authors. - * License: Apache License 2.0 (see the file LICENSE or http://apache.org/licenses/LICENSE-2.0.html). - */ -package io.enmasse.osb.api.bind; - -import io.enmasse.address.model.AddressSpace; -import io.enmasse.address.model.EndpointSpec; -import io.enmasse.address.model.EndpointStatus; -import io.enmasse.api.auth.AuthApi; -import io.enmasse.api.auth.ResourceVerb; -import io.enmasse.api.common.Exceptions; -import io.enmasse.k8s.api.AddressSpaceApi; -import io.enmasse.k8s.api.SchemaProvider; -import io.enmasse.osb.UserApi; -import io.enmasse.osb.api.EmptyResponse; -import io.enmasse.osb.api.OSBServiceBase; -import io.enmasse.user.model.v1.Operation; -import io.enmasse.user.model.v1.User; -import io.enmasse.user.model.v1.UserAuthenticationBuilder; -import io.enmasse.user.model.v1.UserAuthenticationType; -import io.enmasse.user.model.v1.UserAuthorization; -import io.enmasse.user.model.v1.UserAuthorizationBuilder; -import io.enmasse.user.model.v1.UserBuilder; -import io.enmasse.user.model.v1.UserSpecBuilder; - -import javax.ws.rs.Consumes; -import javax.ws.rs.DELETE; -import javax.ws.rs.InternalServerErrorException; -import javax.ws.rs.PUT; -import javax.ws.rs.Path; -import javax.ws.rs.PathParam; -import javax.ws.rs.Produces; -import javax.ws.rs.core.Context; -import javax.ws.rs.core.MediaType; -import javax.ws.rs.core.Response; -import javax.ws.rs.core.SecurityContext; -import java.nio.charset.StandardCharsets; -import java.security.SecureRandom; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Base64; -import java.util.HashSet; -import java.util.LinkedHashMap; -import java.util.List; -import java.util.Map; -import java.util.Random; -import java.util.Set; - -@Path(OSBServiceBase.BASE_URI + "/service_instances/{instanceId}/service_bindings/{bindingId}") -@Consumes({MediaType.APPLICATION_JSON}) -@Produces({MediaType.APPLICATION_JSON}) -public class OSBBindingService extends OSBServiceBase { - - private final UserApi userApi; - private final Random random = new SecureRandom(); - - public OSBBindingService(AddressSpaceApi addressSpaceApi, AuthApi authApi, SchemaProvider schemaProvider, UserApi userApi) { - super(addressSpaceApi, authApi, schemaProvider); - this.userApi = userApi; - } - - @PUT - public Response bindServiceInstance(@Context SecurityContext securityContext, @PathParam("instanceId") String instanceId, @PathParam("bindingId") String bindingId, BindRequest bindRequest) { - log.info("Received bind request for instance {}, binding {} (service id {}, plan id {})", - instanceId, bindingId, bindRequest.getServiceId(), bindRequest.getPlanId()); - verifyAuthorized(securityContext, ResourceVerb.get); - AddressSpace addressSpace = findAddressSpaceByInstanceId(instanceId) - .orElseThrow(() -> Exceptions.notFoundException("Service instance " + instanceId + " does not exist")); - - Map parameters = bindRequest.getParameters(); - - String username = "user-" + bindingId; - String password = generatePassword(); - - try { - - createOrReplaceUser(addressSpace, username, password, parameters); - - Map credentials = new LinkedHashMap<>(); - credentials.put("username", username); - credentials.put("password", password); - - for (EndpointSpec endpointSpec : addressSpace.getSpec().getEndpoints()) { - if (endpointSpec.getService().startsWith("console")) { - continue; - } - String prefix = endpointSpec.getName(); - - EndpointStatus endpointStatus = null; - for (EndpointStatus status : addressSpace.getStatus().getEndpointStatuses()) { - if (status.getName().equals(endpointSpec.getName())) { - endpointStatus = status; - break; - } - } - if (endpointStatus == null) { - continue; - } - - String externalPrefix = "external" + prefix.substring(0, 1).toUpperCase() + prefix.substring(1); - credentials.put(externalPrefix + "Host", endpointStatus.getExternalHost()); - credentials.put(externalPrefix + "Port", String.format("%d", endpointStatus.getExternalPorts().values().iterator().next())); - credentials.put(prefix + "Host", endpointStatus.getServiceHost()); - for (Map.Entry servicePort : endpointStatus.getServicePorts().entrySet()) { - String portName = servicePort.getKey().substring(0, 1).toUpperCase() + servicePort.getKey().substring(1); - credentials.put(prefix + portName + "Port", String.format("%d", servicePort.getValue())); - } - if(endpointSpec.getCert() !=null) { - String cert = getAuthApi().getCert(endpointSpec.getCert().getSecretName()); - credentials.put(prefix + "Cert.pem", cert); - } - } - return Response.status(Response.Status.CREATED).entity(new BindResponse(credentials)).build(); - - } catch (Exception e) { - throw new InternalServerErrorException("Exception interacting with auth service", e); - } - - // TODO: return 200 OK, when binding already exists - - } - - private User createOrReplaceUser(AddressSpace addressSpace, String username, String password, Map parameters) throws Exception { - UserSpecBuilder specBuilder = new UserSpecBuilder(); - specBuilder.withUsername(username); - specBuilder.withAuthentication(new UserAuthenticationBuilder() - .withType(UserAuthenticationType.password) - .withPassword(Base64.getEncoder().encodeToString(password.getBytes(StandardCharsets.UTF_8))) - .build()); - - - List authorizations = new ArrayList<>(); - - authorizations.add(new UserAuthorizationBuilder() - .withOperations(Arrays.asList(Operation.send)) - .withAddresses(getAddresses(parameters.get("sendAddresses"))) - .build()); - - authorizations.add(new UserAuthorizationBuilder() - .withOperations(Arrays.asList(Operation.recv)) - .withAddresses(getAddresses(parameters.get("receiveAddresses"))) - .build()); - - specBuilder.withAuthorization(authorizations); - - User user = new UserBuilder() - .withNewMetadata() - .withNamespace(addressSpace.getMetadata().getNamespace()) - .withName(addressSpace.getMetadata().getName() + "." + username) - .endMetadata() - - .withSpec(specBuilder.build()) - .build(); - - userApi.createOrReplace(user); - return user; - } - - private static final String PASSWORD_CHARACTERS = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_"; - private static final int passwordLength = 32; - - private String generatePassword() { - StringBuilder builder = new StringBuilder(); - int length = passwordLength; - while (length-- != 0) { - int character = (int)(random.nextDouble()*PASSWORD_CHARACTERS.length()); - builder.append(PASSWORD_CHARACTERS.charAt(character)); - } - return builder.toString(); - } - - private List getAddresses(String addressList) { - final Set groups = new HashSet<>(); - if(addressList != null) { - for(String address : addressList.split(",")) { - address = address.trim(); - if(address.length()>0) { - groups.add(address); - } - } - } - return new ArrayList<>(groups); - } - - @DELETE - public Response unbindServiceInstance(@Context SecurityContext securityContext, @PathParam("instanceId") String instanceId, @PathParam("bindingId") String bindingId) { - log.info("Received unbind request for instance {}, binding {}", instanceId, bindingId); - verifyAuthorized(securityContext, ResourceVerb.get); - - AddressSpace addressSpace = findAddressSpaceByInstanceId(instanceId).orElse(null); - if (addressSpace == null) { - return Response.status(Response.Status.GONE).entity("{}").build(); - } - - try { - String username = "user-" + bindingId; - - if(deleteUser(addressSpace, username)) { - return Response.ok(new EmptyResponse()).build(); - } else { - return Response.status(Response.Status.GONE).entity("{}").build(); - } - } catch (Exception e) { - throw new InternalServerErrorException("Exception interacting with auth service", e); - } - - } - - private boolean deleteUser(AddressSpace addressSpace, String username) throws Exception { - return userApi.deleteUser(addressSpace.getMetadata().getNamespace(), addressSpace.getMetadata().getName() + "." + username); - } - -} diff --git a/service-broker/src/main/java/io/enmasse/osb/api/catalog/CatalogResponse.java b/service-broker/src/main/java/io/enmasse/osb/api/catalog/CatalogResponse.java deleted file mode 100644 index 53df7e89391..00000000000 --- a/service-broker/src/main/java/io/enmasse/osb/api/catalog/CatalogResponse.java +++ /dev/null @@ -1,52 +0,0 @@ -/* - * Copyright 2018, EnMasse authors. - * License: Apache License 2.0 (see the file LICENSE or http://apache.org/licenses/LICENSE-2.0.html). - */ -package io.enmasse.osb.api.catalog; - -import java.io.IOException; -import java.util.List; - -import com.fasterxml.jackson.core.JsonGenerator; -import com.fasterxml.jackson.databind.JsonNode; -import com.fasterxml.jackson.databind.JsonSerializer; -import com.fasterxml.jackson.databind.ObjectMapper; -import com.fasterxml.jackson.databind.SerializerProvider; -import com.fasterxml.jackson.databind.annotation.JsonSerialize; -import com.fasterxml.jackson.databind.node.ArrayNode; -import com.fasterxml.jackson.databind.node.ObjectNode; - -@JsonSerialize(using = CatalogResponse.Serializer.class) -public class CatalogResponse { - private static final ObjectMapper mapper = new ObjectMapper(); - - private List services; - - public CatalogResponse(List services) { - this.services = services; - } - - public List getServices() { - return services; - } - - public void setServices(List services) { - this.services = services; - } - - protected static class Serializer extends JsonSerializer { - @Override - public void serialize(CatalogResponse value, JsonGenerator gen, SerializerProvider serializers) throws IOException { - ObjectNode node = mapper.createObjectNode(); - - ArrayNode services = node.putArray("services"); - - value.getServices().stream().forEach(service -> { - JsonNode jsonNode = mapper.valueToTree(service); - services.add(jsonNode); - }); - - mapper.writeValue(gen, node); - } - } -} diff --git a/service-broker/src/main/java/io/enmasse/osb/api/catalog/DashboardClient.java b/service-broker/src/main/java/io/enmasse/osb/api/catalog/DashboardClient.java deleted file mode 100644 index 4866518b461..00000000000 --- a/service-broker/src/main/java/io/enmasse/osb/api/catalog/DashboardClient.java +++ /dev/null @@ -1,58 +0,0 @@ -/* - * Copyright 2018, EnMasse authors. - * License: Apache License 2.0 (see the file LICENSE or http://apache.org/licenses/LICENSE-2.0.html). - */ -package io.enmasse.osb.api.catalog; - -import java.io.IOException; - -import com.fasterxml.jackson.core.JsonGenerator; -import com.fasterxml.jackson.databind.JsonSerializer; -import com.fasterxml.jackson.databind.ObjectMapper; -import com.fasterxml.jackson.databind.SerializerProvider; -import com.fasterxml.jackson.databind.annotation.JsonSerialize; -import com.fasterxml.jackson.databind.node.ObjectNode; - -@JsonSerialize(using = DashboardClient.Serializer.class) -public class DashboardClient { - private static final ObjectMapper mapper = new ObjectMapper(); - - private String id; - private String secret; - private String redirectUri; - - public String getId() { - return id; - } - - public void setId(String id) { - this.id = id; - } - - public String getSecret() { - return secret; - } - - public void setSecret(String secret) { - this.secret = secret; - } - - public String getRedirectUri() { - return redirectUri; - } - - public void setRedirectUri(String redirectUri) { - this.redirectUri = redirectUri; - } - - protected static class Serializer extends JsonSerializer { - @Override - public void serialize(DashboardClient value, JsonGenerator gen, SerializerProvider serializers) throws IOException { - ObjectNode node = mapper.createObjectNode(); - node.put("id", value.getId()); - node.put("secret", value.getSecret()); - node.put("redirect_uri", value.getRedirectUri()); - mapper.writeValue(gen, node); - } - } -} diff --git a/service-broker/src/main/java/io/enmasse/osb/api/catalog/InputParameters.java b/service-broker/src/main/java/io/enmasse/osb/api/catalog/InputParameters.java deleted file mode 100644 index b110d3b7006..00000000000 --- a/service-broker/src/main/java/io/enmasse/osb/api/catalog/InputParameters.java +++ /dev/null @@ -1,42 +0,0 @@ -/* - * Copyright 2018, EnMasse authors. - * License: Apache License 2.0 (see the file LICENSE or http://apache.org/licenses/LICENSE-2.0.html). - */ -package io.enmasse.osb.api.catalog; - -import com.fasterxml.jackson.core.JsonGenerator; -import com.fasterxml.jackson.databind.JsonSerializer; -import com.fasterxml.jackson.databind.ObjectMapper; -import com.fasterxml.jackson.databind.SerializerProvider; -import com.fasterxml.jackson.databind.annotation.JsonSerialize; -import com.fasterxml.jackson.databind.node.ObjectNode; -import com.fasterxml.jackson.module.jsonSchema.JsonSchema; - -import java.io.IOException; - -@JsonSerialize(using = InputParameters.Serializer.class) -public class InputParameters { - private static final ObjectMapper mapper = new ObjectMapper(); - - private JsonSchema parameters; - - public InputParameters() { - } - - public InputParameters(JsonSchema parameters) { - this.parameters = parameters; - } - - public JsonSchema getParameters() { - return parameters; - } - - protected static class Serializer extends JsonSerializer { - @Override - public void serialize(InputParameters value, JsonGenerator gen, SerializerProvider serializers) throws IOException { - ObjectNode node = mapper.createObjectNode(); - node.set("parameters", mapper.valueToTree(value.getParameters())); - mapper.writeValue(gen, node); - } - } -} diff --git a/service-broker/src/main/java/io/enmasse/osb/api/catalog/OSBCatalogService.java b/service-broker/src/main/java/io/enmasse/osb/api/catalog/OSBCatalogService.java deleted file mode 100644 index 5dd6350d56c..00000000000 --- a/service-broker/src/main/java/io/enmasse/osb/api/catalog/OSBCatalogService.java +++ /dev/null @@ -1,39 +0,0 @@ -/* - * Copyright 2018, EnMasse authors. - * License: Apache License 2.0 (see the file LICENSE or http://apache.org/licenses/LICENSE-2.0.html). - */ -package io.enmasse.osb.api.catalog; - -import java.util.List; -import javax.ws.rs.GET; -import javax.ws.rs.Path; -import javax.ws.rs.Produces; -import javax.ws.rs.core.Context; -import javax.ws.rs.core.MediaType; -import javax.ws.rs.core.Response; -import javax.ws.rs.core.SecurityContext; - -import io.enmasse.api.auth.AuthApi; -import io.enmasse.api.auth.ResourceVerb; -import io.enmasse.k8s.api.SchemaProvider; -import io.enmasse.osb.api.OSBServiceBase; -import io.enmasse.k8s.api.AddressSpaceApi; - -@Path(OSBServiceBase.BASE_URI + "/catalog") -@Produces({MediaType.APPLICATION_JSON}) -public class OSBCatalogService extends OSBServiceBase { - - public OSBCatalogService(AddressSpaceApi addressSpaceApi, AuthApi authApi, SchemaProvider schemaProvider) { - super(addressSpaceApi, authApi, schemaProvider); - } - - @GET - public Response getCatalog(@Context SecurityContext securityContext) { - log.info("Received catalog request"); - verifyAuthorized(securityContext, ResourceVerb.list); - List services = getServiceMapping().getServices(); - log.info("Returning {} services", services.size()); - return Response.ok(new CatalogResponse(services)).build(); - } - -} diff --git a/service-broker/src/main/java/io/enmasse/osb/api/catalog/Plan.java b/service-broker/src/main/java/io/enmasse/osb/api/catalog/Plan.java deleted file mode 100644 index e96fbaf8219..00000000000 --- a/service-broker/src/main/java/io/enmasse/osb/api/catalog/Plan.java +++ /dev/null @@ -1,116 +0,0 @@ -/* - * Copyright 2018, EnMasse authors. - * License: Apache License 2.0 (see the file LICENSE or http://apache.org/licenses/LICENSE-2.0.html). - */ -package io.enmasse.osb.api.catalog; - -import java.io.IOException; -import java.util.HashMap; -import java.util.Map; -import java.util.UUID; - -import com.fasterxml.jackson.core.JsonGenerator; -import com.fasterxml.jackson.databind.JsonSerializer; -import com.fasterxml.jackson.databind.ObjectMapper; -import com.fasterxml.jackson.databind.SerializerProvider; -import com.fasterxml.jackson.databind.annotation.JsonSerialize; -import com.fasterxml.jackson.databind.node.ObjectNode; - -@JsonSerialize(using = Plan.Serializer.class) -public class Plan { - private static final ObjectMapper mapper = new ObjectMapper(); - - private UUID uuid; - private String name; - private String description; - private Map metadata = new HashMap<>(); - private boolean free; - private boolean bindable; - private Schemas schemas; - - public Plan() { - } - - public Plan(UUID uuid, String name, String description, boolean free, boolean bindable) { - this.uuid = uuid; - this.name = name; - this.description = description; - this.free = free; - this.bindable = bindable; - } - - public UUID getUuid() { - return uuid; - } - - public void setUuid(UUID uuid) { - this.uuid = uuid; - } - - public String getName() { - return name; - } - - public void setName(String name) { - this.name = name; - } - - public String getDescription() { - return description; - } - - public void setDescription(String description) { - this.description = description; - } - - public Map getMetadata() { - return metadata; - } - - public void setMetadata(Map metadata) { - this.metadata = metadata; - } - - public boolean isFree() { - return free; - } - - public void setFree(boolean free) { - this.free = free; - } - - public boolean isBindable() { - return bindable; - } - - public void setBindable(boolean bindable) { - this.bindable = bindable; - } - - public Schemas getSchemas() { - return schemas; - } - - public void setSchemas(Schemas schemas) { - this.schemas = schemas; - } - - protected static class Serializer extends JsonSerializer { - @Override - public void serialize(Plan value, JsonGenerator gen, SerializerProvider serializers) throws IOException { - ObjectNode node = mapper.createObjectNode(); - node.put("id", value.getUuid().toString()); - node.put("name", value.getName()); - node.put("description", value.getDescription()); - node.put("free", value.isFree()); - node.put("bindable", value.isBindable()); - - ObjectNode metadataNode = node.putObject("metadata"); - value.getMetadata().forEach(metadataNode::put); - - node.set("schemas", mapper.valueToTree(value.getSchemas())); - - mapper.writeValue(gen, node); - } - } -} diff --git a/service-broker/src/main/java/io/enmasse/osb/api/catalog/Schemas.java b/service-broker/src/main/java/io/enmasse/osb/api/catalog/Schemas.java deleted file mode 100644 index b099773665c..00000000000 --- a/service-broker/src/main/java/io/enmasse/osb/api/catalog/Schemas.java +++ /dev/null @@ -1,48 +0,0 @@ -/* - * Copyright 2018, EnMasse authors. - * License: Apache License 2.0 (see the file LICENSE or http://apache.org/licenses/LICENSE-2.0.html). - */ -package io.enmasse.osb.api.catalog; - -import com.fasterxml.jackson.core.JsonGenerator; -import com.fasterxml.jackson.databind.JsonSerializer; -import com.fasterxml.jackson.databind.ObjectMapper; -import com.fasterxml.jackson.databind.SerializerProvider; -import com.fasterxml.jackson.databind.annotation.JsonSerialize; -import com.fasterxml.jackson.databind.node.ObjectNode; - -import java.io.IOException; - -@JsonSerialize(using = Schemas.Serializer.class) -public class Schemas { - private static final ObjectMapper mapper = new ObjectMapper(); - - private ServiceInstanceSchema serviceInstance; - private ServiceBindingSchema serviceBinding; - - public Schemas() { - } - - public Schemas(ServiceInstanceSchema serviceInstance, ServiceBindingSchema serviceBinding) { - this.serviceInstance = serviceInstance; - this.serviceBinding = serviceBinding; - } - - public ServiceInstanceSchema getServiceInstance() { - return serviceInstance; - } - - public ServiceBindingSchema getServiceBinding() { - return serviceBinding; - } - - protected static class Serializer extends JsonSerializer { - @Override - public void serialize(Schemas value, JsonGenerator gen, SerializerProvider serializers) throws IOException { - ObjectNode node = mapper.createObjectNode(); - node.set("service_instance", mapper.valueToTree(value.getServiceInstance())); - node.set("service_binding", mapper.valueToTree(value.getServiceBinding())); - mapper.writeValue(gen, node); - } - } -} diff --git a/service-broker/src/main/java/io/enmasse/osb/api/catalog/Service.java b/service-broker/src/main/java/io/enmasse/osb/api/catalog/Service.java deleted file mode 100644 index 3ee7ff474bf..00000000000 --- a/service-broker/src/main/java/io/enmasse/osb/api/catalog/Service.java +++ /dev/null @@ -1,183 +0,0 @@ -/* - * Copyright 2018, EnMasse authors. - * License: Apache License 2.0 (see the file LICENSE or http://apache.org/licenses/LICENSE-2.0.html). - */ -package io.enmasse.osb.api.catalog; - -import java.io.IOException; -import java.util.*; - -import com.fasterxml.jackson.core.JsonGenerator; -import com.fasterxml.jackson.databind.JsonNode; -import com.fasterxml.jackson.databind.JsonSerializer; -import com.fasterxml.jackson.databind.ObjectMapper; -import com.fasterxml.jackson.databind.SerializerProvider; -import com.fasterxml.jackson.databind.annotation.JsonSerialize; -import com.fasterxml.jackson.databind.node.ArrayNode; -import com.fasterxml.jackson.databind.node.ObjectNode; - -@JsonSerialize(using = Service.Serializer.class) -public class Service { - private static final ObjectMapper mapper = new ObjectMapper(); - - private UUID uuid; - private String name; - private String description; - private List tags = new ArrayList<>(); - private List requires = new ArrayList<>(); - private boolean bindable; - private boolean planUpdatable; - private Map metadata = new HashMap<>(); - private DashboardClient dashboardClient; - private List plans = new ArrayList<>(); - - public Service() { - } - - public Service(UUID uuid, String name, String description, boolean bindable) { - this.uuid = uuid; - this.name = name; - this.description = description; - this.bindable = bindable; - } - - - public UUID getUuid() { - return uuid; - } - - public void setUuid(UUID uuid) { - this.uuid = uuid; - } - - public String getName() { - return name; - } - - public void setName(String name) { - this.name = name; - } - - public String getDescription() { - return description; - } - - public void setDescription(String description) { - this.description = description; - } - - public List getTags() { - return tags; - } - - public void setTags(List tags) { - this.tags = tags; - } - - public List getRequires() { - return requires; - } - - public void setRequires(List requires) { - this.requires = requires; - } - - public boolean isBindable() { - return bindable; - } - - public void setBindable(boolean bindable) { - this.bindable = bindable; - } - - public boolean isPlanUpdatable() { - return planUpdatable; - } - - public void setPlanUpdatable(boolean planUpdatable) { - this.planUpdatable = planUpdatable; - } - - public Map getMetadata() { - return metadata; - } - - public void setMetadata(Map metadata) { - this.metadata = metadata; - } - - public DashboardClient getDashboardClient() { - return dashboardClient; - } - - public void setDashboardClient(DashboardClient dashboardClient) { - this.dashboardClient = dashboardClient; - } - - public List getPlans() { - return plans; - } - - public void setPlans(List plans) { - this.plans = plans; - } - - @Override - public String toString() { - return "Service{" + - "uuid=" + uuid + - ", name='" + name + '\'' + - ", description='" + description + '\'' + - ", tags=" + tags + - ", requires=" + requires + - ", bindable=" + bindable + - ", planUpdatable=" + planUpdatable + - ", metadata=" + metadata + - ", dashboardClient=" + dashboardClient + - ", plans=" + plans + - '}'; - } - - public Optional getPlan(UUID planId) { - for(Plan plan : plans) { - if(plan.getUuid().equals(planId)) { - return Optional.of(plan); - } - } - return Optional.empty(); - } - - protected static class Serializer extends JsonSerializer { - @Override - public void serialize(Service service, JsonGenerator gen, SerializerProvider serializers) throws IOException { - ObjectNode node = mapper.createObjectNode(); - - node.put("id", service.getUuid().toString()); - node.put("name", service.getName()); - node.put("description", service.getDescription()); - node.put("bindable", service.isBindable()); - - ArrayNode tagsNode = node.putArray("tags"); - service.getTags().forEach(tagsNode::add); - - ArrayNode requiresNode = node.putArray("requires"); - service.getRequires().forEach(requiresNode::add); - - ObjectNode metadataNode = node.putObject("metadata"); - service.getMetadata().forEach(metadataNode::put); - - if (service.getDashboardClient() != null) { - node.set("dashboard_client", mapper.valueToTree(service.getDashboardClient())); - } - node.put("plan_updateable", service.isPlanUpdatable()); // The e in updateable is in the OSB API spec! Don't change. - - ArrayNode plansNode = node.putArray("plans"); - service.getPlans().forEach(plan -> { - JsonNode jsonNode = mapper.valueToTree(plan); - plansNode.add(jsonNode); - }); - - mapper.writeValue(gen, node); - } - } -} diff --git a/service-broker/src/main/java/io/enmasse/osb/api/catalog/ServiceBindingSchema.java b/service-broker/src/main/java/io/enmasse/osb/api/catalog/ServiceBindingSchema.java deleted file mode 100644 index a63778436b0..00000000000 --- a/service-broker/src/main/java/io/enmasse/osb/api/catalog/ServiceBindingSchema.java +++ /dev/null @@ -1,41 +0,0 @@ -/* - * Copyright 2018, EnMasse authors. - * License: Apache License 2.0 (see the file LICENSE or http://apache.org/licenses/LICENSE-2.0.html). - */ -package io.enmasse.osb.api.catalog; - -import com.fasterxml.jackson.core.JsonGenerator; -import com.fasterxml.jackson.databind.JsonSerializer; -import com.fasterxml.jackson.databind.ObjectMapper; -import com.fasterxml.jackson.databind.SerializerProvider; -import com.fasterxml.jackson.databind.annotation.JsonSerialize; -import com.fasterxml.jackson.databind.node.ObjectNode; - -import java.io.IOException; - -@JsonSerialize(using = ServiceBindingSchema.Serializer.class) -public class ServiceBindingSchema { - private static final ObjectMapper mapper = new ObjectMapper(); - - private InputParameters createParameters; - - public ServiceBindingSchema() { - } - - public ServiceBindingSchema(InputParameters createParameters) { - this.createParameters = createParameters; - } - - public InputParameters getCreateParameters() { - return createParameters; - } - - protected static class Serializer extends JsonSerializer { - @Override - public void serialize(ServiceBindingSchema value, JsonGenerator gen, SerializerProvider serializers) throws IOException { - ObjectNode node = mapper.createObjectNode(); - node.set("create", mapper.valueToTree(value.getCreateParameters())); - mapper.writeValue(gen, node); - } - } -} diff --git a/service-broker/src/main/java/io/enmasse/osb/api/catalog/ServiceInstanceSchema.java b/service-broker/src/main/java/io/enmasse/osb/api/catalog/ServiceInstanceSchema.java deleted file mode 100644 index 772968ed66e..00000000000 --- a/service-broker/src/main/java/io/enmasse/osb/api/catalog/ServiceInstanceSchema.java +++ /dev/null @@ -1,50 +0,0 @@ -/* - * Copyright 2018, EnMasse authors. - * License: Apache License 2.0 (see the file LICENSE or http://apache.org/licenses/LICENSE-2.0.html). - */ -package io.enmasse.osb.api.catalog; - -import com.fasterxml.jackson.core.JsonGenerator; -import com.fasterxml.jackson.databind.JsonSerializer; -import com.fasterxml.jackson.databind.ObjectMapper; -import com.fasterxml.jackson.databind.SerializerProvider; -import com.fasterxml.jackson.databind.annotation.JsonSerialize; -import com.fasterxml.jackson.databind.node.ObjectNode; - -import java.io.IOException; - -@JsonSerialize(using = ServiceInstanceSchema.Serializer.class) -public class ServiceInstanceSchema { - private static final ObjectMapper mapper = new ObjectMapper(); - - private InputParameters createParameters; - private InputParameters updateParameters; - - public ServiceInstanceSchema() { - } - - public ServiceInstanceSchema(InputParameters createParameters, InputParameters updateParameters) { - this.createParameters = createParameters; - this.updateParameters = updateParameters; - } - - public InputParameters getCreateParameters() { - return createParameters; - } - - public InputParameters getUpdateParameters() { - return updateParameters; - } - - protected static class Serializer extends JsonSerializer { - @Override - public void serialize(ServiceInstanceSchema value, JsonGenerator gen, SerializerProvider serializers) throws IOException { - ObjectNode node = mapper.createObjectNode(); - node.set("create", mapper.valueToTree(value.getCreateParameters())); - if(value.getUpdateParameters() != null) { - node.set("update", mapper.valueToTree(value.getUpdateParameters())); - } - mapper.writeValue(gen, node); - } - } -} diff --git a/service-broker/src/main/java/io/enmasse/osb/api/lastoperation/LastOperationResponse.java b/service-broker/src/main/java/io/enmasse/osb/api/lastoperation/LastOperationResponse.java deleted file mode 100644 index c63bed755a8..00000000000 --- a/service-broker/src/main/java/io/enmasse/osb/api/lastoperation/LastOperationResponse.java +++ /dev/null @@ -1,63 +0,0 @@ -/* - * Copyright 2018, EnMasse authors. - * License: Apache License 2.0 (see the file LICENSE or http://apache.org/licenses/LICENSE-2.0.html). - */ -package io.enmasse.osb.api.lastoperation; - -import com.fasterxml.jackson.core.JsonGenerator; -import com.fasterxml.jackson.databind.JsonSerializer; -import com.fasterxml.jackson.databind.ObjectMapper; -import com.fasterxml.jackson.databind.SerializerProvider; -import com.fasterxml.jackson.databind.annotation.JsonSerialize; -import com.fasterxml.jackson.databind.node.ObjectNode; - -import java.io.IOException; - -@JsonSerialize(using = LastOperationResponse.Serializer.class) -public class LastOperationResponse { - private static final ObjectMapper mapper = new ObjectMapper(); - - private LastOperationState state; - private String description; - - public LastOperationResponse() { - } - - public LastOperationResponse(LastOperationState state, String description) { - this.state = state; - this.description = description; - } - - public LastOperationState getState() { - return state; - } - - public String getDescription() { - return description; - } - - protected static class Serializer extends JsonSerializer { - @Override - public void serialize(LastOperationResponse value, JsonGenerator gen, SerializerProvider serializers) throws IOException { - ObjectNode node = mapper.createObjectNode(); - node.put("state", toStateString(value.getState())); - if (value.getDescription() != null) { - node.put("description", value.getDescription()); - } - mapper.writeValue(gen, node); - } - - private String toStateString(LastOperationState state) { - switch (state) { - case IN_PROGRESS: - return "in progress"; - case SUCCEEDED: - return "succeeded"; - case FAILED: - return "failed"; - default: - throw new InternalError("Unknown LastOperationState " + state); - } - } - } -} diff --git a/service-broker/src/main/java/io/enmasse/osb/api/lastoperation/LastOperationState.java b/service-broker/src/main/java/io/enmasse/osb/api/lastoperation/LastOperationState.java deleted file mode 100644 index dae7e6c4bc1..00000000000 --- a/service-broker/src/main/java/io/enmasse/osb/api/lastoperation/LastOperationState.java +++ /dev/null @@ -1,9 +0,0 @@ -/* - * Copyright 2018, EnMasse authors. - * License: Apache License 2.0 (see the file LICENSE or http://apache.org/licenses/LICENSE-2.0.html). - */ -package io.enmasse.osb.api.lastoperation; - -public enum LastOperationState { - IN_PROGRESS, SUCCEEDED, FAILED -} diff --git a/service-broker/src/main/java/io/enmasse/osb/api/lastoperation/OSBLastOperationService.java b/service-broker/src/main/java/io/enmasse/osb/api/lastoperation/OSBLastOperationService.java deleted file mode 100644 index 0308b671c85..00000000000 --- a/service-broker/src/main/java/io/enmasse/osb/api/lastoperation/OSBLastOperationService.java +++ /dev/null @@ -1,62 +0,0 @@ -/* - * Copyright 2018, EnMasse authors. - * License: Apache License 2.0 (see the file LICENSE or http://apache.org/licenses/LICENSE-2.0.html). - */ -package io.enmasse.osb.api.lastoperation; - -import io.enmasse.api.auth.AuthApi; -import io.enmasse.api.auth.ResourceVerb; -import io.enmasse.k8s.api.SchemaProvider; -import io.enmasse.osb.api.OSBServiceBase; -import io.enmasse.address.model.AddressSpace; -import io.enmasse.k8s.api.AddressSpaceApi; - -import javax.ws.rs.*; -import javax.ws.rs.core.Context; -import javax.ws.rs.core.MediaType; -import javax.ws.rs.core.Response; -import javax.ws.rs.core.SecurityContext; -import java.util.Collections; - -@Path(OSBServiceBase.BASE_URI + "/service_instances/{instanceId}/last_operation") -@Consumes({MediaType.APPLICATION_JSON}) -@Produces({MediaType.APPLICATION_JSON}) -public class OSBLastOperationService extends OSBServiceBase { - - public OSBLastOperationService(AddressSpaceApi addressSpaceApi, AuthApi authApi, SchemaProvider schemaProvider) { - super(addressSpaceApi, authApi, schemaProvider); - } - - @GET - public Response getLastOperationStatus(@Context SecurityContext securityContext, - @PathParam("instanceId") String instanceId, - @QueryParam("service_id") String serviceId, - @QueryParam("plan_id") String planId, - @QueryParam("operation") String operation) throws Exception { - - log.info("Received last_operation request for instance {}, operation {}, service id {}, plan id {}", - instanceId, operation, serviceId, planId); - verifyAuthorized(securityContext, ResourceVerb.get); - - AddressSpace instance = findAddressSpaceByInstanceId(instanceId) - .orElse(null); - - if(instance == null) { - log.info("No such address space found"); - return Response.status(Response.Status.GONE).entity(Collections.EMPTY_MAP).build(); - } else { - LastOperationResponse response; - if (instance.getStatus().isReady()) { - log.info("Address space is ready"); - response = new LastOperationResponse(LastOperationState.SUCCEEDED, "All required pods are ready."); - } else { - log.info("Address space is not yet ready"); - response = new LastOperationResponse(LastOperationState.IN_PROGRESS, "Waiting for pods to be ready"); - } - // TODO LastOperationState.FAILED ? - - return Response.ok(response).build(); - } - } - -} diff --git a/service-broker/src/main/java/io/enmasse/osb/api/provision/ConsoleProxy.java b/service-broker/src/main/java/io/enmasse/osb/api/provision/ConsoleProxy.java deleted file mode 100644 index 5625b49af7e..00000000000 --- a/service-broker/src/main/java/io/enmasse/osb/api/provision/ConsoleProxy.java +++ /dev/null @@ -1,11 +0,0 @@ -/* - * Copyright 2018, EnMasse authors. - * License: Apache License 2.0 (see the file LICENSE or http://apache.org/licenses/LICENSE-2.0.html). - */ -package io.enmasse.osb.api.provision; - -import io.enmasse.address.model.AddressSpace; - -public interface ConsoleProxy { - String getConsoleUrl(AddressSpace addressSpace); -} diff --git a/service-broker/src/main/java/io/enmasse/osb/api/provision/OSBProvisioningService.java b/service-broker/src/main/java/io/enmasse/osb/api/provision/OSBProvisioningService.java deleted file mode 100644 index 96b2c4e2cb6..00000000000 --- a/service-broker/src/main/java/io/enmasse/osb/api/provision/OSBProvisioningService.java +++ /dev/null @@ -1,140 +0,0 @@ -/* - * Copyright 2018, EnMasse authors. - * License: Apache License 2.0 (see the file LICENSE or http://apache.org/licenses/LICENSE-2.0.html). - */ -package io.enmasse.osb.api.provision; - -import java.nio.charset.StandardCharsets; -import java.util.Base64; -import java.util.Optional; -import java.util.UUID; -import javax.ws.rs.*; -import javax.ws.rs.core.*; - -import io.enmasse.address.model.AddressSpaceType; -import io.enmasse.api.auth.AuthApi; -import io.enmasse.api.auth.ResourceVerb; -import io.enmasse.k8s.api.SchemaProvider; -import io.enmasse.address.model.AddressSpace; -import io.enmasse.osb.api.EmptyResponse; -import io.enmasse.api.common.Exceptions; -import io.enmasse.osb.api.OSBServiceBase; -import io.enmasse.osb.api.ServiceMapping; -import io.enmasse.osb.api.catalog.Plan; -import io.enmasse.osb.api.catalog.Service; -import io.enmasse.k8s.api.AddressSpaceApi; -import io.vertx.core.buffer.Buffer; -import io.vertx.core.json.JsonObject; - -@Path(OSBServiceBase.BASE_URI + "/service_instances/{instanceId}") -@Consumes({MediaType.APPLICATION_JSON}) -@Produces({MediaType.APPLICATION_JSON}) -public class OSBProvisioningService extends OSBServiceBase { - - private final ConsoleProxy consoleProxy; - - public OSBProvisioningService(AddressSpaceApi addressSpaceApi, AuthApi authApi, SchemaProvider schemaProvider, ConsoleProxy consoleProxy) { - super(addressSpaceApi, authApi, schemaProvider); - this.consoleProxy = consoleProxy; - } - - @PUT - public Response provisionService(@Context SecurityContext securityContext, - @HeaderParam("X-Broker-API-Originating-Identity") String originatingIdentity, - @PathParam("instanceId") String instanceId, - @QueryParam("accepts_incomplete") @DefaultValue("false") boolean acceptsIncomplete, - ProvisionRequest request) throws Exception { - log.info("Parameters: {}", request.getParameters()); - - verifyAuthorized(securityContext, ResourceVerb.create); - - String userName = null; - String userId = null; - if(originatingIdentity != null && originatingIdentity.split(" +").length>1) { - log.info("identity: " + new String(Base64.getDecoder().decode(originatingIdentity.split(" +")[1])), StandardCharsets.UTF_8); - JsonObject object = new JsonObject(Buffer.buffer(Base64.getDecoder().decode(originatingIdentity.split(" +")[1]))); - userName = object.getString("username"); - userId = object.getString("uid"); - } - - if (!acceptsIncomplete) { - throw Exceptions.unprocessableEntityException("AsyncRequired", "This service plan requires client support for asynchronous service operations."); - } - - ServiceMapping serviceMapping = getServiceMapping(); - Service service = serviceMapping.getService(request.getServiceId()); - if(service == null) { - throw Exceptions.badRequestException("Invalid service_id " + request.getServiceId()); - } - - if (!isValidPlan(service, request.getPlanId())) { - throw Exceptions.badRequestException("Invalid plan_id " + request.getPlanId()); - } - - String name = request.getParameter("name").orElse(service.getName() + "-" + shortenUuid(instanceId)); - String namespace = request.getParameter("namespace").orElseThrow(() -> Exceptions.badRequestException("Parameter namespace not set")); - Optional existingAddressSpace = findAddressSpaceByInstanceId(instanceId); - - if (existingAddressSpace.isPresent()) { - AddressSpace addressSpace = existingAddressSpace.get(); - Optional desiredService = serviceMapping.getServiceForAddressSpaceType(addressSpace.getSpec().getType()); - if (desiredService.isPresent() && desiredService.get().equals(service)) { - Optional plan = service.getPlan(request.getPlanId()); - if (plan.isPresent() && plan.get().getName().equals(addressSpace.getSpec().getPlan())) { - String dashboardUrl = consoleProxy.getConsoleUrl(addressSpace); - return Response.ok(new ProvisionResponse(dashboardUrl, "provision")).build(); - } - } - throw Exceptions.conflictException("Service addressspace " + instanceId + " already exists"); - } - - if(findAddressSpace(name, namespace).isPresent()) { - throw Exceptions.conflictException("Service addressspace with name " + name + " in namespace " + namespace + " already exists"); - } - AddressSpaceType addressSpaceType = serviceMapping.getAddressSpaceTypeForService(service); - AddressSpace addressSpace = createAddressSpace(instanceId, name, namespace, addressSpaceType.getName(), service.getPlan(request.getPlanId()).get().getName(), userId, userName); - - String dashboardUrl = consoleProxy.getConsoleUrl(addressSpace); - - log.info("Returning ProvisionResponse with dashboardUrl {}", dashboardUrl); - return Response.status(Response.Status.ACCEPTED) - .entity(new ProvisionResponse(dashboardUrl, "provision")) - .build(); - } - - private boolean isValidPlan(Service service, UUID planId) { - return service.getPlans().stream().anyMatch(plan -> plan.getUuid().equals(planId)); - } - - // TODO: @PATCH updateService - - @DELETE - public Response deprovisionService(@Context SecurityContext securityContext, @PathParam("instanceId") String instanceId, @QueryParam("service_id") String serviceId, @QueryParam("plan_id") String planId) { - verifyAuthorized(securityContext, ResourceVerb.delete); - log.info("Received deprovision request for addressspace {} (service id {}, plan id {})", - instanceId, serviceId, planId); - - if (serviceId == null) { - throw Exceptions.badRequestException("Missing service_id parameter"); - } - if (planId == null) { - throw Exceptions.badRequestException("Missing plan_id parameter"); - } - AddressSpace addressSpace = findAddressSpaceByInstanceId(instanceId).orElse(null); - if (addressSpace == null) { - return Response.status(Response.Status.GONE).entity("{}").build(); - } - deleteAddressSpace(addressSpace); - return Response.ok(new EmptyResponse()).build(); - - } - - - private String shortenUuid(String uuid) { - int dashIndex = uuid.indexOf('-'); - if (dashIndex == -1) { - throw Exceptions.badRequestException("Bad UUID: " + uuid); - } - return uuid.substring(0, dashIndex); - } -} diff --git a/service-broker/src/main/java/io/enmasse/osb/api/provision/ProvisionRequest.java b/service-broker/src/main/java/io/enmasse/osb/api/provision/ProvisionRequest.java deleted file mode 100644 index 93a6b3a7119..00000000000 --- a/service-broker/src/main/java/io/enmasse/osb/api/provision/ProvisionRequest.java +++ /dev/null @@ -1,118 +0,0 @@ -/* - * Copyright 2018, EnMasse authors. - * License: Apache License 2.0 (see the file LICENSE or http://apache.org/licenses/LICENSE-2.0.html). - */ -package io.enmasse.osb.api.provision; - -import java.io.IOException; -import java.util.HashMap; -import java.util.Iterator; -import java.util.Map; -import java.util.Optional; -import java.util.UUID; - -import com.fasterxml.jackson.core.JsonParser; -import com.fasterxml.jackson.databind.DeserializationContext; -import com.fasterxml.jackson.databind.JsonDeserializer; -import com.fasterxml.jackson.databind.JsonNode; -import com.fasterxml.jackson.databind.ObjectMapper; -import com.fasterxml.jackson.databind.annotation.JsonDeserialize; -import com.fasterxml.jackson.databind.node.ObjectNode; - -import static io.enmasse.osb.api.JsonSerializationUtils.getRequiredField; -import static io.enmasse.osb.api.JsonSerializationUtils.getUuid; - -@JsonDeserialize(using = ProvisionRequest.Deserializer.class) -public class ProvisionRequest { - private static final ObjectMapper mapper = new ObjectMapper(); - - private String organizationId; - private String spaceId; - private UUID serviceId; - private UUID planId; - private Map parameters = new HashMap<>(); - - public ProvisionRequest() { - } - - public ProvisionRequest(UUID serviceId, UUID planId, String organizationId, String spaceId) { - this.serviceId = serviceId; - this.planId = planId; - this.organizationId = organizationId; - this.spaceId = spaceId; - } - - public String getOrganizationId() { - return organizationId; - } - - public void setOrganizationId(String organizationId) { - this.organizationId = organizationId; - } - - public String getSpaceId() { - return spaceId; - } - - public void setSpaceId(String spaceId) { - this.spaceId = spaceId; - } - - public UUID getServiceId() { - return serviceId; - } - - public void setServiceId(UUID serviceId) { - this.serviceId = serviceId; - } - - public UUID getPlanId() { - return planId; - } - - public void setPlanId(UUID planId) { - this.planId = planId; - } - - public Map getParameters() { - return parameters; - } - - public void setParameters(Map parameters) { - this.parameters = parameters; - } - - public Optional getParameter(String name) { - return Optional.ofNullable(parameters.get(name)); - } - - public void putParameter(String name, String value) { - parameters.put(name, value); - } - - protected static class Deserializer extends JsonDeserializer { - - @Override - public ProvisionRequest deserialize(JsonParser p, DeserializationContext ctxt) throws IOException { - ObjectNode node = mapper.readValue(p, ObjectNode.class); - - ProvisionRequest provisionRequest = new ProvisionRequest( - getUuid(node, "service_id"), - getUuid(node, "plan_id"), - getRequiredField(node, "organization_guid").asText(), - getRequiredField(node, "space_guid").asText()); - - ObjectNode parametersNode = (ObjectNode) node.get("parameters"); - if (parametersNode != null) { - Iterator> iterator = parametersNode.fields(); - while (iterator.hasNext()) { - Map.Entry entry = iterator.next(); - provisionRequest.getParameters().put(entry.getKey(), entry.getValue().asText()); - } - } - - return provisionRequest; - } - - } -} diff --git a/service-broker/src/main/java/io/enmasse/osb/api/provision/ProvisionResponse.java b/service-broker/src/main/java/io/enmasse/osb/api/provision/ProvisionResponse.java deleted file mode 100644 index 20babbf887d..00000000000 --- a/service-broker/src/main/java/io/enmasse/osb/api/provision/ProvisionResponse.java +++ /dev/null @@ -1,65 +0,0 @@ -/* - * Copyright 2018, EnMasse authors. - * License: Apache License 2.0 (see the file LICENSE or http://apache.org/licenses/LICENSE-2.0.html). - */ -package io.enmasse.osb.api.provision; - -import java.io.IOException; - -import com.fasterxml.jackson.core.JsonGenerator; -import com.fasterxml.jackson.databind.JsonSerializer; -import com.fasterxml.jackson.databind.ObjectMapper; -import com.fasterxml.jackson.databind.SerializerProvider; -import com.fasterxml.jackson.databind.annotation.JsonSerialize; -import com.fasterxml.jackson.databind.node.ObjectNode; - -@JsonSerialize(using = ProvisionResponse.Serializer.class) -public class ProvisionResponse { - private static final ObjectMapper mapper = new ObjectMapper(); - - private String dashboardUrl; - private String operation; - - public ProvisionResponse() { - } - - public ProvisionResponse(String dashboardUrl) { - this(dashboardUrl, null); - } - - public ProvisionResponse(String dashboardUrl, String operation) { - this.dashboardUrl = dashboardUrl; - this.operation = operation; - } - - - public String getDashboardUrl() { - return dashboardUrl; - } - - public void setDashboardUrl(String dashboardUrl) { - this.dashboardUrl = dashboardUrl; - } - - public String getOperation() { - return operation; - } - - public void setOperation(String operation) { - this.operation = operation; - } - - protected static class Serializer extends JsonSerializer { - @Override - public void serialize(ProvisionResponse value, JsonGenerator gen, SerializerProvider serializers) throws IOException { - ObjectNode node = mapper.createObjectNode(); - if (value.getDashboardUrl() != null) { - node.put("dashboard_url", value.getDashboardUrl()); - } - if (value.getOperation() != null) { - node.put("operation", value.getOperation()); - } - mapper.writeValue(gen, node); - } - } -} diff --git a/service-broker/src/main/resources/logback.xml b/service-broker/src/main/resources/logback.xml deleted file mode 100644 index 9ed3476bbee..00000000000 --- a/service-broker/src/main/resources/logback.xml +++ /dev/null @@ -1,15 +0,0 @@ - - - - - - - %d{yyyy-MM-dd'T'HH:mm:ss.SSS'Z',GMT} %-5p [%c{0}] %m%n - - - - - - - - diff --git a/service-broker/src/main/resources/service.properties b/service-broker/src/main/resources/service.properties deleted file mode 100644 index 1351dd5bf2d..00000000000 --- a/service-broker/src/main/resources/service.properties +++ /dev/null @@ -1,11 +0,0 @@ -# -# Copyright 2018, EnMasse authors. -# License: Apache License 2.0 (see the file LICENSE or http://apache.org/licenses/LICENSE-2.0.html). -# - -SERVICE_BROKER_SERVICE_NAME_PATTERN=enmasse-{0} -SERVICE_BROKER_SERVICE_TAGS=middleware,messaging,amqp,mqtt,enmasse -SERVICE_BROKER_SERVICE_PROVIDER_NAME=EnMasse -# This is https://raw.githubusercontent.com/EnMasseProject/enmasse/master/documentation/_images/logo/enmasse_icon.png converted to data url -SERVICE_BROKER_SERVICE_IMAGE_URL=data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAEAAAABACAMAAACdt4HsAAACdlBMVEUAAAAA//8AgICAgP8AVaoAQIAAM5lmmcwAVaqAqtUASZIAQJ+An98AVaptpNtwn89xnNV5oddxodByntN3otUASpwASJ8ARpsAS54AR6AASJsASp8ASJ0AS54ASJ0AR58ASpwASZ52odR0ntV2oNEAR54ASp8ASZ0ASJ52oNV0odJ0odN0oNMASJ4ASp8ASJ4ASJ0ASp50oNNzodR2n9MASJ0ASp4ASZ51oNR1oNJ2odR1oNJ0oNN0n9QASZ4ASp0ASZ4ASp52oNMASZ4ASp4ASZ91oNMASZ51odQASp4ASZ11n9N1odR1oNN1oNJ2oNN2oNR1oNN1oNMASZ4ASZ51oNMASZ51oNMASZ4ASZ4ASp4ASZ11oNMASZ4ASZ4ASZ4ASZ4ASZ4ASZ11oNN1oNMASZ4ASZ51oNMASZ4ASZ8ASZ51oNN1oNN1oNN1oNMASZ5zntEASZ51oNMASZ5ThsMASZ4ASZ50oNMASZ51oNM0b7V0oNN1oNN1oNN1oNMASZ51oNMASZ48dbl1oNMASZ4BSZ8BSp4CS58DS58DS6AETKAFTKAGTqEHTqEIT6IJUKIJUKMKUKIKUaIMUqMNU6QPVKQTV6cYW6kaXKkfYawgYKwhYq0iYq0iY64jY64lZK4lZa8nZrAoZ7ArabIsarItarIta7Mua7MwbbQ0cLU4c7g5c7g7dbk8dbk+d7o+d7tCerxFfL1GfL5Hfr5Ifr5Jf79KgL9PhMJQhcNShsNTh8RWiMVYisZZi8Zcjcddjshej8lgkMlgkcphkcpjk8tllMxllcxmlc1ql81qmc5smc9tms9um9BzntJ0n9N1n9N1oNP///8Z9M1aAAAAhHRSTlMAAQICAwQFBQYGBwgICQ4QEhMbHR4fICEiKy4wOTo8PT4/QUJDREVGR05PUVZcXV9gYWNkdXh5e3x+goOGiJOVlpicoaOkpKWlpqeoqq+xtLa6u8rLy83Nzs/Q0dHS1NXW2Nnd3uPk5OXm5+fo6e7v7/Dw8fHy8/P09Pb2+Pr7/Pz+/v4wcsHUAAACfklEQVR4Ae3W+VuMURQH8GOYsSSLJBWTRSQii4aQFiNlETIiypDUWMIo2Y1OSsi+L2Rfyt5CESJk6fxJeJ4a973vfe+8/eqZz8/33Of73Pvccw94/be6mS3JNocTnQ5bksVsgo4xhFkLkVVoHWUA3UyRdlSzj9cbY0gGiq0dCTr4xrlQiyvWB9r4jY2etyxrS9GuTdGgMHAlyqwIAIDu4fEbqN2qnsAKzEY5R3CvaZuJMQJYQXnoQcnurcRKUebPRQ8uNpHSMGD4pKFcceUvUlrXCRhxKHfsNfGmAiPExa2///Tx5WJ0K28k3kE/pt6UiQr3vtMfzTexzekPpLIEGBNQ4RG1eVWGf534SGoTmfoudmRdbaV29UcR8Xgjqe3tDf+EocLD6m/U7u1JPPWeBBYAIxE5ZU/cIb4++0wiY9j+UYgqD0huY1f2DlHgDUnNAIYFBa6TzL7+wEhGgdIWkogHVhqKPCdt2/sBy4EiF0hTUTgoFKDQO9IyHZQOoFAFaTgEoCvB4U8k8uNWPnAcKHZHVF5VjjnAsaFYyRfiNF0qRcSlwElCDdeIV3MWEWcDx4JaXhDvZ93dc1HAMaOWIw0ksGf54vlz5yxaONz9Ggu0d6gjDTtnMh3FippKq0kka3IfYIShxJVm4uxPGd0ZFAx2lCi73dBKbi07JvUFlUiUO3Ojqqa2vvZlZcX54ggQMGWgXulGEAlxoT6uoSAWi/rEgJD8d2ak+4KWgFz0LMcfBHRPKJgXBDKB61EuNxjkBsjPweYPnvjMksyJMT1Ah5DVKJZuBn2M49agWmaEEXQzhCbkI8uZEGqAjjEOjkpMzXHituxU65RBRvDy8uQ3B3DomhmHoYYAAAAASUVORK5CYII= -SERVICE_BROKER_DOCUMENTATION_URL=https://github.com/EnMasseProject/enmasse diff --git a/service-broker/src/test/java/io/enmasse/osb/HTTPServerTest.java b/service-broker/src/test/java/io/enmasse/osb/HTTPServerTest.java deleted file mode 100644 index 07bc46021ee..00000000000 --- a/service-broker/src/test/java/io/enmasse/osb/HTTPServerTest.java +++ /dev/null @@ -1,135 +0,0 @@ -/* - * Copyright 2016-2018, EnMasse authors. - * License: Apache License 2.0 (see the file LICENSE or http://apache.org/licenses/LICENSE-2.0.html). - */ - -package io.enmasse.osb; - -import io.enmasse.address.model.AddressSpace; -import io.enmasse.address.model.AddressSpaceBuilder; -import io.enmasse.address.model.EndpointSpecBuilder; -import io.enmasse.admin.model.v1.AuthenticationService; -import io.enmasse.admin.model.v1.AuthenticationServiceBuilder; -import io.enmasse.api.auth.AuthApi; -import io.enmasse.api.auth.SubjectAccessReview; -import io.enmasse.api.auth.TokenReview; -import io.enmasse.k8s.api.AuthenticationServiceRegistry; -import io.enmasse.k8s.api.TestAddressSpaceApi; -import io.enmasse.osb.api.provision.ConsoleProxy; -import io.fabric8.kubernetes.api.model.ObjectMetaBuilder; -import io.vertx.core.Vertx; -import io.vertx.core.http.HttpClient; -import io.vertx.core.http.HttpClientOptions; -import io.vertx.core.http.HttpClientRequest; -import io.vertx.core.json.JsonObject; -import io.vertx.junit5.VertxExtension; -import io.vertx.junit5.VertxTestContext; -import org.junit.jupiter.api.AfterEach; -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Test; -import org.junit.jupiter.api.extension.ExtendWith; - -import java.util.Optional; -import java.util.concurrent.TimeUnit; - -import static org.junit.jupiter.api.Assertions.assertTrue; -import static org.mockito.ArgumentMatchers.*; -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.when; - -@ExtendWith(VertxExtension.class) -public class HTTPServerTest { - - private Vertx vertx; - private TestAddressSpaceApi instanceApi; - private AddressSpace addressSpace; - private HTTPServer httpServer; - - @BeforeEach - public void setup(VertxTestContext context) { - vertx = Vertx.vertx(); - instanceApi = new TestAddressSpaceApi(); - String addressSpaceName = "myinstance"; - addressSpace = createAddressSpace(addressSpaceName); - instanceApi.createAddressSpace(addressSpace); - - AuthenticationServiceRegistry authenticationServiceRegistry = mock(AuthenticationServiceRegistry.class); - AuthenticationService authenticationService = new AuthenticationServiceBuilder() - .withNewMetadata() - .withName("standard") - .endMetadata() - .withNewStatus() - .withHost("example") - .withPort(5671) - .endStatus() - .build(); - when(authenticationServiceRegistry.findAuthenticationService(any())).thenReturn(Optional.of(authenticationService)); - when(authenticationServiceRegistry.resolveDefaultAuthenticationService()).thenReturn(Optional.of(authenticationService)); - - AuthApi authApi = mock(AuthApi.class); - when(authApi.getNamespace()).thenReturn("controller"); - TokenReview tokenReview = new TokenReview("foo", "myid", null, null, true); - when(authApi.performTokenReview(eq("mytoken"))).thenReturn(tokenReview); - when(authApi.performSubjectAccessReviewResource(eq(tokenReview), any(), any(), any(), anyString())).thenReturn(new SubjectAccessReview("foo", true)); - when(authApi.performSubjectAccessReviewResource(eq(tokenReview), any(), any(), any(), anyString())).thenReturn(new SubjectAccessReview("foo", true)); - httpServer = new HTTPServer(instanceApi, new TestSchemaProvider(), authApi, null, false, null, 0, new ConsoleProxy() { - @Override - public String getConsoleUrl(AddressSpace addressSpace) { - return "http://localhost/console/" + addressSpaceName; - } - }); - vertx.deployVerticle(httpServer, context.succeeding(id -> context.completeNow())); - } - - @AfterEach - public void teardown(VertxTestContext context) { - vertx.close(context.succeeding(id -> context.completeNow())); - } - - private AddressSpace createAddressSpace(String name) { - return new AddressSpaceBuilder() - .withMetadata(new ObjectMetaBuilder() - .withName(name) - .withNamespace(name) - .build()) - - .withNewSpec() - .withType("mytype") - .withPlan("myplan") - - .addToEndpoints(new EndpointSpecBuilder() - .withName("foo") - .withService("messaging") - .build()) - .endSpec() - - .withNewStatus(false) - - .build(); - } - - @Test - public void testOpenServiceBrokerAPI(VertxTestContext context) throws InterruptedException { - HttpClientOptions options = new HttpClientOptions(); - HttpClient client = vertx.createHttpClient(options); - try { - HttpClientRequest request = client.get(httpServer.getActualPort(), "localhost", "/osbapi/v2/catalog", response -> { - response.bodyHandler(buffer -> { - JsonObject data = buffer.toJsonObject(); - context.verify(() -> assertTrue(data.containsKey("services"))); - context.completeNow(); - }); - }); - putAuthzToken(request); - request.end(); - context.awaitCompletion(60, TimeUnit.SECONDS); - } finally { - client.close(); - } - } - - private static HttpClientRequest putAuthzToken(HttpClientRequest request) { - request.putHeader("Authorization", "Bearer mytoken"); - return request; - } -} diff --git a/service-broker/src/test/java/io/enmasse/osb/TestSchemaProvider.java b/service-broker/src/test/java/io/enmasse/osb/TestSchemaProvider.java deleted file mode 100644 index 485b3ba9d04..00000000000 --- a/service-broker/src/test/java/io/enmasse/osb/TestSchemaProvider.java +++ /dev/null @@ -1,18 +0,0 @@ -/* - * Copyright 2016-2018, EnMasse authors. - * License: Apache License 2.0 (see the file LICENSE or http://apache.org/licenses/LICENSE-2.0.html). - */ -package io.enmasse.osb; - -import io.enmasse.address.model.Schema; -import io.enmasse.k8s.api.SchemaProvider; -import io.enmasse.k8s.api.TestSchemaApi; - -public class TestSchemaProvider implements SchemaProvider { - public TestSchemaApi api = new TestSchemaApi(); - - @Override - public Schema getSchema() { - return api.getSchema(); - } -} diff --git a/service-broker/src/test/java/io/enmasse/osb/api/BindingServiceTest.java b/service-broker/src/test/java/io/enmasse/osb/api/BindingServiceTest.java deleted file mode 100644 index c19e2553765..00000000000 --- a/service-broker/src/test/java/io/enmasse/osb/api/BindingServiceTest.java +++ /dev/null @@ -1,146 +0,0 @@ -/* - * Copyright 2016-2018, EnMasse authors. - * License: Apache License 2.0 (see the file LICENSE or http://apache.org/licenses/LICENSE-2.0.html). - */ - -package io.enmasse.osb.api; - -import io.enmasse.api.common.ConflictException; -import io.enmasse.api.common.GoneException; -import io.enmasse.api.common.UnprocessableEntityException; -import io.enmasse.osb.api.bind.BindRequest; -import io.enmasse.osb.api.bind.BindResponse; -import io.enmasse.osb.api.provision.ProvisionRequest; -import org.jboss.resteasy.util.HttpResponseCodes; -import org.junit.jupiter.api.Disabled; -import org.junit.jupiter.api.Test; - -import javax.ws.rs.BadRequestException; -import javax.ws.rs.NotFoundException; -import javax.ws.rs.core.Response; -import javax.ws.rs.core.UriInfo; -import java.util.UUID; - -import static org.hamcrest.CoreMatchers.is; -import static org.hamcrest.CoreMatchers.notNullValue; -import static org.hamcrest.MatcherAssert.assertThat; -import static org.junit.jupiter.api.Assertions.assertThrows; -import static org.mockito.Mockito.mock; - -@Disabled -public class BindingServiceTest extends OSBTestBase { - - public static final String BINDING_ID = UUID.randomUUID().toString(); - - @Override - public void setup() throws Exception { - super.setup(); - provisionService(SERVICE_INSTANCE_ID); - } - - @Test - public void testSyncProvisioningRequest() throws Exception { - UriInfo uriInfo = mock(UriInfo.class); - assertThrows(UnprocessableEntityException.class, - () -> provisioningService.provisionService(getSecurityContext(), null, "123", false, new ProvisionRequest(QUEUE_SERVICE_ID, QUEUE_PLAN_ID, ORGANIZATION_ID, SPACE_ID))); - } - - @Test - @Disabled - public void testBind() throws Exception { - Response response = bindingService.bindServiceInstance(getSecurityContext(), SERVICE_INSTANCE_ID, BINDING_ID, new BindRequest(QUEUE_SERVICE_ID, QUEUE_PLAN_ID)); - BindResponse bindResponse = (BindResponse) response.getEntity(); - - assertThat(response.getStatus(), is(HttpResponseCodes.SC_CREATED)); - assertThat(bindResponse.getCredentials().get("namespace"), notNullValue()); - assertThat(bindResponse.getCredentials().get("destination-address"), notNullValue()); - // TODO: Set fake hosts -// assertThat(bindResponse.getCredentials().get("internal-messaging-host"), notNullValue()); -// assertThat(bindResponse.getCredentials().get("internal-mqtt-host"), notNullValue()); - } - - @Test - public void testInvalidBindingUuid() throws Exception { - bindingService.bindServiceInstance(getSecurityContext(), SERVICE_INSTANCE_ID, "123", new BindRequest(QUEUE_SERVICE_ID, QUEUE_PLAN_ID)); - } - - @Test - @Disabled - public void testBindOnNonexistentService() throws Exception { - assertThrows(NotFoundException.class, - () -> bindingService.bindServiceInstance(getSecurityContext(), UUID.randomUUID().toString(), BINDING_ID, new BindRequest(QUEUE_SERVICE_ID, QUEUE_PLAN_ID))); - } - - @Test - @Disabled - public void testBindWithoutServiceId() throws Exception { - assertThrows(BadRequestException.class, - () -> bindingService.bindServiceInstance(getSecurityContext(), SERVICE_INSTANCE_ID, BINDING_ID, new BindRequest(null, QUEUE_PLAN_ID))); - } - - @Test - @Disabled - public void testBindWithoutPlanId() throws Exception { - assertThrows(BadRequestException.class, - () -> bindingService.bindServiceInstance(getSecurityContext(), SERVICE_INSTANCE_ID, BINDING_ID, new BindRequest(QUEUE_SERVICE_ID, null))); - } - - @Disabled("Not implemented yet") - @Test - public void testWrongServiceId() throws Exception { - assertThrows(BadRequestException.class, - () -> bindingService.bindServiceInstance(getSecurityContext(), SERVICE_INSTANCE_ID, BINDING_ID, new BindRequest(UUID.randomUUID(), QUEUE_PLAN_ID))); - } - - @Disabled("Not implemented yet") - @Test - public void testWrongPlanId() throws Exception { - assertThrows(BadRequestException.class, - () -> bindingService.bindServiceInstance(getSecurityContext(), SERVICE_INSTANCE_ID, BINDING_ID, new BindRequest(QUEUE_SERVICE_ID, UUID.randomUUID()))); - } - - @Disabled("bindings aren't persisted yet, so we can't do this yet") - @Test - public void testBindTwiceWithDifferentPrameters() throws Exception { - bindingService.bindServiceInstance(getSecurityContext(), SERVICE_INSTANCE_ID, BINDING_ID, new BindRequest(QUEUE_SERVICE_ID, QUEUE_PLAN_ID)); - - String otherServiceId = UUID.randomUUID().toString(); - provisionService(otherServiceId); - - assertThrows(ConflictException.class, () -> bindingService.bindServiceInstance(getSecurityContext(), otherServiceId, BINDING_ID, new BindRequest(QUEUE_SERVICE_ID, QUEUE_PLAN_ID))); - } - - @Disabled("bindings aren't persisted yet, so we can't do this yet") - @Test - public void testBindTwiceWithSameParameters() throws Exception { - bindingService.bindServiceInstance(getSecurityContext(), SERVICE_INSTANCE_ID, BINDING_ID, new BindRequest(QUEUE_SERVICE_ID, QUEUE_PLAN_ID)); - - Response response = bindingService.bindServiceInstance(getSecurityContext(), SERVICE_INSTANCE_ID, BINDING_ID, new BindRequest(QUEUE_SERVICE_ID, QUEUE_PLAN_ID)); - assertThat(response.getStatus(), is(HttpResponseCodes.SC_OK)); - } - - @Test - public void testUnbind() throws Exception { - bindingService.bindServiceInstance(getSecurityContext(), SERVICE_INSTANCE_ID, BINDING_ID, new BindRequest(QUEUE_SERVICE_ID, QUEUE_PLAN_ID)); - - Response response = bindingService.unbindServiceInstance(getSecurityContext(), SERVICE_INSTANCE_ID, BINDING_ID); - assertThat(response.getStatus(), is(HttpResponseCodes.SC_OK)); - } - - @Disabled("bindings aren't persisted yet, so we can't do this yet. OSB spec mandates the broker MUST return Gone, when binding doesn't exist") - @Test - public void testUnbindNonexistingBinding() throws Exception { - assertThrows(GoneException.class, - () -> bindingService.unbindServiceInstance(getSecurityContext(), SERVICE_INSTANCE_ID, UUID.randomUUID().toString())); - } - - @Disabled("bindings aren't persisted yet, so we can't do this yet. OSB spec mandates the broker MUST return Gone, when binding doesn't exist") - @Test - public void testUnbindTwice() throws Exception { - bindingService.bindServiceInstance(getSecurityContext(), SERVICE_INSTANCE_ID, BINDING_ID, new BindRequest(QUEUE_SERVICE_ID, QUEUE_PLAN_ID)); - bindingService.unbindServiceInstance(getSecurityContext(), SERVICE_INSTANCE_ID, BINDING_ID); - - assertThrows(GoneException.class, () -> bindingService.unbindServiceInstance(getSecurityContext(), SERVICE_INSTANCE_ID, BINDING_ID)); - } - -} diff --git a/service-broker/src/test/java/io/enmasse/osb/api/CatalogServiceTest.java b/service-broker/src/test/java/io/enmasse/osb/api/CatalogServiceTest.java deleted file mode 100644 index 29aa1c3b869..00000000000 --- a/service-broker/src/test/java/io/enmasse/osb/api/CatalogServiceTest.java +++ /dev/null @@ -1,43 +0,0 @@ -/* - * Copyright 2016-2018, EnMasse authors. - * License: Apache License 2.0 (see the file LICENSE or http://apache.org/licenses/LICENSE-2.0.html). - */ - -package io.enmasse.osb.api; - -import org.junit.jupiter.api.Disabled; - -@Disabled -public class CatalogServiceTest extends OSBTestBase { -/* - @Test - public void testCatalog() throws IOException { - OSBCatalogService catalogService = new OSBCatalogService(new TestAddressSpaceApi(), schemaProvider, "controller"); - - Response response = catalogService.getCatalog(getSecurityContext()); - CatalogResponse catalogResponse = (CatalogResponse) response.getEntity(); - List services = catalogResponse.getServices(); - - assertThat(services.size(), is(4)); - assertService(services.get(0), "enmasse-anycast", "standard"); - assertService(services.get(1), "enmasse-multicast", "standard"); - assertService(services.get(2), "enmasse-queue", "inmemory", "persisted", "pooled-inmemory", "pooled-persisted"); - assertService(services.get(3), "enmasse-topic", "inmemory", "persisted"); - - Service service = services.get(2); - Plan plan = service.getPlans().get(0); - assertThat(plan.getMetadata().get("displayName"), is("In-memory")); - assertThat(plan.getDescription(), is("Creates a standalone broker cluster for queues. Messages are not persisted on stable storage.")); - } - - private void assertService(Service service, String name, String... planNames) { - assertThat(service.getName(), is(name)); - assertThat(service.getPlans().size(), is(planNames.length)); - for (int i = 0; i < planNames.length; i++) { - String planName = planNames[i]; - Plan plan = service.getPlans().get(i); - assertThat(plan.getName(), is(planName)); - } - } - */ -} diff --git a/service-broker/src/test/java/io/enmasse/osb/api/DeserializationTest.java b/service-broker/src/test/java/io/enmasse/osb/api/DeserializationTest.java deleted file mode 100644 index 3621ed343af..00000000000 --- a/service-broker/src/test/java/io/enmasse/osb/api/DeserializationTest.java +++ /dev/null @@ -1,86 +0,0 @@ -/* - * Copyright 2018, EnMasse authors. - * License: Apache License 2.0 (see the file LICENSE or http://apache.org/licenses/LICENSE-2.0.html). - */ -package io.enmasse.osb.api; - -import static org.hamcrest.CoreMatchers.is; -import static org.hamcrest.MatcherAssert.assertThat; - -import com.fasterxml.jackson.databind.ObjectMapper; -import io.enmasse.osb.api.bind.BindRequest; -import io.enmasse.osb.api.provision.ProvisionRequest; -import org.junit.jupiter.api.Test; - -import java.io.IOException; -import java.util.HashMap; -import java.util.Map; -import java.util.UUID; - -/** - * - */ -public class DeserializationTest { - - private static final ObjectMapper mapper = new ObjectMapper(); - - @Test - public void testProvisionRequest() throws IOException { - UUID serviceUuid = UUID.randomUUID(); - UUID planUuid = UUID.randomUUID(); - UUID organizationUuid = UUID.randomUUID(); - UUID spaceUuid = UUID.randomUUID(); - - Map map = new HashMap<>(); - map.put("service_id", serviceUuid.toString()); - map.put("plan_id", planUuid.toString()); - map.put("organization_guid", organizationUuid.toString()); - map.put("space_guid", spaceUuid.toString()); - - Map parameters = new HashMap<>(); - parameters.put("foo", "bar"); - parameters.put("baz", "qux"); - map.put("parameters", parameters); - - String serialized = mapper.writeValueAsString(map); - - ProvisionRequest request = mapper.readValue(serialized, ProvisionRequest.class); - assertThat(request.getServiceId(), is(serviceUuid)); - assertThat(request.getPlanId(), is(planUuid)); - assertThat(request.getOrganizationId(), is(organizationUuid.toString())); - assertThat(request.getSpaceId(), is(spaceUuid.toString())); - assertThat(request.getParameters(), is(parameters)); - } - - @Test - public void testBindRequest() throws IOException { - UUID serviceUuid = UUID.randomUUID(); - UUID planUuid = UUID.randomUUID(); - UUID appUuid = UUID.randomUUID(); - - Map map = new HashMap<>(); - map.put("service_id", serviceUuid.toString()); - map.put("plan_id", planUuid.toString()); - - Map bindResource = new HashMap<>(); - bindResource.put("app_guid", appUuid.toString()); - bindResource.put("route", "some-address"); - map.put("bind_resource", bindResource); - - Map parameters = new HashMap<>(); - parameters.put("foo", "bar"); - parameters.put("baz", "qux"); - map.put("parameters", parameters); - - - String serialized = mapper.writeValueAsString(map); - - BindRequest request = mapper.readValue(serialized, BindRequest.class); - assertThat(request.getServiceId(), is(serviceUuid)); - assertThat(request.getPlanId(), is(planUuid)); - assertThat(request.getBindResource().getAppId(), is(appUuid.toString())); - assertThat(request.getBindResource().getRoute(), is("some-address")); - assertThat(request.getParameters(), is(parameters)); - } - -} diff --git a/service-broker/src/test/java/io/enmasse/osb/api/OSBTestBase.java b/service-broker/src/test/java/io/enmasse/osb/api/OSBTestBase.java deleted file mode 100644 index 77eb5dd2785..00000000000 --- a/service-broker/src/test/java/io/enmasse/osb/api/OSBTestBase.java +++ /dev/null @@ -1,79 +0,0 @@ -/* - * Copyright 2018, EnMasse authors. - * License: Apache License 2.0 (see the file LICENSE or http://apache.org/licenses/LICENSE-2.0.html). - */ -package io.enmasse.osb.api; - -import io.enmasse.address.model.AddressSpace; -import io.enmasse.k8s.api.TestAddressSpaceApi; -import io.enmasse.osb.api.bind.OSBBindingService; -import io.enmasse.osb.api.lastoperation.OSBLastOperationService; -import io.enmasse.osb.api.provision.ConsoleProxy; -import io.enmasse.osb.api.provision.OSBProvisioningService; -import io.enmasse.osb.api.provision.ProvisionRequest; -import org.apache.http.auth.BasicUserPrincipal; -import org.junit.jupiter.api.BeforeEach; - -import javax.ws.rs.core.SecurityContext; -import javax.ws.rs.core.UriInfo; -import java.util.UUID; - -import static org.mockito.ArgumentMatchers.any; -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.when; - -/** - * - */ - -public class OSBTestBase { - protected static final UUID QUEUE_SERVICE_ID = ServiceType.QUEUE.uuid(); - protected static final UUID TOPIC_SERVICE_ID = ServiceType.TOPIC.uuid(); - protected static final UUID QUEUE_PLAN_ID = UUID.fromString("3c7f4fdc-0597-11e8-abdb-507b9def37d9"); - protected static final UUID TOPIC_PLAN_ID = UUID.fromString("48837510-0597-11e8-8517-507b9def37d9"); - protected static final String SERVICE_INSTANCE_ID = UUID.randomUUID().toString(); - protected static final String ORGANIZATION_ID = UUID.randomUUID().toString(); - protected static final String SPACE_ID = UUID.randomUUID().toString(); - - protected OSBProvisioningService provisioningService; - protected TestAddressSpaceApi addressSpaceApi; - protected OSBBindingService bindingService; - protected OSBLastOperationService lastOperationService; - - @BeforeEach - public void setup() throws Exception { - addressSpaceApi = new TestAddressSpaceApi(); - String brokerId = "myspace"; - provisioningService = new OSBProvisioningService(addressSpaceApi, null, null, new ConsoleProxy() { - @Override - public String getConsoleUrl(AddressSpace addressSpace) { - return "http://localhost/console/" + addressSpace.getMetadata().getName(); - } - }); - bindingService = new OSBBindingService(addressSpaceApi, null, null, null); - lastOperationService = new OSBLastOperationService(addressSpaceApi, null, null); - } - - protected void provisionService(String serviceInstanceId) throws Exception { - provisionService(serviceInstanceId, ORGANIZATION_ID, SPACE_ID); - } - - protected SecurityContext getSecurityContext() { - SecurityContext securityContext = mock(SecurityContext.class); - when(securityContext.isUserInRole(any())).thenReturn(true); - when(securityContext.isSecure()).thenReturn(true); - when(securityContext.getUserPrincipal()).thenReturn(new BasicUserPrincipal("myuser")); - return securityContext; - } - - protected String provisionService(String serviceInstanceId, String organizationId, String spaceId) throws Exception { - ProvisionRequest provisionRequest = new ProvisionRequest(QUEUE_SERVICE_ID, QUEUE_PLAN_ID, organizationId, spaceId); - provisionRequest.putParameter("name", "my-queue"); - provisionRequest.putParameter("group", "my-group"); - - UriInfo uriInfo = mock(UriInfo.class); - provisioningService.provisionService(getSecurityContext(), null, serviceInstanceId, true, provisionRequest); - // TODO: wait for provisioning to finish (poll lastOperation endpoint) - return serviceInstanceId; - } -} diff --git a/service-broker/src/test/java/io/enmasse/osb/api/ProvisionServiceTest.java b/service-broker/src/test/java/io/enmasse/osb/api/ProvisionServiceTest.java deleted file mode 100644 index 94c708a533b..00000000000 --- a/service-broker/src/test/java/io/enmasse/osb/api/ProvisionServiceTest.java +++ /dev/null @@ -1,207 +0,0 @@ -/* - * Copyright 2016-2018, EnMasse authors. - * License: Apache License 2.0 (see the file LICENSE or http://apache.org/licenses/LICENSE-2.0.html). - */ - -package io.enmasse.osb.api; - -import io.enmasse.address.model.Address; -import io.enmasse.address.model.AddressBuilder; -import io.enmasse.api.common.ConflictException; -import io.enmasse.api.common.GoneException; -import io.enmasse.api.common.UnprocessableEntityException; -import io.enmasse.osb.api.lastoperation.LastOperationResponse; -import io.enmasse.osb.api.lastoperation.LastOperationState; -import io.enmasse.osb.api.provision.ProvisionRequest; -import io.enmasse.osb.api.provision.ProvisionResponse; -import io.fabric8.kubernetes.api.model.ObjectMetaBuilder; - -import org.jboss.resteasy.util.HttpResponseCodes; -import org.junit.jupiter.api.Disabled; -import org.junit.jupiter.api.Test; - -import javax.ws.rs.BadRequestException; -import javax.ws.rs.core.Response; -import javax.ws.rs.core.UriInfo; -import java.util.UUID; - -import static org.hamcrest.CoreMatchers.is; -import static org.hamcrest.CoreMatchers.notNullValue; -import static org.hamcrest.MatcherAssert.assertThat; -import static org.junit.jupiter.api.Assertions.assertThrows; -import static org.mockito.Mockito.mock; - -@Disabled -public class ProvisionServiceTest extends OSBTestBase { - - public static final String QUEUE_SERVICE_ID_STRING = QUEUE_SERVICE_ID.toString(); - public static final String QUEUE_PLAN_ID_STRING = QUEUE_PLAN_ID.toString(); - - public static final String ADDRESS = "my-queue"; - public static final String TRANSACTIONAL = "transactional"; - private UriInfo uriInfo = mock(UriInfo.class); - - @Test - public void testSyncProvisioningRequest() throws Exception { - assertThrows(UnprocessableEntityException.class, - () -> provisioningService.provisionService(getSecurityContext(), null, "123", false, new ProvisionRequest(QUEUE_SERVICE_ID, QUEUE_PLAN_ID, ORGANIZATION_ID, SPACE_ID))); - } - - @Test - public void testInvalidServiceUuid() throws Exception { - assertThrows(BadRequestException.class, - () -> provisioningService.provisionService(getSecurityContext(), null, "123", true, new ProvisionRequest(QUEUE_SERVICE_ID, QUEUE_PLAN_ID, ORGANIZATION_ID, SPACE_ID))); - } - - @Test - public void testInvalidPlan() throws Exception { - assertThrows(BadRequestException.class, - () -> provisioningService.provisionService(getSecurityContext(), null, "123", true, new ProvisionRequest(QUEUE_SERVICE_ID, TOPIC_PLAN_ID, ORGANIZATION_ID, SPACE_ID))); - } - - @Test - public void testInvalidServiceInstandeUuid() throws Exception { - assertThrows(BadRequestException.class, - () -> provisioningService.provisionService(getSecurityContext(), null, "123", true, new ProvisionRequest(QUEUE_SERVICE_ID, QUEUE_PLAN_ID, ORGANIZATION_ID, SPACE_ID))); - } - - @Test - public void testProvision() throws Exception { - ProvisionRequest provisionRequest = new ProvisionRequest(QUEUE_SERVICE_ID, QUEUE_PLAN_ID, ORGANIZATION_ID, SPACE_ID); - provisionRequest.putParameter("name", ADDRESS); - provisionRequest.putParameter("transactional", "true"); - Response response = provisioningService.provisionService(getSecurityContext(), null, SERVICE_INSTANCE_ID, true, provisionRequest); - ProvisionResponse provisionResponse = (ProvisionResponse) response.getEntity(); - - assertThat(response.getStatus(), is(HttpResponseCodes.SC_ACCEPTED)); -// assertThat(provisionResponse.getDashboardUrl(), notNullValue()); - assertThat(provisionResponse.getOperation(), notNullValue()); - - Address destination = new AddressBuilder() - .withMetadata(new ObjectMetaBuilder() - .withName(ADDRESS) - .withNamespace(ADDRESS) - .build()) - - .withNewSpec() - .withAddressSpace("unknown") - .withType("queue") - .withPlan("myplan") - .endSpec() - - .withNewStatus(false) - - .build(); - //assertThat(addressSpaceApi.getAddresses(), is(new HashSet<>(Collections.singletonList(destination)))); - - LastOperationResponse lastOperationResponse = getLastOperationResponse(SERVICE_INSTANCE_ID, QUEUE_SERVICE_ID_STRING, QUEUE_PLAN_ID_STRING, provisionResponse.getOperation()); - assertThat(lastOperationResponse.getState(), is(LastOperationState.IN_PROGRESS)); - - addressSpaceApi.setAllInstancesReady(true); - - lastOperationResponse = getLastOperationResponse(SERVICE_INSTANCE_ID, QUEUE_SERVICE_ID_STRING, QUEUE_PLAN_ID_STRING, provisionResponse.getOperation()); - assertThat(lastOperationResponse.getState(), is(LastOperationState.IN_PROGRESS)); - - addressSpaceApi.getAddressApis().iterator().next().setAllAddressesReady(true); - - lastOperationResponse = getLastOperationResponse(SERVICE_INSTANCE_ID, QUEUE_SERVICE_ID_STRING, QUEUE_PLAN_ID_STRING, provisionResponse.getOperation()); - assertThat(lastOperationResponse.getState(), is(LastOperationState.SUCCEEDED)); - - destination = new AddressBuilder() - .withMetadata(new ObjectMetaBuilder() - .withName(ADDRESS) - .build()) - - .withNewSpec() - .withAddress(ADDRESS) - .withAddressSpace("unknown") - .withType("queue") - .withPlan("myplan") - .endSpec() - - .withNewStatus(true) - - .build(); - - //assertThat(addressSpaceApi.getAddresses(), is(new HashSet<>(Collections.singletonList(destination)))); - } - - private LastOperationResponse getLastOperationResponse(String serviceInstanceId, String serviceId, String planId, String operation) throws Exception { - Response response = lastOperationService.getLastOperationStatus(getSecurityContext(), serviceInstanceId, serviceId, planId, operation); - return (LastOperationResponse) response.getEntity(); - } - - - @Test - public void testProvisionTwiceWithDifferentPrameters() throws Exception { - provisioningService.provisionService(getSecurityContext(), null, SERVICE_INSTANCE_ID, true, new ProvisionRequest(QUEUE_SERVICE_ID, QUEUE_PLAN_ID, ORGANIZATION_ID, SPACE_ID)); - assertThrows(ConflictException.class, () -> provisioningService.provisionService(getSecurityContext(), null, SERVICE_INSTANCE_ID, true, new ProvisionRequest(ServiceType.TOPIC.uuid(), TOPIC_PLAN_ID, ORGANIZATION_ID, SPACE_ID))); - } - - @Test - public void testProvisionTwiceWithSameParameters() throws Exception { - ProvisionRequest provisionRequest = new ProvisionRequest(QUEUE_SERVICE_ID, QUEUE_PLAN_ID, ORGANIZATION_ID, SPACE_ID); - provisioningService.provisionService(getSecurityContext(), null, SERVICE_INSTANCE_ID, true, provisionRequest); - Response response = provisioningService.provisionService(getSecurityContext(), null, SERVICE_INSTANCE_ID, true, provisionRequest); - assertThat(response.getStatus(), is(HttpResponseCodes.SC_OK)); - } - - @Test - public void testDeprovisionNonexistingServiceInstance() throws Exception { - assertThrows(GoneException.class, - () -> provisioningService.deprovisionService(getSecurityContext(), SERVICE_INSTANCE_ID, QUEUE_SERVICE_ID_STRING, QUEUE_PLAN_ID_STRING)); - } - - @Test - public void testDeprovisionWithoutServiceId() throws Exception { - assertThrows(BadRequestException.class, - () -> provisioningService.deprovisionService(getSecurityContext(), SERVICE_INSTANCE_ID, null, QUEUE_PLAN_ID_STRING)); - } - - @Test - public void testDeprovisionWithoutPlanId() throws Exception { - assertThrows(BadRequestException.class, - () -> provisioningService.deprovisionService(getSecurityContext(), SERVICE_INSTANCE_ID, QUEUE_SERVICE_ID_STRING, null)); - } - - @Test - public void testDeprovision() throws Exception { - provisionService(SERVICE_INSTANCE_ID); - Response response = provisioningService.deprovisionService(getSecurityContext(), SERVICE_INSTANCE_ID, QUEUE_SERVICE_ID_STRING, QUEUE_PLAN_ID_STRING); - assertThat(response.getStatus(), is(HttpResponseCodes.SC_OK)); - //assertThat(addressSpaceApi.getAddresses(), is(Collections.EMPTY_SET)); - } - - @Test - public void testDeprovisionTwice() throws Exception { - provisionService(SERVICE_INSTANCE_ID); - provisioningService.deprovisionService(getSecurityContext(), SERVICE_INSTANCE_ID, QUEUE_SERVICE_ID_STRING, QUEUE_PLAN_ID_STRING); - - assertThrows(GoneException.class, () -> provisioningService.deprovisionService(getSecurityContext(), SERVICE_INSTANCE_ID, QUEUE_SERVICE_ID_STRING, QUEUE_PLAN_ID_STRING)); - } - - @Test - public void testDeprovisionGivenMultipleOrganizations() throws Exception { - String serviceId11 = provisionService(randomUUID(), ORGANIZATION_ID, SPACE_ID); - provisionService(randomUUID(), ORGANIZATION_ID, SPACE_ID); - - String organizationId2 = randomUUID(); - String spaceId2 = randomUUID(); - String serviceId21 = provisionService(randomUUID(), organizationId2, spaceId2); - provisionService(randomUUID(), organizationId2, spaceId2); - - Response response = provisioningService.deprovisionService(getSecurityContext(), serviceId21, QUEUE_SERVICE_ID_STRING, QUEUE_PLAN_ID_STRING); - assertThat(response.getStatus(), is(HttpResponseCodes.SC_OK)); - //assertThat(addressSpaceApi.getAddressUuids(), not(hasItem(serviceId21))); - - response = provisioningService.deprovisionService(getSecurityContext(), serviceId11, QUEUE_SERVICE_ID_STRING, QUEUE_PLAN_ID_STRING); - assertThat(response.getStatus(), is(HttpResponseCodes.SC_OK)); - //assertThat(addressSpaceApi.getAddressUuids(), not(hasItem(serviceId11))); - } - - private String randomUUID() { - return UUID.randomUUID().toString(); - } - - -} diff --git a/service-broker/src/test/java/io/enmasse/osb/api/SerializationTest.java b/service-broker/src/test/java/io/enmasse/osb/api/SerializationTest.java deleted file mode 100644 index 3532bd86044..00000000000 --- a/service-broker/src/test/java/io/enmasse/osb/api/SerializationTest.java +++ /dev/null @@ -1,115 +0,0 @@ -/* - * Copyright 2018, EnMasse authors. - * License: Apache License 2.0 (see the file LICENSE or http://apache.org/licenses/LICENSE-2.0.html). - */ -package io.enmasse.osb.api; - -import com.fasterxml.jackson.databind.ObjectMapper; -import com.fasterxml.jackson.module.jsonSchema.types.ObjectSchema; -import io.enmasse.osb.api.bind.BindResponse; -import io.enmasse.osb.api.catalog.*; -import io.enmasse.osb.api.lastoperation.LastOperationResponse; -import io.enmasse.osb.api.lastoperation.LastOperationState; -import io.enmasse.osb.api.provision.ProvisionResponse; -import org.junit.jupiter.api.Test; - -import java.io.IOException; -import java.util.*; - -import static org.hamcrest.CoreMatchers.*; -import static org.hamcrest.MatcherAssert.assertThat; - -/** - * - */ -public class SerializationTest { - - private static final ObjectMapper mapper = new ObjectMapper(); - - @Test - public void testCatalogResponse() throws IOException { - UUID serviceId = UUID.randomUUID(); - Service service = new Service(serviceId, "test-service", "test-description", true); - service.setPlanUpdatable(false); - - UUID planId = UUID.randomUUID(); - Plan plan = new Plan(planId, "test-plan", "test-plan-description", true, true); - plan.setSchemas(new Schemas(new ServiceInstanceSchema(new InputParameters(new ObjectSchema()), null), null)); - service.getPlans().add(plan); - - CatalogResponse response = new CatalogResponse(Collections.singletonList(service)); - - String serialized = mapper.writeValueAsString(response); - Map map = mapper.readValue(serialized, Map.class); - - List services = (List) map.get("services"); - assertThat(services.size(), is(1)); - - Map serviceMap = (Map) services.get(0); - assertThat(serviceMap.get("name"), is("test-service")); - assertThat(serviceMap.get("id"), is(serviceId.toString())); - assertThat(serviceMap.get("description"), is("test-description")); - assertThat(serviceMap.get("tags"), is(Collections.EMPTY_LIST)); - assertThat(serviceMap.get("requires"), is(Collections.EMPTY_LIST)); - assertThat(serviceMap.get("bindable"), is(true)); - assertThat(serviceMap.get("metadata"), notNullValue()); - assertThat(serviceMap.get("dashboard_client"), nullValue()); - assertThat(serviceMap.get("plan_updateable"), is(false)); - - List plans = (List) serviceMap.get("plans"); - assertThat(plans.size(), is(1)); - - Map planMap = (Map) plans.get(0); - assertThat(planMap.get("id"), is(planId.toString())); - assertThat(planMap.get("name"), is("test-plan")); - assertThat(planMap.get("description"), is("test-plan-description")); - assertThat(planMap.get("metadata"), notNullValue()); - assertThat(planMap.get("free"), is(true)); - assertThat(planMap.get("bindable"), is(true)); - - assertThat(planMap.get("schemas"), notNullValue()); // TODO: expand this - } - - @Test - public void testProvisionResponse() throws IOException { - ProvisionResponse response = new ProvisionResponse("dashboard-url", "some-operation"); - String serialized = mapper.writeValueAsString(response); - - Map map = mapper.readValue(serialized, Map.class); - - assertThat(map.get("dashboard_url"), is("dashboard-url")); - assertThat(map.get("operation"), is("some-operation")); - } - - @Test - public void testBindResponse() throws IOException { - Map credentials = new HashMap<>(); - credentials.put("foo", "bar"); - credentials.put("baz", "qux"); - BindResponse response = new BindResponse(credentials); - response.setRouteServiceUrl("route-service-url"); - response.setSyslogDrainUrl("syslog-drain-url"); - String serialized = mapper.writeValueAsString(response); - - Map map = mapper.readValue(serialized, Map.class); - assertThat(map.get("route_service_url"), is("route-service-url")); - assertThat(map.get("syslog_drain_url"), is("syslog-drain-url")); - assertThat(map.get("credentials"), is(credentials)); - } - - @Test - public void testLastOperationResponse() throws IOException { - assertLastOperationResponse(new LastOperationResponse(LastOperationState.IN_PROGRESS, "operation-in-progress"), "in progress"); - assertLastOperationResponse(new LastOperationResponse(LastOperationState.SUCCEEDED, "operation-succeeded"), "succeeded"); - assertLastOperationResponse(new LastOperationResponse(LastOperationState.FAILED, "operation-failed"), "failed"); - } - - private void assertLastOperationResponse(LastOperationResponse response, String expectedState) throws IOException { - String serialized = mapper.writeValueAsString(response); - - Map map = mapper.readValue(serialized, Map.class); - assertThat(map.get("state"), is(expectedState)); - assertThat(map.get("description"), is(response.getDescription())); - } - -} diff --git a/service-broker/src/test/java/io/enmasse/osb/api/ServiceMappingTest.java b/service-broker/src/test/java/io/enmasse/osb/api/ServiceMappingTest.java deleted file mode 100644 index 4bf75a589c4..00000000000 --- a/service-broker/src/test/java/io/enmasse/osb/api/ServiceMappingTest.java +++ /dev/null @@ -1,26 +0,0 @@ -/* - * Copyright 2017-2018, EnMasse authors. - * License: Apache License 2.0 (see the file LICENSE or http://apache.org/licenses/LICENSE-2.0.html). - */ -package io.enmasse.osb.api; - - -import org.junit.jupiter.api.Test; - -import static org.junit.jupiter.api.Assertions.assertFalse; -import static org.junit.jupiter.api.Assertions.assertTrue; - -public class ServiceMappingTest { - @Test - public void testRegexp() { - String regexp = ServiceMapping.addressRegexp; - assertTrue("foobar".matches(regexp)); - assertFalse("/foobar".matches(regexp)); - assertFalse("/foobar/".matches(regexp)); - assertFalse("foobar/".matches(regexp)); - assertTrue("foo.bar".matches(regexp)); - assertTrue("foo/bar".matches(regexp)); - assertTrue("foo/bar/baz".matches(regexp)); - } - -} diff --git a/standard-controller/Dockerfile b/standard-controller/Dockerfile deleted file mode 100644 index fc88ac91a4a..00000000000 --- a/standard-controller/Dockerfile +++ /dev/null @@ -1,10 +0,0 @@ -FROM quay.io/enmasse/java-base:11-5 - -ARG version -ARG maven_version -ARG revision -ENV VERSION=${version} REVISION=${revision} MAVEN_VERSION=${maven_version} - -ADD target/standard-controller-${maven_version}-dist.tar.gz / - -CMD ["/opt/run-java/launch_java.sh", "-jar", "/opt/standard-controller.jar"] diff --git a/standard-controller/Makefile b/standard-controller/Makefile deleted file mode 100644 index 27e8bca799a..00000000000 --- a/standard-controller/Makefile +++ /dev/null @@ -1 +0,0 @@ -include ../Makefile.java.mk diff --git a/standard-controller/pom.xml b/standard-controller/pom.xml deleted file mode 100644 index 0dc4593c8bd..00000000000 --- a/standard-controller/pom.xml +++ /dev/null @@ -1,139 +0,0 @@ - - - - io.enmasse - enmasse - 0.32-SNAPSHOT - - 4.0.0 - standard-controller - - - io.enmasse - api-model - - - io.enmasse - amqp-utils - compile - - - io.enmasse - metrics-api - compile - - - io.vertx - vertx-core - compile - - - io.vertx - vertx-proton - compile - - - io.enmasse - k8s-api - compile - - - io.fabric8 - openshift-client - compile - - - org.slf4j - slf4j-api - compile - - - ch.qos.logback - logback-classic - runtime - - - org.junit.jupiter - junit-jupiter-api - test - - - org.junit.jupiter - junit-jupiter-engine - test - - - org.hamcrest - hamcrest - test - - - org.junit.platform - junit-platform-launcher - test - - - org.mockito - mockito-core - test - - - io.enmasse - k8s-api-testutil - test - - - - - - - src/main/resources - true - - - - - org.apache.maven.plugins - maven-shade-plugin - - - package - - shade - - - - - - - io.enmasse.controller.standard.StandardController - - - - - - - - org.apache.maven.plugins - maven-assembly-plugin - - - dist - package - - single - - - false - - src/assembly/unix-dist.xml - - posix - - - - - - - diff --git a/standard-controller/src/assembly/unix-dist.xml b/standard-controller/src/assembly/unix-dist.xml deleted file mode 100644 index eb34cc1c20e..00000000000 --- a/standard-controller/src/assembly/unix-dist.xml +++ /dev/null @@ -1,45 +0,0 @@ - - - - - dist - - - tar.gz - zip - - False - - - ${project.basedir}/target/classes - /opt - - templates/* - - - - - - ${project.basedir}/target/standard-controller-${project.version}.jar - /opt - standard-controller.jar - 0644 - - - diff --git a/standard-controller/src/main/java/io/enmasse/controller/standard/AddressCanaryHealth.java b/standard-controller/src/main/java/io/enmasse/controller/standard/AddressCanaryHealth.java deleted file mode 100644 index a0eb280430d..00000000000 --- a/standard-controller/src/main/java/io/enmasse/controller/standard/AddressCanaryHealth.java +++ /dev/null @@ -1,138 +0,0 @@ -/* - * Copyright 2020, EnMasse authors. - * License: Apache License 2.0 (see the file LICENSE or http://apache.org/licenses/LICENSE-2.0.html). - */ -package io.enmasse.controller.standard; - -import io.enmasse.metrics.api.MetricLabel; -import io.enmasse.metrics.api.MetricType; -import io.enmasse.metrics.api.MetricValue; -import io.enmasse.metrics.api.Metrics; -import io.enmasse.metrics.api.ScalarMetric; -import io.fabric8.kubernetes.api.model.Pod; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import java.time.Duration; -import java.util.ArrayList; -import java.util.Collections; -import java.util.HashSet; -import java.util.List; -import java.util.Set; -import java.util.concurrent.atomic.AtomicInteger; -import java.util.stream.Collectors; - -public class AddressCanaryHealth implements Runnable { - private static final Logger log = LoggerFactory.getLogger(AddressCanaryHealth.class); - - private final Kubernetes kubernetes; - private final AddressProber runner; - - private volatile boolean running = false; - private Thread thread; - private final Duration checkInterval; - - private volatile List latestResult = Collections.emptyList(); - private final AtomicInteger healthCheckFailures = new AtomicInteger(0); - private final String addressSpace; - - AddressCanaryHealth(Kubernetes kubernetes, Duration checkInterval, AddressProber runner, Metrics metrics, String addressSpace) { - this.kubernetes = kubernetes; - this.checkInterval = checkInterval; - this.runner = runner; - this.addressSpace = addressSpace; - registerMetrics(metrics); - } - - private void registerMetrics(Metrics metrics) { - MetricLabel staticLabel = new MetricLabel("addressspace", this.addressSpace); - metrics.registerMetric(new ScalarMetric( - "address_canary_health_failures_total", - "Total number of health check failures due to failure to send and receive messages to probe addresses.", - MetricType.gauge, - () -> latestResult.stream().flatMap( - result -> { - List values = new ArrayList<>(); - values.add(new MetricValue(result.getFailed().size(), staticLabel, new MetricLabel("router", result.getRouterId()))); - for (String failed : result.getFailed()) { - values.add(new MetricValue(1, staticLabel, new MetricLabel("router", result.getRouterId()), new MetricLabel("address", failed))); - } - - for (String passed : result.getPassed()) { - values.add(new MetricValue(0, staticLabel, new MetricLabel("router", result.getRouterId()), new MetricLabel("address", passed))); - } - return values.stream(); - }).collect(Collectors.toList()))); - - metrics.registerMetric(new ScalarMetric( - "address_canary_health_check_failures_total", - "Total number of attempted health check runs that failed due to controller errors.", - MetricType.counter, - () -> Collections.singletonList(new MetricValue(healthCheckFailures.get())))); - } - - void checkHealth(List brokerClusters) { - List routers = new ArrayList<>(kubernetes.listRouters()); - - List brokers = brokerClusters.stream() - .flatMap(cluster -> kubernetes.listBrokers(cluster.getClusterId()).stream()) - .collect(Collectors.toList()); - - // Check if we can send/receive to all brokers through all routers - Set addresses = new HashSet<>(); - addresses.add("!!HEALTH_CHECK_ROUTER"); - for (Pod broker : brokers) { - addresses.add(String.format("!!HEALTH_CHECK_BROKER_%s", broker.getMetadata().getName())); - } - - List addressCanaryResults = new ArrayList<>(routers.size()); - for (Pod router : routers) { - try { - log.debug("[route {}] Running health check against {}:55671 for addresses {}", router.getMetadata().getName(), router.getStatus().getPodIP(), addresses); - Set passed = runner.run(router.getStatus().getPodIP(), 55671, addresses); - Set failed = new HashSet<>(addresses); - failed.removeAll(passed); - log.debug("[router {}] Passed: {}. Failed: {}", router.getMetadata().getName(), passed, failed); - addressCanaryResults.add(new AddressCanaryResult(router.getMetadata().getName(), passed, failed)); - } catch (Exception e) { - log.warn("Error checking address health", e); - healthCheckFailures.incrementAndGet(); - } - } - this.latestResult = addressCanaryResults; - } - - public void start() { - running = true; - thread = new Thread(this); - thread.setName("router-status-collector"); - thread.setDaemon(true); - thread.start(); - } - - public void stop() { - try { - running = false; - thread.interrupt(); - thread.join(); - } catch (InterruptedException ignored) { - log.warn("Interrupted while stopping", ignored); - } - } - - @Override - public void run() { - while (running) { - try { - Thread.sleep(checkInterval.toMillis()); - checkHealth(kubernetes.listClusters()); - } catch (InterruptedException e) { - Thread.currentThread().interrupt(); - running = false; - log.warn("AddressCanaryHealth interrupted, stopping."); - } catch (Exception e) { - log.warn("Exception in collector task", e); - } - } - } -} diff --git a/standard-controller/src/main/java/io/enmasse/controller/standard/AddressCanaryResult.java b/standard-controller/src/main/java/io/enmasse/controller/standard/AddressCanaryResult.java deleted file mode 100644 index c6f6455ce64..00000000000 --- a/standard-controller/src/main/java/io/enmasse/controller/standard/AddressCanaryResult.java +++ /dev/null @@ -1,31 +0,0 @@ -/* - * Copyright 2020, EnMasse authors. - * License: Apache License 2.0 (see the file LICENSE or http://apache.org/licenses/LICENSE-2.0.html). - */ -package io.enmasse.controller.standard; - -import java.util.Set; - -public class AddressCanaryResult { - private final String routerId; - private final Set passed; - private final Set failed; - - public AddressCanaryResult(String routerId, Set passed, Set failed) { - this.routerId = routerId; - this.passed = passed; - this.failed = failed; - } - - public String getRouterId() { - return routerId; - } - - public Set getPassed() { - return passed; - } - - public Set getFailed() { - return failed; - } -} diff --git a/standard-controller/src/main/java/io/enmasse/controller/standard/AddressController.java b/standard-controller/src/main/java/io/enmasse/controller/standard/AddressController.java deleted file mode 100644 index 5848af8424e..00000000000 --- a/standard-controller/src/main/java/io/enmasse/controller/standard/AddressController.java +++ /dev/null @@ -1,990 +0,0 @@ -/* - * Copyright 2016-2018, EnMasse authors. - * License: Apache License 2.0 (see the file LICENSE or http://apache.org/licenses/LICENSE-2.0.html). - */ -package io.enmasse.controller.standard; - -import static io.enmasse.address.model.Phase.Active; -import static io.enmasse.address.model.Phase.Configuring; -import static io.enmasse.address.model.Phase.Failed; -import static io.enmasse.address.model.Phase.Pending; -import static io.enmasse.address.model.Phase.Terminating; -import static io.enmasse.controller.standard.ControllerKind.Broker; -import static io.enmasse.controller.standard.ControllerReason.BrokerUpgraded; -import static io.enmasse.k8s.api.EventLogger.Type.Normal; - -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Collections; -import java.util.EnumSet; -import java.util.HashMap; -import java.util.LinkedHashSet; -import java.util.List; -import java.util.Map; -import java.util.Objects; -import java.util.Optional; -import java.util.Set; -import java.util.function.Predicate; -import java.util.stream.Collectors; - -import io.enmasse.address.model.*; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import com.fasterxml.jackson.databind.ObjectMapper; - -import io.enmasse.admin.model.AddressPlan; -import io.enmasse.admin.model.AddressSpacePlan; -import io.enmasse.admin.model.v1.InfraConfig; -import io.enmasse.admin.model.v1.StandardInfraConfig; -import io.enmasse.admin.model.v1.StandardInfraConfigBuilder; -import io.enmasse.amqp.RouterManagement; -import io.enmasse.config.AnnotationKeys; -import io.enmasse.k8s.api.AddressApi; -import io.enmasse.k8s.api.AddressSpaceApi; -import io.enmasse.k8s.api.EventLogger; -import io.enmasse.k8s.api.ResourceChecker; -import io.enmasse.k8s.api.SchemaProvider; -import io.enmasse.k8s.api.Watch; -import io.enmasse.k8s.api.Watcher; -import io.enmasse.metrics.api.MetricLabel; -import io.enmasse.metrics.api.MetricType; -import io.enmasse.metrics.api.MetricValue; -import io.enmasse.metrics.api.Metrics; -import io.enmasse.metrics.api.ScalarMetric; -import io.fabric8.kubernetes.api.model.HasMetadata; -import io.fabric8.kubernetes.api.model.Pod; -import io.fabric8.kubernetes.api.model.PodTemplateSpec; -import io.fabric8.kubernetes.api.model.apps.Deployment; -import io.fabric8.kubernetes.api.model.apps.DeploymentSpec; -import io.fabric8.kubernetes.api.model.apps.StatefulSet; -import io.fabric8.kubernetes.api.model.apps.StatefulSetSpec; -import io.fabric8.kubernetes.client.KubernetesClientException; -import io.fabric8.kubernetes.client.internal.readiness.Readiness; -import io.vertx.core.Vertx; - -/** - * Controller for a single standard address space - */ -public class AddressController implements Watcher
{ - private static final Logger log = LoggerFactory.getLogger(AddressController.class); - private final StandardControllerOptions options; - private final AddressSpaceApi addressSpaceApi; - private final AddressApi addressApi; - private final Kubernetes kubernetes; - private final BrokerSetGenerator clusterGenerator; - private Watch watch; - private final EventLogger eventLogger; - private final SchemaProvider schemaProvider; - private final Vertx vertx; - private final BrokerIdGenerator brokerIdGenerator; - private final BrokerClientFactory brokerClientFactory; - private final RouterStatusCache statusCollector; - private final ResourceChecker
reconciler; - - // Metrics - private volatile Long readyAddressCount; - private volatile Long notReadyAddressCount; - private volatile Long readyForwarders; - private volatile Long notReadyForwarders; - private volatile Long numAddresses; - private volatile Long numForwarders; - private volatile Long totalTime; - private volatile Map countByPhase = new HashMap<>(); - - public AddressController(StandardControllerOptions options, AddressSpaceApi addressSpaceApi, AddressApi addressApi, Kubernetes kubernetes, BrokerSetGenerator clusterGenerator, EventLogger eventLogger, SchemaProvider schemaProvider, Vertx vertx, Metrics metrics, BrokerIdGenerator brokerIdGenerator, BrokerClientFactory brokerClientFactory) { - this.options = options; - this.addressSpaceApi = addressSpaceApi; - this.addressApi = addressApi; - this.kubernetes = kubernetes; - this.clusterGenerator = clusterGenerator; - this.eventLogger = eventLogger; - this.schemaProvider = schemaProvider; - this.vertx = vertx; - this.brokerIdGenerator = brokerIdGenerator; - this.brokerClientFactory = brokerClientFactory; - RouterManagement routerManagement = RouterManagement.withCertsInDir(vertx, "standard-controller", options.getManagementConnectTimeout(), options.getManagementQueryTimeout(), options.getCertDir()); - this.statusCollector = new RouterStatusCache(routerManagement, kubernetes, eventLogger, options.getAddressSpace(), options.getStatusCheckInterval()); - reconciler = new ResourceChecker<>(this, options.getRecheckInterval()); - registerMetrics(metrics); - } - - private void registerMetrics(Metrics metrics) { - String componentName = "standard-controller-" + options.getInfraUuid(); - metrics.registerMetric(new ScalarMetric( - "version", - "The version of the standard-controller", - MetricType.gauge, - () -> Collections.singletonList(new MetricValue(0, new MetricLabel("name", componentName), new MetricLabel("version", options.getVersion()))))); - - MetricLabel[] metricLabels = new MetricLabel[]{new MetricLabel("address_space_name", options.getAddressSpace()), new MetricLabel("namespace", options.getAddressSpaceNamespace())}; - metrics.registerMetric(new ScalarMetric( - "addresses_ready_total", - "Total number of addresses in ready state", - MetricType.gauge, - () -> Collections.singletonList(new MetricValue(readyAddressCount, metricLabels)))); - - metrics.registerMetric(new ScalarMetric( - "addresses_not_ready_total", - "Total number of address in a not ready state", - MetricType.gauge, - () -> Collections.singletonList(new MetricValue(notReadyAddressCount, metricLabels)))); - - metrics.registerMetric(new ScalarMetric( - "addresses_total", - "Total number of addresses", - MetricType.gauge, - () -> Collections.singletonList(new MetricValue(numAddresses, metricLabels)))); - - metrics.registerMetric(new ScalarMetric( - "addresses_pending_total", - "Total number of addresses in Pending state", - MetricType.gauge, - () -> Collections.singletonList(new MetricValue(countByPhase.get(Pending), metricLabels)))); - - metrics.registerMetric(new ScalarMetric( - "addresses_failed_total", - "Total number of addresses in Failed state", - MetricType.gauge, - () -> Collections.singletonList(new MetricValue(countByPhase.get(Failed), metricLabels)))); - - metrics.registerMetric(new ScalarMetric( - "addresses_terminating_total", - "Total number of addresses in Terminating state", - MetricType.gauge, - () -> Collections.singletonList(new MetricValue(countByPhase.get(Terminating), metricLabels)))); - - metrics.registerMetric(new ScalarMetric( - "addresses_configuring_total", - "Total number of addresses in Configuring state", - MetricType.gauge, - () -> Collections.singletonList(new MetricValue(countByPhase.get(Configuring), metricLabels)))); - - metrics.registerMetric(new ScalarMetric( - "addresses_active_total", - "Total number of addresses in Active state", - MetricType.gauge, - () -> Collections.singletonList(new MetricValue(countByPhase.get(Active), metricLabels)))); - - metrics.registerMetric(new ScalarMetric( - "addresses_forwarders_total", - "Total number of forwarders", - MetricType.gauge, - () -> Collections.singletonList(new MetricValue(numForwarders, metricLabels)))); - - metrics.registerMetric(new ScalarMetric( - "addresses_forwarders_ready_total", - "Total number of forwarders in ready state", - MetricType.gauge, - () -> Collections.singletonList(new MetricValue(readyForwarders, metricLabels)))); - - metrics.registerMetric(new ScalarMetric( - "addresses_forwarders_not_ready_total", - "Total number of forwarders in not ready state", - MetricType.gauge, - () -> Collections.singletonList(new MetricValue(notReadyForwarders, metricLabels)))); - - metrics.registerMetric(new ScalarMetric( - "standard_controller_loop_duration_seconds", - "Time spent in controller loop", - MetricType.gauge, - () -> Collections.singletonList(new MetricValue((double) totalTime / 1_000_000_000.0, metricLabels)))); - - metrics.registerMetric(new ScalarMetric( - "standard_controller_router_check_failures_total", - "Number of RouterCheckFailures", - MetricType.counter, - () -> Collections.singletonList(new MetricValue(statusCollector.getRouterCheckFailures(), metricLabels)))); - } - - public void start() throws Exception { - // Run initial status check so that existing addresses are ready - statusCollector.checkRouterStatus(); - statusCollector.start(); - reconciler.start(); - watch = addressApi.watchAddresses(reconciler, options.getResyncInterval()); - } - - public void stop() throws Exception { - if (watch != null) { - watch.close(); - } - statusCollector.stop(); - reconciler.stop(); - } - - @Override - public void onUpdate(List
addressList) throws Exception { - long start = System.nanoTime(); - - Schema schema = schemaProvider.getSchema(); - if (schema == null) { - log.info("No schema available"); - return; - } - AddressSpaceType addressSpaceType = schema.findAddressSpaceType("standard").orElseThrow(() -> new RuntimeException("Unable to handle updates: standard address space not found in schema!")); - AddressResolver addressResolver = new AddressResolver(addressSpaceType); - if (addressSpaceType.getPlans().isEmpty()) { - log.info("No address space plan available"); - return; - } - - String addressPrefix = String.format("%s.", options.getAddressSpace()); - // make a deep copy of the list first, but only for relevant items - addressList = addressList.stream() - .filter(a -> a.getMetadata().getName().startsWith(addressPrefix)) - // copy item - .map(a -> new AddressBuilder(a).build()) - .collect(Collectors.toList()); - - AddressSpaceResolver addressSpaceResolver = new AddressSpaceResolver(schema); - - final Map previousStatus = addressList.stream() - .collect(Collectors.toMap(a -> a.getMetadata().getName(), - a -> new ProvisionState(a.getStatus(), a.getAnnotation(AnnotationKeys.APPLIED_PLAN)))); - - AddressSpacePlan addressSpacePlan = addressSpaceType.findAddressSpacePlan(options.getAddressSpacePlanName()).orElseThrow(() -> new RuntimeException("Unable to handle updates: address space plan " + options.getAddressSpacePlanName() + " not found!")); - InfraConfig infraConfig = addressSpaceResolver.getInfraConfig("standard", addressSpacePlan.getMetadata().getName()); - boolean withMqtt = isWithMqtt(infraConfig); - - long resolvedPlan = System.nanoTime(); - - AddressSpace addressSpace = addressSpaceApi.getAddressSpaceWithName(options.getAddressSpaceNamespace(), options.getAddressSpace()).orElse(null); - if (addressSpace == null) { - log.warn("Unable to find address space, will not validate address forwarders"); - } - - AddressProvisioner provisioner = new AddressProvisioner(addressSpaceResolver, addressResolver, addressSpacePlan, clusterGenerator, kubernetes, eventLogger, options.getInfraUuid(), brokerIdGenerator); - - Map validAddresses = new HashMap<>(); - List readyPhases = Arrays.asList(Configuring, Active); - for (Address address : addressList) { - address.getStatus().clearMessages(); - if (readyPhases.contains(address.getStatus().getPhase())) { - address.getStatus().setReady(true); - } - - if (address.getSpec().getForwarders() != null) { - List forwarderStatuses = new ArrayList<>(); - for (AddressSpecForwarder forwarder : address.getSpec().getForwarders()) { - forwarderStatuses.add(new AddressStatusForwarderBuilder() - .withName(forwarder.getName()) - .withReady(true) - .build()); - } - address.getStatus().setForwarders(forwarderStatuses); - } - - if (!validateAddress(address, addressSpace, addressResolver)) { - continue; - } - - Address existing = validAddresses.get(address.getSpec().getAddress()); - if (existing != null) { - if (!address.getStatus().getPhase().equals(Pending) && existing.getStatus().getPhase().equals(Pending)) { - // If existing address is pending, and we are not pending, we take priority - String errorMessage = String.format("Address '%s' already exists with resource name '%s'", address.getSpec().getAddress(), address.getMetadata().getName()); - existing.getStatus().setPhase(Pending); - existing.getStatus().appendMessage(errorMessage); - validAddresses.put(address.getSpec().getAddress(), address); - } else { - // Existing address has already been accepted, or we are both pending, existing takes priority. - String errorMessage = String.format("Address '%s' already exists with resource name '%s'", address.getSpec().getAddress(), existing.getMetadata().getName()); - address.getStatus().setPhase(Pending); - address.getStatus().appendMessage(errorMessage); - } - continue; - } - - if ("subscription".equals(address.getSpec().getType())) { - String topic = address.getSpec().getTopic(); - if (topic == null) { - String errorMessage = String.format("Subscription address '%s' (resource name '%s') must reference a known topic address.", - address.getSpec().getAddress(), - address.getMetadata().getName()); - address.getStatus().setPhase(Pending); - address.getStatus().appendMessage(errorMessage); - continue; - } else { - Optional
refTopic = addressList.stream().filter(a -> topic.equals(a.getSpec().getAddress())).findFirst(); - if (refTopic.isEmpty()) { - String errorMessage = String.format( - "Subscription address '%s' (resource name '%s') references a topic address '%s' that does not exist.", - address.getSpec().getAddress(), - address.getMetadata().getName(), - topic); - address.getStatus().setPhase(Pending); - address.getStatus().appendMessage(errorMessage); - continue; - } else { - Address target = refTopic.get(); - if (!"topic".equals(target.getSpec().getType())) { - String errorMessage = String.format( - "Subscription address '%s' (resource name '%s') references a topic address '%s'" + - " (resource name '%s') that is not of expected type 'topic' (found type '%s' instead).", - address.getSpec().getAddress(), - address.getMetadata().getName(), - topic, - target.getMetadata().getName(), - target.getSpec().getType()); - address.getStatus().setPhase(Pending); - address.getStatus().appendMessage(errorMessage); - continue; - } - } - } - } - validAddresses.put(address.getSpec().getAddress(), address); - } - - Set
addressSet = new LinkedHashSet<>(validAddresses.values()); - - Map countByPhase = countPhases(addressSet); - log.info("Total: {}, Active: {}, Configuring: {}, Pending: {}, Terminating: {}, Failed: {}", addressSet.size(), countByPhase.get(Active), countByPhase.get(Configuring), countByPhase.get(Pending), countByPhase.get(Terminating), countByPhase.get(Failed)); - - Map> usageMap = provisioner.checkUsage(filterByNotPhases(addressSet, EnumSet.of(Pending))); - - log.info("Usage: {}", usageMap); - - long calculatedUsage = System.nanoTime(); - Set
pendingAddresses = filterBy(addressSet, address -> address.getStatus().getPhase().equals(Pending) || - AddressProvisioner.hasPlansChanged(addressResolver, address)); - - Map> neededMap = provisioner.checkQuota(usageMap, pendingAddresses, addressSet); - - // If we have address in configuring or pending, wake up the checker thread - if (countByPhase.get(Configuring) > 0 || countByPhase.get(Pending) > 0) { - statusCollector.wakeup(); - } - - log.info("Needed: {}", neededMap); - - long checkedQuota = System.nanoTime(); - - List clusterList = kubernetes.listClusters(); - RouterCluster routerCluster = kubernetes.getRouterCluster(); - long listClusters = System.nanoTime(); - - StandardInfraConfig desiredConfig = (StandardInfraConfig) addressSpaceResolver.getInfraConfig("standard", addressSpacePlan.getMetadata().getName()); - provisioner.provisionResources(routerCluster, clusterList, neededMap, pendingAddresses, desiredConfig); - - long provisionResources = System.nanoTime(); - - Set
liveAddresses = filterByPhases(addressSet, EnumSet.of(Configuring, Active)); - boolean checkRouterLinks = liveAddresses.stream() - .anyMatch(a -> Arrays.asList("queue", "subscription").contains(a.getSpec().getType()) && - a.getSpec().getForwarders() != null && !a.getSpec().getForwarders().isEmpty()); - - List routerStatusList = checkRouterStatuses(checkRouterLinks); - - Set subserveTopics = Collections.emptySet(); - if (withMqtt) { - subserveTopics = checkRegisteredSubserveTopics(); - } - checkAddressStatuses(liveAddresses, addressResolver, routerStatusList, subserveTopics, withMqtt); - - long checkStatuses = System.nanoTime(); - for (Address address : liveAddresses) { - if (address.getStatus().isReady()) { - address.getStatus().setPhase(Active); - } - } - - checkAndMoveMigratingBrokersToDraining(addressSet, clusterList); - checkAndRemoveDrainingBrokers(addressSet); - - Set
notTerminating = filterByNotPhases(addressSet, EnumSet.of(Terminating)); - List unusedClusters = determineUnusedClusters(clusterList, notTerminating); - deprovisionUnused(unusedClusters); - - long deprovisionUnused = System.nanoTime(); - - List usedClusters = new ArrayList<>(clusterList); - usedClusters.removeAll(unusedClusters); - upgradeClusters(desiredConfig, addressResolver, usedClusters, notTerminating); - - long upgradeClusters = System.nanoTime(); - - int staleCount = 0; - for (Address address : addressList) { - var addressName = address.getMetadata().getNamespace() + ":" + address.getMetadata().getName(); - ProvisionState previous = previousStatus.get(address.getMetadata().getName()); - ProvisionState current = new ProvisionState(address.getStatus(), address.getAnnotation(AnnotationKeys.APPLIED_PLAN)); - log.debug("Compare current state\nPrevious: {}\nCurrent : {}", previous, current); - if (!current.equals(previous)) { - log.debug("Address did change: {}", addressName); - try { - if(!addressApi.replaceAddress(address)) { - log.info("Failed to persist address state: {}", addressName); - } - } catch (KubernetesClientException e) { - if (e.getStatus().getCode() == 409) { - // The address record is stale. The address controller will be notified again by the watcher, - // so safe ignore the stale record. - log.debug("Address {} has stale resource version {}", address.getMetadata().getName(), address.getMetadata().getResourceVersion()); - staleCount++; - } else { - throw e; - } - } - } else { - log.debug("No change for address: {}", addressName); - } - } - - if (staleCount > 0) { - log.info("{} address(es) were stale.", staleCount); - } - - long replaceAddresses = System.nanoTime(); - garbageCollectTerminating(filterByPhases(addressSet, EnumSet.of(Terminating)), addressResolver, routerStatusList, subserveTopics, withMqtt); - long gcTerminating = System.nanoTime(); - - log.info("Time spent: Total: {} ns, resolvedPlan: {} ns, calculatedUsage: {} ns, checkedQuota: {} ns, listClusters: {} ns, provisionResources: {} ns, checkStatuses: {} ns, deprovisionUnused: {} ns, upgradeClusters: {} ns, replaceAddresses: {} ns, gcTerminating: {} ns", gcTerminating - start, resolvedPlan - start, calculatedUsage - resolvedPlan, checkedQuota - calculatedUsage, listClusters - checkedQuota, provisionResources - listClusters, checkStatuses - provisionResources, deprovisionUnused - checkStatuses, upgradeClusters - deprovisionUnused, replaceAddresses - upgradeClusters, gcTerminating - replaceAddresses); - - if (routerStatusList.isEmpty()) { - readyAddressCount = null; - notReadyAddressCount = null; - notReadyForwarders = null; - readyForwarders = null; - } else { - readyAddressCount = addressList.stream() - .filter(a -> a.getStatus().isReady()) - .count(); - notReadyAddressCount = addressList.size() - readyAddressCount; - - Set
addressesWithForwarders = filterBy(addressSet, address -> (address.getStatus().getForwarders() != null && !address.getStatus().getForwarders().isEmpty())); - readyForwarders = countForwardersReady(addressesWithForwarders, true); - notReadyForwarders = countForwardersReady(addressesWithForwarders, false); - numForwarders = readyForwarders + notReadyForwarders; - } - - numAddresses = (long) addressList.size(); - totalTime = gcTerminating - start; - this.countByPhase = countByPhase; - } - - private long countForwardersReady(Set
addressesWithForwarders, boolean desired) { - long total = 0; - for (Address address : addressesWithForwarders) { - for (AddressStatusForwarder forwarder : address.getStatus().getForwarders()) { - if (forwarder.isReady() == desired) { - total++; - } - } - } - return total; - } - - private Set checkRegisteredSubserveTopics() { - SubserveStatusCollector statusCollector = new SubserveStatusCollector(vertx, options.getCertDir()); - - for (Pod router : kubernetes.listRouters()) { - if (Readiness.isPodReady(router)) { - try { - return statusCollector.collect(router); - } catch (Exception e) { - log.info("Error requesting registered topics from {}. Ignoring", router.getMetadata().getName(), e); - } - } - } - return Collections.emptySet(); - } - - private boolean isWithMqtt(InfraConfig infraConfig) { - return infraConfig.getMetadata().getAnnotations() != null && Boolean.parseBoolean(infraConfig.getMetadata().getAnnotations().getOrDefault(AnnotationKeys.WITH_MQTT, "false")); - } - - private void upgradeClusters(StandardInfraConfig desiredConfig, AddressResolver addressResolver, List clusterList, Set
addresses) throws Exception { - for (BrokerCluster cluster : clusterList) { - final StandardInfraConfig currentConfig = cluster.getInfraConfig(); - if (!desiredConfig.equals(currentConfig)) { - if (options.getVersion().equals(desiredConfig.getSpec().getVersion())) { - if (!desiredConfig.getUpdatePersistentVolumeClaim() && currentConfig != null && !currentConfig.getSpec().getBroker().getResources().getStorage().equals(desiredConfig.getSpec().getBroker().getResources().getStorage())) { - desiredConfig = new StandardInfraConfigBuilder(desiredConfig) - .editSpec() - .editBroker() - .editResources() - .withStorage(currentConfig.getSpec().getBroker().getResources().getStorage()) - .endResources() - .endBroker() - .endSpec() - .build(); - } - BrokerCluster upgradedCluster = null; - if (!cluster.getClusterId().startsWith("broker-sharded")) { - upgradedCluster = clusterGenerator.generateCluster(cluster.getClusterId(), 1, null, null, desiredConfig); - } else { - Address address = addresses.stream() - .filter(a -> a.getStatus().getBrokerStatuses().stream().map(BrokerStatus::getClusterId).collect(Collectors.toSet()).contains(cluster.getClusterId())) - .findFirst() - .orElse(null); - if (address != null) { - AddressPlan plan = addressResolver.getPlan(address); - int brokerNeeded = 0; - for (Map.Entry resourceRequest : plan.getResources().entrySet()) { - if (resourceRequest.getKey().equals("broker")) { - brokerNeeded = resourceRequest.getValue().intValue(); - break; - } - } - upgradedCluster = clusterGenerator.generateCluster(cluster.getClusterId(), brokerNeeded, address, plan, desiredConfig); - } - } - log.info("Upgrading broker {}", cluster.getClusterId()); - cluster.updateResources(upgradedCluster, desiredConfig); - boolean updatePersistentVolumeClaim = desiredConfig.getUpdatePersistentVolumeClaim(); - List itemsToBeApplied = new ArrayList<>(cluster.getResources().getItems()); - try { - kubernetes.apply(cluster.getResources(), updatePersistentVolumeClaim, itemsToBeApplied::remove); - } catch (KubernetesClientException original) { - // Workaround for #2880 Failure executing: PATCH... Message: Unable to access invalid index: 20. - if (!itemsToBeApplied.isEmpty() && original.getMessage() != null && original.getMessage().contains("Unable to access invalid index")) { - HasMetadata failedResource = itemsToBeApplied.get(0); - if (failedResource instanceof StatefulSet || failedResource instanceof Deployment) { - log.warn("Failed to apply {} for cluster {}, will try #2880 workaround", failedResource, cluster.getClusterId(), original); - try { - if (failedResource instanceof StatefulSet) { - StatefulSetSpec spec = ((StatefulSet) failedResource).getSpec(); - stripEnvironmentFromResource(spec.getTemplate()); - spec.setReplicas(0); - } else { - DeploymentSpec spec = ((Deployment) failedResource).getSpec(); - stripEnvironmentFromResource(spec.getTemplate()); - spec.setReplicas(0); - } - Kubernetes.addObjectAnnotation(failedResource, AnnotationKeys.APPLIED_INFRA_CONFIG, new ObjectMapper().writeValueAsString(currentConfig)); - kubernetes.apply(failedResource, updatePersistentVolumeClaim); - log.warn("Applied #2880 workaround for {} of {}, next upgrade cycle should complete upgrade.", failedResource.getMetadata(), cluster.getClusterId()); - } catch (KubernetesClientException e) { - log.error("Failed to apply failed resource {} of {} for #2880 workaround. " + - "Manual intervention may be required to complete upgrade", failedResource.getMetadata(), cluster.getClusterId()); - } - } else { - log.warn("Don't know how to work around #2880 for resource type {}", failedResource.getClass()); - } - } - throw original; - } - eventLogger.log(BrokerUpgraded, "Upgraded broker", Normal, Broker, cluster.getClusterId()); - } else { - log.info("Version of desired config ({}) does not match controller version ({}), skipping upgrade", desiredConfig.getSpec().getVersion(), options.getVersion()); - } - } - } - } - - private void stripEnvironmentFromResource(PodTemplateSpec resource) { - resource.getSpec().getContainers().forEach( - c -> { - c.setEnv(Collections.emptyList()); - } - ); - resource.getSpec().getInitContainers().forEach( - c -> { - c.setEnv(Collections.emptyList()); - } - ); - } - - private List determineUnusedClusters(List clusters, Set
addressSet) { - List unused = new ArrayList<>(); - - for (BrokerCluster cluster : clusters) { - int numFound = 0; - for (Address address : addressSet) { - Set clusterIds = address.getStatus().getBrokerStatuses().stream() - .map(BrokerStatus::getClusterId) - .collect(Collectors.toSet()); - if (clusterIds.contains(cluster.getClusterId())) { - numFound++; - } - for (BrokerStatus brokerStatus : address.getStatus().getBrokerStatuses()) { - if (brokerStatus.getClusterId().equals(cluster.getClusterId())) { - numFound++; - } - } - } - - if (numFound == 0) { - unused.add(cluster); - } - } - return unused; - } - - private void deprovisionUnused(List unused) { - unused.forEach(cluster -> { - try { - kubernetes.delete(cluster.getResources()); - eventLogger.log(ControllerReason.BrokerDeleted, "Deleted broker " + cluster.getClusterId(), Normal, ControllerKind.Address, cluster.getClusterId()); - } catch (Exception e) { - log.warn("Error deleting cluster {}", cluster.getClusterId(), e); - eventLogger.log(ControllerReason.BrokerDeleteFailed, "Error deleting broker cluster " + cluster.getClusterId() + ": " + e.getMessage(), EventLogger.Type.Warning, ControllerKind.Address, cluster.getClusterId()); - } - }); - } - - private Set
filterBy(Set
addressSet, Predicate
predicate) { - return addressSet.stream() - .filter(predicate::test) - .collect(Collectors.toSet()); - } - - private Set
filterByPhases(Set
addressSet, Set phases) { - return addressSet.stream() - .filter(address -> phases.contains(address.getStatus().getPhase())) - .collect(Collectors.toSet()); - } - - private Map countPhases(Set
addressSet) { - Map countMap = new HashMap<>(); - for (Phase phase : Phase.values()) { - countMap.put(phase, 0L); - } - for (Address address : addressSet) { - countMap.put(address.getStatus().getPhase(), 1 + countMap.get(address.getStatus().getPhase())); - } - return countMap; - } - - private Set
filterByNotPhases(Set
addressSet, Set phases) { - return addressSet.stream() - .filter(address -> !phases.contains(address.getStatus().getPhase())) - .collect(Collectors.toSet()); - } - - private void garbageCollectTerminating(Set
addresses, AddressResolver addressResolver, List routerStatusList, Set subserveTopics, boolean withMqtt) throws Exception { - Map okMap = checkAddressStatuses(addresses, addressResolver, routerStatusList, subserveTopics, withMqtt); - for (Map.Entry entry : okMap.entrySet()) { - if (entry.getValue() == 0) { - log.info("Garbage collecting {}", entry.getKey()); - addressApi.deleteAddress(entry.getKey()); - } - } - } - - private void checkAndMoveMigratingBrokersToDraining(Set
addresses, List brokerList) throws Exception { - for (Address address : addresses) { - int numActive = 0; - int numReadyActive = 0; - for (BrokerStatus brokerStatus : address.getStatus().getBrokerStatuses()) { - if (BrokerState.Active.equals(brokerStatus.getState())) { - numActive++; - for (BrokerCluster cluster : brokerList) { - if (brokerStatus.getClusterId().equals(cluster.getClusterId()) && cluster.getReadyReplicas() > 0) { - numReadyActive++; - break; - } - } - } - } - - if (numActive == numReadyActive) { - for (BrokerStatus brokerStatus : address.getStatus().getBrokerStatuses()) { - if (BrokerState.Migrating.equals(brokerStatus.getState())) { - brokerStatus.setState(BrokerState.Draining); - } - } - } - } - } - - private void checkAndRemoveDrainingBrokers(Set
addresses) throws Exception { - BrokerStatusCollector brokerStatusCollector = new BrokerStatusCollector(kubernetes, brokerClientFactory, options); - for (Address address : addresses) { - List brokerStatuses = new ArrayList<>(); - for (BrokerStatus brokerStatus : address.getStatus().getBrokerStatuses()) { - if (BrokerState.Draining.equals(brokerStatus.getState())) { - try { - long messageCount = brokerStatusCollector.getQueueMessageCount(address.getSpec().getAddress(), brokerStatus.getClusterId()); - if (messageCount > 0) { - brokerStatuses.add(brokerStatus); - } - } catch (Exception e) { - log.warn("Error checking status of broker {}:{} in state Draining. Keeping.", brokerStatus.getClusterId(), brokerStatus.getContainerId(), e); - brokerStatuses.add(brokerStatus); - } - } else { - brokerStatuses.add(brokerStatus); - } - } - address.getStatus().setBrokerStatuses(brokerStatuses); - } - } - - private List checkRouterStatuses(boolean checkRouterLinks) throws Exception { - - statusCollector.setCheckRouterLinks(checkRouterLinks); - - return statusCollector.getLatestResults(); - } - - private Map checkAddressStatuses(Set
addresses, AddressResolver addressResolver, List routerStatusList, Set subserveTopics, boolean withMqtt) throws Exception { - - Map numOk = new HashMap<>(); - if (addresses.isEmpty()) { - return numOk; - } - Map clusterOk = new HashMap<>(); - Map> clusterAddresses = new HashMap<>(); - Map> clusterQueues = new HashMap<>(); - - BrokerStatusCollector brokerStatusCollector = new BrokerStatusCollector(kubernetes, brokerClientFactory, options); - for (Address address : addresses) { - AddressType addressType = addressResolver.getType(address); - AddressPlan addressPlan = addressResolver.getPlan(addressType, address); - - int ok = 0; - switch (addressType.getName()) { - case "queue": - ok += checkBrokerStatus(brokerStatusCollector, address, clusterOk, clusterAddresses, clusterQueues); - for (RouterStatus routerStatus : routerStatusList) { - ok += routerStatus.checkAddress(address); - ok += routerStatus.checkAutoLinks(address); - } - for (BrokerStatus brokerStatus : address.getStatus().getBrokerStatuses()) { - Set addressNames = clusterAddresses.get(brokerStatus.getClusterId()); - Set queueNames = clusterQueues.get(brokerStatus.getClusterId()); - if (!addressNames.contains(address.getSpec().getAddress())) { - address.getStatus().setReady(false); - address.getStatus().appendMessage("Address " + address.getSpec().getAddress() + " is not configured on broker " + brokerStatus.getContainerId()); - } else { - ok++; - } - if (!queueNames.contains(address.getSpec().getAddress())) { - address.getStatus().setReady(false); - address.getStatus().appendMessage("Queue " + address.getSpec().getAddress() + " is not configured on broker " + brokerStatus.getContainerId()); - } else { - ok++; - } - } - ok += RouterStatus.checkActiveAutoLink(address, routerStatusList); - ok += RouterStatus.checkForwarderLinks(address, routerStatusList); - updateMessageTtlStatus(addressPlan, address); - break; - case "subscription": - ok += checkBrokerStatus(brokerStatusCollector, address, clusterOk, clusterAddresses, clusterQueues); - for (BrokerStatus brokerStatus : address.getStatus().getBrokerStatuses()) { - Set addressNames = clusterAddresses.get(brokerStatus.getClusterId()); - Set queueNames = clusterQueues.get(brokerStatus.getClusterId()); - if (!addressNames.contains(address.getSpec().getTopic())) { - address.getStatus().setReady(false); - address.getStatus().appendMessage("Address " + address.getSpec().getTopic() + " is not configured on broker " + brokerStatus.getContainerId()); - } else { - ok++; - } - if (!queueNames.contains(address.getSpec().getAddress())) { - address.getStatus().setReady(false); - address.getStatus().appendMessage("Queue " + address.getSpec().getAddress() + " is not configured on broker " + brokerStatus.getContainerId()); - } else { - ok++; - } - } - ok += RouterStatus.checkForwarderLinks(address, routerStatusList); - break; - case "topic": - ok += checkBrokerStatus(brokerStatusCollector, address, clusterOk, clusterAddresses, clusterQueues); - for (RouterStatus routerStatus : routerStatusList) { - ok += routerStatus.checkLinkRoutes(address); - } - if (isPooled(addressPlan)) { - for (BrokerStatus brokerStatus : address.getStatus().getBrokerStatuses()) { - Set addressNames = clusterAddresses.get(brokerStatus.getClusterId()); - if (!addressNames.contains(address.getSpec().getAddress())) { - address.getStatus().setReady(false); - address.getStatus().appendMessage("Address " + address.getSpec().getAddress() + " is not configured on broker " + brokerStatus.getContainerId()); - } else { - ok++; - } - } - ok += RouterStatus.checkActiveLinkRoute(address, routerStatusList); - } else { - ok += RouterStatus.checkConnection(address, routerStatusList); - if (withMqtt) { - SubserveStatusCollector.checkTopicRegistration(subserveTopics, address, addressPlan); - } - } - updateMessageTtlStatus(addressPlan, address); - break; - case "anycast": - case "multicast": - for (RouterStatus routerStatus : routerStatusList) { - ok += routerStatus.checkAddress(address); - } - break; - } - numOk.put(address, ok); - } - - return numOk; - } - - private void updateMessageTtlStatus(AddressPlan addressPlan, Address address) { - MessageTtlBuilder status = new MessageTtlBuilder(); - - MessageTtl planTtl = addressPlan.getTtl(); - if (planTtl != null) { - Long min = sanitizeTtlValue(planTtl.getMinimum()); - Long max = sanitizeTtlValue(planTtl.getMaximum()); - - boolean maxGtMin = min == null || max == null || max > min; - if (max != null && maxGtMin) { - status.withMaximum(max); - } - if (min != null && maxGtMin) { - status.withMinimum(min); - } - } - - MessageTtl addrTtl = address.getSpec().getMessageTtl(); - if (addrTtl != null) { - Long min = sanitizeTtlValue(addrTtl.getMinimum()); - Long max = sanitizeTtlValue(addrTtl.getMaximum()); - - boolean maxGtMin = min == null || max == null || max > min; - if (max != null && maxGtMin && (!status.hasMaximum() || status.getMaximum() > max)) { - status.withMaximum(max); - } - if (min != null && maxGtMin && (!status.hasMinimum() || status.getMinimum() < min)) { - status.withMinimum(min); - } - } - - address.getStatus().setMessageTtl(status.hasMaximum() || status.hasMinimum() ? status.build() : null); - - } - - private Long sanitizeTtlValue(Long ttl) { - return ttl != null && ttl < 1 ? null : ttl; - } - - private int checkBrokerStatus(BrokerStatusCollector brokerStatusCollector, Address address, Map clusterOk, Map> clusterAddresses, Map> clusterQueues) throws Exception { - Set clusterIds = address.getStatus().getBrokerStatuses().stream() - .map(BrokerStatus::getClusterId) - .collect(Collectors.toSet()); - - int numOk = 0; - for (String clusterId : clusterIds) { - if (!clusterOk.containsKey(clusterId)) { - if (!kubernetes.isDestinationClusterReady(clusterId)) { - address.getStatus().setReady(false).appendMessage("Cluster " + clusterId + " is unavailable"); - clusterOk.put(clusterId, 0); - } else { - clusterOk.put(clusterId, 1); - if (!clusterAddresses.containsKey(clusterId)) { - try { - clusterAddresses.put(clusterId, brokerStatusCollector.getAddressNames(clusterId)); - } catch (Exception e) { - log.warn("Error retrieving address names for cluster {}: {}", clusterId, e.getMessage()); - } - } - - if (!clusterQueues.containsKey(clusterId)) { - try { - clusterQueues.put(clusterId, brokerStatusCollector.getQueueNames(clusterId)); - } catch (Exception e) { - log.warn("Error retrieving queue names for cluster {}: {}", clusterId, e.getMessage()); - } - } - } - } - if (!clusterAddresses.containsKey(clusterId)) { - clusterAddresses.put(clusterId, Collections.emptySet()); - } - if (!clusterQueues.containsKey(clusterId)) { - clusterQueues.put(clusterId, Collections.emptySet()); - } - numOk += clusterOk.get(clusterId); - } - return numOk; - } - - private boolean validateAddress(Address address, AddressSpace addressSpace, AddressResolver addressResolver) { - if (!addressResolver.validate(address)) { - return false; - } - - boolean valid = true; - if (addressSpace == null) { - return valid; - } - - if (address.getSpec().getForwarders() != null && !address.getSpec().getForwarders().isEmpty()) { - if (addressSpace.getSpec().getConnectors() == null || addressSpace.getSpec().getConnectors().isEmpty()) { - valid = false; - address.getStatus().appendMessage(String.format("Unable to create forwarders: There are no connectors configured for address space '%s'", addressSpace.getMetadata().getName())); - } - - if (!Arrays.asList("queue", "subscription").contains(address.getSpec().getType())) { - valid = false; - address.getStatus().appendMessage(String.format("Unable to create forwarders for address type '%s': Forwarders can only be created for address types 'queue' and 'subscription'", address.getSpec().getType())); - } - - for (AddressSpecForwarder forwarder : address.getSpec().getForwarders()) { - boolean found = false; - for (AddressSpaceSpecConnector connector : addressSpace.getSpec().getConnectors()) { - if (forwarder.getRemoteAddress().startsWith(connector.getName())) { - found = true; - break; - } - } - if (!found) { - valid = false; - address.getStatus().appendMessage(String.format("Unable to create forwarder '%s': remoteAddress '%s' is not prefixed with any connector in address space '%s'", forwarder.getName(), forwarder.getRemoteAddress(), address.getMetadata().getName())); - } - - if ("subscription".equals(address.getSpec().getType()) && AddressSpecForwarderDirection.in.equals(forwarder.getDirection())) { - valid = false; - address.getStatus().appendMessage(String.format("Unable to create forwarder '%s': direction 'in' is not allowed on 'subscription' address type", forwarder.getName())); - } - } - } - if (!valid) { - address.getStatus().setReady(false); - } - - return valid; - } - - private boolean isPooled(AddressPlan plan) { - for (Map.Entry request : plan.getResources().entrySet()) { - if ("broker".equals(request.getKey()) && request.getValue() < 1.0) { - return true; - } - } - return false; - } - - private class ProvisionState { - private final AddressStatus status; - private final String plan; - - public ProvisionState(AddressStatus status, String plan) { - this.status = new AddressStatusBuilder(status).build(); - this.plan = plan; - } - - @Override - public boolean equals(Object o) { - if (this == o) return true; - if (o == null || getClass() != o.getClass()) return false; - ProvisionState that = (ProvisionState) o; - return Objects.equals(status, that.status) && - Objects.equals(plan, that.plan); - } - - @Override - public int hashCode() { - return Objects.hash(status, plan); - } - - @Override - public String toString() { - return "ProvisionState{" + - "status=" + status + - ", plan='" + plan + '\'' + - '}'; - } - } -} diff --git a/standard-controller/src/main/java/io/enmasse/controller/standard/AddressProbeClient.java b/standard-controller/src/main/java/io/enmasse/controller/standard/AddressProbeClient.java deleted file mode 100644 index c481172fece..00000000000 --- a/standard-controller/src/main/java/io/enmasse/controller/standard/AddressProbeClient.java +++ /dev/null @@ -1,190 +0,0 @@ -/* - * Copyright 2020, EnMasse authors. - * License: Apache License 2.0 (see the file LICENSE or http://apache.org/licenses/LICENSE-2.0.html). - */ -package io.enmasse.controller.standard; - -import io.vertx.core.Context; -import io.vertx.core.Vertx; -import io.vertx.core.impl.ConcurrentHashSet; -import io.vertx.proton.ProtonClient; -import io.vertx.proton.ProtonClientOptions; -import io.vertx.proton.ProtonConnection; -import io.vertx.proton.ProtonReceiver; -import io.vertx.proton.ProtonSender; -import org.apache.qpid.proton.Proton; -import org.apache.qpid.proton.amqp.messaging.Accepted; -import org.apache.qpid.proton.amqp.messaging.AmqpValue; -import org.apache.qpid.proton.amqp.messaging.ApplicationProperties; -import org.apache.qpid.proton.message.Message; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import java.time.Duration; -import java.util.ArrayList; -import java.util.Collections; -import java.util.HashSet; -import java.util.List; -import java.util.Map; -import java.util.Set; -import java.util.UUID; -import java.util.concurrent.CompletableFuture; -import java.util.concurrent.CountDownLatch; -import java.util.concurrent.TimeUnit; - -public class AddressProbeClient implements AutoCloseable { - private static final Logger log = LoggerFactory.getLogger(AddressProbeClient.class); - - private final Vertx vertx; - private final String containerId; - - private Context context; - private ProtonConnection connection; - private final List senders = new ArrayList<>(); - private final List receivers = new ArrayList<>(); - - public AddressProbeClient(Vertx vertx, String containerId) { - this.vertx = vertx; - this.containerId = containerId; - } - - public void connect(String host, int port, ProtonClientOptions clientOptions, CompletableFuture promise) { - if (connection != null) { - log.debug("Already connected"); - promise.complete(null); - return; - } - - ProtonClient client = ProtonClient.create(vertx); - log.debug("Connecting to {}:{}", host, port); - client.connect(clientOptions, host, port, result -> { - if (result.succeeded()) { - log.debug("Connected to {}:{}", host, port); - connection = result.result(); - context = vertx.getOrCreateContext(); - connection.setContainer(containerId); - connection.open(); - promise.complete(null); - } else { - log.info("Connection to {}:{} failed", host, port, result.cause()); - promise.completeExceptionally(result.cause()); - } - }); - } - - public Set probeAddresses(Set addresses, Duration timeout) { - if (connection == null) { - throw new RuntimeException("HelathProbeClient not connected"); - } - - String payload = String.format("PING %s", UUID.randomUUID().toString()); - Map properties = Collections.singletonMap("probe", "true"); - Message message = Proton.message(); - message.setBody(new AmqpValue(payload)); - message.setApplicationProperties(new ApplicationProperties(properties)); - - Set passed = new ConcurrentHashSet<>(); - CountDownLatch done = new CountDownLatch(addresses.size()); - - runOnContext(context, () -> { - for (String address : addresses) { - ProtonReceiver receiver = connection.createReceiver(address); - receiver.handler((delivery, m) -> { - String data = (String) ((AmqpValue) m.getBody()).getValue(); - delivery.disposition(Accepted.getInstance(), true); - if (payload.equals(data)) { - passed.add(address); - done.countDown(); - } - }); - receiver.openHandler(protonReceiverAsyncResult -> { - if (protonReceiverAsyncResult.succeeded()) { - ProtonSender sender = connection.createSender(address); - sender.openHandler(protonSenderAsyncResult -> { - if (protonSenderAsyncResult.succeeded()) { - sender.send(message, protonDelivery -> { - log.debug("Message sent to {}!", address); - }); - } - }); - sender.closeHandler(remoteCloseHandler -> { - if (remoteCloseHandler.failed()) { - log.warn("Error closing sender for {}: {}", address, sender.getRemoteCondition().getDescription()); - } - }); - vertx.setTimer(4000, h -> context.runOnContext(p -> { - log.debug("Opening sender for {}", address); - sender.open(); - synchronized (this.senders) { - this.senders.add(sender); - } - })); - } - }); - receiver.closeHandler(remoteCloseHandler -> { - if (remoteCloseHandler.failed()) { - log.warn("Error closing receiver for {}: {}", address, receiver.getRemoteCondition().getDescription()); - } - }); - synchronized (this.receivers) { - this.receivers.add(receiver); - } - log.debug("Opening receiver for {}", address); - receiver.open(); - } - }); - - try { - done.await(timeout.getSeconds(), TimeUnit.SECONDS); - } catch (InterruptedException ignored) { - Thread.currentThread().interrupt(); - } - return new HashSet<>(passed); - } - - @Override - public void close() throws Exception { - if (context != null) { - CompletableFuture.allOf( - runOnContext(context, () -> { - synchronized (senders) { - for (ProtonSender sender : senders) { - if (sender != null) { - sender.close(); - } - } - } - }), - runOnContext(context, () -> { - synchronized (receivers) { - for (ProtonReceiver receiver : receivers) { - if (receiver != null) { - receiver.close(); - } - } - } - }), - runOnContext(context, () -> { - if (connection != null) { - connection.close(); - } - })).get(1, TimeUnit.MINUTES); - } - senders.clear(); - receivers.clear(); - connection = null; - } - - private static CompletableFuture runOnContext(Context context, Runnable runnable) { - CompletableFuture promise = new CompletableFuture<>(); - context.runOnContext(h -> { - try { - runnable.run(); - promise.complete(null); - } catch (Exception e) { - promise.completeExceptionally(e); - } - }); - return promise; - } -} diff --git a/standard-controller/src/main/java/io/enmasse/controller/standard/AddressProber.java b/standard-controller/src/main/java/io/enmasse/controller/standard/AddressProber.java deleted file mode 100644 index 1beb1ecd32d..00000000000 --- a/standard-controller/src/main/java/io/enmasse/controller/standard/AddressProber.java +++ /dev/null @@ -1,62 +0,0 @@ -/* - * Copyright 2020, EnMasse authors. - * License: Apache License 2.0 (see the file LICENSE or http://apache.org/licenses/LICENSE-2.0.html). - */ -package io.enmasse.controller.standard; - -import io.vertx.core.Vertx; -import io.vertx.core.net.PemKeyCertOptions; -import io.vertx.core.net.PemTrustOptions; -import io.vertx.proton.ProtonClientOptions; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import java.io.File; -import java.time.Duration; -import java.util.Set; -import java.util.concurrent.CompletableFuture; -import java.util.concurrent.TimeUnit; - -public class AddressProber { - private static final Logger log = LoggerFactory.getLogger(AddressProber.class); - private final Vertx vertx; - private final String containerId; - private final ProtonClientOptions options; - private final Duration probeTimeout; - - public AddressProber(Vertx vertx, String containerId, ProtonClientOptions options, Duration probeTimeout) { - this.vertx = vertx; - this.containerId = containerId; - this.options = options; - this.probeTimeout = probeTimeout; - } - - public static AddressProber withCertsInDir(Vertx vertx, String containerId, Duration queryTimeout, String certDir) { - ProtonClientOptions clientOptions = new ProtonClientOptions() - .setSsl(true) - .addEnabledSaslMechanism("EXTERNAL") - .setHostnameVerificationAlgorithm("") - .setPemTrustOptions(new PemTrustOptions() - .addCertPath(new File(certDir, "ca.crt").getAbsolutePath())) - .setPemKeyCertOptions(new PemKeyCertOptions() - .setCertPath(new File(certDir, "tls.crt").getAbsolutePath()) - .setKeyPath(new File(certDir, "tls.key").getAbsolutePath())); - return new AddressProber(vertx, containerId, clientOptions, queryTimeout); - } - - public Set run(String host, int port, Set addresses) throws Exception { - Duration timeLeft = Duration.from(probeTimeout); - try (AddressProbeClient client = new AddressProbeClient(vertx, containerId)) { - CompletableFuture connected = new CompletableFuture<>(); - - long now = System.nanoTime(); - log.debug("Connecting probe runner to {}:{} within {} seconds", host, port, timeLeft.getSeconds()); - client.connect(host, port, options, connected); - connected.get(timeLeft.getSeconds(), TimeUnit.SECONDS); - - timeLeft = timeLeft.minus(Duration.ofNanos(System.nanoTime() - now)); - log.debug("Connected! Send/receive messages for {} within {} seconds", addresses, timeLeft.getSeconds()); - return client.probeAddresses(addresses, timeLeft); - } - } -} diff --git a/standard-controller/src/main/java/io/enmasse/controller/standard/AddressProvisioner.java b/standard-controller/src/main/java/io/enmasse/controller/standard/AddressProvisioner.java deleted file mode 100644 index d899bc10af8..00000000000 --- a/standard-controller/src/main/java/io/enmasse/controller/standard/AddressProvisioner.java +++ /dev/null @@ -1,640 +0,0 @@ -/* - * Copyright 2018, EnMasse authors. - * License: Apache License 2.0 (see the file LICENSE or http://apache.org/licenses/LICENSE-2.0.html). - */ -package io.enmasse.controller.standard; - -import static io.enmasse.controller.standard.ControllerKind.Broker; -import static io.enmasse.controller.standard.ControllerReason.BrokerCreateFailed; -import static io.enmasse.controller.standard.ControllerReason.BrokerCreated; -import static io.enmasse.k8s.api.EventLogger.Type.Normal; -import static io.enmasse.k8s.api.EventLogger.Type.Warning; - -import java.io.IOException; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Collections; -import java.util.Comparator; -import java.util.HashMap; -import java.util.HashSet; -import java.util.List; -import java.util.Map; -import java.util.Optional; -import java.util.Set; -import java.util.regex.Pattern; -import java.util.stream.Collectors; -import java.util.zip.CRC32; - -import io.enmasse.address.model.SubscriptionStatus; -import io.enmasse.address.model.SubscriptionStatusBuilder; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import io.enmasse.address.model.Address; -import io.enmasse.address.model.AddressPlanStatus; -import io.enmasse.address.model.AddressResolver; -import io.enmasse.address.model.AddressSpaceResolver; -import io.enmasse.address.model.AddressStatus; -import io.enmasse.address.model.AddressStatusBuilder; -import io.enmasse.address.model.AddressType; -import io.enmasse.address.model.BrokerState; -import io.enmasse.address.model.BrokerStatus; -import io.enmasse.address.model.Phase; -import io.enmasse.admin.model.AddressPlan; -import io.enmasse.admin.model.AddressSpacePlan; -import io.enmasse.admin.model.v1.StandardInfraConfig; -import io.enmasse.config.AnnotationKeys; -import io.enmasse.k8s.api.EventLogger; - -public class AddressProvisioner { - private static final Logger log = LoggerFactory.getLogger(AddressProvisioner.class); - private final AddressSpaceResolver addressSpaceResolver; - private final AddressResolver addressResolver; - private final AddressSpacePlan addressSpacePlan; - private final BrokerSetGenerator clusterGenerator; - private final Kubernetes kubernetes; - private final EventLogger eventLogger; - private final String infraUuid; - private final String pooledClusterIdPrefix; - private final BrokerIdGenerator brokerIdGenerator; - - public AddressProvisioner(AddressSpaceResolver addressSpaceResolver, AddressResolver addressResolver, AddressSpacePlan addressSpacePlan, BrokerSetGenerator clusterGenerator, Kubernetes kubernetes, EventLogger eventLogger, String infraUuid, BrokerIdGenerator brokerIdGenerator) { - this.addressSpaceResolver = addressSpaceResolver; - this.addressResolver = addressResolver; - this.addressSpacePlan = addressSpacePlan; - this.clusterGenerator = clusterGenerator; - this.kubernetes = kubernetes; - this.eventLogger = eventLogger; - this.infraUuid = infraUuid; - this.pooledPattern = Pattern.compile("^broker-" + infraUuid + "-.*"); - this.pooledClusterIdPrefix = "broker-" + infraUuid + "-"; - this.brokerIdGenerator = brokerIdGenerator; - } - - /** - * Computes the resource usage for a set of addresses - */ - public Map> checkUsage(Set
addressSet) { - Map> usageMap = new HashMap<>(); - - for (Address address : addressSet) { - addToUsage(usageMap, address); - } - return usageMap; - } - - private void addToUsage(Map> usageMap, Address address) { - AddressType addressType = addressResolver.getType(address); - AddressPlanStatus appliedPlan = addressResolver.getAppliedPlan(address) - .orElseGet(() -> AddressPlanStatus.fromAddressPlan(addressResolver.getDesiredPlan(address))); - - for (Map.Entry resourceRequest : appliedPlan.getResources().entrySet()) { - if ("subscription".equals(address.getSpec().getType())) { - if (address.getStatus().getBrokerStatuses().isEmpty()) { - log.warn("Unexpected pooled address without cluster id: " + address.getSpec().getAddress()); - return; - } - for (BrokerStatus status : address.getStatus().getBrokerStatuses()) { - if (BrokerState.Active.equals(status.getState())) { - addToUsage(usageMap, status.getContainerId(), "subscription", resourceRequest.getValue()); - } - } - } else if (resourceRequest.getKey().equals("router")) { - addToUsage(usageMap, "all", "router", resourceRequest.getValue()); - - } else if (resourceRequest.getKey().equals("broker") && ("queue".equals(addressType.getName()) || resourceRequest.getValue() < 1)) { - if (address.getStatus().getBrokerStatuses().isEmpty()) { - log.warn("Unexpected pooled address without assigned cluster: " + address.getSpec().getAddress()); - return; - } - for (BrokerStatus status : address.getStatus().getBrokerStatuses()) { - if (BrokerState.Active.equals(status.getState())) { - addToUsage(usageMap, status.getClusterId(), "broker", getPartitionedCredits(resourceRequest.getValue(), appliedPlan.getPartitions())); - } - } - - } else if (resourceRequest.getKey().equals("broker")) { - if (address.getStatus().getBrokerStatuses().isEmpty()) { - log.warn("Unexpected sharded address without assigned cluster: " + address.getSpec().getAddress()); - return; - } - for (BrokerStatus status : address.getStatus().getBrokerStatuses()) { - if (BrokerState.Active.equals(status.getState())) { - addToUsage(usageMap, status.getClusterId(), "broker", resourceRequest.getValue()); - } - } - } - - } - } - - private static void addToUsage(Map> usageMap, String id, String resourceName, double credit) { - Map resourceUsage = usageMap.computeIfAbsent(resourceName, k -> new HashMap<>()); - UsageInfo info = resourceUsage.computeIfAbsent(id, i -> new UsageInfo()); - info.addUsed(credit); - } - - public Map> checkQuota(Map> usageMap, Set
pending, Set
all) { - Map> newUsageMap = usageMap; - Map limits = addressSpacePlan.getResourceLimits(); - - Set
pendingSubscriptionsWithConfiguredTopics = filterSubscriptionsWithConfiguredTopics(pending, all); - Set
pendingSubscriptionsWithPendingTopics = filterSubscriptionsWithPendingTopics(pending, all); - Set
pendingNonSubscriptions = filterByNotType(pending, "subscription"); - - newUsageMap = addQuotaForAddress(pendingSubscriptionsWithConfiguredTopics , all, newUsageMap, limits); - - while(!pendingSubscriptionsWithPendingTopics.isEmpty()) { - Address address = pendingSubscriptionsWithPendingTopics.iterator().next(); - Address topic = findAddress(address.getSpec().getTopic(), all); - newUsageMap = addQuotaForAddress(new HashSet<>(Arrays.asList(topic)), all, newUsageMap, limits); - pendingNonSubscriptions.remove(topic); - - Set
subscriptionsWithNewlyConfiguredTopics = filterSubscriptionsWithConfiguredTopics(pendingSubscriptionsWithPendingTopics, all); - newUsageMap = addQuotaForAddress(subscriptionsWithNewlyConfiguredTopics, all, newUsageMap, limits); - pendingSubscriptionsWithPendingTopics.removeAll(subscriptionsWithNewlyConfiguredTopics); - } - newUsageMap = addQuotaForAddress(pendingNonSubscriptions, all, newUsageMap, limits); - - return newUsageMap; - } - - private Map> addQuotaForAddress(Set
pending, Set
all, - Map> newUsageMap, Map limits) { - for (Address address : pending) { - if (!Phase.Configuring.equals(address.getStatus().getPhase())) { - AddressStatus previousStatus = new AddressStatusBuilder(address.getStatus()).build(); - - Map> neededMap = checkQuotaForAddress(limits, newUsageMap, address, all); - if (neededMap != null) { - newUsageMap = neededMap; - AddressPlan addressPlan = addressResolver.getDesiredPlan(address); - address.getStatus().setPhase(Phase.Configuring); - address.getStatus().setPlanStatus(AddressPlanStatus.fromAddressPlan(addressPlan)); - int maxConsumers = 1; - if (address.getSpec().getSubscription() != null && address.getSpec().getSubscription().getMaxConsumers() != null) { - maxConsumers = address.getSpec().getSubscription().getMaxConsumers(); - } - address.getStatus().setSubscription(new SubscriptionStatusBuilder() - .withMaxConsumers(maxConsumers) - .build()); - address.putAnnotation(AnnotationKeys.APPLIED_PLAN, address.getSpec().getPlan()); - } else { - address.getStatus().setBrokerStatuses(previousStatus.getBrokerStatuses()); - } - } - } - return newUsageMap; - } - - private Set
filterSubscriptionsWithConfiguredTopics(Set
addressSet, Set
all) { - return addressSet.stream() - .filter(address -> "subscription".equals(address.getSpec().getType()) && Arrays.asList(Phase.Configuring, Phase.Active).contains(findAddress(address.getSpec().getTopic(), all).getStatus().getPhase())) - .collect(Collectors.toSet()); - } - private Set
filterSubscriptionsWithPendingTopics(Set
addressSet, Set
all) { - return addressSet.stream() - .filter(address -> "subscription".equals(address.getSpec().getType()) && Arrays.asList(Phase.Pending).contains(findAddress(address.getSpec().getTopic(), all).getStatus().getPhase())) - .collect(Collectors.toSet()); - } - private Set
filterByNotType(Set
addressSet, String type) { - return addressSet.stream() - .filter(address -> !type.equals(address.getSpec().getType())) - .collect(Collectors.toSet()); - } - - private static Map> copyUsageMap(Map> usageMap) { - Map> newUsageMap = new HashMap<>(); - for (Map.Entry> entry : usageMap.entrySet()) { - newUsageMap.put(entry.getKey(), new HashMap<>()); - for (Map.Entry innerEntry : entry.getValue().entrySet()) { - newUsageMap.get(entry.getKey()).put(innerEntry.getKey(), new UsageInfo(innerEntry.getValue())); - } - } - return newUsageMap; - } - - private Address findAddress(String name, Set
addressSet) { - for (Address address : addressSet) { - if (name.equals(address.getSpec().getAddress())) { - return address; - } - } - return null; - } - - private boolean scheduleSubscription(Address subscription, Address topic, Map brokerUsage, Map subscriptionUsage, double requestedValue) { - if (topic.getStatus().getBrokerStatuses().isEmpty()) { - log.warn("Unexpected empty list of brokers for topic {}", topic); - return false; - } - BrokerStatus brokerStatus = topic.getStatus().getBrokerStatuses().get(0); - - AddressPlan topicPlan = addressResolver.getPlan(topic); - boolean isPooled = false; - - for (Map.Entry resourceRequest : topicPlan.getResources().entrySet()) { - if ("broker".equals(resourceRequest.getKey()) && resourceRequest.getValue() < 1) { - isPooled = true; - break; - } - } - - if (isPooled) { - UsageInfo usageInfo = subscriptionUsage.computeIfAbsent(brokerStatus.getContainerId(), k -> new UsageInfo()); - if (usageInfo.getUsed() + requestedValue <= 1) { - usageInfo.addUsed(requestedValue); - - subscription.getStatus().setBrokerStatuses(Collections.singletonList(brokerStatus)); - } else { - log.info("no quota available on broker {} for {} on topic {}", brokerStatus.getClusterId(), subscription.getSpec().getAddress(), topic.getSpec().getAddress()); - } - } else { - List shardedBrokers = new ArrayList<>(); - for (String host : brokerUsage.keySet()) { - if (host.equals(brokerStatus.getClusterId())) { - UsageInfo brokerUsageInfo = brokerUsage.get(host); - int replicas = brokerUsageInfo.getNeeded(); - for (String container : subscriptionUsage.keySet()) { - shardedBrokers.add(new BrokerInfo(container, subscriptionUsage.get(container).getUsed())); - } - shardedBrokers.sort(Comparator.comparingDouble(BrokerInfo::getCredit)); - if (shardedBrokers.size() < replicas) { - shardedBrokers.add(0, new BrokerInfo(brokerStatus.getClusterId()+"-"+shardedBrokers.size(), 0)); - } - } - } - for (BrokerInfo brokerInfo : shardedBrokers) { - UsageInfo usageInfo = subscriptionUsage.computeIfAbsent(brokerInfo.getBrokerId(), k -> new UsageInfo()); - if (brokerInfo.getCredit() + requestedValue <= 1) { - - BrokerStatus newBrokerStatus = new BrokerStatus(brokerStatus.getClusterId(), brokerInfo.getBrokerId()); - brokerStatus.setState(BrokerState.Active); - subscription.getStatus().setBrokerStatuses(Collections.singletonList(newBrokerStatus)); - usageInfo.addUsed(requestedValue); - break; - } - } - } - return !subscription.getStatus().getBrokerStatuses().isEmpty(); - } - - private Map> checkQuotaForAddress(Map limits, Map> usage, Address address, Set
addressSet) { - AddressPlan desiredPlan = addressResolver.getDesiredPlan(address); - AddressPlanStatus appliedPlan = Optional.ofNullable(address.getStatus()).map(AddressStatus::getPlanStatus).orElse(null); - - Map> needed = copyUsageMap(usage); - - for (Map.Entry resourceRequest : desiredPlan.getResources().entrySet()) { - - String resourceName = resourceRequest.getKey(); - Map resourceUsage = needed.computeIfAbsent(resourceName, k -> new HashMap<>()); - if ("router".equals(resourceName)) { - UsageInfo info = resourceUsage.computeIfAbsent("all", k -> new UsageInfo()); - - // Remove existing usage - if ( - appliedPlan != null - && appliedPlan.getResources() != null - && appliedPlan.getResources().get(resourceName) != null - ) { - info.subUsed(appliedPlan.getResources().get(resourceName)); - } - - // Add new usage - info.addUsed(resourceRequest.getValue()); - } else if ("broker".equals(resourceName)) { - if ("subscription".equals(address.getSpec().getType())) { - Map subscriptionUsage = needed.computeIfAbsent("subscription", k -> new HashMap<>()); - if (address.getSpec().getTopic() != null ) { - Address topic = findAddress(address.getSpec().getTopic(), addressSet); - if (!scheduleSubscription(address, topic, resourceUsage, subscriptionUsage, resourceRequest.getValue())) { - log.warn("Unable to find broker for scheduling subscription: {}", address); - return null; - } - } else { - log.warn("No topic specified for subscription {}", address.getSpec().getAddress()); - } - } else if ("queue".equals(address.getSpec().getType()) || resourceRequest.getValue() < 1) { - // Remove existing usage - if ( - appliedPlan != null - && appliedPlan.getResources() != null - && appliedPlan.getResources().get(resourceName) != null - && address.getStatus() != null) { - for (BrokerStatus brokerStatus : address.getStatus().getBrokerStatuses()) { - UsageInfo info = resourceUsage.get(brokerStatus.getClusterId()); - if (info != null) { - info.subUsed(appliedPlan.getResources().get(resourceName)); - } - } - } - - // Schedule address and add usage - boolean scheduled = false; - int partitions = getQueuePartitions(resourceRequest.getValue(), desiredPlan.getPartitions()); - for (int retry = 0; !scheduled && retry < partitions + 1; retry++) { - scheduled = scheduleAddress(resourceUsage, address, resourceRequest.getValue(), partitions); - if (!scheduled) { - allocateBroker(resourceUsage, pooledClusterIdPrefix); - } - } - if (!scheduled) { - log.warn("Unable to find broker for scheduling {}", address); - return null; - } - } else { - String clusterId = getShardedClusterId(address); - UsageInfo info = new UsageInfo(); - info.addUsed(resourceRequest.getValue()); - resourceUsage.put(clusterId, info); - - List brokers = new ArrayList<>(); - for (String host : resourceUsage.keySet()) { - if (host.startsWith(address.getSpec().getAddress())) { - brokers.add(new BrokerInfo(host, resourceUsage.get(host).getUsed())); - } - } - - BrokerStatus brokerStatus = new BrokerStatus(clusterId, address.getSpec().getAddress()); - brokerStatus.setState(BrokerState.Active); - - updateBrokerStatus(address, Collections.singletonList(brokerStatus)); - - brokers.sort(Comparator.comparing(BrokerInfo::getBrokerId)); - - for (BrokerInfo brokerInfo : brokers) { - if (brokerInfo.getCredit() + resourceRequest.getValue() < 1) { - UsageInfo used = resourceUsage.get(brokerInfo.getBrokerId()); - used.addUsed(resourceRequest.getValue()); - break; - } else { - log.warn("not enough credit on {} for {} ",brokerInfo.getBrokerId(), address.getSpec().getAddress() ); - } - } - } - } else { - log.warn("should not be called with: {}", resourceName); - } - - double resourceNeeded = sumNeeded(resourceUsage); - if (resourceNeeded > limits.get(resourceName)) { - log.info("address {} for {} needed {} > limit {}", address.getSpec().getAddress(), resourceName, resourceNeeded, limits.get(resourceRequest.getKey())); - address.getStatus().appendMessage("Quota exceeded"); - return null; - } - } - - log.debug("address: {}, usage {}, needed: {}, aggregate: {}", address.getSpec().getAddress(), usage, needed, limits); - - double totalNeeded = sumTotalNeeded(needed); - if (totalNeeded > limits.get("aggregate")) { - log.info("address {} usage {}, total needed {} > limit {}", address.getSpec().getAddress(), usage, totalNeeded, limits.get("aggregate")); - address.getStatus().appendMessage("Quota exceeded"); - return null; - } - return needed; - } - - static double getPartitionedCredits(double credits, int partitions) { - return credits / getQueuePartitions(credits, partitions); - } - - static int getQueuePartitions(double credits, int partitions) { - return (int)Math.max(partitions, Math.ceil(credits)); - } - - static boolean hasPlansChanged(AddressResolver addressResolver, Address address) { - AddressPlan addressPlan = addressResolver.getDesiredPlan(address); - return hasPlansChanged(addressPlan, address); - } - - static boolean hasPlansChanged(AddressPlan addressPlan, Address address) { - return !AddressPlanStatus.fromAddressPlan(addressPlan).equals(address.getStatus().getPlanStatus()) || - !address.getSpec().getPlan().equals(address.getAnnotation(AnnotationKeys.APPLIED_PLAN)); - } - - static int sumTotalNeeded(Map> usageMap) { - int totalNeeded = 0; - for (String resource : usageMap.keySet()) { - Map usage = usageMap.get(resource); - for (UsageInfo value : usage.values()) { - if (!"subscription".equals(resource)) { - totalNeeded += value.getNeeded(); - } - } - } - return totalNeeded; - } - - static int sumNeeded(Map resourceUsage) { - int needed = 0; - for (UsageInfo value : resourceUsage.values()) { - needed += value.getNeeded(); - } - return needed; - } - - static int sumNeededMatching(Map resourceUsage, Pattern pattern) { - int needed = 0; - for (Map.Entry entry : resourceUsage.entrySet()) { - if (pattern.matcher(entry.getKey()).matches()) { - needed += entry.getValue().getNeeded(); - } - } - return needed; - } - - private static void updateBrokerStatus(Address address, List brokerStatuses) { - List toAdd = new ArrayList<>(brokerStatuses); - for (BrokerStatus brokerStatus : address.getStatus().getBrokerStatuses()) { - boolean found = false; - for (BrokerStatus newBrokerStatus : brokerStatuses) { - if (newBrokerStatus.getContainerId().equals(brokerStatus.getContainerId()) && newBrokerStatus.getClusterId().equals(brokerStatus.getClusterId())) { - brokerStatus.setState(newBrokerStatus.getState()); - toAdd.remove(newBrokerStatus); - found = true; - break; - } - } - if (!found) { - brokerStatus.setState(BrokerState.Migrating); - } - } - - address.getStatus().addAllBrokerStatuses(toAdd); - } - - public String getShardedClusterId(Address address) { - CRC32 crc32 = new CRC32(); - crc32.update(address.getMetadata().getNamespace().getBytes()); - crc32.update(Address.extractAddressSpace(address).getBytes()); - crc32.update(address.getSpec().getAddress().getBytes()); - return "broker-sharded-" + Long.toHexString(crc32.getValue()) + "-" + infraUuid; - } - - public void provisionResources(RouterCluster router, List existingClusters, Map> neededMap, Set
addressSet, StandardInfraConfig desiredInfraConfig) throws IOException { - - for (Map.Entry> entry : neededMap.entrySet()) { - String resourceName = entry.getKey(); - if ("router".equals(resourceName)) { - int totalNeeded = sumNeeded(entry.getValue()); - int infraConfigMin = Optional.ofNullable(router.getInfraConfig()) - .flatMap(infraConfig -> Optional.ofNullable(router.getInfraConfig().getSpec().getRouter())) - .flatMap(routerSpec -> Optional.ofNullable(routerSpec.getMinReplicas())) - .orElse(1); - router.setNewReplicas(Math.max(totalNeeded, infraConfigMin)); - } else if ("broker".equals(resourceName)) { - // Provision pooled broker - int needPooled = sumNeededMatching(entry.getValue(), pooledPattern); - if (needPooled > 0) { - for (String id : entry.getValue().keySet()) { - if (pooledPattern.matcher(id).matches()) { - provisionBroker(existingClusters, id, 1, null, null); - } - } - } - - // Collect all sharded brokers that we know have cluster id set - Map addressByClusterId = new HashMap<>(); - for (Address address : addressSet) { - if (!"subscription".equals(address.getSpec().getType())) { - if (!address.getStatus().getBrokerStatuses().isEmpty()) { - addressByClusterId.putIfAbsent(address.getStatus().getBrokerStatuses().get(0).getClusterId(), address); - } - } - } - - Map shardedBrokers = new HashMap<>(); - for (Map.Entry usageEntry : entry.getValue().entrySet()) { - if (addressByClusterId.containsKey(usageEntry.getKey())) { - shardedBrokers.put(usageEntry.getKey(), usageEntry.getValue().getNeeded()); - } - } - - for (Map.Entry brokerIdEntry : shardedBrokers.entrySet()) { - Address address = addressByClusterId.get(brokerIdEntry.getKey()); - if ("subscription".equals(address.getSpec().getType())) { - break; - } - AddressType addressType = addressResolver.getType(address); - AddressPlan addressPlan = addressResolver.getPlan(addressType, address); - provisionBroker(existingClusters, brokerIdEntry.getKey(), brokerIdEntry.getValue(), address, addressPlan); - } - } - } - - if (router.hasChanged() && desiredInfraConfig != null && desiredInfraConfig.equals(router.getInfraConfig())) { - log.info("Scaling router to {} replicas", router.getNewReplicas()); - kubernetes.scaleStatefulSet(router.getName(), router.getNewReplicas()); - } - - for (BrokerCluster cluster : existingClusters) { - if (cluster.hasChanged() && desiredInfraConfig != null && desiredInfraConfig.equals(cluster.getInfraConfig())) { - log.info("Scaling broker cluster {} to {} replicas", cluster.getClusterId(), cluster.getNewReplicas()); - kubernetes.scaleStatefulSet(cluster.getClusterId(), cluster.getNewReplicas()); - } - } - - - } - - private final Pattern pooledPattern; - private boolean scheduleAddress(Map usageMap, Address address, double credit, int partitions) { - - List brokers = new ArrayList<>(); - for (String host : usageMap.keySet()) { - if (pooledPattern.matcher(host).matches()) { - brokers.add(new BrokerInfo(host, usageMap.get(host).getUsed())); - } - } - - brokers.sort(Comparator.comparing(BrokerInfo::getBrokerId)); - - double partitionCredit = credit / partitions; - List selectedBrokers = new ArrayList<>(); - List brokerStatuses = new ArrayList<>(); - for (BrokerInfo brokerInfo : brokers) { - if (partitions <= 0) { - break; - } - if (brokerInfo.getCredit() + partitionCredit <= 1) { - BrokerStatus brokerStatus = new BrokerStatus(brokerInfo.getBrokerId(), brokerInfo.getBrokerId() + "-0"); - brokerStatus.setState(BrokerState.Active); - brokerStatuses.add(brokerStatus); - - selectedBrokers.add(brokerInfo); - partitions--; - } - } - - // Unable to find enough brokers for all partition - if (partitions > 0) { - return false; - } - - for (BrokerInfo brokerInfo : selectedBrokers) { - UsageInfo used = usageMap.get(brokerInfo.getBrokerId()); - used.addUsed(partitionCredit); - } - - updateBrokerStatus(address, brokerStatuses); - return true; - } - - private String allocateBroker(Map resourceNeeded, String clusterIdPrefix) { - String randomId = brokerIdGenerator.generateBrokerId(); - String clusterId = clusterIdPrefix + randomId; - resourceNeeded.put(clusterId, new UsageInfo()); - return clusterId; - } - - private void provisionBroker(List clusterList, String clusterId, int numReplicas, Address address, AddressPlan addressPlan) { - try { - for (BrokerCluster cluster : clusterList) { - if (cluster.getClusterId().equals(clusterId)) { - cluster.setNewReplicas(numReplicas); - return; - } - } - - // Needs to be created - StandardInfraConfig desiredConfig = (StandardInfraConfig) addressSpaceResolver.getInfraConfig("standard", addressSpacePlan.getMetadata().getName()); - BrokerCluster cluster = clusterGenerator.generateCluster(clusterId, numReplicas, address, addressPlan, desiredConfig); - if (!cluster.getResources().getItems().isEmpty()) { - kubernetes.create(cluster.getResources()); - eventLogger.log(BrokerCreated, "Created broker " + cluster.getClusterId() + " with " + numReplicas + " replicas", Normal, Broker, cluster.getClusterId()); - } - clusterList.add(cluster); - } catch (Exception e) { - log.warn("Error creating broker", e); - eventLogger.log(BrokerCreateFailed, "Error creating broker: " + e.getMessage(), Warning, Broker, clusterId); - address.getStatus().setPhase(Phase.Failed); - address.getStatus().appendMessage("Error creating broker: " + e.getMessage()); - } - } - - private static class BrokerInfo { - private final String brokerId; - private final double credit; - - public String getBrokerId() { - return brokerId; - } - - public double getCredit() { - return credit; - } - - private BrokerInfo(String brokerId, double credit) { - this.brokerId = brokerId; - this.credit = credit; - } - - @Override - public String toString() { - return "{brokerId=" + brokerId + ",credit=" + credit + "}"; - } - } - -} diff --git a/standard-controller/src/main/java/io/enmasse/controller/standard/BrokerClientFactory.java b/standard-controller/src/main/java/io/enmasse/controller/standard/BrokerClientFactory.java deleted file mode 100644 index 6dd70b99129..00000000000 --- a/standard-controller/src/main/java/io/enmasse/controller/standard/BrokerClientFactory.java +++ /dev/null @@ -1,11 +0,0 @@ -/* - * Copyright 2016-2018, EnMasse authors. - * License: Apache License 2.0 (see the file LICENSE or http://apache.org/licenses/LICENSE-2.0.html). - */ -package io.enmasse.controller.standard; - -import io.enmasse.amqp.SyncRequestClient; - -public interface BrokerClientFactory { - SyncRequestClient connectBrokerManagementClient(String host, int port) throws Exception; -} diff --git a/standard-controller/src/main/java/io/enmasse/controller/standard/BrokerCluster.java b/standard-controller/src/main/java/io/enmasse/controller/standard/BrokerCluster.java deleted file mode 100644 index 36f16a07d42..00000000000 --- a/standard-controller/src/main/java/io/enmasse/controller/standard/BrokerCluster.java +++ /dev/null @@ -1,185 +0,0 @@ -/* - * Copyright 2017-2018, EnMasse authors. - * License: Apache License 2.0 (see the file LICENSE or http://apache.org/licenses/LICENSE-2.0.html). - */ - -package io.enmasse.controller.standard; - -import com.fasterxml.jackson.databind.ObjectMapper; -import io.enmasse.admin.model.v1.StandardInfraConfig; -import io.enmasse.config.AnnotationKeys; -import io.fabric8.kubernetes.api.model.*; -import io.fabric8.kubernetes.api.model.apps.Deployment; -import io.fabric8.kubernetes.api.model.apps.DeploymentStatus; -import io.fabric8.kubernetes.api.model.apps.StatefulSet; -import io.fabric8.kubernetes.api.model.apps.StatefulSetStatus; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import java.io.IOException; -import java.util.List; -import java.util.Map; -import java.util.Optional; - -/** - * Represents a cluster of resources for a given destination. - */ -public class BrokerCluster { - private static final Logger log = LoggerFactory.getLogger(BrokerCluster.class); - private static final ObjectMapper mapper = new ObjectMapper(); - private final String clusterId; - private final int replicas; - private KubernetesList resources; - private int newReplicas; - private final int readyReplicas; - - public BrokerCluster(String clusterId, KubernetesList resources) { - this.clusterId = clusterId; - this.resources = resources; - this.replicas = findReplicas(resources.getItems()); - this.readyReplicas = findReadyReplicas(resources.getItems()); - this.newReplicas = replicas; - } - - private int findReadyReplicas(List items) { - for (HasMetadata item : items) { - if (item instanceof StatefulSet) { - return Optional.ofNullable(((StatefulSet)item).getStatus()).map(StatefulSetStatus::getReadyReplicas).orElse(0); - } else if (item instanceof Deployment) { - return Optional.ofNullable(((Deployment)item).getStatus()).map(DeploymentStatus::getReadyReplicas).orElse(0); - } - } - return 0; - } - - private int findReplicas(List items) { - for (HasMetadata item : items) { - if (item instanceof StatefulSet) { - return ((StatefulSet)item).getSpec().getReplicas(); - } else if (item instanceof Deployment) { - return ((Deployment)item).getSpec().getReplicas(); - } - } - return 0; - } - - private StandardInfraConfig findStandardInfraConfig(List items) throws IOException { - StandardInfraConfig config = null; - for (HasMetadata item : items) { - if (item instanceof StatefulSet) { - if (item.getMetadata().getAnnotations() != null && item.getMetadata().getAnnotations().get(AnnotationKeys.APPLIED_INFRA_CONFIG) != null) { - config = mapper.readValue(item.getMetadata().getAnnotations().get(AnnotationKeys.APPLIED_INFRA_CONFIG), StandardInfraConfig.class); - break; - } - } - } - return config; - } - - - public void setNewReplicas(int replicas) { - this.newReplicas = replicas; - } - - public int getReplicas() { - return replicas; - } - - public int getNewReplicas() { - return newReplicas; - } - - public boolean hasChanged() { - return replicas != newReplicas; - } - - public KubernetesList getResources() { - return resources; - } - - public String getClusterId() { - return clusterId; - } - - public StandardInfraConfig getInfraConfig() throws IOException { - return findStandardInfraConfig(resources.getItems()); - } - - public void updateResources(BrokerCluster upgradedCluster, StandardInfraConfig infraConfig) throws Exception { - if (upgradedCluster != null) { - - // Only 1 statefulset and 1 claim for a broker - - PersistentVolumeClaim existingClaim = null; - StatefulSet existingBroker = null; - for (HasMetadata item : resources.getItems()) { - if (item instanceof PersistentVolumeClaim) { - existingClaim = (PersistentVolumeClaim) item; - } - - if (item instanceof StatefulSet) { - existingBroker = (StatefulSet) item; - } - } - - KubernetesList newResources = upgradedCluster.getResources(); - StatefulSet newBroker = null; - for (HasMetadata item : newResources.getItems()) { - if (item instanceof StatefulSet) { - newBroker = (StatefulSet) item; - Kubernetes.addObjectAnnotation(item, AnnotationKeys.APPLIED_INFRA_CONFIG, mapper.writeValueAsString(infraConfig)); - break; - } - } - - if (newBroker == null) { - log.warn("Unable to find new StatefulSet for broker {}, will not apply upgrade", clusterId); - return; - } - - if (existingClaim != null && existingBroker != null) { - // Allow only storage size to change - Map newRequests = newBroker.getSpec().getVolumeClaimTemplates().get(0).getSpec().getResources().getRequests(); - - // NOTE: Workaround for https://github.com/kubernetes/kubernetes/issues/68737: - // To change a persistent volume, the existing generated PVC must be modified, and the template in - // the statefulset must remain the same. This code will reset the template resource requests, and - // add the PVC to the list of resources that should be applied. - newBroker.getSpec().setVolumeClaimTemplates(existingBroker.getSpec().getVolumeClaimTemplates()); - existingClaim.getSpec().getResources().setRequests(newRequests); - newResources = new KubernetesListBuilder() - .withItems(newResources.getItems()) - .addToItems(existingClaim) - .build(); - } - this.resources = newResources; - } - } - - public int getReadyReplicas() { - return readyReplicas; - } - - @Override - public boolean equals(Object o) { - if (this == o) return true; - if (o == null || getClass() != o.getClass()) return false; - - BrokerCluster that = (BrokerCluster) o; - - if (!clusterId.equals(that.clusterId)) return false; - return resources.equals(that.resources); - } - - @Override - public int hashCode() { - int result = clusterId.hashCode(); - result = 31 * result + resources.hashCode(); - return result; - } - - @Override - public String toString() { - return clusterId; - } -} diff --git a/standard-controller/src/main/java/io/enmasse/controller/standard/BrokerIdGenerator.java b/standard-controller/src/main/java/io/enmasse/controller/standard/BrokerIdGenerator.java deleted file mode 100644 index 2122e27cfd7..00000000000 --- a/standard-controller/src/main/java/io/enmasse/controller/standard/BrokerIdGenerator.java +++ /dev/null @@ -1,9 +0,0 @@ -/* - * Copyright 2018, EnMasse authors. - * License: Apache License 2.0 (see the file LICENSE or http://apache.org/licenses/LICENSE-2.0.html). - */ -package io.enmasse.controller.standard; - -public interface BrokerIdGenerator { - String generateBrokerId(); -} diff --git a/standard-controller/src/main/java/io/enmasse/controller/standard/BrokerSetGenerator.java b/standard-controller/src/main/java/io/enmasse/controller/standard/BrokerSetGenerator.java deleted file mode 100644 index 1d8fa2cb509..00000000000 --- a/standard-controller/src/main/java/io/enmasse/controller/standard/BrokerSetGenerator.java +++ /dev/null @@ -1,24 +0,0 @@ -/* - * Copyright 2016-2018, EnMasse authors. - * License: Apache License 2.0 (see the file LICENSE or http://apache.org/licenses/LICENSE-2.0.html). - */ -package io.enmasse.controller.standard; - -import io.enmasse.address.model.Address; -import io.enmasse.admin.model.AddressPlan; -import io.enmasse.admin.model.v1.StandardInfraConfig; - -/** - * Generates clusters for a set of addresses. - */ -public interface BrokerSetGenerator { - - /** - * Generate broker set - * @param clusterId The id of the cluster - * @param numReplicas Number of replicas for the initial set - * @param address Address to pass as template parameter (null is allowed) - * @param addressPlan - */ - BrokerCluster generateCluster(String clusterId, int numReplicas, Address address, AddressPlan addressPlan, StandardInfraConfig standardInfraConfig) throws Exception; -} diff --git a/standard-controller/src/main/java/io/enmasse/controller/standard/BrokerStatusCollector.java b/standard-controller/src/main/java/io/enmasse/controller/standard/BrokerStatusCollector.java deleted file mode 100644 index f0d993568f7..00000000000 --- a/standard-controller/src/main/java/io/enmasse/controller/standard/BrokerStatusCollector.java +++ /dev/null @@ -1,106 +0,0 @@ -/* - * Copyright 2016-2019, EnMasse authors. - * License: Apache License 2.0 (see the file LICENSE or http://apache.org/licenses/LICENSE-2.0.html). - */ -package io.enmasse.controller.standard; - -import io.enmasse.amqp.Artemis; -import io.enmasse.amqp.SyncRequestClient; -import io.fabric8.kubernetes.api.model.Pod; -import io.fabric8.kubernetes.client.internal.readiness.Readiness; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import java.util.HashSet; -import java.util.List; -import java.util.Set; - -class BrokerStatusCollector { - private static final Logger log = LoggerFactory.getLogger(BrokerStatusCollector.class); - - private final Kubernetes kubernetes; - private final BrokerClientFactory brokerClientFactory; - private final StandardControllerOptions options; - - BrokerStatusCollector(Kubernetes kubernetes, BrokerClientFactory brokerClientFactory, StandardControllerOptions options) { - this.kubernetes = kubernetes; - this.brokerClientFactory = brokerClientFactory; - this.options = options; - } - - Set getAddressNames(String clusterId) throws Exception { - Set addressNames = new HashSet<>(); - List pods = kubernetes.listBrokers(clusterId); - for (Pod broker : pods) { - if (Readiness.isPodReady(broker)) { - try ( - SyncRequestClient brokerClient = brokerClientFactory.connectBrokerManagementClient(broker.getStatus().getPodIP(), 5673); - Artemis artemis = new Artemis(brokerClient, options.getManagementQueryTimeout().toMillis()); - ) { - addressNames.addAll(artemis.getAddressNames()); - } - } else { - throw new IllegalStateException(String.format("Broker pod '%s' in cluster '%s' is not ready (%s), cannot get address names at this time.", - broker.getMetadata().getName(), - clusterId, - broker.getStatus())); - } - } - log.debug("Cluster '{}' ({} replica(s)) has the following addresses: {}", clusterId, pods.size(), addressNames); - return addressNames; - } - - Set getQueueNames(String clusterId) throws Exception { - Set queueNames = new HashSet<>(); - List pods = kubernetes.listBrokers(clusterId); - for (Pod broker : pods) { - if (Readiness.isPodReady(broker)) { - try ( - SyncRequestClient brokerClient = brokerClientFactory.connectBrokerManagementClient(broker.getStatus().getPodIP(), 5673); - Artemis artemis = new Artemis(brokerClient, options.getManagementQueryTimeout().toMillis()); - ) { - queueNames.addAll(artemis.getQueueNames()); - } - } else { - throw new IllegalStateException(String.format("Broker pod '%s' in cluster '%s' is not ready (%s), cannot get queue names at this time.", - broker.getMetadata().getName(), - clusterId, - broker.getStatus())); - } - } - log.debug("Cluster '{}' ({} replica(s)) has the following queues: {}", clusterId, pods.size(), queueNames); - return queueNames; - } - - long getQueueMessageCount(String queue, String clusterId) throws Exception { - long totalMessageCount = 0; - List pods = kubernetes.listBrokers(clusterId); - for (Pod broker : pods) { - if (Readiness.isPodReady(broker)) { - try ( - SyncRequestClient brokerClient = brokerClientFactory.connectBrokerManagementClient(broker.getStatus().getPodIP(), 5673); - Artemis artemis = new Artemis(brokerClient, options.getManagementQueryTimeout().toMillis()); - ) { - long queueMessageCount = artemis.getQueueMessageCount(queue); - if (queueMessageCount < 0) { - // ARTEMIS-1982? - throw new IllegalStateException(String.format("Depth for queue '%s' on broker pod '%s' in cluster '%s' reported negative (%d)", - queue, - broker.getMetadata().getName(), - clusterId, - queueMessageCount)); - } - totalMessageCount += queueMessageCount; - } - } else { - throw new IllegalStateException(String.format("Broker pod '%s' in cluster '%s' is not ready (%s), cannot get depth for queue '%s' at this time.", - broker.getMetadata().getName(), - clusterId, - broker.getStatus(), - queue)); - } - } - log.info("Queue '{}' on cluster '{}' ({} replica(s)) has depth: {}", queue, clusterId, pods.size(), totalMessageCount); - return totalMessageCount; - } -} diff --git a/standard-controller/src/main/java/io/enmasse/controller/standard/ControllerKind.java b/standard-controller/src/main/java/io/enmasse/controller/standard/ControllerKind.java deleted file mode 100644 index 2eede47bb30..00000000000 --- a/standard-controller/src/main/java/io/enmasse/controller/standard/ControllerKind.java +++ /dev/null @@ -1,13 +0,0 @@ -/* - * Copyright 2017-2018, EnMasse authors. - * License: Apache License 2.0 (see the file LICENSE or http://apache.org/licenses/LICENSE-2.0.html). - */ -package io.enmasse.controller.standard; - -import io.enmasse.k8s.api.EventLogger; - -public enum ControllerKind implements EventLogger.ObjectKind { - Address, - AddressSpace, - Broker -} diff --git a/standard-controller/src/main/java/io/enmasse/controller/standard/ControllerReason.java b/standard-controller/src/main/java/io/enmasse/controller/standard/ControllerReason.java deleted file mode 100644 index 20ee570393f..00000000000 --- a/standard-controller/src/main/java/io/enmasse/controller/standard/ControllerReason.java +++ /dev/null @@ -1,17 +0,0 @@ -/* - * Copyright 2017-2018, EnMasse authors. - * License: Apache License 2.0 (see the file LICENSE or http://apache.org/licenses/LICENSE-2.0.html). - */ -package io.enmasse.controller.standard; - -import io.enmasse.k8s.api.EventLogger; - -public enum ControllerReason implements EventLogger.Reason { - BrokerCreated, - BrokerCreateFailed, - RouterCheckFailed, - BrokerDeleted, - BrokerUpgraded, - BrokerDeleteFailed, - AddressSyncFailed; -} diff --git a/standard-controller/src/main/java/io/enmasse/controller/standard/HTTPServer.java b/standard-controller/src/main/java/io/enmasse/controller/standard/HTTPServer.java deleted file mode 100644 index e8ec308f21b..00000000000 --- a/standard-controller/src/main/java/io/enmasse/controller/standard/HTTPServer.java +++ /dev/null @@ -1,68 +0,0 @@ -/* - * Copyright 2016-2018, EnMasse authors. - * License: Apache License 2.0 (see the file LICENSE or http://apache.org/licenses/LICENSE-2.0.html). - */ -package io.enmasse.controller.standard; - -import java.io.IOException; -import java.io.OutputStream; -import java.net.InetSocketAddress; -import java.nio.charset.StandardCharsets; - -import com.sun.net.httpserver.HttpExchange; -import com.sun.net.httpserver.HttpHandler; -import com.sun.net.httpserver.HttpServer; - -import io.enmasse.metrics.api.Metrics; -import io.enmasse.metrics.api.MetricsFormatter; -import io.enmasse.metrics.api.PrometheusMetricsFormatter; - -public class HTTPServer { - private final HttpServer server; - - public HTTPServer(int port, Metrics metrics) throws IOException { - this.server = HttpServer.create(new InetSocketAddress(port), 0); - this.server.createContext("/healthz", new HealthHandler()); - this.server.createContext("/metrics", new MetricsHandler(metrics)); - this.server.setExecutor(null); // creates a default executor - } - - public void start() { - server.start(); - } - - public void stop() { - server.stop(0); - } - - private static class HealthHandler implements HttpHandler { - - @Override - public void handle(HttpExchange t) throws IOException { - byte [] response = "OK".getBytes(StandardCharsets.UTF_8); - t.sendResponseHeaders(200, response.length); - OutputStream os = t.getResponseBody(); - os.write(response); - os.close(); - } - } - - private static class MetricsHandler implements HttpHandler { - private final Metrics metrics; - private static final MetricsFormatter metricsFormatter = new PrometheusMetricsFormatter(); - - private MetricsHandler(Metrics metrics) { - this.metrics = metrics; - } - - @Override - public void handle(HttpExchange t) throws IOException { - byte [] response = metricsFormatter.format(metrics.getMetrics(), System.currentTimeMillis()).getBytes(StandardCharsets.UTF_8); - t.getResponseHeaders().add("Content-Type", "text/html"); - t.sendResponseHeaders(200, response.length); - OutputStream os = t.getResponseBody(); - os.write(response); - os.close(); - } - } -} diff --git a/standard-controller/src/main/java/io/enmasse/controller/standard/Kubernetes.java b/standard-controller/src/main/java/io/enmasse/controller/standard/Kubernetes.java deleted file mode 100644 index 1dc94e26b00..00000000000 --- a/standard-controller/src/main/java/io/enmasse/controller/standard/Kubernetes.java +++ /dev/null @@ -1,65 +0,0 @@ -/* - * Copyright 2016-2018, EnMasse authors. - * License: Apache License 2.0 (see the file LICENSE or http://apache.org/licenses/LICENSE-2.0.html). - */ -package io.enmasse.controller.standard; - -import io.fabric8.kubernetes.api.model.HasMetadata; -import io.fabric8.kubernetes.api.model.KubernetesList; -import io.fabric8.kubernetes.api.model.Pod; -import java.io.IOException; -import java.util.LinkedHashMap; -import java.util.List; -import java.util.Map; -import java.util.function.Consumer; - -public interface Kubernetes { - List listClusters(); - boolean isDestinationClusterReady(String clusterId); - List listRouters(); - - - static void addObjectLabel(KubernetesList items, String labelKey, String labelValue) { - for (HasMetadata item : items.getItems()) { - Map labels = item.getMetadata().getLabels(); - if (labels == null) { - labels = new LinkedHashMap<>(); - } - labels.put(labelKey, labelValue); - item.getMetadata().setLabels(labels); - } - } - - static void addObjectAnnotation(HasMetadata item, String annotationKey, String annotationValue) { - Map annotations = item.getMetadata().getAnnotations(); - if (annotations == null) { - annotations = new LinkedHashMap<>(); - } - annotations.put(annotationKey, annotationValue); - item.getMetadata().setAnnotations(annotations); - } - - static void addObjectAnnotation(KubernetesList items, String annotationKey, String annotationValue) { - for (HasMetadata item : items.getItems()) { - addObjectAnnotation(item, annotationKey, annotationValue); - } - } - - void create(KubernetesList resources); - - void apply(KubernetesList resources, boolean patchPersistentVolumeClaims, Consumer failedResourceSupplier); - - void apply(HasMetadata resource, boolean patchPersistentVolumeClaims); - - void delete(KubernetesList resources); - - KubernetesList processTemplate(String templateName, Map parameters); - - RouterCluster getRouterCluster() throws IOException; - - void scaleStatefulSet(String name, int numReplicas); - - List listBrokers(String clusterId); - - String getNamespace(); -} diff --git a/standard-controller/src/main/java/io/enmasse/controller/standard/KubernetesHelper.java b/standard-controller/src/main/java/io/enmasse/controller/standard/KubernetesHelper.java deleted file mode 100644 index 5d401cddd53..00000000000 --- a/standard-controller/src/main/java/io/enmasse/controller/standard/KubernetesHelper.java +++ /dev/null @@ -1,188 +0,0 @@ -/* - * Copyright 2017-2018, EnMasse authors. - * License: Apache License 2.0 (see the file LICENSE or http://apache.org/licenses/LICENSE-2.0.html). - */ -package io.enmasse.controller.standard; - -import java.io.File; -import java.io.IOException; -import java.util.*; -import java.util.function.Consumer; -import java.util.stream.Collectors; - -import io.fabric8.kubernetes.client.KubernetesClient; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import com.fasterxml.jackson.databind.ObjectMapper; - -import io.enmasse.admin.model.v1.StandardInfraConfig; -import io.enmasse.config.AnnotationKeys; -import io.enmasse.config.LabelKeys; -import io.enmasse.k8s.util.Templates; -import io.fabric8.kubernetes.api.model.ConfigMap; -import io.fabric8.kubernetes.api.model.HasMetadata; -import io.fabric8.kubernetes.api.model.KubernetesList; -import io.fabric8.kubernetes.api.model.PersistentVolumeClaim; -import io.fabric8.kubernetes.api.model.Pod; -import io.fabric8.kubernetes.api.model.Secret; -import io.fabric8.kubernetes.api.model.Service; -import io.fabric8.kubernetes.api.model.ServiceAccount; -import io.fabric8.kubernetes.api.model.apps.Deployment; -import io.fabric8.kubernetes.api.model.apps.StatefulSet; -import io.fabric8.kubernetes.client.KubernetesClientException; -import io.fabric8.kubernetes.client.internal.readiness.Readiness; - -public class KubernetesHelper implements Kubernetes { - private static final Logger log = LoggerFactory.getLogger(KubernetesHelper.class); - private static final ObjectMapper mapper = new ObjectMapper(); - private final File templateDir; - private static final String TEMPLATE_SUFFIX = ".yaml"; - private final KubernetesClient client; - private final String infraUuid; - - public KubernetesHelper(KubernetesClient client, File templateDir, String infraUuid) { - this.client = client; - this.templateDir = templateDir; - this.infraUuid = infraUuid; - } - - @Override - public List listClusters() { - Map> resourceMap = new HashMap<>(); - - // Add other resources part of a destination cluster - List objects = new ArrayList<>(); - objects.addAll(client.apps().deployments().withLabel(LabelKeys.INFRA_UUID, infraUuid).list().getItems()); - objects.addAll(client.apps().statefulSets().withLabel(LabelKeys.INFRA_UUID, infraUuid).list().getItems()); - objects.addAll(client.persistentVolumeClaims().withLabel(LabelKeys.INFRA_UUID, infraUuid).list().getItems()); - objects.addAll(client.configMaps().withLabel(LabelKeys.INFRA_UUID, infraUuid).withLabelNotIn("type", "address-config", "address-space", "address-space-plan", "address-plan").list().getItems()); - objects.addAll(client.services().withLabel(LabelKeys.INFRA_UUID, infraUuid).list().getItems()); - - for (HasMetadata config : objects) { - Map annotations = config.getMetadata().getAnnotations(); - - if (annotations != null && annotations.containsKey(AnnotationKeys.CLUSTER_ID)) { - String groupId = annotations.get(AnnotationKeys.CLUSTER_ID); - - Map labels = config.getMetadata().getLabels(); - - if (labels != null) { - if (!resourceMap.containsKey(groupId)) { - resourceMap.put(groupId, new ArrayList<>()); - } - resourceMap.get(groupId).add(config); - } - } - } - - return resourceMap.entrySet().stream() - .map(entry -> { - KubernetesList list = new KubernetesList(); - list.setItems(entry.getValue()); - return new BrokerCluster(entry.getKey(), list); - }).collect(Collectors.toList()); - } - - @Override - public RouterCluster getRouterCluster() throws IOException { - StatefulSet s = client.apps().statefulSets().withName("qdrouterd-" + infraUuid).get(); - StandardInfraConfig infraConfig = null; - if (s.getMetadata().getAnnotations() != null && s.getMetadata().getAnnotations().get(AnnotationKeys.APPLIED_INFRA_CONFIG) != null) { - infraConfig = mapper.readValue(s.getMetadata().getAnnotations().get(AnnotationKeys.APPLIED_INFRA_CONFIG), StandardInfraConfig.class); - } - return new RouterCluster(s.getMetadata().getName(), s.getSpec().getReplicas(), infraConfig); - } - - @Override - public boolean isDestinationClusterReady(String clusterId) { - return listClusters().stream() - .filter(dc -> clusterId.equals(dc.getClusterId())) - .anyMatch(KubernetesHelper::areAllDeploymentsReady); - } - - private static boolean areAllDeploymentsReady(BrokerCluster dc) { - return dc.getResources().getItems().stream().filter(KubernetesHelper::isDeployment).allMatch(Readiness::isReady); - } - - public static boolean isDeployment(HasMetadata res) { - return res.getKind().equals("Deployment") || res.getKind().equals("StatefulSet"); // TODO: is there an existing constant for this somewhere? - } - - @Override - public List listRouters() { - return client.pods().withLabel(LabelKeys.CAPABILITY, "router").withLabel(LabelKeys.INFRA_UUID, infraUuid).list().getItems(); - } - - @Override - public void create(KubernetesList resources) { - client.lists().create(resources); - } - - @Override - public void apply(KubernetesList resources, boolean patchPersistentVolumeClaims, Consumer appliedResourceConsumer) { - for (HasMetadata resource : resources.getItems()) { - apply(resource, patchPersistentVolumeClaims); - if (appliedResourceConsumer != null) { - appliedResourceConsumer.accept(resource); - } - } - } - - @Override - public void apply(HasMetadata resource, boolean patchPersistentVolumeClaims) { - try { - if (resource instanceof ConfigMap) { - client.configMaps().withName(resource.getMetadata().getName()).patch((ConfigMap) resource); - } else if (resource instanceof Secret) { - client.secrets().withName(resource.getMetadata().getName()).patch((Secret) resource); - } else if (resource instanceof Deployment) { - client.apps().deployments().withName(resource.getMetadata().getName()).patch((Deployment) resource); - } else if (resource instanceof StatefulSet) { - client.apps().statefulSets().withName(resource.getMetadata().getName()).cascading(false).patch((StatefulSet) resource); - } else if (resource instanceof Service) { - client.services().withName(resource.getMetadata().getName()).patch((Service) resource); - } else if (resource instanceof ServiceAccount) { - client.serviceAccounts().withName(resource.getMetadata().getName()).patch((ServiceAccount) resource); - } else if (resource instanceof PersistentVolumeClaim && patchPersistentVolumeClaims) { - client.persistentVolumeClaims().withName(resource.getMetadata().getName()).patch((PersistentVolumeClaim) resource); - } - } catch (KubernetesClientException e) { - if (e.getCode() == 404) { - // Create it if it does not exist - client.resource(resource).createOrReplace(); - } else { - throw e; - } - } - } - - @Override - public void delete(KubernetesList resources) { - for (HasMetadata resource : resources.getItems()) { - client.resource(resource).cascading(true).delete(); - } - } - - @Override - public void scaleStatefulSet(String name, int numReplicas) { - log.info("Scaling stateful set with id {} and {} replicas", name, numReplicas); - client.apps().statefulSets().withName(name).scale(numReplicas); - } - - @Override - public List listBrokers(String clusterId) { - return client.pods().withLabel(LabelKeys.ROLE, "broker").withLabel(LabelKeys.NAME, clusterId).withLabel(LabelKeys.INFRA_UUID, infraUuid).list().getItems(); - } - - @Override - public KubernetesList processTemplate(String templateName, Map parameters) { - File templateFile = new File(templateDir, templateName + TEMPLATE_SUFFIX); - return Templates.process(templateFile, parameters); - } - - @Override - public String getNamespace() { - return client.getNamespace(); - } -} diff --git a/standard-controller/src/main/java/io/enmasse/controller/standard/MutualTlsBrokerClientFactory.java b/standard-controller/src/main/java/io/enmasse/controller/standard/MutualTlsBrokerClientFactory.java deleted file mode 100644 index 78e5b3bf5c8..00000000000 --- a/standard-controller/src/main/java/io/enmasse/controller/standard/MutualTlsBrokerClientFactory.java +++ /dev/null @@ -1,53 +0,0 @@ -/* - * Copyright 2016-2018, EnMasse authors. - * License: Apache License 2.0 (see the file LICENSE or http://apache.org/licenses/LICENSE-2.0.html). - */ -package io.enmasse.controller.standard; - -import io.enmasse.amqp.ProtonRequestClient; -import io.enmasse.amqp.SyncRequestClient; -import io.vertx.core.Vertx; -import io.vertx.core.net.PemKeyCertOptions; -import io.vertx.core.net.PemTrustOptions; -import io.vertx.proton.ProtonClientOptions; - -import java.io.File; -import java.util.concurrent.CompletableFuture; -import java.util.concurrent.TimeUnit; - -public class MutualTlsBrokerClientFactory implements BrokerClientFactory { - private final Vertx vertx; - private final ProtonClientOptions protonClientOptions; - private final StandardControllerOptions options; - - public MutualTlsBrokerClientFactory(Vertx vertx, StandardControllerOptions options) { - this.vertx = vertx; - String certDir = options.getCertDir(); - this.protonClientOptions = new ProtonClientOptions() - .setSsl(true) - .setHostnameVerificationAlgorithm("") - .setPemTrustOptions(new PemTrustOptions() - .addCertPath(new File(certDir, "ca.crt").getAbsolutePath())) - .setPemKeyCertOptions(new PemKeyCertOptions() - .setCertPath(new File(certDir, "tls.crt").getAbsolutePath()) - .setKeyPath(new File(certDir, "tls.key").getAbsolutePath())); - this.options = options; - } - - @Override - public SyncRequestClient connectBrokerManagementClient(String host, int port) throws Exception { - ProtonRequestClient client = null; - try { - client = new ProtonRequestClient(vertx, "standard-controller"); - CompletableFuture promise = new CompletableFuture<>(); - client.connect(host, port, protonClientOptions, "activemq.management", promise); - - promise.get(options.getManagementConnectTimeout().getSeconds(), TimeUnit.SECONDS); - } catch (Exception e) { - if (client != null) { - client.close(); - } - } - return client; - } -} diff --git a/standard-controller/src/main/java/io/enmasse/controller/standard/RandomBrokerIdGenerator.java b/standard-controller/src/main/java/io/enmasse/controller/standard/RandomBrokerIdGenerator.java deleted file mode 100644 index 64dbec77991..00000000000 --- a/standard-controller/src/main/java/io/enmasse/controller/standard/RandomBrokerIdGenerator.java +++ /dev/null @@ -1,25 +0,0 @@ -/* - * Copyright 2018, EnMasse authors. - * License: Apache License 2.0 (see the file LICENSE or http://apache.org/licenses/LICENSE-2.0.html). - */ -package io.enmasse.controller.standard; - -import java.util.Random; - -public class RandomBrokerIdGenerator implements BrokerIdGenerator { - private final Random random = new Random(System.currentTimeMillis()); - private static final String ALPHA_NUMERIC_STRING = "abcdefghijklmnopqrstuvwxyz0123456789"; - - public String generateBrokerId() { - return generateAlphaNumberic(4); - } - - private String generateAlphaNumberic(int length) { - StringBuilder builder = new StringBuilder(); - while (length-- != 0) { - int character = (int)(random.nextDouble()*ALPHA_NUMERIC_STRING.length()); - builder.append(ALPHA_NUMERIC_STRING.charAt(character)); - } - return builder.toString(); - } -} diff --git a/standard-controller/src/main/java/io/enmasse/controller/standard/RouterCluster.java b/standard-controller/src/main/java/io/enmasse/controller/standard/RouterCluster.java deleted file mode 100644 index 90567a36c75..00000000000 --- a/standard-controller/src/main/java/io/enmasse/controller/standard/RouterCluster.java +++ /dev/null @@ -1,45 +0,0 @@ -/* - * Copyright 2017-2018, EnMasse authors. - * License: Apache License 2.0 (see the file LICENSE or http://apache.org/licenses/LICENSE-2.0.html). - */ -package io.enmasse.controller.standard; - -import io.enmasse.admin.model.v1.StandardInfraConfig; - -public class RouterCluster { - private final String name; - private final int replicas; - private final StandardInfraConfig infraConfig; - private int newReplicas; - - public RouterCluster(String name, int replicas, StandardInfraConfig infraConfig) { - this.name = name; - this.replicas = replicas; - this.infraConfig = infraConfig; - this.newReplicas = replicas; - } - - public String getName() { - return name; - } - - public void setNewReplicas(int replicas) { - this.newReplicas = replicas; - } - - public int getReplicas() { - return replicas; - } - - public int getNewReplicas() { - return newReplicas; - } - - public boolean hasChanged() { - return replicas != newReplicas; - } - - public StandardInfraConfig getInfraConfig() { - return infraConfig; - } -} diff --git a/standard-controller/src/main/java/io/enmasse/controller/standard/RouterStatus.java b/standard-controller/src/main/java/io/enmasse/controller/standard/RouterStatus.java deleted file mode 100644 index 271af9f845f..00000000000 --- a/standard-controller/src/main/java/io/enmasse/controller/standard/RouterStatus.java +++ /dev/null @@ -1,268 +0,0 @@ -/* - * Copyright 2016-2020, EnMasse authors. - * License: Apache License 2.0 (see the file LICENSE or http://apache.org/licenses/LICENSE-2.0.html). - */ -package io.enmasse.controller.standard; - -import java.util.HashSet; -import java.util.List; -import java.util.Set; - -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import io.enmasse.address.model.Address; -import io.enmasse.address.model.AddressSpecForwarder; -import io.enmasse.address.model.AddressStatusForwarder; -import io.enmasse.address.model.BrokerStatus; - -class RouterStatus { - private static final Logger log = LoggerFactory.getLogger(RouterStatus.class); - - private final String routerId; - private final List addresses; - private final List> autoLinks; - private final List> linkRoutes; - private final List connections; - private final List> links; - - RouterStatus(String routerId, List addresses, List> autoLinks, List> linkRoutes, List connections, List> links) { - this.routerId = routerId; - this.addresses = addresses; - this.autoLinks = autoLinks; - this.linkRoutes = linkRoutes; - this.connections = connections; - this.links = links; - } - - public String getRouterId() { - return routerId; - } - - public int checkAddress(Address address) { - - int ok = 0; - final String addressName = address.getSpec().getAddress(); - - boolean found = this.addresses.contains(addressName); - if (!found) { - var msg = "Address " + addressName + " not found on " + this.routerId; - log.debug(msg); - address.getStatus().setReady(false).appendMessage(msg); - } else { - ok++; - log.debug("Address found for '{}'", addressName); - } - - return ok; - } - - public int checkAutoLinks(Address address) { - - int ok = 0; - final String addressName = address.getSpec().getAddress(); - - for (List autoLink : autoLinks) { - String addr = autoLink.get(0); - - if (addressName.equals(addr)) { - ok++; - } - } - - if (ok < 2) { - var msg = "Address " + addressName + " is missing autoLinks on " + routerId; - log.debug(msg); - address.getStatus().setReady(false).appendMessage(msg); - } else { - log.debug("Address {} has all required auto links: {}", addressName); - } - - return ok; - } - - public int checkLinkRoutes(Address address) { - int ok = 0; - final String addressName = address.getSpec().getAddress(); - - for (List linkRoute : linkRoutes) { - if (linkRoute.size() > 0) { - String prefix = linkRoute.get(0); - - // Pooled topics have active link routes - if (addressName.equals(prefix)) { - ok++; - } - } - } - - if (ok < 2) { - var msg = "Address " + addressName + " is missing linkRoutes on " + this.routerId; - log.debug(msg); - address.getStatus().setReady(false).appendMessage(msg); - } else { - log.debug("Address {} has all required link routes: {}", addressName); - } - - return ok; - } - - public static int checkActiveAutoLink(Address address, List routerStatusList) { - int ok = 0; - final Set active = new HashSet<>(); - final String addressName = address.getSpec().getAddress(); - - for (RouterStatus routerStatus : routerStatusList) { - - log.debug("Router Auto Links: {}", routerStatus.autoLinks); - - for (List autoLink : routerStatus.autoLinks) { - if (autoLink.size() > 3) { - String addr = autoLink.get(0); - String dir = autoLink.get(2); - String operStatus = autoLink.get(3); - - log.debug("Addr: {}, Dir: {}, operStatus: {}", addr, dir, operStatus); - - if (addressName.equals(addr) && "active".equals(operStatus)) { - active.add(dir); - log.debug(" Match!"); - } - } - } - } - - if (active.size() < 2) { - var msg = "Address " + addressName + " is missing active autoLink (active in dirs: " + active + ")"; - log.debug(msg); - address.getStatus().setReady(false).appendMessage(msg); - } else { - log.debug("Address {} has all required auto links: {}", addressName, active); - ok++; - } - return ok; - } - - public static int checkActiveLinkRoute(Address address, List routerStatusList) { - int ok = 0; - Set active = new HashSet<>(); - final String addressName = address.getSpec().getAddress(); - - for (RouterStatus routerStatus : routerStatusList) { - - for (List linkRoute : routerStatus.linkRoutes) { - if (linkRoute.size() > 3) { - String addr = linkRoute.get(0); - @SuppressWarnings("unused") - String containerId = linkRoute.get(1); - String dir = linkRoute.get(2); - String operStatus = linkRoute.get(3); - - if (addressName.equals(addr) && "active".equals(operStatus)) { - active.add(dir); - } - } - } - } - - if (active.size() < 2) { - var msg = "Address " + addressName + " is missing active linkRoute (active in dirs: " + active + ")"; - log.debug(msg); - address.getStatus().setReady(false).appendMessage(msg); - } else { - log.debug("Address {} has all required link routes: {}", addressName, active); - ok++; - } - return ok; - } - - public static int checkConnection(Address address, List routerStatusList) { - int ok = 0; - for (RouterStatus routerStatus : routerStatusList) { - for (String containerId : routerStatus.connections) { - boolean found = false; - for (BrokerStatus brokerStatus : address.getStatus().getBrokerStatuses()) { - if (containerId.startsWith(brokerStatus.getClusterId())) { - ok++; - found = true; - break; - } - } - if (found) { - break; - } - } - } - - final String addressName = address.getSpec().getAddress(); - if (ok == 0) { - var msg = "Address " + addressName + " is missing connection from broker"; - log.debug(msg); - address.getStatus().setReady(false).appendMessage(msg); - } else { - log.debug("Address {} has all required connections", addressName); - } - return ok; - } - - public static int checkForwarderLinks(Address address, List routerStatusList) { - if (address.getSpec().getForwarders() == null || address.getSpec().getForwarders().isEmpty()) { - return 0; - } - - final String addressName = address.getSpec().getAddress(); - - int ok = 0; - for (AddressSpecForwarder forwarder : address.getSpec().getForwarders()) { - boolean found = false; - boolean isUp = false; - for (RouterStatus routerStatus : routerStatusList) { - for (List entry : routerStatus.links) { - String linkName = entry.get(0); - if (address.getForwarderLinkName(forwarder).equals(linkName)) { - found = true; - String operStatus = entry.get(1); - - if ("up".equals(operStatus)) { - isUp = true; - } - break; - } - } - } - - final String forwarderName = forwarder.getName(); - if (!found) { - updateForwarderStatus(addressName, forwarderName, false, "Unable to find link for forwarder '" + forwarderName + "'", address.getStatus().getForwarders()); - } else if (!isUp) { - updateForwarderStatus(addressName, forwarderName, false, "Unable to find link in the up state for forwarder '" + forwarderName + "'", address.getStatus().getForwarders()); - } else { - ok++; - log.debug("Forwarder {} for address {} is ok", forwarder.getName(), address.getSpec().getAddress()); - } - } - return ok; - } - - private static void updateForwarderStatus(String addressName, String forwarderName, boolean isReady, String message, List forwarderStatuses) { - log.debug("Address: {} - {}", addressName, message); - forwarderStatuses.stream() - .filter(c -> c.getName().equals(forwarderName)) - .findFirst().ifPresent(s -> { - s.setReady(isReady); - s.appendMessage(message); - }); - } - - @Override - public String toString() { - return new StringBuilder() - .append("{routerId=").append(routerId).append(",") - .append("addresses=").append(addresses).append(",") - .append("autoLinks=").append(autoLinks).append(",") - .append("linkRoutes=").append(linkRoutes).append(",") - .append("connections=").append(connections).append(",") - .append("links=").append(links).append("}") - .toString(); - } -} diff --git a/standard-controller/src/main/java/io/enmasse/controller/standard/RouterStatusCache.java b/standard-controller/src/main/java/io/enmasse/controller/standard/RouterStatusCache.java deleted file mode 100644 index 9598a34e01c..00000000000 --- a/standard-controller/src/main/java/io/enmasse/controller/standard/RouterStatusCache.java +++ /dev/null @@ -1,136 +0,0 @@ -/* - * Copyright 2019, EnMasse authors. - * License: Apache License 2.0 (see the file LICENSE or http://apache.org/licenses/LICENSE-2.0.html). - */ -package io.enmasse.controller.standard; - -import io.enmasse.amqp.RouterManagement; -import io.enmasse.k8s.api.EventLogger; -import io.fabric8.kubernetes.api.model.Pod; -import io.fabric8.kubernetes.client.internal.readiness.Readiness; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import java.time.Duration; -import java.util.ArrayList; -import java.util.Collections; -import java.util.List; -import java.util.concurrent.ExecutorCompletionService; -import java.util.concurrent.ForkJoinPool; -import java.util.concurrent.atomic.AtomicInteger; -import java.util.stream.Collectors; - -import static io.enmasse.controller.standard.ControllerKind.AddressSpace; -import static io.enmasse.controller.standard.ControllerReason.RouterCheckFailed; -import static io.enmasse.k8s.api.EventLogger.Type.Warning; - -public class RouterStatusCache implements Runnable { - private static final Logger log = LoggerFactory.getLogger(RouterStatusCache.class); - private final RouterManagement routerManagement; - private final Kubernetes kubernetes; - private final EventLogger eventLogger; - private final String addressSpace; - - private volatile boolean running = false; - private Thread thread; - private final Duration checkInterval; - private final Object monitor = new Object(); - private boolean needCheck = false; - - private AtomicInteger routerCheckFailures = new AtomicInteger(0); - private volatile boolean checkRouterLinks = false; - private volatile List latestResult = Collections.emptyList(); - - RouterStatusCache(RouterManagement routerManagement, Kubernetes kubernetes, EventLogger eventLogger, String addressSpace, Duration checkInterval) - { - this.routerManagement = routerManagement; - this.kubernetes = kubernetes; - this.eventLogger = eventLogger; - this.addressSpace = addressSpace; - this.checkInterval = checkInterval; - } - - List getLatestResults() { - return latestResult; - } - - void checkRouterStatus() { - RouterStatusCollector routerStatusCollector = new RouterStatusCollector(routerManagement, checkRouterLinks); - List routers = kubernetes.listRouters().stream() - .filter(Readiness::isPodReady) - .collect(Collectors.toList()); - - log.info("Collecting status from {} routers", routers.size()); - - ExecutorCompletionService service = new ExecutorCompletionService<>(ForkJoinPool.commonPool()); - for (Pod router : routers) { - service.submit(() -> routerStatusCollector.collect(router)); - } - List routerStatusList = new ArrayList<>(routers.size()); - for (int i = 0; i < routers.size(); i++) { - try { - RouterStatus status = service.take().get(); - if (status != null) { - routerStatusList.add(status); - } - } catch (Exception e) { - log.info("Error requesting router status. Ignoring", e); - eventLogger.log(RouterCheckFailed, e.getMessage(), Warning, AddressSpace, addressSpace); - routerCheckFailures.incrementAndGet(); - } - } - this.latestResult = routerStatusList; - } - - public void setCheckRouterLinks(boolean checkRouterLinks) { - this.checkRouterLinks = checkRouterLinks; - } - - public int getRouterCheckFailures() { - return routerCheckFailures.get(); - } - - public void start() { - running = true; - thread = new Thread(this); - thread.setName("router-status-collector"); - thread.setDaemon(true); - thread.start(); - } - - public void stop() { - try { - running = false; - thread.interrupt(); - thread.join(); - } catch (InterruptedException ignored) { - log.warn("Interrupted while stopping", ignored); - } - } - - @Override - public void run() { - while (running) { - try { - checkRouterStatus(); - synchronized (monitor) { - if (!needCheck) { - monitor.wait(checkInterval.toMillis()); - } - needCheck = false; - } - } catch (InterruptedException e) { - Thread.currentThread().interrupt(); - } catch (Exception e) { - log.warn("Exception in collector task", e); - } - } - } - - public void wakeup() { - synchronized (monitor) { - needCheck = true; - monitor.notifyAll(); - } - } -} diff --git a/standard-controller/src/main/java/io/enmasse/controller/standard/RouterStatusCollector.java b/standard-controller/src/main/java/io/enmasse/controller/standard/RouterStatusCollector.java deleted file mode 100644 index b3a2492bbc5..00000000000 --- a/standard-controller/src/main/java/io/enmasse/controller/standard/RouterStatusCollector.java +++ /dev/null @@ -1,100 +0,0 @@ -/* - * Copyright 2016-2018, EnMasse authors. - * License: Apache License 2.0 (see the file LICENSE or http://apache.org/licenses/LICENSE-2.0.html). - */ -package io.enmasse.controller.standard; - -import io.enmasse.amqp.RouterEntity; -import io.enmasse.amqp.RouterManagement; -import io.fabric8.kubernetes.api.model.Container; -import io.fabric8.kubernetes.api.model.ContainerPort; -import io.fabric8.kubernetes.api.model.Pod; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import java.util.ArrayList; -import java.util.Collections; -import java.util.List; -import java.util.Map; - -class RouterStatusCollector { - private static final Logger log = LoggerFactory.getLogger(RouterStatusCollector.class); - private final RouterManagement routerManagement; - private final boolean checkRouterLinks; - - public RouterStatusCollector(RouterManagement routerManagement, boolean checkRouterLinks) { - this.routerManagement = routerManagement; - this.checkRouterLinks = checkRouterLinks; - } - - public RouterStatus collect(Pod router) throws Exception { - int port = 0; - for (Container container : router.getSpec().getContainers()) { - if (container.getName().equals("router")) { - for (ContainerPort containerPort : container.getPorts()) { - if (containerPort.getName().equals("amqps-normal")) { - port = containerPort.getContainerPort(); - } - } - } - } - - if (port != 0) { - return doCollectStatus(router, port); - } else { - log.info("Unable to find appropriate port for router {}, skipping address check", router.getMetadata().getName()); - return null; - } - } - - private static final RouterEntity address = new RouterEntity("org.apache.qpid.dispatch.router.config.address", "prefix"); - private static final RouterEntity autoLink = new RouterEntity("org.apache.qpid.dispatch.router.config.autoLink", "address", "containerId", "direction", "operStatus"); - - private static final RouterEntity linkRoute = new RouterEntity("org.apache.qpid.dispatch.router.config.linkRoute", "prefix", "containerId", "direction", "operStatus"); - private static final RouterEntity connection = new RouterEntity("org.apache.qpid.dispatch.connection", "container"); - - private static final RouterEntity link = new RouterEntity("org.apache.qpid.dispatch.router.link", "linkName", "operStatus", "linkDir"); - - private RouterStatus doCollectStatus(Pod router, int port) throws Exception { - String host = router.getStatus().getPodIP(); - log.debug("Collecting router status of router : {}", router.getMetadata().getName()); - - Map>> results; - if (checkRouterLinks) { - results = routerManagement.query(host, port, address, autoLink, linkRoute, connection, link); - } else { - results = routerManagement.query(host, port, address, autoLink, linkRoute, connection); - } - - String routerId = router.getMetadata().getName(); - return new RouterStatus(routerId, - filterOnAttribute(String.class, 0, results.get(address)), - toTyped(String.class, results.get(autoLink)), - toTyped(String.class, results.get(linkRoute)), - filterOnAttribute(String.class, 0, results.get(connection)), - toTyped(String.class, results.getOrDefault(link, Collections.emptyList()))); - } - - private static List> toTyped(Class type, List> list) { - List> typed = new ArrayList<>(); - for (List entry : list) { - List values = new ArrayList<>(); - for (Object value : entry) { - values.add(type.cast(value)); - } - typed.add(values); - } - return typed; - } - - private static List filterOnAttribute(Class type, int attrNum, List> list) { - List filtered = new ArrayList<>(); - for (List entry : list) { - T filteredValue = type.cast(entry.get(attrNum)); - if (filteredValue != null) { - filtered.add(filteredValue); - } - } - return filtered; - } -} diff --git a/standard-controller/src/main/java/io/enmasse/controller/standard/StandardController.java b/standard-controller/src/main/java/io/enmasse/controller/standard/StandardController.java deleted file mode 100644 index 94da2bdb1c5..00000000000 --- a/standard-controller/src/main/java/io/enmasse/controller/standard/StandardController.java +++ /dev/null @@ -1,209 +0,0 @@ -/* - * Copyright 2017-2018, EnMasse authors. - * License: Apache License 2.0 (see the file LICENSE or http://apache.org/licenses/LICENSE-2.0.html). - */ -package io.enmasse.controller.standard; - -import io.enmasse.address.model.Address; -import io.enmasse.address.model.AddressSpace; -import io.enmasse.config.AnnotationKeys; -import io.enmasse.k8s.api.AddressApi; -import io.enmasse.k8s.api.AddressSpaceApi; -import io.enmasse.k8s.api.CachingSchemaProvider; -import io.enmasse.k8s.api.EventLogger; -import io.enmasse.k8s.api.KubeAddressSpaceApi; -import io.enmasse.k8s.api.KubeEventLogger; -import io.enmasse.k8s.api.KubeSchemaApi; -import io.enmasse.k8s.api.LogEventLogger; -import io.enmasse.k8s.api.SchemaApi; -import io.enmasse.metrics.api.Metrics; -import io.enmasse.model.CustomResourceDefinitions; -import io.fabric8.kubernetes.client.Config; -import io.fabric8.kubernetes.client.ConfigBuilder; -import io.fabric8.kubernetes.client.DefaultKubernetesClient; -import io.fabric8.kubernetes.client.NamespacedKubernetesClient; -import io.fabric8.kubernetes.client.utils.HttpClientUtils; -import io.vertx.core.Vertx; -import okhttp3.OkHttpClient; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import java.time.Clock; -import java.util.Map; - - -/** - * The standard controller is responsible for watching address spaces of type standard, creating - * infrastructure required and propagating relevant status information. - */ - -public class StandardController { - private static final Logger log = LoggerFactory.getLogger(StandardController.class.getName()); - - static { - try { - CustomResourceDefinitions.registerAll(); - } catch (RuntimeException t) { - t.printStackTrace(); - throw new ExceptionInInitializerError(t); - } - } - - public static void main(String[] args) throws Exception { - setUncaughtExceptionHandler(); - StandardController standardController = null; - try { - Map env = System.getenv(); - StandardControllerOptions options = StandardControllerOptions.fromEnv(env); - log.info("StandardController starting with options: {}", options); - standardController = new StandardController(options); - standardController.start(); - } catch (IllegalArgumentException e) { - System.out.println(String.format("Unable to parse arguments: %s", e.getMessage())); - System.exit(1); - } catch (Exception e) { - System.out.println("Error starting address space controller: " + e.getMessage()); - System.exit(1); - } finally { - if (standardController != null) { - Runtime.getRuntime().addShutdownHook(new Thread(standardController::stop)); - } - } - } - - private final NamespacedKubernetesClient kubeClient; - private final StandardControllerOptions options; - private AddressController addressController; - private AddressCanaryHealth addressCanaryHealth; - private HTTPServer httpServer; - - public StandardController(StandardControllerOptions options) { - Config config = new ConfigBuilder().build(); - OkHttpClient httpClient = HttpClientUtils.createHttpClient(config); - httpClient = httpClient.newBuilder() - .connectTimeout(options.getKubernetesApiConnectTimeout()) - .writeTimeout(options.getKubernetesApiWriteTimeout()) - .readTimeout(options.getKubernetesApiReadTimeout()) - .build(); - this.kubeClient = new DefaultKubernetesClient(httpClient, config); - this.options = options; - } - - public void start() throws Exception { - - SchemaApi schemaApi = KubeSchemaApi.create(kubeClient, kubeClient.getNamespace(), options.getVersion(), false, false); - CachingSchemaProvider schemaProvider = new CachingSchemaProvider(); - schemaApi.watchSchema(schemaProvider, options.getResyncInterval()); - - Kubernetes kubernetes = new KubernetesHelper(kubeClient, options.getTemplateDir(), options.getInfraUuid()); - BrokerSetGenerator clusterGenerator = new TemplateBrokerSetGenerator(kubernetes, options, System.getenv()); - - EventLogger eventLogger = options.isEnableEventLogger() ? new KubeEventLogger(kubeClient, kubeClient.getNamespace(), Clock.systemUTC(), "standard-controller") - : new LogEventLogger(); - - Metrics metrics = new Metrics(); - - Vertx vertx = Vertx.vertx(); - - BrokerClientFactory brokerClientFactory = new MutualTlsBrokerClientFactory(vertx, options); - - AddressSpaceApi addressSpaceApi = KubeAddressSpaceApi.create(kubeClient, options.getAddressSpaceNamespace(), options.getVersion()); - AddressSpace addressSpace = addressSpaceApi.getAddressSpaceWithName(options.getAddressSpaceNamespace(), options.getAddressSpace()).orElseThrow(() -> - new IllegalStateException("Unable to lookup address space " + options.getAddressSpace())); - - AddressApi addressApi = addressSpaceApi.withAddressSpace(addressSpace); - - String addressPrefix = String.format("%s.", options.getAddressSpace()); - // Replace resources as part of upgrade if version is different - for (Address address : addressApi.listAddresses(options.getAddressSpaceNamespace())) { - if (address.getMetadata().getName().startsWith(addressPrefix)) { - if (!options.getVersion().equals(address.getAnnotation(AnnotationKeys.VERSION))) { - try { - // Version will be updated by replaceAddress - addressApi.replaceAddress(address); - } catch (Exception e) { - log.warn("Error replacing {}", address.getMetadata().getName(), e); - } - } - } - } - - addressController = new AddressController( - options, - addressSpaceApi, - addressApi, - kubernetes, - clusterGenerator, - eventLogger, - schemaProvider, - vertx, - metrics, - new RandomBrokerIdGenerator(), - brokerClientFactory); - - log.info("Starting standard controller for " + options.getAddressSpace()); - addressController.start(); - - if (options.getHealthCheckInterval().getSeconds() > 0) { - AddressProber probeRunner = AddressProber.withCertsInDir(vertx, "standard-controller-healthcheck", options.getHealthProbeTimeout(), options.getCertDir()); - addressCanaryHealth = new AddressCanaryHealth(kubernetes, options.getHealthCheckInterval(), probeRunner, metrics, options.getAddressSpace()); - - log.info("Starting health checker for " + options.getAddressSpace()); - addressCanaryHealth.start(); - } - - httpServer = new HTTPServer( 8889, metrics); - httpServer.start(); - } - - public void stop() { - try { - log.info("StandardController stopping"); - - if (httpServer != null) { - httpServer.stop(); - } - } finally { - try { - if (addressController != null) { - try { - addressController.stop(); - } catch (Exception ignore) { - } finally { - if (addressCanaryHealth != null) { - try { - addressCanaryHealth.stop(); - } catch (Exception ignore) { - } - } - } - } - } finally { - kubeClient.close(); - log.info("StandardController stopped"); - } - } - } - - protected static void setUncaughtExceptionHandler() { - Thread.setDefaultUncaughtExceptionHandler((t, e) -> { - try { - System.err.println("########################################################################"); - System.err.println("#"); - System.err.print("# Uncaught Exception "); - System.err.print(e.toString()); - System.err.print(" in Thread "); - System.err.println(t.getName()); - System.err.println("#"); - System.err.println("#"); - System.err.println("########################################################################"); - e.printStackTrace(System.err); - - log.error("Fatal, uncaught exception", e); - - } finally { - Runtime.getRuntime().halt(1); - } - }); - } -} diff --git a/standard-controller/src/main/java/io/enmasse/controller/standard/StandardControllerOptions.java b/standard-controller/src/main/java/io/enmasse/controller/standard/StandardControllerOptions.java deleted file mode 100644 index fc89bbc428b..00000000000 --- a/standard-controller/src/main/java/io/enmasse/controller/standard/StandardControllerOptions.java +++ /dev/null @@ -1,328 +0,0 @@ -/* - * Copyright 2017-2018, EnMasse authors. - * License: Apache License 2.0 (see the file LICENSE or http://apache.org/licenses/LICENSE-2.0.html). - */ -package io.enmasse.controller.standard; - -import java.io.File; -import java.time.Duration; -import java.util.Map; -import java.util.Optional; - -public class StandardControllerOptions { - private File templateDir; - private String certDir; - private String addressSpace; - private String addressSpaceNamespace; - private String addressSpacePlanName; - private String infraUuid; - private Duration resyncInterval; - private Duration recheckInterval; - private Duration statusCheckInterval; - private String version; - private boolean enableEventLogger; - private String authenticationServiceHost; - private String authenticationServicePort; - private String authenticationServiceCaSecret; - private String authenticationServiceClientSecret; - private String authenticationServiceSaslInitHost; - private Duration managementQueryTimeout; - private Duration managementConnectTimeout; - private Duration kubernetesApiConnectTimeout; - private Duration kubernetesApiReadTimeout; - private Duration kubernetesApiWriteTimeout; - private Duration healthProbeTimeout; - private Duration healthCheckInterval; - - public String getCertDir() { - return certDir; - } - - public void setCertDir(String certDir) { - this.certDir = certDir; - } - - public String getAddressSpace() { - return addressSpace; - } - - public void setAddressSpace(String addressSpace) { - this.addressSpace = addressSpace; - } - - public String getAddressSpacePlanName() { - return addressSpacePlanName; - } - - public void setAddressSpacePlanName(String addressSpacePlanName) { - this.addressSpacePlanName = addressSpacePlanName; - } - - public String getInfraUuid() { - return infraUuid; - } - - public void setInfraUuid(String infraUuid) { - this.infraUuid = infraUuid; - } - - public Duration getResyncInterval() { - return resyncInterval; - } - - public void setResyncInterval(Duration resyncInterval) { - this.resyncInterval = resyncInterval; - } - - public Duration getRecheckInterval() { - return recheckInterval; - } - - public void setRecheckInterval(Duration recheckInterval) { - this.recheckInterval = recheckInterval; - } - - public String getVersion() { - return version; - } - - public void setVersion(String version) { - this.version = version; - } - - public boolean isEnableEventLogger() { - return enableEventLogger; - } - - public void setEnableEventLogger(boolean enableEventLogger) { - this.enableEventLogger = enableEventLogger; - } - - public String getAuthenticationServiceHost() { - return authenticationServiceHost; - } - - public void setAuthenticationServiceHost(String authenticationServiceHost) { - this.authenticationServiceHost = authenticationServiceHost; - } - - public String getAuthenticationServicePort() { - return authenticationServicePort; - } - - public void setAuthenticationServicePort(String authenticationServicePort) { - this.authenticationServicePort = authenticationServicePort; - } - - public String getAuthenticationServiceCaSecret() { - return authenticationServiceCaSecret; - } - - public void setAuthenticationServiceCaSecret(String authenticationServiceCaSecret) { - this.authenticationServiceCaSecret = authenticationServiceCaSecret; - } - - public String getAuthenticationServiceClientSecret() { - return authenticationServiceClientSecret; - } - - public void setAuthenticationServiceClientSecret(String authenticationServiceClientSecret) { - this.authenticationServiceClientSecret = authenticationServiceClientSecret; - } - - public String getAuthenticationServiceSaslInitHost() { - return authenticationServiceSaslInitHost; - } - - public void setAuthenticationServiceSaslInitHost(String authenticationServiceSaslInitHost) { - this.authenticationServiceSaslInitHost = authenticationServiceSaslInitHost; - } - - public static StandardControllerOptions fromEnv(Map env) { - - StandardControllerOptions options = new StandardControllerOptions(); - - options.setAuthenticationServiceHost(env.get("AUTHENTICATION_SERVICE_HOST")); - options.setAuthenticationServicePort(env.get("AUTHENTICATION_SERVICE_PORT")); - options.setAuthenticationServiceCaSecret(env.get("AUTHENTICATION_SERVICE_CA_SECRET")); - options.setAuthenticationServiceClientSecret(env.get("AUTHENTICATION_SERVICE_CLIENT_SECRET")); - options.setAuthenticationServiceSaslInitHost(env.get("AUTHENTICATION_SERVICE_SASL_INIT_HOST")); - - File templateDir = new File(getEnvOrThrow(env, "TEMPLATE_DIR")); - if (!templateDir.exists()) { - throw new IllegalArgumentException("Template directory " + templateDir.getAbsolutePath() + " not found"); - } - options.setTemplateDir(templateDir); - - options.setCertDir(getEnvOrThrow(env, "CERT_DIR")); - options.setAddressSpace(getEnvOrThrow(env, "ADDRESS_SPACE")); - options.setAddressSpaceNamespace(getEnvOrThrow(env, "ADDRESS_SPACE_NAMESPACE")); - options.setAddressSpacePlanName(getEnvOrThrow(env, "ADDRESS_SPACE_PLAN")); - options.setInfraUuid(getEnvOrThrow(env, "INFRA_UUID")); - - options.setResyncInterval(getEnv(env, "RESYNC_INTERVAL") - .map(i -> Duration.ofSeconds(Long.parseLong(i))) - .orElse(Duration.ofMinutes(5))); - - options.setRecheckInterval(getEnv(env, "CHECK_INTERVAL") - .map(i -> Duration.ofSeconds(Long.parseLong(i))) - .orElse(Duration.ofSeconds(30))); - - options.setStatusCheckInterval(getEnv(env, "STATUS_CHECK_INTERVAL") - .map(i -> Duration.ofSeconds(Long.parseLong(i))) - .orElse(Duration.ofSeconds(30))); - - options.setVersion(getEnvOrThrow(env, "VERSION")); - options.setEnableEventLogger(getEnv(env, "ENABLE_EVENT_LOGGER").map(Boolean::parseBoolean).orElse(false)); - - options.setManagementQueryTimeout(getEnv(env, "MANAGEMENT_QUERY_TIMEOUT") - .map(i -> Duration.ofSeconds(Long.parseLong(i))) - .orElse(Duration.ofSeconds(60))); - - options.setManagementConnectTimeout(getEnv(env, "MANAGEMENT_CONNECT_TIMEOUT") - .map(i -> Duration.ofSeconds(Long.parseLong(i))) - .orElse(Duration.ofSeconds(30))); - - options.setKubernetesApiConnectTimeout(getEnv(env, "KUBERNETES_API_CONNECT_TIMEOUT") - .map(i -> Duration.ofSeconds(Long.parseLong(i))) - .orElse(Duration.ofSeconds(30))); - - options.setKubernetesApiReadTimeout(getEnv(env, "KUBERNETES_API_READ_TIMEOUT") - .map(i -> Duration.ofSeconds(Long.parseLong(i))) - .orElse(Duration.ofSeconds(30))); - - options.setKubernetesApiWriteTimeout(getEnv(env, "KUBERNETES_API_WRITE_TIMEOUT") - .map(i -> Duration.ofSeconds(Long.parseLong(i))) - .orElse(Duration.ofSeconds(30))); - - options.setHealthProbeTimeout(getEnv(env, "HEALTH_PROBE_TIMEOUT") - .map(i -> Duration.ofSeconds(Long.parseLong(i))) - .orElse(Duration.ofSeconds(10))); - - options.setHealthCheckInterval(getEnv(env, "HEALTH_CHECK_INTERVAL") - .map(i -> Duration.ofSeconds(Long.parseLong(i))) - .orElse(Duration.ofSeconds(30))); - - return options; - } - - private static String getEnvOrThrow(Map env, String envVar) { - String var = env.get(envVar); - if (var == null) { - throw new IllegalArgumentException(String.format("Unable to find value for required environment var '%s'", envVar)); - } - return var; - } - - private static Optional getEnv(Map env, String envVar) { - return Optional.ofNullable(env.get(envVar)); - } - - public File getTemplateDir() { - return templateDir; - } - - public void setTemplateDir(File templateDir) { - this.templateDir = templateDir; - } - - public String getAddressSpaceNamespace() { - return addressSpaceNamespace; - } - - @Override - public String toString() { - return "StandardControllerOptions{" + - "templateDir=" + templateDir + - ", certDir='" + certDir + '\'' + - ", addressSpace='" + addressSpace + '\'' + - ", addressSpaceNamespace='" + addressSpaceNamespace + '\'' + - ", addressSpacePlanName='" + addressSpacePlanName + '\'' + - ", infraUuid='" + infraUuid + '\'' + - ", resyncInterval=" + resyncInterval + - ", recheckInterval=" + recheckInterval + - ", version='" + version + '\'' + - ", enableEventLogger=" + enableEventLogger + - ", authenticationServiceHost='" + authenticationServiceHost + '\'' + - ", authenticationServicePort='" + authenticationServicePort + '\'' + - ", authenticationServiceCaSecret='" + authenticationServiceCaSecret + '\'' + - ", authenticationServiceClientSecret='" + authenticationServiceClientSecret + '\'' + - ", authenticationServiceSaslInitHost='" + authenticationServiceSaslInitHost + '\'' + - ", managementQueryTimeout='" + managementQueryTimeout + '\'' + - ", managementConnectTimeout='" + managementConnectTimeout + '\'' + - ", kubernetesApiConnectTimeout='" + kubernetesApiConnectTimeout + '\'' + - ", kubernetesApiReadTimeout='" + kubernetesApiReadTimeout + '\'' + - ", kubernetesApiWriteTimeout='" + kubernetesApiWriteTimeout + '\'' + - ", healthProbeTimeout='" + healthProbeTimeout + '\'' + - ", healthCheckInterval='" + healthCheckInterval + '\'' + - '}'; - } - - public void setAddressSpaceNamespace(String addressSpaceNamespace) { - this.addressSpaceNamespace = addressSpaceNamespace; - } - - public Duration getManagementQueryTimeout() { - return managementQueryTimeout; - } - - public void setManagementQueryTimeout(Duration managementQueryTimeout) { - this.managementQueryTimeout = managementQueryTimeout; - } - - public Duration getManagementConnectTimeout() { - return managementConnectTimeout; - } - - public void setManagementConnectTimeout(Duration managementConnectTimeout) { - this.managementConnectTimeout = managementConnectTimeout; - } - - public Duration getStatusCheckInterval() { - return statusCheckInterval; - } - - public void setStatusCheckInterval(Duration statusCheckInterval) { - this.statusCheckInterval = statusCheckInterval; - } - - public Duration getKubernetesApiConnectTimeout() { - return kubernetesApiConnectTimeout; - } - - public void setKubernetesApiConnectTimeout(Duration kubernetesApiConnectTimeout) { - this.kubernetesApiConnectTimeout = kubernetesApiConnectTimeout; - } - - public Duration getKubernetesApiReadTimeout() { - return kubernetesApiReadTimeout; - } - - public void setKubernetesApiReadTimeout(Duration kubernetesApiReadTimeout) { - this.kubernetesApiReadTimeout = kubernetesApiReadTimeout; - } - - public Duration getKubernetesApiWriteTimeout() { - return kubernetesApiWriteTimeout; - } - - public void setKubernetesApiWriteTimeout(Duration kubernetesApiWriteTimeout) { - this.kubernetesApiWriteTimeout = kubernetesApiWriteTimeout; - } - - public Duration getHealthProbeTimeout() { - return healthProbeTimeout; - } - - public void setHealthProbeTimeout(Duration healthProbeTimeout) { - this.healthProbeTimeout = healthProbeTimeout; - } - - public Duration getHealthCheckInterval() { - return healthCheckInterval; - } - - public void setHealthCheckInterval(Duration healthCheckInterval) { - this.healthCheckInterval = healthCheckInterval; - } -} diff --git a/standard-controller/src/main/java/io/enmasse/controller/standard/SubserveStatusCollector.java b/standard-controller/src/main/java/io/enmasse/controller/standard/SubserveStatusCollector.java deleted file mode 100644 index 9478692bb33..00000000000 --- a/standard-controller/src/main/java/io/enmasse/controller/standard/SubserveStatusCollector.java +++ /dev/null @@ -1,112 +0,0 @@ -/* - * Copyright 2019, EnMasse authors. - * License: Apache License 2.0 (see the file LICENSE or http://apache.org/licenses/LICENSE-2.0.html). - */ -package io.enmasse.controller.standard; - -import java.io.File; -import java.util.Collections; -import java.util.HashSet; -import java.util.List; -import java.util.Set; -import java.util.concurrent.CompletableFuture; -import java.util.concurrent.TimeUnit; - -import org.apache.qpid.proton.Proton; -import org.apache.qpid.proton.amqp.messaging.AmqpValue; -import org.apache.qpid.proton.message.Message; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import io.enmasse.address.model.Address; -import io.enmasse.admin.model.AddressPlan; -import io.enmasse.amqp.ProtonRequestClient; -import io.enmasse.amqp.SyncRequestClient; -import io.fabric8.kubernetes.api.model.Container; -import io.fabric8.kubernetes.api.model.ContainerPort; -import io.fabric8.kubernetes.api.model.Pod; -import io.vertx.core.Vertx; -import io.vertx.core.net.PemKeyCertOptions; -import io.vertx.core.net.PemTrustOptions; -import io.vertx.proton.ProtonClientOptions; - -public class SubserveStatusCollector { - private static final Logger log = LoggerFactory.getLogger(RouterStatusCollector.class); - private final Vertx vertx; - private final String certDir; - - SubserveStatusCollector(Vertx vertx, String certDir) { - this.vertx = vertx; - this.certDir = certDir; - } - - public Set collect(Pod router) throws Exception { - int port = 0; - for (Container container : router.getSpec().getContainers()) { - if (container.getName().equals("router")) { - for (ContainerPort containerPort : container.getPorts()) { - if (containerPort.getName().equals("amqps-normal")) { - port = containerPort.getContainerPort(); - } - } - } - } - - if (port != 0) { - log.debug("Checking status of subserv via router : {}" + router.getStatus().getPodIP()); - ProtonClientOptions clientOptions = new ProtonClientOptions() - .setSsl(true) - .addEnabledSaslMechanism("EXTERNAL") - .setHostnameVerificationAlgorithm("") - .setPemTrustOptions(new PemTrustOptions() - .addCertPath(new File(certDir, "ca.crt").getAbsolutePath())) - .setPemKeyCertOptions(new PemKeyCertOptions() - .setCertPath(new File(certDir, "tls.crt").getAbsolutePath()) - .setKeyPath(new File(certDir, "tls.key").getAbsolutePath())); - - try (ProtonRequestClient client = new ProtonRequestClient(vertx, "standard-controller")) { - CompletableFuture promise = new CompletableFuture<>(); - client.connect(router.getStatus().getPodIP(), port, clientOptions, "$subctrl", promise); - - promise.get(10, TimeUnit.SECONDS); - - Set topics = collectRegisteredTopics(client); - log.debug("Subserv registered topics : {}", topics); - return topics; - } - } else { - log.info("Unable to find appropriate router port, skipping subserv check"); - return Collections.emptySet(); - } - } - - @SuppressWarnings("unchecked") - private Set collectRegisteredTopics(SyncRequestClient client) throws Exception { - Message message = Proton.message(); - message.setSubject("list_topics"); - - Message response = client.request(message, 10, TimeUnit.SECONDS); - AmqpValue value = (AmqpValue) response.getBody(); - - if (value == null || value.getValue() == null) { - throw new IllegalArgumentException("Unexpected null response body"); - } else if (!(value.getValue() instanceof List)) { - throw new IllegalArgumentException(String.format("Unexpected response type : %s", value.getValue().getClass())); - } - return new HashSet<>(((List) value.getValue())); - } - - static void checkTopicRegistration(Set subserveTopics, Address address, AddressPlan addressPlan) { - if (addressPlan.getResources() != null && addressPlan.getResources().containsKey("broker")) { - Double credit = addressPlan.getResources().get("broker"); - if (credit != null && credit >= 1) { - String name = address.getSpec().getAddress(); - if (!subserveTopics.contains(name)) { - address.getStatus().setReady(false).appendMessage(String.format("Address %s is not registered with subserv", name)); - } - - } - } - } - -} diff --git a/standard-controller/src/main/java/io/enmasse/controller/standard/TemplateBrokerSetGenerator.java b/standard-controller/src/main/java/io/enmasse/controller/standard/TemplateBrokerSetGenerator.java deleted file mode 100644 index 67f035e9874..00000000000 --- a/standard-controller/src/main/java/io/enmasse/controller/standard/TemplateBrokerSetGenerator.java +++ /dev/null @@ -1,194 +0,0 @@ -/* - * Copyright 2016-2018, EnMasse authors. - * License: Apache License 2.0 (see the file LICENSE or http://apache.org/licenses/LICENSE-2.0.html). - */ - -package io.enmasse.controller.standard; - -import static io.enmasse.address.model.KubeUtil.applyCpuMemory; -import static io.enmasse.address.model.KubeUtil.applyPodTemplate; -import static io.enmasse.address.model.KubeUtil.overrideFsGroup; -import static io.enmasse.config.Apps.setPartOf; - -import java.util.LinkedHashMap; -import java.util.Map; -import java.util.Optional; -import java.util.concurrent.TimeUnit; - -import com.fasterxml.jackson.databind.ObjectMapper; - -import io.enmasse.address.model.Address; -import io.enmasse.admin.model.AddressPlan; -import io.enmasse.admin.model.v1.StandardInfraConfig; -import io.enmasse.config.AnnotationKeys; -import io.fabric8.kubernetes.api.model.HasMetadata; -import io.fabric8.kubernetes.api.model.KubernetesList; -import io.fabric8.kubernetes.api.model.KubernetesListBuilder; -import io.fabric8.kubernetes.api.model.PersistentVolumeClaim; -import io.fabric8.kubernetes.api.model.PodTemplateSpec; -import io.fabric8.kubernetes.api.model.apps.Deployment; -import io.fabric8.kubernetes.api.model.apps.StatefulSet; - -/** - * Generates sets of brokers using Openshift templates. - */ -public class TemplateBrokerSetGenerator implements BrokerSetGenerator { - private static final ObjectMapper mapper = new ObjectMapper(); - private final Kubernetes kubernetes; - private final StandardControllerOptions options; - private final Map env; - - public TemplateBrokerSetGenerator(Kubernetes kubernetes, StandardControllerOptions options, Map env) { - this.kubernetes = kubernetes; - this.options = options; - this.env = env; - } - - private boolean isShardedTopic(AddressPlan addressPlan) { - if (addressPlan.getAddressType().equals("topic")) { - boolean isSharded = true; - for (Map.Entry resourceRequest : addressPlan.getResources().entrySet()) { - if (resourceRequest.getKey().equals("broker") && resourceRequest.getValue() < 1) { - isSharded = false; - break; - } - } - return isSharded; - } - return false; - } - - private String getTemplateName(Address address, AddressPlan addressPlan, StandardInfraConfig standardInfraConfig) { - if (address == null || addressPlan == null) { - return getAnnotation(standardInfraConfig.getMetadata().getAnnotations(), AnnotationKeys.QUEUE_TEMPLATE_NAME, "queue-persisted"); - } else { - if (isShardedTopic(addressPlan)) { - return getAnnotation(standardInfraConfig.getMetadata().getAnnotations(), AnnotationKeys.TOPIC_TEMPLATE_NAME, "topic-persisted"); - } else { - return getAnnotation(standardInfraConfig.getMetadata().getAnnotations(), AnnotationKeys.QUEUE_TEMPLATE_NAME, "queue-persisted"); - } - } - } - - private String getAnnotation(Map annotations, String key, String defaultValue) { - return Optional.ofNullable(annotations) - .flatMap(m -> Optional.ofNullable(m.get(key))) - .orElse(defaultValue); - } - - /** - * Generate cluster for a given destination group. - * - * NOTE: This method assumes that all destinations within a group share the same properties. - * - */ - @Override - public BrokerCluster generateCluster(String clusterId, int numReplicas, Address address, AddressPlan addressPlan, StandardInfraConfig standardInfraConfig) throws Exception { - - KubernetesListBuilder resourcesBuilder = new KubernetesListBuilder(); - - if (standardInfraConfig != null) { - String templateName = getTemplateName(address, addressPlan, standardInfraConfig); - KubernetesList newResources = processTemplate(clusterId, numReplicas, address, templateName, standardInfraConfig); - resourcesBuilder.addAllToItems(newResources.getItems()); - } - return new BrokerCluster(clusterId, resourcesBuilder.build()); - } - - private KubernetesList processTemplate(String clusterId, int numReplicas, Address address, String templateName, StandardInfraConfig standardInfraConfig) throws Exception { - Map paramMap = new LinkedHashMap<>(); - - paramMap.put(TemplateParameter.NAME, clusterId); - paramMap.put(TemplateParameter.INFRA_UUID, options.getInfraUuid()); - paramMap.put(TemplateParameter.CLUSTER_ID, clusterId); - paramMap.put(TemplateParameter.ADDRESS_SPACE, options.getAddressSpace()); - paramMap.put(TemplateParameter.AUTHENTICATION_SERVICE_HOST, options.getAuthenticationServiceHost()); - paramMap.put(TemplateParameter.AUTHENTICATION_SERVICE_PORT, options.getAuthenticationServicePort()); - paramMap.put(TemplateParameter.AUTHENTICATION_SERVICE_CA_SECRET, options.getAuthenticationServiceCaSecret()); - paramMap.put(TemplateParameter.AUTHENTICATION_SERVICE_CLIENT_SECRET, options.getAuthenticationServiceClientSecret()); - paramMap.put(TemplateParameter.AUTHENTICATION_SERVICE_SASL_INIT_HOST, options.getAuthenticationServiceSaslInitHost()); - setIfEnvPresent(paramMap, TemplateParameter.BROKER_IMAGE); - setIfEnvPresent(paramMap, TemplateParameter.BROKER_PLUGIN_IMAGE); - setIfEnvPresent(paramMap, TemplateParameter.TOPIC_FORWARDER_IMAGE); - setIfEnvPresent(paramMap, TemplateParameter.IMAGE_PULL_POLICY); - - if (address != null) { - paramMap.put(TemplateParameter.ADDRESS, address.getSpec().getAddress()); - } - - if (standardInfraConfig.getSpec().getBroker() != null) { - if (standardInfraConfig.getSpec().getBroker().getResources() != null) { - if (standardInfraConfig.getSpec().getBroker().getResources().getStorage() != null) { - paramMap.put(TemplateParameter.BROKER_STORAGE_CAPACITY, standardInfraConfig.getSpec().getBroker().getResources().getStorage()); - } - } - - if (standardInfraConfig.getSpec().getBroker().getAddressFullPolicy() != null) { - paramMap.put(TemplateParameter.BROKER_ADDRESS_FULL_POLICY, standardInfraConfig.getSpec().getBroker().getAddressFullPolicy()); - } - - if (standardInfraConfig.getSpec().getBroker().getGlobalMaxSize() != null) { - paramMap.put(TemplateParameter.BROKER_GLOBAL_MAX_SIZE, standardInfraConfig.getSpec().getBroker().getGlobalMaxSize()); - } - - if (standardInfraConfig.getSpec().getBroker().getConnectorIdleTimeout() != null) { - paramMap.put(TemplateParameter.BROKER_CONNECTOR_IDLE_TIMEOUT_MS, String.valueOf(TimeUnit.SECONDS.toMillis(standardInfraConfig.getSpec().getBroker().getConnectorIdleTimeout()))); - } - - if (standardInfraConfig.getSpec().getBroker().getConnectorWorkerThreads() != null) { - paramMap.put(TemplateParameter.BROKER_CONNECTOR_NETTY_THREADS, String.valueOf(standardInfraConfig.getSpec().getBroker().getConnectorWorkerThreads())); - } - - if (standardInfraConfig.getSpec().getBroker().getJavaOpts() != null) { - paramMap.put(TemplateParameter.BROKER_JAVA_OPTS, standardInfraConfig.getSpec().getBroker().getJavaOpts()); - } - - } - - KubernetesList items = kubernetes.processTemplate(templateName, paramMap); - - for (HasMetadata item : items.getItems()) { - if (item instanceof StatefulSet) { - StatefulSet set = (StatefulSet) item; - set.getSpec().setReplicas(numReplicas); - if (standardInfraConfig.getSpec().getBroker() != null && standardInfraConfig.getSpec().getBroker().getStorageClassName() != null) { - for (PersistentVolumeClaim persistentVolumeClaim : set.getSpec().getVolumeClaimTemplates()) { - persistentVolumeClaim.getSpec().setStorageClassName(standardInfraConfig.getSpec().getBroker().getStorageClassName()); - } - } - Kubernetes.addObjectAnnotation(item, AnnotationKeys.APPLIED_INFRA_CONFIG, mapper.writeValueAsString(standardInfraConfig)); - setPartOf(set, options.getInfraUuid()); - - if (standardInfraConfig.getSpec().getBroker() != null && standardInfraConfig.getSpec().getBroker().getResources() != null) { - applyCpuMemory(set.getSpec().getTemplate(), standardInfraConfig.getSpec().getBroker().getResources().getCpu(), standardInfraConfig.getSpec().getBroker().getResources().getMemory()); - } - - if (standardInfraConfig.getSpec().getBroker() != null && standardInfraConfig.getSpec().getBroker().getPodTemplate() != null) { - PodTemplateSpec podTemplate = standardInfraConfig.getSpec().getBroker().getPodTemplate(); - PodTemplateSpec actualPodTemplate = set.getSpec().getTemplate(); - applyPodTemplate(actualPodTemplate, podTemplate); - } - - overrideFsGroup(set.getSpec().getTemplate(), "broker", kubernetes.getNamespace()); - } else if (item instanceof Deployment) { - Deployment deployment = (Deployment) item; - deployment.getSpec().setReplicas(numReplicas); - setPartOf(deployment, options.getInfraUuid()); - } - } - - // These are attributes that we need to identify components belonging to this address - Kubernetes.addObjectAnnotation(items, AnnotationKeys.CLUSTER_ID, clusterId); - Kubernetes.addObjectAnnotation(items, AnnotationKeys.ADDRESS_SPACE, options.getAddressSpace()); - if (address != null) { - Kubernetes.addObjectAnnotation(items, AnnotationKeys.ADDRESS, address.getSpec().getAddress()); - } - return items; - } - - private void setIfEnvPresent(Map parameters, String key) { - if (env.get(key) != null) { - parameters.put(key, env.get(key)); - } - } -} diff --git a/standard-controller/src/main/java/io/enmasse/controller/standard/TemplateParameter.java b/standard-controller/src/main/java/io/enmasse/controller/standard/TemplateParameter.java deleted file mode 100644 index 1236522647b..00000000000 --- a/standard-controller/src/main/java/io/enmasse/controller/standard/TemplateParameter.java +++ /dev/null @@ -1,37 +0,0 @@ -/* - * Copyright 2017-2018, EnMasse authors. - * License: Apache License 2.0 (see the file LICENSE or http://apache.org/licenses/LICENSE-2.0.html). - */ - -package io.enmasse.controller.standard; - -/** - * Template parameters that are dynamically set by the standard controller. - */ -public interface TemplateParameter { - String NAME = "NAME"; - String ADDRESS = "ADDRESS"; - String INFRA_UUID = "INFRA_UUID"; - String ADDRESS_SPACE = "ADDRESS_SPACE"; - String CLUSTER_ID = "CLUSTER_ID"; - - String AUTHENTICATION_SERVICE_HOST = "AUTHENTICATION_SERVICE_HOST"; - String AUTHENTICATION_SERVICE_PORT = "AUTHENTICATION_SERVICE_PORT"; - String AUTHENTICATION_SERVICE_CA_SECRET = "AUTHENTICATION_SERVICE_CA_SECRET"; - String AUTHENTICATION_SERVICE_CLIENT_SECRET = "AUTHENTICATION_SERVICE_CLIENT_SECRET"; - String AUTHENTICATION_SERVICE_SASL_INIT_HOST = "AUTHENTICATION_SERVICE_SASL_INIT_HOST"; - String REPLICAS = "REPLICAS"; - - String BROKER_MEMORY_LIMIT = "BROKER_MEMORY_LIMIT"; - String BROKER_ADDRESS_FULL_POLICY = "BROKER_ADDRESS_FULL_POLICY"; - String BROKER_STORAGE_CAPACITY = "BROKER_STORAGE_CAPACITY"; - String BROKER_GLOBAL_MAX_SIZE = "BROKER_GLOBAL_MAX_SIZE"; - String BROKER_CONNECTOR_NETTY_THREADS = "BROKER_CONNECTOR_NETTY_THREADS"; - String BROKER_CONNECTOR_IDLE_TIMEOUT_MS = "BROKER_CONNECTOR_IDLE_TIMEOUT_MS"; - String BROKER_JAVA_OPTS = "BROKER_JAVA_OPTS"; - - String IMAGE_PULL_POLICY = "IMAGE_PULL_POLICY"; - String BROKER_IMAGE = "BROKER_IMAGE"; - String BROKER_PLUGIN_IMAGE = "BROKER_PLUGIN_IMAGE"; - String TOPIC_FORWARDER_IMAGE = "TOPIC_FORWARDER_IMAGE"; -} diff --git a/standard-controller/src/main/java/io/enmasse/controller/standard/UsageInfo.java b/standard-controller/src/main/java/io/enmasse/controller/standard/UsageInfo.java deleted file mode 100644 index 07884812f58..00000000000 --- a/standard-controller/src/main/java/io/enmasse/controller/standard/UsageInfo.java +++ /dev/null @@ -1,59 +0,0 @@ -/* - * Copyright 2018, EnMasse authors. - * License: Apache License 2.0 (see the file LICENSE or http://apache.org/licenses/LICENSE-2.0.html). - */ -package io.enmasse.controller.standard; - -import java.util.Objects; - -class UsageInfo { - private double used; - private int needed; - - public UsageInfo() { - this.used = 0; - this.needed = 0; - } - - public UsageInfo(UsageInfo copy) { - this.used = copy.used; - this.needed = copy.needed; - } - - public void addUsed(double added) { - this.used += added; - needed = (int) Math.ceil(used); - } - - public void subUsed(double sub) { - this.used = Math.max(0, used - sub); - needed = (int) Math.ceil(used); - } - - @Override - public boolean equals(Object o) { - if (this == o) return true; - if (o == null || getClass() != o.getClass()) return false; - UsageInfo usageInfo = (UsageInfo) o; - return Double.compare(usageInfo.used, used) == 0 && - needed == usageInfo.needed; - } - - @Override - public int hashCode() { - return Objects.hash(used, needed); - } - - @Override - public String toString() { - return "{used=" + used + ", needed=" + needed + "}"; - } - - public double getUsed() { - return used; - } - - public int getNeeded() { - return needed; - } -} diff --git a/standard-controller/src/main/resources/logback.xml b/standard-controller/src/main/resources/logback.xml deleted file mode 100644 index 9ed3476bbee..00000000000 --- a/standard-controller/src/main/resources/logback.xml +++ /dev/null @@ -1,15 +0,0 @@ - - - - - - - %d{yyyy-MM-dd'T'HH:mm:ss.SSS'Z',GMT} %-5p [%c{0}] %m%n - - - - - - - - diff --git a/standard-controller/src/main/resources/templates/queue-persisted.yaml b/standard-controller/src/main/resources/templates/queue-persisted.yaml deleted file mode 100644 index 0a99b7b5c63..00000000000 --- a/standard-controller/src/main/resources/templates/queue-persisted.yaml +++ /dev/null @@ -1,310 +0,0 @@ -apiVersion: v1 -kind: Template -metadata: - labels: - app: enmasse - name: queue-persisted -objects: -- apiVersion: v1 - kind: Service - metadata: - annotations: - addressSpace: ${ADDRESS_SPACE} - cluster_id: ${CLUSTER_ID} - labels: - monitoring-key: enmasse-tenants - app: enmasse - component: broker - infraType: standard - infraUuid: ${INFRA_UUID} - name: ${NAME} - spec: - ports: - - name: health-tls - port: 8161 - protocol: TCP - targetPort: jolokia - clusterIP: None - selector: - name: ${NAME} - role: broker - infraType: standard - infraUuid: ${INFRA_UUID} -- apiVersion: apps/v1 - kind: StatefulSet - metadata: - annotations: - address: ${ADDRESS} - addressSpace: ${ADDRESS_SPACE} - cluster_id: ${CLUSTER_ID} - enmasse.io/cert-cn: broker.${INFRA_UUID} - enmasse.io/cert-secret: broker-internal-cert.${INFRA_UUID} - labels: - app: enmasse - infraType: standard - infraUuid: ${INFRA_UUID} - name: ${NAME} - spec: - replicas: 1 - serviceName: ${NAME} - selector: - matchLabels: - addresstype: queue - app: enmasse - name: ${NAME} - role: broker - infraType: standard - infraUuid: ${INFRA_UUID} - template: - metadata: - annotations: - address: ${ADDRESS} - addressSpace: ${ADDRESS_SPACE} - cluster_id: ${CLUSTER_ID} - labels: - addresstype: queue - app: enmasse - name: ${NAME} - role: broker - infraType: standard - infraUuid: ${INFRA_UUID} - spec: - affinity: - podAntiAffinity: - preferredDuringSchedulingIgnoredDuringExecution: - - weight: 1 - podAffinityTerm: - labelSelector: - matchLabels: - app: enmasse - role: broker - infraType: standard - infraUuid: ${INFRA_UUID} - topologyKey: kubernetes.io/hostname - nodeAffinity: - preferredDuringSchedulingIgnoredDuringExecution: - - weight: 1 - preference: - matchExpressions: - - key: node-role.enmasse.io/messaging-infra - operator: In - values: - - "true" - containers: - - env: - - name: MESSAGING_SERVICE_HOST - value: messaging-${INFRA_UUID} - - name: MESSAGING_SERVICE_PORT_AMQPS_BROKER - value: 56671 - - name: MESSAGING_SERVICE_PORT_AMQPS_NORMAL - value: 55671 - - name: INFRA_UUID - value: ${INFRA_UUID} - - name: QUEUE_NAME - value: ${ADDRESS} - - name: CLUSTER_ID - value: ${CLUSTER_ID} - - name: CERT_DIR - value: /etc/enmasse-certs - - name: CONNECTOR_IN_NETTY_THREADS - value: ${BROKER_CONNECTOR_NETTY_THREADS} - - name: CONNECTOR_OUT_NETTY_THREADS - value: ${BROKER_CONNECTOR_NETTY_THREADS} - - name: CONNECTOR_IN_IDLE_TIMEOUT_MS - value: ${BROKER_CONNECTOR_IDLE_TIMEOUT_MS} - - name: CONNECTOR_OUT_IDLE_TIMEOUT_MS - value: ${BROKER_CONNECTOR_IDLE_TIMEOUT_MS} - - name: ADDRESS_FULL_POLICY - value: ${BROKER_ADDRESS_FULL_POLICY} - - name: JAVA_OPTS - value: ${BROKER_JAVA_OPTS} - - name: AUTHENTICATION_SERVICE_HOST - value: ${AUTHENTICATION_SERVICE_HOST} - - name: AUTHENTICATION_SERVICE_PORT - value: ${AUTHENTICATION_SERVICE_PORT} - - name: AUTHENTICATION_SERVICE_CLIENT_SECRET - value: ${AUTHENTICATION_SERVICE_CLIENT_SECRET} - - name: AUTHENTICATION_SERVICE_SASL_INIT_HOST - value: ${AUTHENTICATION_SERVICE_SASL_INIT_HOST} - - name: AMQ_NAME - value: serverData - - name: HOME - value: /var/run/artemis/split-1/ - image: ${BROKER_IMAGE} - imagePullPolicy: ${IMAGE_PULL_POLICY} - command: - - /opt/apache-artemis/custom/bin/launch-broker.sh - livenessProbe: - exec: - command: - - sh - - -c - - $ARTEMIS_HOME/custom/bin/probe.sh - initialDelaySeconds: 300 - name: broker - ports: - - containerPort: 5673 - name: amqp - - containerPort: 8161 - name: jolokia - readinessProbe: - exec: - command: - - sh - - -c - - $ARTEMIS_HOME/custom/bin/probe.sh - initialDelaySeconds: 10 - volumeMounts: - - mountPath: /var/run/artemis - name: data - - mountPath: /opt/apache-artemis/custom - name: broker-custom - readOnly: false - - mountPath: /opt/apache-artemis/support - name: broker-support - readOnly: true - initContainers: - - env: - - name: MESSAGING_SERVICE_HOST - value: messaging-${INFRA_UUID} - - name: MESSAGING_SERVICE_PORT_AMQPS_BROKER - value: 56671 - - name: MESSAGING_SERVICE_PORT_AMQPS_NORMAL - value: 55671 - - name: QUEUE_SCHEDULER_SERVICE_HOST - value: queue-scheduler-${INFRA_UUID} - - name: QUEUE_SCHEDULER_SERVICE_PORT - value: 5672 - - name: INFRA_UUID - value: ${INFRA_UUID} - - name: QUEUE_NAME - value: ${ADDRESS} - - name: CLUSTER_ID - value: ${CLUSTER_ID} - - name: CONNECTOR_IN_NETTY_THREADS - value: ${BROKER_CONNECTOR_NETTY_THREADS} - - name: CONNECTOR_OUT_NETTY_THREADS - value: ${BROKER_CONNECTOR_NETTY_THREADS} - - name: CONNECTOR_IN_IDLE_TIMEOUT_MS - value: ${BROKER_CONNECTOR_IDLE_TIMEOUT_MS} - - name: CONNECTOR_OUT_IDLE_TIMEOUT_MS - value: ${BROKER_CONNECTOR_IDLE_TIMEOUT_MS} - - name: GLOBAL_MAX_SIZE - value: ${BROKER_GLOBAL_MAX_SIZE} - - name: ADDRESS_FULL_POLICY - value: ${BROKER_ADDRESS_FULL_POLICY} - - name: JAVA_OPTS - value: ${BROKER_JAVA_OPTS} - - name: AUTHENTICATION_SERVICE_HOST - value: ${AUTHENTICATION_SERVICE_HOST} - - name: AUTHENTICATION_SERVICE_PORT - value: ${AUTHENTICATION_SERVICE_PORT} - - name: AUTHENTICATION_SERVICE_CLIENT_SECRET - value: ${AUTHENTICATION_SERVICE_CLIENT_SECRET} - - name: AUTHENTICATION_SERVICE_SASL_INIT_HOST - value: ${AUTHENTICATION_SERVICE_SASL_INIT_HOST} - - name: AMQ_NAME - value: serverData - - name: HOME - value: /var/run/artemis/split-1/ - image: ${BROKER_PLUGIN_IMAGE} - imagePullPolicy: ${IMAGE_PULL_POLICY} - name: broker-plugin - volumeMounts: - - mountPath: /etc/enmasse-certs - name: broker-internal-cert - readOnly: true - - mountPath: /etc/authservice-ca - name: authservice-ca - readOnly: true - - mountPath: /opt/apache-artemis/custom - name: broker-custom - readOnly: false - volumes: - - name: broker-custom - emptyDir: {} - - name: authservice-ca - secret: - secretName: authservice-ca.${INFRA_UUID} - - name: broker-internal-cert - secret: - secretName: broker-internal-cert.${INFRA_UUID} - - name: broker-support - secret: - secretName: broker-support-${INFRA_UUID} - volumeClaimTemplates: - - apiVersion: v1 - kind: PersistentVolumeClaim - metadata: - annotations: - addressSpace: ${ADDRESS_SPACE} - cluster_id: ${CLUSTER_ID} - labels: - app: enmasse - infraType: standard - infraUuid: ${INFRA_UUID} - name: data - spec: - accessModes: - - ReadWriteOnce - resources: - requests: - storage: ${BROKER_STORAGE_CAPACITY} -parameters: -- name: INFRA_UUID - description: UUID to use for infrastructure - required: true -- description: Storage capacity required for volume claims - name: BROKER_STORAGE_CAPACITY - value: 2Gi -- description: A valid addressSpace name for the address Space - name: ADDRESS_SPACE - required: true -- description: A valid name for the deployment - name: NAME - required: true -- description: A valid group id for the deployment - name: CLUSTER_ID - required: true -- description: The address to use for the queue - name: ADDRESS - value: '' -- description: The hostname of the authentication service used by this address - space - name: AUTHENTICATION_SERVICE_HOST - required: true -- description: The port of the authentication service used by this address space - name: AUTHENTICATION_SERVICE_PORT - required: true -- description: The secret to use for validating authentication service cert - name: AUTHENTICATION_SERVICE_CA_SECRET - required: true -- description: The client cert to use as identity against authentication service - name: AUTHENTICATION_SERVICE_CLIENT_SECRET -- description: The hostname to use in sasl init - name: AUTHENTICATION_SERVICE_SASL_INIT_HOST -- description: Address full policy - name: BROKER_ADDRESS_FULL_POLICY - value: FAIL -- description: Broker Java Options - name: BROKER_JAVA_OPTS - value: "" -- description: Global max size for all addresses in broker. Cannot be larger than - 1/4 of JVM size - name: BROKER_GLOBAL_MAX_SIZE - value: "-1" -- name: BROKER_CONNECTOR_NETTY_THREADS - description: Number of threads to allocate to connectors - value: "4" -- name: BROKER_CONNECTOR_IDLE_TIMEOUT_MS - description: Idle timeout set for connectors - value: "16000" -- name: IMAGE_PULL_POLICY - description: Image Pull Policy - value: ${env.IMAGE_PULL_POLICY} -- name: BROKER_IMAGE - description: Broker Image - value: ${env.BROKER_IMAGE} -- name: BROKER_PLUGIN_IMAGE - description: Broker Plugin Image - value: ${env.BROKER_PLUGIN_IMAGE} diff --git a/standard-controller/src/main/resources/templates/topic-persisted.yaml b/standard-controller/src/main/resources/templates/topic-persisted.yaml deleted file mode 100644 index 11b655bcc92..00000000000 --- a/standard-controller/src/main/resources/templates/topic-persisted.yaml +++ /dev/null @@ -1,332 +0,0 @@ -apiVersion: v1 -kind: Template -metadata: - labels: - app: enmasse - name: topic-persisted -objects: -- apiVersion: v1 - kind: Service - metadata: - annotations: - addressSpace: ${ADDRESS_SPACE} - cluster_id: ${CLUSTER_ID} - labels: - monitoring-key: enmasse-tenants - app: enmasse - component: broker - infraType: standard - infraUuid: ${INFRA_UUID} - name: ${NAME} - spec: - ports: - - name: health-tls - port: 8161 - protocol: TCP - targetPort: jolokia - clusterIP: None - selector: - name: ${NAME} - role: broker - infraType: standard - infraUuid: ${INFRA_UUID} -- apiVersion: apps/v1 - kind: StatefulSet - metadata: - annotations: - address: ${ADDRESS} - addressSpace: ${ADDRESS_SPACE} - cluster_id: ${CLUSTER_ID} - enmasse.io/cert-cn: broker.${INFRA_UUID} - enmasse.io/cert-secret: broker-internal-cert.${INFRA_UUID} - labels: - app: enmasse - infraType: standard - infraUuid: ${INFRA_UUID} - name: ${NAME} - spec: - replicas: 1 - serviceName: ${NAME} - selector: - matchLabels: - addresstype: topic - app: enmasse - name: ${NAME} - role: broker - infraType: standard - infraUuid: ${INFRA_UUID} - template: - metadata: - annotations: - address: ${ADDRESS} - addressSpace: ${ADDRESS_SPACE} - cluster_id: ${CLUSTER_ID} - labels: - addresstype: topic - app: enmasse - name: ${NAME} - role: broker - infraType: standard - infraUuid: ${INFRA_UUID} - spec: - affinity: - nodeAffinity: - preferredDuringSchedulingIgnoredDuringExecution: - - weight: 1 - preference: - matchExpressions: - - key: node-role.enmasse.io/messaging-infra - operator: In - values: - - "true" - serviceAccountName: address-space-admin - containers: - - env: - - name: INFRA_UUID - value: ${INFRA_UUID} - - name: MESSAGING_SERVICE_HOST - value: messaging-${INFRA_UUID} - - name: MESSAGING_SERVICE_PORT_AMQPS_BROKER - value: 56671 - - name: MESSAGING_SERVICE_PORT_AMQPS_NORMAL - value: 55671 - - name: TOPIC_NAME - value: ${ADDRESS} - - name: CLUSTER_ID - value: ${CLUSTER_ID} - - name: CERT_DIR - value: /etc/enmasse-certs - - name: CONNECTOR_IN_NETTY_THREADS - value: ${BROKER_CONNECTOR_NETTY_THREADS} - - name: CONNECTOR_OUT_NETTY_THREADS - value: ${BROKER_CONNECTOR_NETTY_THREADS} - - name: CONNECTOR_IN_IDLE_TIMEOUT_MS - value: ${BROKER_CONNECTOR_IDLE_TIMEOUT_MS} - - name: CONNECTOR_OUT_IDLE_TIMEOUT_MS - value: ${BROKER_CONNECTOR_IDLE_TIMEOUT_MS} - - name: ADDRESS_FULL_POLICY - value: ${BROKER_ADDRESS_FULL_POLICY} - - name: JAVA_OPTS - value: ${BROKER_JAVA_OPTS} - - name: AUTHENTICATION_SERVICE_HOST - value: ${AUTHENTICATION_SERVICE_HOST} - - name: AUTHENTICATION_SERVICE_PORT - value: ${AUTHENTICATION_SERVICE_PORT} - - name: AUTHENTICATION_SERVICE_CLIENT_SECRET - value: ${AUTHENTICATION_SERVICE_CLIENT_SECRET} - - name: AUTHENTICATION_SERVICE_SASL_INIT_HOST - value: ${AUTHENTICATION_SERVICE_SASL_INIT_HOST} - - name: AMQ_NAME - value: serverData - - name: HOME - value: /var/run/artemis/split-1/ - image: ${BROKER_IMAGE} - imagePullPolicy: ${IMAGE_PULL_POLICY} - command: - - /opt/apache-artemis/custom/bin/launch-broker.sh - livenessProbe: - exec: - command: - - sh - - -c - - $ARTEMIS_HOME/custom/bin/probe.sh - initialDelaySeconds: 300 - name: broker - ports: - - containerPort: 5673 - name: amqp - - containerPort: 8161 - name: jolokia - readinessProbe: - exec: - command: - - sh - - -c - - $ARTEMIS_HOME/custom/bin/probe.sh - initialDelaySeconds: 10 - volumeMounts: - - mountPath: /var/run/artemis - name: data - - mountPath: /opt/apache-artemis/custom - name: broker-custom - readOnly: false - - mountPath: /opt/apache-artemis/support - name: broker-support - readOnly: true - - env: - - name: INFRA_UUID - value: ${INFRA_UUID} - - name: TOPIC_NAME - value: ${ADDRESS} - - name: CLUSTER_ID - value: ${NAME} - - name: CERT_DIR - value: /etc/enmasse-certs - image: ${TOPIC_FORWARDER_IMAGE} - imagePullPolicy: ${IMAGE_PULL_POLICY} - livenessProbe: - httpGet: - path: /health - port: health - initialDelaySeconds: 60 - name: forwarder - ports: - - containerPort: 8088 - name: health - resources: - limits: - memory: 128Mi - requests: - memory: 128Mi - volumeMounts: - - mountPath: /etc/enmasse-certs - name: broker-internal-cert - readOnly: true - initContainers: - - env: - - name: INFRA_UUID - value: ${INFRA_UUID} - - name: MESSAGING_SERVICE_HOST - value: messaging-${INFRA_UUID} - - name: MESSAGING_SERVICE_PORT_AMQPS_BROKER - value: 56671 - - name: MESSAGING_SERVICE_PORT_AMQPS_NORMAL - value: 55671 - - name: QUEUE_SCHEDULER_SERVICE_HOST - value: queue-scheduler-${INFRA_UUID} - - name: QUEUE_SCHEDULER_SERVICE_PORT - value: 5672 - - name: TOPIC_NAME - value: ${ADDRESS} - - name: CLUSTER_ID - value: ${CLUSTER_ID} - - name: CONNECTOR_IN_NETTY_THREADS - value: ${BROKER_CONNECTOR_NETTY_THREADS} - - name: CONNECTOR_OUT_NETTY_THREADS - value: ${BROKER_CONNECTOR_NETTY_THREADS} - - name: CONNECTOR_IN_IDLE_TIMEOUT_MS - value: ${BROKER_CONNECTOR_IDLE_TIMEOUT_MS} - - name: CONNECTOR_OUT_IDLE_TIMEOUT_MS - value: ${BROKER_CONNECTOR_IDLE_TIMEOUT_MS} - - name: GLOBAL_MAX_SIZE - value: ${BROKER_GLOBAL_MAX_SIZE} - - name: ADDRESS_FULL_POLICY - value: ${BROKER_ADDRESS_FULL_POLICY} - - name: JAVA_OPTS - value: ${BROKER_JAVA_OPTS} - - name: AUTHENTICATION_SERVICE_HOST - value: ${AUTHENTICATION_SERVICE_HOST} - - name: AUTHENTICATION_SERVICE_PORT - value: ${AUTHENTICATION_SERVICE_PORT} - - name: AUTHENTICATION_SERVICE_CLIENT_SECRET - value: ${AUTHENTICATION_SERVICE_CLIENT_SECRET} - - name: AUTHENTICATION_SERVICE_SASL_INIT_HOST - value: ${AUTHENTICATION_SERVICE_SASL_INIT_HOST} - - name: AMQ_NAME - value: serverData - - name: HOME - value: /var/run/artemis/split-1/ - image: ${BROKER_PLUGIN_IMAGE} - imagePullPolicy: ${IMAGE_PULL_POLICY} - name: broker-plugin - volumeMounts: - - mountPath: /etc/enmasse-certs - name: broker-internal-cert - readOnly: true - - mountPath: /etc/authservice-ca - name: authservice-ca - readOnly: true - - mountPath: /opt/apache-artemis/custom - name: broker-custom - readOnly: false - volumes: - - name: broker-custom - emptyDir: {} - - name: authservice-ca - secret: - secretName: authservice-ca.${INFRA_UUID} - - name: broker-internal-cert - secret: - secretName: broker-internal-cert.${INFRA_UUID} - - name: broker-support - secret: - secretName: broker-support-${INFRA_UUID} - volumeClaimTemplates: - - apiVersion: v1 - kind: PersistentVolumeClaim - metadata: - annotations: - addressSpace: ${ADDRESS_SPACE} - cluster_id: ${CLUSTER_ID} - labels: - app: enmasse - infraType: standard - infraUuid: ${INFRA_UUID} - name: data - spec: - accessModes: - - ReadWriteOnce - resources: - requests: - storage: ${BROKER_STORAGE_CAPACITY} -parameters: -- name: INFRA_UUID - description: UUID to use for infrastructure - required: true -- description: Storage capacity required for volume claims - name: BROKER_STORAGE_CAPACITY - value: 2Gi -- description: A valid addressSpace name for the address Space - name: ADDRESS_SPACE - required: true -- description: A valid name for the deployment - name: NAME - required: true -- description: A valid group id for the deployment - name: CLUSTER_ID - required: true -- description: The address to use for the topic - name: ADDRESS - value: '' -- description: The hostname of the authentication service used by this address - space - name: AUTHENTICATION_SERVICE_HOST - required: true -- description: The port of the authentication service used by this address space - name: AUTHENTICATION_SERVICE_PORT - required: true -- description: The secret to use for validating authentication service cert - name: AUTHENTICATION_SERVICE_CA_SECRET - required: true -- description: The client cert to use as identity against authentication service - name: AUTHENTICATION_SERVICE_CLIENT_SECRET -- description: The hostname to use in sasl init - name: AUTHENTICATION_SERVICE_SASL_INIT_HOST -- description: Address full policy - name: BROKER_ADDRESS_FULL_POLICY - value: FAIL -- description: Broker Java Options - name: BROKER_JAVA_OPTS - value: "" -- description: Global max size for all addresses in broker. Cannot be larger than - 1/4 of JVM size - name: BROKER_GLOBAL_MAX_SIZE - value: "-1" -- name: BROKER_CONNECTOR_NETTY_THREADS - description: Number of threads to allocate to connectors - value: "4" -- name: BROKER_CONNECTOR_IDLE_TIMEOUT_MS - description: Idle timeout set for connectors - value: "16000" -- name: IMAGE_PULL_POLICY - description: Image Pull Policy - value: ${env.IMAGE_PULL_POLICY} -- name: BROKER_IMAGE - description: Broker Image - value: ${env.BROKER_IMAGE} -- name: BROKER_PLUGIN_IMAGE - description: Broker Plugin Image - value: ${env.BROKER_PLUGIN_IMAGE} -- name: TOPIC_FORWARDER_IMAGE - description: Topic Forwarder Image - value: ${env.TOPIC_FORWARDER_IMAGE} diff --git a/standard-controller/src/test/java/io/enmasse/controller/standard/AddressControllerTest.java b/standard-controller/src/test/java/io/enmasse/controller/standard/AddressControllerTest.java deleted file mode 100644 index 3eab01c79dd..00000000000 --- a/standard-controller/src/test/java/io/enmasse/controller/standard/AddressControllerTest.java +++ /dev/null @@ -1,894 +0,0 @@ -/* - * Copyright 2016-2018, EnMasse authors. - * License: Apache License 2.0 (see the file LICENSE or http://apache.org/licenses/LICENSE-2.0.html). - */ - -package io.enmasse.controller.standard; - -import static java.util.Collections.singletonList; -import static org.hamcrest.MatcherAssert.assertThat; -import static org.hamcrest.collection.IsCollectionWithSize.hasSize; -import static org.hamcrest.core.Is.is; -import static org.junit.jupiter.api.Assertions.*; -import static org.mockito.ArgumentMatchers.any; -import static org.mockito.ArgumentMatchers.eq; -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.verify; -import static org.mockito.Mockito.when; -import static org.mockito.internal.verification.VerificationModeFactory.times; - -import java.io.IOException; -import java.time.Duration; -import java.util.Arrays; -import java.util.List; - -import org.junit.jupiter.api.AfterEach; -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Test; -import org.mockito.ArgumentCaptor; - -import io.enmasse.address.model.Address; -import io.enmasse.address.model.AddressBuilder; -import io.enmasse.address.model.AddressPlanStatus; -import io.enmasse.address.model.AddressSpec; -import io.enmasse.address.model.AddressSpecBuilder; -import io.enmasse.address.model.AddressSpecForwarderDirection; -import io.enmasse.address.model.AddressStatus; -import io.enmasse.address.model.BrokerState; -import io.enmasse.address.model.BrokerStatus; -import io.enmasse.address.model.MessageTtlBuilder; -import io.enmasse.address.model.Phase; - -import io.enmasse.config.AnnotationKeys; -import io.enmasse.k8s.api.AddressApi; -import io.enmasse.k8s.api.AddressSpaceApi; -import io.enmasse.k8s.api.EventLogger; -import io.enmasse.metrics.api.Metrics; -import io.fabric8.kubernetes.api.model.ConfigMapBuilder; -import io.fabric8.kubernetes.api.model.KubernetesList; -import io.fabric8.kubernetes.api.model.KubernetesListBuilder; -import io.fabric8.kubernetes.api.model.apps.StatefulSetBuilder; -import io.fabric8.openshift.client.OpenShiftClient; -import io.vertx.core.Vertx; - -public class AddressControllerTest { - private Kubernetes mockHelper; - private AddressApi mockApi; - private AddressController controller; - @SuppressWarnings("unused") - private OpenShiftClient mockClient; - private BrokerSetGenerator mockGenerator; - private int id = 0; - private BrokerIdGenerator idGenerator = () -> String.valueOf(id++); - private StandardControllerSchema standardControllerSchema = new StandardControllerSchema(); - private Vertx vertx; - - @BeforeEach - public void setUp() throws IOException { - id = 0; - mockHelper = mock(Kubernetes.class); - mockGenerator = mock(BrokerSetGenerator.class); - mockApi = mock(AddressApi.class); - AddressSpaceApi mockSpaceApi = mock(AddressSpaceApi.class); - mockClient = mock(OpenShiftClient.class); - EventLogger eventLogger = mock(EventLogger.class); - when(mockHelper.getRouterCluster()).thenReturn(new RouterCluster("qdrouterd", 1, null)); - StandardControllerOptions options = new StandardControllerOptions(); - options.setAddressSpace("myspace"); - options.setAddressSpaceNamespace("ns"); - options.setInfraUuid("infra"); - options.setAddressSpacePlanName("plan1"); - options.setResyncInterval(Duration.ofSeconds(5)); - options.setVersion("1.0"); - vertx = Vertx.vertx(); - controller = new AddressController(options, mockSpaceApi, mockApi, mockHelper, mockGenerator, eventLogger, standardControllerSchema, vertx, new Metrics(), idGenerator, new MutualTlsBrokerClientFactory(vertx, options)); - } - - @AfterEach - public void cleanup () { - if (vertx != null) { - vertx.close(); - } - } - - @Test - public void testAddressGarbageCollection() throws Exception { - Address alive = new AddressBuilder() - .withNewMetadata() - .withName("myspace.q1") - .withNamespace("ns") - .addToAnnotations(AnnotationKeys.APPLIED_PLAN, "small-queue") - .endMetadata() - - .withNewSpec() - .withAddress("q1") - .withAddressSpace("myspace") - .withType("queue") - .withPlan("small-queue") - .endSpec() - - .withNewStatus() - .withReady(true) - .withPhase(Phase.Active) - .addNewBrokerStatus() - .withClusterId("broker-infra-0") - .withContainerId("broker-infra-0-0") - .endBrokerStatus() - .withPlanStatus(AddressPlanStatus.fromAddressPlan(standardControllerSchema.getType().findAddressType("queue").get().findAddressPlan("small-queue").get())) - .editOrNewPlanStatus() - .withName("small-queue") - .endPlanStatus() - .endStatus() - - .build(); - - Address terminating = new AddressBuilder() - .withNewMetadata() - .withName("myspace.q2") - .withNamespace("ns") - .addToAnnotations(AnnotationKeys.APPLIED_PLAN, "small-queue") - .endMetadata() - - .withNewSpec() - .withAddress("q2") - .withAddressSpace("myspace") - .withType("queue") - .withPlan("small-queue") - .endSpec() - - .withNewStatus() - .withReady(false) - .withPhase(Phase.Terminating) - .addNewBrokerStatus() - .withClusterId("broker-infra-0") - .withContainerId("broker-infra-0-0") - .endBrokerStatus() - .withPlanStatus(AddressPlanStatus.fromAddressPlan(standardControllerSchema.getType().findAddressType("queue").get().findAddressPlan("small-queue").get())) - - .endStatus() - - .build(); - when(mockHelper.listClusters()).thenReturn(Arrays.asList(new BrokerCluster("broker-infra-0", new KubernetesList()))); - controller.onUpdate(Arrays.asList(alive, terminating)); - verify(mockApi).deleteAddress(any()); - verify(mockApi).deleteAddress(eq(terminating)); - } - - @Test - public void testDuplicatePendingPendingAddresses() throws Exception { - - Address a1 = new AddressBuilder() - .withNewMetadata() - .withName("myspace.a1") - .endMetadata() - .withNewSpec() - .withAddress("a") - .withType("anycast") - .withPlan("small-anycast") - .endSpec() - .build(); - - Address a2 = new AddressBuilder() - .withNewMetadata() - .withName("myspace.a2") - .endMetadata() - .withNewSpec() - .withAddress("a") - .withType("anycast") - .withPlan("small-anycast") - .endSpec() - .build(); - - controller.onUpdate(Arrays.asList(a1, a2)); - - List
captured = captureAddresses(2); - - a1 = captured.get(0); - a2 = captured.get(1); - - assertEquals(Phase.Configuring, a1.getStatus().getPhase()); - - assertEquals(Phase.Pending, a2.getStatus().getPhase()); - assertEquals("Address 'a' already exists with resource name 'myspace.a1'", a2.getStatus().getMessages().get(0)); - } - - @Test - public void testDuplicatePendingActiveAddresses() throws Exception { - - Address a1 = new AddressBuilder() - .withNewMetadata() - .withName("myspace.a1") - .endMetadata() - .withNewSpec() - .withAddress("a") - .withType("anycast") - .withPlan("small-anycast") - .endSpec() - .build(); - - Address a2 = new AddressBuilder() - .withNewMetadata() - .withName("myspace.a2") - .endMetadata() - .withNewSpec() - .withAddress("a") - .withType("anycast") - .withPlan("small-anycast") - .endSpec() - .editOrNewStatus() - .withPhase(Phase.Active) - .endStatus() - .build(); - - controller.onUpdate(Arrays.asList(a1, a2)); - - List
captured = captureAddresses(2); - - a1 = captured.get(0); - a2 = captured.get(1); - - assertEquals(Phase.Pending, a1.getStatus().getPhase()); - assertThat(a1.getStatus().getMessages(), is(singletonList("Address 'a' already exists with resource name 'myspace.a2'"))); - - assertEquals(Phase.Active, a2.getStatus().getPhase()); - } - - @Test - public void testDuplicateActivePendingAddresses() throws Exception { - - Address a1 = new AddressBuilder() - .withNewMetadata() - .withName("myspace.a1") - .endMetadata() - .withNewSpec() - .withAddress("a") - .withType("anycast") - .withPlan("small-anycast") - .endSpec() - .editOrNewStatus() - .withPhase(Phase.Active) - .endStatus() - .build(); - - Address a2 = new AddressBuilder() - .withNewMetadata() - .withName("myspace.a2") - .endMetadata() - .withNewSpec() - .withAddress("a") - .withType("anycast") - .withPlan("small-anycast") - .endSpec() - .build(); - - controller.onUpdate(Arrays.asList(a1, a2)); - - List
captured = captureAddresses(2); - - a1 = captured.get(0); - a2 = captured.get(1); - - assertEquals(Phase.Active, a1.getStatus().getPhase()); - - assertEquals(Phase.Pending, a2.getStatus().getPhase()); - assertThat(a2.getStatus().getMessages(), is(singletonList("Address 'a' already exists with resource name 'myspace.a1'"))); - } - - @Test - public void testDuplicateActiveActiveAddresses() throws Exception { - - Address a1 = new AddressBuilder() - .withNewMetadata() - .withName("myspace.a1") - .endMetadata() - .withNewSpec() - .withAddress("a") - .withType("anycast") - .withPlan("small-anycast") - .endSpec() - .editOrNewStatus() - .withPhase(Phase.Active) - .endStatus() - .build(); - - Address a2 = new AddressBuilder() - .withNewMetadata() - .withName("myspace.a2") - .endMetadata() - .withNewSpec() - .withAddress("a") - .withType("anycast") - .withPlan("small-anycast") - .endSpec() - .editOrNewStatus() - .withPhase(Phase.Active) - .endStatus() - .build(); - - controller.onUpdate(Arrays.asList(a1, a2)); - - List
captured = captureAddresses(2); - - a1 = captured.get(0); - a2 = captured.get(1); - - assertEquals(Phase.Active, a1.getStatus().getPhase()); - - assertEquals(Phase.Pending, a2.getStatus().getPhase()); - assertEquals("Address 'a' already exists with resource name 'myspace.a1'", a2.getStatus().getMessages().get(0)); - } - - @Test - public void testDeleteUnusedClusters() throws Exception { - Address alive = new AddressBuilder() - .withNewMetadata() - .withName("myspace.q1") - .withNamespace("ns") - .endMetadata() - - .withNewSpec() - .withAddress("q1") - .withAddressSpace("myspace") - .withType("queue") - .withPlan("small-queue") - .endSpec() - - .withNewStatus() - .withReady(true) - .withPhase(Phase.Active) - .addToBrokerStatuses(new BrokerStatus("broker-infra-0", "broker-infra-0-0", BrokerState.Active)) - .addToBrokerStatuses(new BrokerStatus("broker-infra-1", "broker-infra-1-0", BrokerState.Draining)) - .endStatus() - - .build(); - - KubernetesList oldList = new KubernetesListBuilder() - .addToConfigMapItems(new ConfigMapBuilder() - .withNewMetadata() - .withName("mymap") - .endMetadata() - .build()) - .build(); - - KubernetesList newList = new KubernetesListBuilder() - .addToStatefulSetItems(new StatefulSetBuilder() - .editOrNewMetadata() - .withName("broker-infra-0") - .endMetadata() - .editOrNewSpec() - .withReplicas(1) - .endSpec() - .editOrNewStatus() - .withReadyReplicas(1) - .endStatus() - .build()) - .build(); - - when(mockHelper.listClusters()).thenReturn(Arrays.asList( - new BrokerCluster("broker-infra-0", newList), - new BrokerCluster("broker-infra-1", oldList))); - - controller.onUpdate(Arrays.asList(alive)); - - ArgumentCaptor
captor = ArgumentCaptor.forClass(Address.class); - verify(mockApi).replaceAddress(captor.capture()); - Address captured = captor.getValue(); - - assertEquals(1, captured.getStatus().getBrokerStatuses().size()); - assertEquals("broker-infra-0", captured.getStatus().getBrokerStatuses().get(0).getClusterId()); - verify(mockHelper).delete(any()); - verify(mockHelper).delete(eq(oldList)); - } - - @Test - public void testPendingIsNeverReady() throws Exception { - Address pending = new AddressBuilder() - .withNewMetadata() - .withName("myspace.q1") - .withNamespace("ns") - .endMetadata() - - .withNewSpec() - .withAddress("q1") - .withAddressSpace("myspace") - .withType("queue") - .withPlan("mega-xlarge-queue") - .endSpec() - - .build(); - - controller.onUpdate(Arrays.asList(pending)); - - ArgumentCaptor
captor = ArgumentCaptor.forClass(Address.class); - verify(mockApi).replaceAddress(captor.capture()); - Address captured = captor.getValue(); - - assertFalse(captured.getStatus().isReady()); - assertFalse(captured.getStatus().getMessages().isEmpty()); - } - - @Test - public void testChangedPlanIsReplaced() throws Exception { - Address a = new AddressBuilder() - .withNewMetadata() - .withName("myspace.a1") - .withNamespace("ns") - .addToAnnotations(AnnotationKeys.APPLIED_PLAN, "") - .endMetadata() - - .withNewSpec() - .withAddress("a1") - .withAddressSpace("myspace") - .withType("anycast") - .withPlan("small-anycast") - .endSpec() - .withNewStatus() - .withReady(true) - .withPhase(Phase.Active) - .endStatus() - .build(); - - - assertNotEquals(a.getSpec().getPlan(), a.getAnnotation(AnnotationKeys.APPLIED_PLAN)); - controller.onUpdate(Arrays.asList(a)); - ArgumentCaptor
captor = ArgumentCaptor.forClass(Address.class); - verify(mockApi).replaceAddress(captor.capture()); - Address captured = captor.getValue(); - // ensure that the replaced address has the correct plan - assertEquals(captured.getSpec().getPlan(), captured.getAnnotation(AnnotationKeys.APPLIED_PLAN)); - // but the instance provided to onUpdate did not change - assertNotEquals(a.getSpec().getPlan(), a.getAnnotation(AnnotationKeys.APPLIED_PLAN)); - } - - @Test - public void testMovesBrokersToDrained() throws Exception { - Address alive = new AddressBuilder() - .withNewMetadata() - .withName("myspace.q1") - .withNamespace("ns") - .endMetadata() - - .withNewSpec() - .withAddress("q1") - .withAddressSpace("myspace") - .withType("queue") - .withPlan("small-queue") - .endSpec() - - .withStatus(new AddressStatus(true).setPhase(Phase.Active) - .appendBrokerStatus(new BrokerStatus("broker-infra-0", "broker-infra-0-0", BrokerState.Migrating)) - .appendBrokerStatus(new BrokerStatus("broker-infra-1", "broker-infra-1-0", BrokerState.Active))) - - .build(); - - KubernetesList oldList = new KubernetesListBuilder() - .addToStatefulSetItems(new StatefulSetBuilder() - .editOrNewMetadata() - .withName("broker-infra-0") - .endMetadata() - .editOrNewSpec() - .withReplicas(1) - .endSpec() - .editOrNewStatus() - .withReadyReplicas(0) - .endStatus() - .build()) - .build(); - - when(mockHelper.listClusters()).thenReturn(Arrays.asList( - new BrokerCluster("broker-infra-0", oldList), - new BrokerCluster("broker-infra-1", oldList))); - - - ArgumentCaptor
captor = ArgumentCaptor.forClass(Address.class); - - controller.onUpdate(Arrays.asList(alive)); - - verify(mockApi).replaceAddress(captor.capture()); - Address captured = captor.getValue(); - - assertEquals(2, captured.getStatus().getBrokerStatuses().size()); - assertEquals("broker-infra-0", captured.getStatus().getBrokerStatuses().get(0).getClusterId()); - assertEquals(BrokerState.Migrating, captured.getStatus().getBrokerStatuses().get(0).getState()); - - oldList = new KubernetesListBuilder() - .addToStatefulSetItems(new StatefulSetBuilder() - .editOrNewMetadata() - .withName("broker-infra-0") - .endMetadata() - .editOrNewSpec() - .withReplicas(1) - .endSpec() - .editOrNewStatus() - .withReadyReplicas(1) - .endStatus() - .build()) - .build(); - - when(mockHelper.listClusters()).thenReturn(Arrays.asList( - new BrokerCluster("broker-infra-0", oldList), - new BrokerCluster("broker-infra-1", oldList))); - - controller.onUpdate(Arrays.asList(captured)); - - verify(mockApi, times(2)).replaceAddress(captor.capture()); - captured = captor.getValue(); - - assertEquals(1, captured.getStatus().getBrokerStatuses().size()); - assertEquals("broker-infra-1", captured.getStatus().getBrokerStatuses().get(0).getClusterId()); - assertEquals(BrokerState.Active, captured.getStatus().getBrokerStatuses().get(0).getState()); - } - - @Test - public void testForwarderStatus() throws Exception { - Address a = new AddressBuilder() - .withNewMetadata() - .withName("myspace.a1") - .withNamespace("ns") - .endMetadata() - - .withNewSpec() - .withAddress("q1") - .withType("queue") - .withPlan("small-queue") - .addNewForwarder() - .withName("fwd1") - .withRemoteAddress("remote1/queue1") - .withDirection(AddressSpecForwarderDirection.in) - .endForwarder() - .endSpec() - .withNewStatus() - .withReady(true) - .withPhase(Phase.Configuring) - .endStatus() - .build(); - - - controller.onUpdate(Arrays.asList(a)); - ArgumentCaptor
captor = ArgumentCaptor.forClass(Address.class); - verify(mockApi).replaceAddress(captor.capture()); - Address captured = captor.getValue(); - assertEquals(captured.getStatus().getForwarders().size(), a.getSpec().getForwarders().size()); - assertFalse(captured.getStatus().getForwarders().get(0).isReady()); - } - - @Test - public void testSubscriptionWithoutTopicAddress() throws Exception { - Address sub = new AddressBuilder() - .withNewMetadata() - .withName("myspace.a1") - .endMetadata() - .withNewSpec() - .withAddress("a1") - .withType("subscription") - .withPlan("small-subscription") - .endSpec() - .build(); - - controller.onUpdate(singletonList(sub)); - - List
captured = captureAddresses(1); - - sub = captured.get(0); - assertEquals(Phase.Pending, sub.getStatus().getPhase()); - assertThat(sub.getStatus().getMessages(), is(singletonList("Subscription address 'a1' (resource name 'myspace.a1') must reference a known topic address."))); - } - - @Test - public void testSubscriptionRefersToUnknownTopicAddress() throws Exception { - Address sub = new AddressBuilder() - .withNewMetadata() - .withName("myspace.a1") - .endMetadata() - .withNewSpec() - .withAddress("a1") - .withTopic("unknown") - .withType("subscription") - .withPlan("small-subscription") - .endSpec() - .build(); - - controller.onUpdate(singletonList(sub)); - - List
captured = captureAddresses(1); - - sub = captured.get(0); - assertEquals(Phase.Pending, sub.getStatus().getPhase()); - assertThat(sub.getStatus().getMessages(), is(singletonList("Subscription address 'a1' (resource name 'myspace.a1') references a topic address 'unknown' that does not exist."))); - } - @Test - public void testSubscriptionRefersToAddressWithWrongType() throws Exception { - Address nonTopic = new AddressBuilder() - .withNewMetadata() - .withName("myspace.myanycast") - .endMetadata() - .withNewSpec() - .withAddress("myanycast") - .withType("anycast") - .withPlan("small-anycast") - .endSpec() - .editOrNewStatus() - .withPhase(Phase.Active) - .endStatus() - .build(); - - Address sub = new AddressBuilder() - .withNewMetadata() - .withName("myspace.a1") - .endMetadata() - .withNewSpec() - .withAddress("a1") - .withTopic(nonTopic.getSpec().getAddress()) - .withType("subscription") - .withPlan("small-subscription") - .endSpec() - .build(); - - controller.onUpdate(Arrays.asList(sub, nonTopic)); - - List
captured = captureAddresses(2); - - sub = captured.get(0); - assertEquals(Phase.Pending, sub.getStatus().getPhase()); - assertThat(sub.getStatus().getMessages(), is(singletonList("Subscription address 'a1' (resource name 'myspace.a1') references a topic address 'myanycast' (resource name 'myspace.myanycast') that is not of expected type 'topic' (found type 'anycast' instead)."))); - } - - @Test - public void testNoMessageTtlStatus() throws Exception { - when(mockHelper.listClusters()).thenReturn(Arrays.asList(new BrokerCluster("broker-infra-0", new KubernetesList()))); - - Address a1 = new AddressBuilder() - .withNewMetadata() - .withName("myspace.a1") - .endMetadata() - .withNewSpec() - .withAddress("a1") - .withType("queue") - .withPlan("small-queue") - .endSpec() - .build(); - - controller.onUpdate(singletonList(a1)); - - List
captured = captureAddresses(1); - - a1 = captured.get(0); - assertEquals(Phase.Configuring, a1.getStatus().getPhase()); - assertNull(a1.getStatus().getMessageTtl()); - } - - @Test - public void testAddressSpecifiedMessageTtlStatus() throws Exception { - when(mockHelper.listClusters()).thenReturn(List.of( - new BrokerCluster("broker-infra-0", new KubernetesList()), - new BrokerCluster("broker-infra-1", new KubernetesList()))); - - AddressSpec t = new AddressSpecBuilder() - .withType("queue") - .withPlan("small-queue") - .build(); - - Address a1 = new AddressBuilder() - .withNewMetadata() - .withName("myspace.a1") - .endMetadata() - .withNewSpecLike(t) - .withAddress("a1") - .withMessageTtl(new MessageTtlBuilder().withMaximum(30000L).build()) - .endSpec() - .build(); - - Address a2 = new AddressBuilder() - .withNewMetadata() - .withName("myspace.a2") - .endMetadata() - .withNewSpecLike(t) - .withAddress("a2") - .withMessageTtl(new MessageTtlBuilder().withMinimum(10000L).build()) - .endSpec() - .build(); - - Address a3 = new AddressBuilder() - .withNewMetadata() - .withName("myspace.a3") - .endMetadata() - .withNewSpecLike(t) - .withAddress("a3") - .withMessageTtl(new MessageTtlBuilder().withMinimum(10000L).withMaximum(20000L).build()) - .endSpec() - .build(); - - controller.onUpdate(List.of(a1, a2, a3)); - - List
captured = captureAddresses(3); - - a1 = captured.get(0); - AddressStatus status1 = a1.getStatus(); - assertEquals(Phase.Configuring, status1.getPhase()); - assertNotNull(status1.getMessageTtl()); - assertEquals(30000, status1.getMessageTtl().getMaximum()); - assertNull(status1.getMessageTtl().getMinimum()); - - a2 = captured.get(1); - AddressStatus status2 = a2.getStatus(); - assertEquals(Phase.Configuring, status2.getPhase()); - assertNotNull(status2.getMessageTtl()); - assertNull(status2.getMessageTtl().getMaximum()); - assertEquals(10000, status2.getMessageTtl().getMinimum()); - - a3 = captured.get(2); - AddressStatus status3 = a3.getStatus(); - assertEquals(Phase.Configuring, status3.getPhase()); - assertNotNull(status3.getMessageTtl()); - assertEquals(20000, status3.getMessageTtl().getMaximum()); - assertEquals(10000, status3.getMessageTtl().getMinimum()); - } - - @Test - public void testInvalidAddressSpecifiedMessageTtlStatus() throws Exception { - when(mockHelper.listClusters()).thenReturn(List.of( - new BrokerCluster("broker-infra-0", new KubernetesList()), - new BrokerCluster("broker-infra-1", new KubernetesList()))); - - Address a1 = new AddressBuilder() - .withNewMetadata() - .withName("myspace.a1") - .endMetadata() - .withNewSpec() - .withAddress("a1") - .withType("queue") - .withPlan("small-queue") - .withMessageTtl(new MessageTtlBuilder() - .withMaximum(30000L) - .withMinimum(30001L) - .build()) - .endSpec() - .build(); - - controller.onUpdate(List.of(a1)); - - List
captured = captureAddresses(1); - - a1 = captured.get(0); - AddressStatus status1 = a1.getStatus(); - assertEquals(Phase.Configuring, status1.getPhase()); - assertNull(status1.getMessageTtl()); - - } - - @Test - public void testAddressPlanSpecifiedMaxMessageTtlStatus() throws Exception { - when(mockHelper.listClusters()).thenReturn(List.of( - new BrokerCluster("broker-infra-0", new KubernetesList()), - new BrokerCluster("broker-infra-1", new KubernetesList()))); - - AddressSpec t = new AddressSpecBuilder() - .withType("queue") - .withPlan("small-queue-with-maxttl") - .build(); - - Address a1 = new AddressBuilder() - .withNewMetadata() - .withName("myspace.a1") - .endMetadata() - .withNewSpecLike(t) - .withAddress("a1") - .endSpec() - .build(); - - Address a2 = new AddressBuilder() - .withNewMetadata() - .withName("myspace.a2") - .endMetadata() - .withNewSpecLike(t) - .withAddress("a2") - .withMessageTtl(new MessageTtlBuilder() - .withMaximum(29000L) - .withMinimum(10000L) - .build()) - .endSpec() - .build(); - - Address a3 = new AddressBuilder() - .withNewMetadata() - .withName("myspace.a3") - .endMetadata() - .withNewSpecLike(t) - .withAddress("a3") - .withMessageTtl(new MessageTtlBuilder() - .withMaximum(31000L) - .withMinimum(10000L) - .build()) - .endSpec() - .build(); - - controller.onUpdate(List.of(a1, a2, a3)); - - List
captured = captureAddresses(3); - - a1 = captured.get(0); - AddressStatus status1 = a1.getStatus(); - assertEquals(Phase.Configuring, status1.getPhase()); - assertNotNull(status1.getMessageTtl()); - assertEquals(30000L, status1.getMessageTtl().getMaximum()); // From plan - assertNull(status1.getMessageTtl().getMinimum()); - - a2 = captured.get(1); - AddressStatus status2 = a2.getStatus(); - assertEquals(Phase.Configuring, status2.getPhase()); - assertNotNull(status2.getMessageTtl()); - assertEquals(29000L, status2.getMessageTtl().getMaximum()); // Overridden by address - assertEquals(10000L, status2.getMessageTtl().getMinimum()); - - a3 = captured.get(2); - AddressStatus status3 = a3.getStatus(); - assertEquals(Phase.Configuring, status3.getPhase()); - assertNotNull(status3.getMessageTtl()); - assertEquals(30000L, status3.getMessageTtl().getMaximum()); // From plan - not overridden by address - assertEquals(10000L, status3.getMessageTtl().getMinimum()); - } - - @Test - public void testAddressPlanSpecifiedMinMessageTtlStatus() throws Exception { - when(mockHelper.listClusters()).thenReturn(List.of( - new BrokerCluster("broker-infra-0", new KubernetesList()), - new BrokerCluster("broker-infra-1", new KubernetesList()))); - - AddressSpec t = new AddressSpecBuilder() - .withType("queue") - .withPlan("small-queue-with-minttl") - .build(); - - Address a1 = new AddressBuilder() - .withNewMetadata() - .withName("myspace.a1") - .endMetadata() - .withNewSpecLike(t) - .withAddress("a1") - .withMessageTtl(new MessageTtlBuilder() - .withMinimum(10001L) - .build()) - .endSpec() - .build(); - - Address a2 = new AddressBuilder() - .withNewMetadata() - .withName("myspace.a2") - .endMetadata() - .withNewSpecLike(t) - .withAddress("a2") - .withMessageTtl(new MessageTtlBuilder() - .withMinimum(9999L) - .build()) - .endSpec() - .build(); - - controller.onUpdate(List.of(a1, a2)); - - List
captured = captureAddresses(2); - - a1 = captured.get(0); - AddressStatus status1 = a1.getStatus(); - assertEquals(Phase.Configuring, status1.getPhase()); - assertNotNull(status1.getMessageTtl()); - assertNull(status1.getMessageTtl().getMaximum()); - assertEquals(10001, status1.getMessageTtl().getMinimum()); // Overridden by address - - a2 = captured.get(1); - AddressStatus status2 = a2.getStatus(); - assertEquals(Phase.Configuring, status2.getPhase()); - assertNotNull(status2.getMessageTtl()); - assertNull(status2.getMessageTtl().getMaximum()); - assertEquals(10000, status2.getMessageTtl().getMinimum()); // From plan - not overridden by address - - } - - private List
captureAddresses(int expectedAddresses) { - ArgumentCaptor
captor = ArgumentCaptor.forClass(Address.class); - verify(mockApi, times(expectedAddresses)).replaceAddress(captor.capture()); - List
captured = captor.getAllValues(); - assertThat(captured, hasSize(expectedAddresses)); - return captured; - } - -} diff --git a/standard-controller/src/test/java/io/enmasse/controller/standard/AddressProvisionerTest.java b/standard-controller/src/test/java/io/enmasse/controller/standard/AddressProvisionerTest.java deleted file mode 100644 index ae0d82db1b0..00000000000 --- a/standard-controller/src/test/java/io/enmasse/controller/standard/AddressProvisionerTest.java +++ /dev/null @@ -1,817 +0,0 @@ -/* - * Copyright 2018, EnMasse authors. - * License: Apache License 2.0 (see the file LICENSE or http://apache.org/licenses/LICENSE-2.0.html). - */ -package io.enmasse.controller.standard; - -import static io.enmasse.address.model.Phase.Active; -import static io.enmasse.address.model.Phase.Configuring; -import static io.enmasse.address.model.Phase.Pending; -import static org.hamcrest.CoreMatchers.is; -import static org.hamcrest.MatcherAssert.assertThat; -import static org.hamcrest.Matchers.not; -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertFalse; -import static org.junit.jupiter.api.Assertions.assertNotEquals; -import static org.junit.jupiter.api.Assertions.assertTrue; -import static org.mockito.ArgumentMatchers.any; -import static org.mockito.ArgumentMatchers.anyInt; -import static org.mockito.ArgumentMatchers.eq; -import static org.mockito.Mockito.atLeast; -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.never; -import static org.mockito.Mockito.verify; -import static org.mockito.Mockito.when; - -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Collections; -import java.util.HashMap; -import java.util.HashSet; -import java.util.Iterator; -import java.util.LinkedHashSet; -import java.util.List; -import java.util.Map; -import java.util.Set; -import java.util.stream.Collectors; - -import io.enmasse.address.model.*; -import io.enmasse.admin.model.v1.StandardInfraConfig; -import io.enmasse.admin.model.v1.StandardInfraConfigSpec; -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Test; -import org.mockito.internal.util.collections.Sets; - -import io.enmasse.admin.model.v1.ResourceAllowance; -import io.enmasse.config.AnnotationKeys; -import io.enmasse.k8s.api.EventLogger; -import io.fabric8.kubernetes.api.model.KubernetesList; -import io.fabric8.kubernetes.api.model.KubernetesListBuilder; -import io.fabric8.kubernetes.api.model.ObjectMetaBuilder; -import io.fabric8.kubernetes.api.model.apps.StatefulSetBuilder; - -public class AddressProvisionerTest { - private BrokerSetGenerator generator; - private Kubernetes kubernetes; - private int id = 0; - private BrokerIdGenerator idGenerator = () -> String.valueOf(id++); - private final StandardInfraConfig infraConfig = new StandardInfraConfig(); - - @BeforeEach - public void setup() { - id = 0; - generator = mock(BrokerSetGenerator.class); - kubernetes = mock(Kubernetes.class); - infraConfig.setSpec(new StandardInfraConfigSpec()); - } - - private class ProvisionerTestFixture { - StandardControllerSchema standardControllerSchema; - AddressResolver resolver; - AddressSpaceResolver addressSpaceResolver; - EventLogger logger = mock(EventLogger.class); - AddressProvisioner addressProvisioner; - - public ProvisionerTestFixture() { - standardControllerSchema = new StandardControllerSchema(); - resolver = new AddressResolver(standardControllerSchema.getType()); - addressSpaceResolver = new AddressSpaceResolver(standardControllerSchema.getSchema()); - logger = mock(EventLogger.class); - addressProvisioner = new AddressProvisioner(addressSpaceResolver, resolver, standardControllerSchema.getPlan(), generator, kubernetes, logger, "1234", idGenerator); - } - - public ProvisionerTestFixture(List resourceAllowances) { - standardControllerSchema = new StandardControllerSchema(resourceAllowances); - resolver = new AddressResolver(standardControllerSchema.getType()); - addressSpaceResolver = new AddressSpaceResolver(standardControllerSchema.getSchema()); - logger = mock(EventLogger.class); - addressProvisioner = new AddressProvisioner(addressSpaceResolver, resolver, standardControllerSchema.getPlan(), generator, kubernetes, logger, "1234", idGenerator); - } - } - - @Test - public void testUsageCheck() { - Set
addresses = new HashSet<>(); - addresses.add(new AddressBuilder() - .withMetadata(new ObjectMetaBuilder() - .withNamespace("ns") - .build()) - - .withNewSpec() - .withAddress("a1") - .withAddressSpace("myspace") - .withPlan("small-anycast") - .withType("anycast") - .endSpec() - - .build()); - AddressProvisioner provisioner = new ProvisionerTestFixture().addressProvisioner; - Map> usageMap = provisioner.checkUsage(addresses); - - assertThat(usageMap.size(), is(1)); - assertThat(usageMap.get("router").size(), is(1)); - assertEquals(0.2, usageMap.get("router").get("all").getUsed(), 0.01); - - addresses.add(new AddressBuilder() - .withMetadata(new ObjectMetaBuilder() - .withNamespace("ns") - .build()) - - .withNewSpec() - .withAddress("q1") - .withAddressSpace("myspace") - .withPlan("small-queue") - .withType("queue") - .endSpec() - - .withStatus(new AddressStatusBuilder() - .withReady(true) - .addToBrokerStatuses(new BrokerStatus("broker-0", "broker-0-0").setState(BrokerState.Active)) - .build()) - - .build()); - - usageMap = provisioner.checkUsage(addresses); - - assertThat(usageMap.size(), is(2)); - assertThat(usageMap.get("router").size(), is(1)); - assertThat(usageMap.get("broker").size(), is(1)); - assertEquals(0.4, usageMap.get("router").get("all").getUsed(), 0.01); - assertEquals(0.4, usageMap.get("broker").get("broker-0").getUsed(), 0.01); - - addresses.add(new AddressBuilder() - .withMetadata(new ObjectMetaBuilder() - .withNamespace("ns") - .build()) - - .withNewSpec() - .withAddress("q2") - .withAddressSpace("myspace") - .withPlan("small-queue") - .withType("queue") - .endSpec() - .withNewStatus() - .addNewBrokerStatus() - .withClusterId("broker-0") - .withContainerId("broker-0-0") - .withState(BrokerState.Active) - .endBrokerStatus() - .endStatus() - - .build()); - - usageMap = provisioner.checkUsage(addresses); - - assertThat(usageMap.size(), is(2)); - assertThat(usageMap.get("router").size(), is(1)); - assertThat(usageMap.get("broker").size(), is(1)); - assertEquals(0.6, usageMap.get("router").get("all").getUsed(), 0.01); - assertEquals(0.8, usageMap.get("broker").get("broker-0").getUsed(), 0.01); - } - - @Test - public void testQuotaCheck() { - Set
addresses = new HashSet<>(); - addresses.add(createQueue("q1", "small-queue", createPooledBrokerStatus("broker-1234-0"))); - addresses.add(createQueue("q2", "small-queue", createPooledBrokerStatus("broker-1234-0"))); - addresses.add(createQueue("q3", "small-queue", createPooledBrokerStatus("broker-1234-1"))); - - AddressProvisioner provisioner = new ProvisionerTestFixture().addressProvisioner; - Map> usageMap = provisioner.checkUsage(addresses); - id = 2; - - Address largeQueue = createQueue("q4", "xlarge-queue"); - Map> neededMap = provisioner.checkQuota(usageMap, Sets.newSet(largeQueue), Sets.newSet(largeQueue)); - - assertThat(neededMap, is(usageMap)); - assertThat(largeQueue.getStatus().getPhase(), is(Pending)); - assertTrue(largeQueue.getStatus().getMessages().contains("Quota exceeded")); - - Address smallQueue = createQueue("q4", "small-queue"); - neededMap = provisioner.checkQuota(usageMap, Sets.newSet(smallQueue), Sets.newSet(smallQueue)); - - assertThat(neededMap, is(not(usageMap))); - } - - @Test - public void testQuotaCheckPartitionedQueues() { - - ProvisionerTestFixture testFixture = new ProvisionerTestFixture(Arrays.asList( - new ResourceAllowance("broker", 1), - new ResourceAllowance("router", 1), - new ResourceAllowance("aggregate", 4))); - - Address q1 = new AddressBuilder(createQueue("q1", "small-sharded-queue", createPooledBrokerStatus("broker-1234-0"))) - .editStatus() - .withPhase(Active) - .withPlanStatus(AddressPlanStatus.fromAddressPlan(testFixture.standardControllerSchema.getType().findAddressType("queue").get().findAddressPlan("small-queue").get())) - .endStatus() - .build(); - - AddressProvisioner provisioner = testFixture.addressProvisioner; - - Map> usageMap = provisioner.checkUsage(Set.of(q1)); - - Map> neededMap = provisioner.checkQuota(usageMap, Set.of(q1), Sets.newSet(q1)); - - assertEquals(usageMap, neededMap); - assertThat(q1.getStatus().getPhase(), is(Active)); - assertTrue(q1.getStatus().getMessages().contains("Quota exceeded")); - } - - @Test - public void testQuotaCheckMany() { - Map addresses = new HashMap<>(); - for (int i = 0; i < 200; i++) { - addresses.put("a" + i, createAddress("a" + i, "anycast", "small-anycast")); - } - - - AddressProvisioner provisioner = new ProvisionerTestFixture().addressProvisioner; - - Map> usageMap = new HashMap<>(); - Map> provisionMap = provisioner.checkQuota(usageMap, new LinkedHashSet<>(addresses.values()), new LinkedHashSet<>(addresses.values())); - - assertThat(provisionMap.get("router").get("all").getNeeded(), is(1)); - int numConfiguring = 0; - for (Address address : addresses.values()) { - if (address.getStatus().getPhase().equals(Phase.Configuring)) { - numConfiguring++; - } - } - assertThat(numConfiguring, is(5)); - } - - @Test - public void testProvisioningColocated() throws Exception { - Set
addresses = new HashSet<>(); - addresses.add(createAddress("a1", "anycast", "small-anycast")); - addresses.add(createAddress("q1", "queue", "small-queue", createPooledBrokerStatus("broker-1234-0"))); - - AddressProvisioner provisioner = new ProvisionerTestFixture().addressProvisioner; - Map> usageMap = provisioner.checkUsage(addresses); - - Address queue = createAddress("q2", "queue", "small-queue"); - Map> neededMap = provisioner.checkQuota(usageMap, Sets.newSet(queue), Sets.newSet(queue)); - - List clusterList = Arrays.asList(new BrokerCluster("broker-1234-0", new KubernetesList())); - provisioner.provisionResources(createDeployment(1), clusterList, neededMap, Sets.newSet(queue), infraConfig); - - assertThat(clusterList.get(0).getResources().getItems().size(), is(0)); - assertTrue(queue.getStatus().getMessages().isEmpty(), queue.getStatus().getMessages().toString()); - assertThat(queue.getStatus().getPhase(), is(Phase.Configuring)); - assertThat(queue.getStatus().getBrokerStatuses().get(0).getContainerId(), is("broker-1234-0-0")); - assertThat(queue.getStatus().getBrokerStatuses().get(0).getClusterId(), is("broker-1234-0")); - } - - private RouterCluster createDeployment(int replicas) { - return new RouterCluster("router", replicas, infraConfig); - } - - @Test - public void testScalingColocated() throws Exception { - Set
addresses = new HashSet<>(); - addresses.add(createAddress("a1", "anycast", "small-anycast")); - addresses.add(createAddress("q1", "queue", "small-queue", createPooledBrokerStatus("broker-1234-0"))); - addresses.add(createAddress("q2", "queue", "small-queue", createPooledBrokerStatus("broker-1234-0"))); - id = 1; - - AddressProvisioner provisioner = new ProvisionerTestFixture().addressProvisioner; - Map> usageMap = provisioner.checkUsage(addresses); - - Address queue = createAddress("q3", "queue", "small-queue"); - Map> provisionMap = provisioner.checkQuota(usageMap, Sets.newSet(queue), Sets.newSet(queue)); - - List clusterList = new ArrayList<>(); - clusterList.add(new BrokerCluster("broker-1234-0", new KubernetesList())); - when(generator.generateCluster(eq("broker-1234-1"), anyInt(), any(), any(), any())).thenReturn(new BrokerCluster("broker-1234-1", new KubernetesList())); - provisioner.provisionResources(createDeployment(1), clusterList, provisionMap, Sets.newSet(queue), infraConfig); - - assertTrue(queue.getStatus().getMessages().isEmpty(), queue.getStatus().getMessages().toString()); - assertThat(queue.getStatus().getPhase(), is(Phase.Configuring)); - assertThat(queue.getStatus().getBrokerStatuses().get(0).getClusterId(), is("broker-1234-1")); - assertThat(queue.getStatus().getBrokerStatuses().get(0).getContainerId(), is("broker-1234-1-0")); - } - - @Test - public void testProvisionColocated() throws Exception { - AddressProvisioner provisioner = new ProvisionerTestFixture(Arrays.asList( - new ResourceAllowance("broker", 2), - new ResourceAllowance("router", 1), - new ResourceAllowance("aggregate", 2))).addressProvisioner; - - Set
addressSet = Sets.newSet( - createQueue("q9", "pooled-queue-tiny"), - createQueue("q8", "pooled-queue-tiny"), - createQueue("q11", "pooled-queue-tiny"), - createQueue("q12", "pooled-queue-tiny"), - createQueue("q10", "pooled-queue-tiny"), - createQueue("q1", "pooled-queue-large"), - createQueue("q7", "pooled-queue-tiny"), - createQueue("q6", "pooled-queue-small"), - createQueue("q5", "pooled-queue-small"), - createQueue("q4", "pooled-queue-small"), - createQueue("q3", "pooled-queue-small"), - createQueue("q2", "pooled-queue-large")); - - Map> usageMap = provisioner.checkUsage(Collections.emptySet()); - Map> neededMap = provisioner.checkQuota(usageMap, addressSet, addressSet); - - assertThat(neededMap.keySet().size(), is(1)); - assertThat(AddressProvisioner.sumTotalNeeded(neededMap), is(2)); - - List brokerClusters = Arrays.asList( - createCluster("broker-1234-0", 1), - createCluster("broker-1234-1", 1)); - - provisioner.provisionResources(new RouterCluster("router", 1, null), brokerClusters, neededMap, addressSet, infraConfig); - - for (Address address : addressSet) { - assertThat(address.getStatus().getPhase(), is(Phase.Configuring)); - } - } - - private BrokerCluster createCluster(String clusterId, int replicas) { - KubernetesListBuilder builder = new KubernetesListBuilder(); - builder.addToStatefulSetItems(new StatefulSetBuilder() - .editOrNewMetadata() - .withName(clusterId) - .endMetadata() - .editOrNewSpec() - .withReplicas(replicas) - .endSpec() - .build()); - return new BrokerCluster(clusterId, builder.build()); - } - - private Address createQueue(String address, String plan) { - return createQueue(address, plan, (BrokerStatus[])null); - } - - private Address createQueue(String address, String plan, BrokerStatus ... brokerStatuses) { - return createAddress(address, "queue", plan, brokerStatuses); - } - - - private static Address createAddress(String address, String type, String plan) { - return createAddress(address, type, plan, (BrokerStatus[])null); - } - - private BrokerStatus createPooledBrokerStatus(String clusterId) { - return new BrokerStatusBuilder() - .withClusterId(clusterId) - .withContainerId(clusterId + "-0") - .withState(BrokerState.Active) - .build(); - } - - private static Address createAddress(String address, String type, String plan, BrokerStatus ... brokerStatuses) { - - final AddressBuilder addressBuilder = new AddressBuilder() - .withNewMetadata() - .withName("myspace." + address) - .withNamespace("ns") - .endMetadata() - - .withNewSpec() - .withAddress(address) - .withAddressSpace("myspace") - .withPlan(plan) - .withType(type) - .endSpec(); - - if (brokerStatuses != null && brokerStatuses.length > 0) { - addressBuilder.withNewStatus() - .addToBrokerStatuses(brokerStatuses) - .endStatus(); - } - - return addressBuilder.build(); - } - - private Address createSubscription(String address, String topic, String plan) { - return new AddressBuilder() - .withNewMetadata() - .withNamespace("ns") - .endMetadata() - - .withNewSpec() - .withAddress(address) - .withAddressSpace("myspace") - .withPlan(plan) - .withType("subscription") - .withTopic(topic) - .endSpec() - - .build(); - } - - @Test - public void testShardedPooled() throws Exception { - Address q2 = createQueue("q1", "medium-sharded-queue"); - AddressProvisioner provisioner = new ProvisionerTestFixture(Arrays.asList( - new ResourceAllowance("broker", 2), - new ResourceAllowance("router", 1), - new ResourceAllowance("aggregate", 4))).addressProvisioner; - Map> usageMap = new HashMap<>(); - Map> neededMap = provisioner.checkQuota(usageMap, Set.of(q2), Set.of(q2)); - - assertThat(neededMap.size(), is(2)); - assertThat(neededMap.get("broker").size(), is(2)); - - usageMap = provisioner.checkUsage(Set.of(q2)); - assertEquals(neededMap, usageMap); - } - - @Test - public void testProvisioningShardedTopic() throws Exception { - Set
addresses = new HashSet<>(); - addresses.add(createAddress("a1", "anycast", "small-anycast")); - - AddressProvisioner provisioner = new ProvisionerTestFixture(Arrays.asList( - new ResourceAllowance("broker", 3), - new ResourceAllowance("router", 1), - new ResourceAllowance("aggregate", 4))).addressProvisioner; - Map> usageMap = provisioner.checkUsage(addresses); - - Address t1 = createAddress("t1", "topic", "xlarge-topic"); - Address t2 = createAddress("t2", "topic", "large-topic"); - Map> neededMap = provisioner.checkQuota(usageMap, Sets.newSet(t1, t2), Sets.newSet(t1, t2)); - - when(generator.generateCluster(eq(provisioner.getShardedClusterId(t1)), anyInt(), eq(t1), any(), any())).thenReturn(new BrokerCluster(provisioner.getShardedClusterId(t1), new KubernetesList())); - when(generator.generateCluster(eq(provisioner.getShardedClusterId(t2)), anyInt(), eq(t2), any(), any())).thenReturn(new BrokerCluster(provisioner.getShardedClusterId(t2), new KubernetesList())); - provisioner.provisionResources(createDeployment(1), new ArrayList<>(), neededMap, Sets.newSet(t1, t2), infraConfig); - - assertTrue(t1.getStatus().getMessages().isEmpty(), t1.getStatus().getMessages().toString()); - assertThat(t1.getStatus().getPhase(), is(Phase.Configuring)); - assertThat(t1.getStatus().getBrokerStatuses().get(0).getContainerId(), is("t1")); - verify(generator).generateCluster(eq(provisioner.getShardedClusterId(t1)), eq(2), eq(t1), any(), any()); - - assertTrue(t2.getStatus().getMessages().isEmpty(), t2.getStatus().getMessages().toString()); - assertThat(t2.getStatus().getPhase(), is(Phase.Configuring)); - assertThat(t2.getStatus().getBrokerStatuses().get(0).getContainerId(), is("t2")); - verify(generator).generateCluster(eq(provisioner.getShardedClusterId(t2)), eq(1), eq(t2), any(), any()); - } - - @Test - public void testProvisioningSharded() throws Exception { - Set
addresses = new HashSet<>(); - addresses.add(createAddress("a1", "anycast", "small-anycast")); - - AddressProvisioner provisioner = new ProvisionerTestFixture(Arrays.asList( - new ResourceAllowance("broker", 3), - new ResourceAllowance("router", 1), - new ResourceAllowance("aggregate", 4))).addressProvisioner; - Map> usageMap = provisioner.checkUsage(addresses); - - Address q1 = createQueue("q1", "xlarge-queue"); - Address q2 = createQueue("q2", "large-queue"); - Map> neededMap = provisioner.checkQuota(usageMap, Sets.newSet(q1, q2), Sets.newSet(q1, q2)); - - when(generator.generateCluster(eq("broker-1234-0"), anyInt(), any(), any(), any())).thenReturn(new BrokerCluster("broker-1234-0", new KubernetesList())); - when(generator.generateCluster(eq("broker-1234-1"), anyInt(), any(), any(), any())).thenReturn(new BrokerCluster("broker-1234-1", new KubernetesList())); - when(generator.generateCluster(eq("broker-1234-2"), anyInt(), any(), any(), any())).thenReturn(new BrokerCluster("broker-1234-2", new KubernetesList())); - provisioner.provisionResources(createDeployment(1), new ArrayList<>(), neededMap, Sets.newSet(q1, q2), infraConfig); - - assertTrue(q1.getStatus().getMessages().isEmpty(), q1.getStatus().getMessages().toString()); - assertThat(q1.getStatus().getPhase(), is(Phase.Configuring)); - assertThat(q1.getStatus().getBrokerStatuses().size(), is(2)); - assertTrue(q1.getStatus().getBrokerStatuses().stream().map(BrokerStatus::getContainerId).collect(Collectors.toSet()).contains("broker-1234-1-0")); - assertTrue(q1.getStatus().getBrokerStatuses().stream().map(BrokerStatus::getContainerId).collect(Collectors.toSet()).contains("broker-1234-2-0")); - verify(generator).generateCluster(eq("broker-1234-1"), eq(1), any(), any(), any()); - verify(generator).generateCluster(eq("broker-1234-2"), eq(1), any(), any(), any()); - - assertTrue(q2.getStatus().getMessages().isEmpty(), q2.getStatus().getMessages().toString()); - assertThat(q2.getStatus().getPhase(), is(Phase.Configuring)); - assertThat(q2.getStatus().getBrokerStatuses().size(), is(1)); - assertThat(q2.getStatus().getBrokerStatuses().get(0).getContainerId(), is("broker-1234-0-0")); - verify(generator).generateCluster(eq("broker-1234-0"), eq(1), any(), any(), any()); - } - - @Test - public void testUpgradeFromNoAppliedPlan() throws Exception { - - AddressProvisioner provisioner = new ProvisionerTestFixture(Arrays.asList( - new ResourceAllowance("broker", 3), - new ResourceAllowance("router", 1), - new ResourceAllowance("aggregate", 4))).addressProvisioner; - - Set
addresses = new HashSet<>(); - Address q1 = createQueue("q1", "xlarge-queue"); - Address a1 = createAddress("a1", "anycast", "small-anycast"); - addresses.add(q1); - addresses.add(a1); - Map> usageMap = provisioner.checkUsage(addresses); - - assertNotEquals(q1.getSpec().getPlan(), q1.getAnnotation(AnnotationKeys.APPLIED_PLAN)); - assertNotEquals(a1.getSpec().getPlan(), a1.getAnnotation(AnnotationKeys.APPLIED_PLAN)); - @SuppressWarnings("unused") - Map> neededMap = provisioner.checkQuota(usageMap, Sets.newSet(a1, q1), Sets.newSet(a1, q1)); - - assertEquals(q1.getSpec().getPlan(), q1.getAnnotation(AnnotationKeys.APPLIED_PLAN)); - assertEquals(a1.getSpec().getPlan(), a1.getAnnotation(AnnotationKeys.APPLIED_PLAN)); - } - - @Test - public void testSwitchShardedAddressPlan() throws Exception { - - AddressProvisioner provisioner = new ProvisionerTestFixture(Arrays.asList( - new ResourceAllowance("broker", 2), - new ResourceAllowance("router", 1), - new ResourceAllowance("aggregate", 4))).addressProvisioner; - - Address q1 = createQueue("q1", "large-queue"); - Map> usageMap = provisioner.checkUsage(Collections.emptySet()); - - provisioner.checkQuota(usageMap, Sets.newSet(q1), Sets.newSet(q1)); - assertEquals(q1.getSpec().getPlan(), q1.getAnnotation(AnnotationKeys.APPLIED_PLAN)); - - q1 = new AddressBuilder(q1) - .editOrNewSpec() - .withPlan("xlarge-queue") - .endSpec() - .build(); - - q1.getStatus().setPhase(Active); - - usageMap = provisioner.checkUsage(Sets.newSet(q1)); - @SuppressWarnings("unused") - Map> neededMap = provisioner.checkQuota(usageMap, Sets.newSet(q1), Sets.newSet(q1)); - assertEquals(q1.getSpec().getPlan(), q1.getAnnotation(AnnotationKeys.APPLIED_PLAN)); - } - - @Test - public void testSwitchPooledToShardedQuotaCheck() throws Exception { - - AddressProvisioner provisioner = new ProvisionerTestFixture(Arrays.asList( - new ResourceAllowance("broker", 1), - new ResourceAllowance("router", 1), - new ResourceAllowance("aggregate", 4))).addressProvisioner; - - Address q1 = createQueue("q1", "small-queue"); - Map> usageMap = provisioner.checkUsage(Collections.emptySet()); - - provisioner.checkQuota(usageMap, Sets.newSet(q1), Sets.newSet(q1)); - assertEquals(q1.getSpec().getPlan(), q1.getAnnotation(AnnotationKeys.APPLIED_PLAN)); - - q1.getStatus().setPhase(Active); - q1 = new AddressBuilder(q1) - .editOrNewSpec() - .withPlan("large-queue") - .endSpec() - .build(); - - - usageMap = provisioner.checkUsage(Sets.newSet(q1)); - provisioner.checkQuota(usageMap, Sets.newSet(q1), Sets.newSet(q1)); - assertTrue(q1.getStatus().getMessages().isEmpty()); - assertThat(q1.getStatus().getPhase(), is(Configuring)); - assertEquals(q1.getSpec().getPlan(), q1.getAnnotation(AnnotationKeys.APPLIED_PLAN)); - } - - /* - @Test - public void testReuseExistingBrokerWhenSharding() throws Exception { - - AddressProvisioner provisioner = new ProvisionerTestFixture(Arrays.asList( - new ResourceAllowance("broker", 2), - new ResourceAllowance("router", 1), - new ResourceAllowance("aggregate", 4))).addressProvisioner; - - Address q1 = createQueue("q1", "large-queue"); - Map> usageMap = provisioner.checkUsage(Collections.emptySet()); - - provisioner.checkQuota(usageMap, Sets.newSet(q1), Sets.newSet(q1)); - assertEquals(q1.getSpec().getPlan(), q1.getAnnotation(AnnotationKeys.APPLIED_PLAN)); - - q1.getStatus().setPhase(Active); - q1 = new AddressBuilder(q1) - .editOrNewSpec() - .withPlan("xlarge-queue") - .endSpec() - .build(); - - - usageMap = provisioner.checkUsage(Sets.newSet(q1)); - provisioner.checkQuota(usageMap, Sets.newSet(q1), Sets.newSet(q1)); - assertTrue(q1.getStatus().getMessages().isEmpty()); - assertThat(q1.getStatus().getPhase(), is(Configuring)); - assertEquals(q1.getSpec().getPlan(), q1.getAnnotation(AnnotationKeys.APPLIED_PLAN)); - } - */ - - @Test - public void testScalingRouter() throws Exception { - Set
addresses = new HashSet<>(); - for (int i = 0; i < 199; i++) { - addresses.add(createAddress("a" + i, "anycast", "small-anycast")); - } - - - AddressProvisioner provisioner = new ProvisionerTestFixture(Arrays.asList( - new ResourceAllowance("broker", 0), - new ResourceAllowance("router", 100000), - new ResourceAllowance("aggregate", 100000))).addressProvisioner; - - Map> usageMap = new HashMap<>(); - Map> neededMap = provisioner.checkQuota(usageMap, addresses, addresses); - - provisioner.provisionResources(createDeployment(1), new ArrayList<>(), neededMap, addresses, infraConfig); - - verify(kubernetes, atLeast(1)).scaleStatefulSet(eq("router"), eq(40)); - verify(kubernetes, never()).scaleStatefulSet(eq("router"), eq(41)); - } - - @Test - public void testDurableSubscriptionsColocated() { - AddressProvisioner provisioner = new ProvisionerTestFixture(Arrays.asList( - new ResourceAllowance("broker", 2), - new ResourceAllowance("router", 1), - new ResourceAllowance("aggregate", 3))).addressProvisioner; - - Set
addressSet = Sets.newSet( - createAddress("t1", "topic", "small-topic"), - createAddress("t2", "topic", "small-topic"), - createSubscription("s1", "t1", "small-subscription")); - - - Map> usageMap = provisioner.checkUsage(Collections.emptySet()); - Map> neededMap = provisioner.checkQuota(usageMap, addressSet, addressSet); - - assertThat(neededMap.keySet().size(), is(3)); - assertThat(AddressProvisioner.sumTotalNeeded(neededMap), is(2)); - assertThat(AddressProvisioner.sumNeeded(neededMap.get("router")), is(1)); - assertThat(AddressProvisioner.sumNeeded(neededMap.get("broker")), is(1)); - assertThat(AddressProvisioner.sumNeeded(neededMap.get("subscription")), is(1)); - - for (Address address : addressSet) { - assertThat(address.getStatus().getPhase(), is(Phase.Configuring)); - } - } - - @Test - public void testDurableSubscriptionsColocatedStaysOnTopicBroker() { - AddressProvisioner provisioner = new ProvisionerTestFixture(Arrays.asList( - new ResourceAllowance("broker", 2), - new ResourceAllowance("router", 1), - new ResourceAllowance("aggregate", 3))).addressProvisioner; - - Set
addressSet = Sets.newSet( - createAddress("t1", "topic", "small-topic"), - createSubscription("s1", "t1", "small-subscription"), - createSubscription("s2", "t1", "small-subscription"), - createSubscription("s3", "t1", "small-subscription"), - createSubscription("s4", "t1", "small-subscription"), - createSubscription("s5", "t1", "small-subscription"), - createSubscription("s6", "t1", "small-subscription"), - createSubscription("s7", "t1", "small-subscription"), - createSubscription("s8", "t1", "small-subscription"), - createSubscription("s9", "t1", "small-subscription"), - createSubscription("s10", "t1", "small-subscription"), - createSubscription("s11", "t1", "small-subscription"), - createSubscription("s12", "t1", "small-subscription")); - - Map> usageMap = provisioner.checkUsage(Collections.emptySet()); - Map> neededMap = provisioner.checkQuota(usageMap, addressSet, addressSet); - - assertThat(AddressProvisioner.sumTotalNeeded(neededMap), is(2)); - assertThat(AddressProvisioner.sumNeeded(neededMap.get("router")), is(1)); - assertThat(AddressProvisioner.sumNeeded(neededMap.get("broker")), is(1)); - assertThat(AddressProvisioner.sumNeeded(neededMap.get("subscription")), is(1)); - - Set
configured = new HashSet<>(); - Set
unConfigured = new HashSet<>(); - - - for (Address address : addressSet) { - if (address.getStatus().getPhase().equals(Phase.Pending)) { - unConfigured.add(address); - } else if (address.getStatus().getPhase().equals(Phase.Configuring)) { - configured.add(address); - } - } - assertEquals(2, unConfigured.size()); - assertEquals(11, configured.size(), "contains topic + 10 subscriptions"); - Iterator
unconfiguredIterator = unConfigured.iterator(); - assertFalse(configured.contains(unconfiguredIterator.next())); - assertFalse(configured.contains(unconfiguredIterator.next())); - } - - @Test - public void testDurableSubscriptionsSharded() throws Exception { - AddressProvisioner provisioner = new ProvisionerTestFixture(Arrays.asList( - new ResourceAllowance("broker", 2), - new ResourceAllowance("router", 1), - new ResourceAllowance("aggregate", 3))).addressProvisioner; - - Address t1 = createAddress("t1", "topic", "large-topic"); - Address t2 = createAddress("t2", "topic", "large-topic"); - Address s1 = createSubscription("s1", "t1", "small-subscription"); - Set
addressSet = Sets.newSet( - t1, - t2, - s1); - - Map> usageMap = provisioner.checkUsage(Collections.emptySet()); - Map> neededMap = provisioner.checkQuota(usageMap, addressSet, addressSet); - - assertThat(neededMap.keySet().size(), is(3)); - assertThat(AddressProvisioner.sumTotalNeeded(neededMap), is(3)); - - List brokerClusters = new ArrayList<>(Arrays.asList(createCluster("broker", 1))); - - when(generator.generateCluster(eq(provisioner.getShardedClusterId(t1)), anyInt(), eq(t1), any(), any())).thenReturn(new BrokerCluster(provisioner.getShardedClusterId(t1), new KubernetesList())); - when(generator.generateCluster(eq(provisioner.getShardedClusterId(t2)), anyInt(), eq(t2), any(), any())).thenReturn(new BrokerCluster(provisioner.getShardedClusterId(t2), new KubernetesList())); - provisioner.provisionResources(createDeployment(1), brokerClusters, neededMap, addressSet, infraConfig); - - for (Address address : addressSet) { - assertThat(address.getStatus().getPhase(), is(Phase.Configuring)); - } - verify(generator).generateCluster(eq(provisioner.getShardedClusterId(t2)), eq(1), eq(t2), any(), any()); - verify(generator).generateCluster(eq(provisioner.getShardedClusterId(t1)), eq(1), eq(t1), any(), any()); - } - - @Test - public void testLargeSubscription() throws Exception { - AddressProvisioner provisioner = new ProvisionerTestFixture(Arrays.asList( - new ResourceAllowance("broker", 2), - new ResourceAllowance("router", 1), - new ResourceAllowance("aggregate", 3))).addressProvisioner; - - Address t1 = createAddress("t1", "topic", "large-topic"); - Address s1 = createSubscription("s1", "t1", "large-subscription"); - Set
addressSet = Sets.newSet( - t1, - s1); - - Map> usageMap = provisioner.checkUsage(Collections.emptySet()); - Map> neededMap = provisioner.checkQuota(usageMap, addressSet, addressSet); - - assertThat(neededMap.keySet().size(), is(3)); - assertThat(AddressProvisioner.sumTotalNeeded(neededMap), is(2)); - - List brokerClusters = new ArrayList<>(Arrays.asList(createCluster("broker", 1))); - - when(generator.generateCluster(eq(provisioner.getShardedClusterId(t1)), anyInt(), eq(t1), any(), any())).thenReturn(new BrokerCluster(provisioner.getShardedClusterId(t1), new KubernetesList())); - provisioner.provisionResources(createDeployment(1), brokerClusters, neededMap, addressSet, infraConfig); - - for (Address address : addressSet) { - assertThat(address.getStatus().getPhase(), is(Configuring)); - } - verify(generator).generateCluster(eq(provisioner.getShardedClusterId(t1)), eq(1), eq(t1), any(), any()); - } - - @Test - public void testDurableSubscriptionsShardedStaysOnTopicBroker() { - AddressProvisioner provisioner = new ProvisionerTestFixture(Arrays.asList( - new ResourceAllowance("broker", 2), - new ResourceAllowance("router", 1), - new ResourceAllowance("aggregate", 3))).addressProvisioner; - - Address t1 = createAddress("t1", "topic", "small-topic"); - Address t2 = createAddress("t2", "topic", "small-topic"); - - Set
addressSet = Sets.newSet( - t1, - createSubscription("s1", "t1", "small-subscription"), - createSubscription("s2", "t1", "small-subscription"), - createSubscription("s3", "t1", "small-subscription"), - createSubscription("s4", "t1", "small-subscription"), - createSubscription("s5", "t1", "small-subscription"), - createSubscription("s6", "t1", "small-subscription"), - createSubscription("s7", "t1", "small-subscription"), - createSubscription("s8", "t1", "small-subscription"), - createSubscription("s9", "t1", "small-subscription"), - createSubscription("s10", "t1", "small-subscription"), - createSubscription("s11", "t1", "small-subscription"), - createSubscription("s12", "t1", "small-subscription"), - t2); - - Map> usageMap = provisioner.checkUsage(Collections.emptySet()); - Map> neededMap = provisioner.checkQuota(usageMap, addressSet, addressSet); - - assertThat(neededMap.keySet().size(), is(3)); - assertThat(AddressProvisioner.sumTotalNeeded(neededMap), is(2)); - assertThat(AddressProvisioner.sumNeeded(neededMap.get("router")), is(1)); - assertThat(AddressProvisioner.sumNeeded(neededMap.get("broker")), is(1)); - assertThat(AddressProvisioner.sumNeeded(neededMap.get("subscription")), is(1)); - - Set
configured = new HashSet<>(); - Set
unConfigured = new HashSet<>(); - - for (Address address : addressSet) { - if (address.getStatus().getPhase().equals(Phase.Pending)) { - unConfigured.add(address); - } else if (address.getStatus().getPhase().equals(Phase.Configuring)) { - configured.add(address); - } - } - assertEquals(2, unConfigured.size()); - assertTrue(configured.contains(t1)); - assertTrue(configured.contains(t2)); - assertEquals(12, configured.size(), "contains 2 topic + 10 subscriptions"); - Iterator
unconfiguredIterator = unConfigured.iterator(); - assertFalse(configured.contains(unconfiguredIterator.next())); - assertFalse(configured.contains(unconfiguredIterator.next())); - } -} diff --git a/standard-controller/src/test/java/io/enmasse/controller/standard/BrokerClusterTest.java b/standard-controller/src/test/java/io/enmasse/controller/standard/BrokerClusterTest.java deleted file mode 100644 index 4842995dbfc..00000000000 --- a/standard-controller/src/test/java/io/enmasse/controller/standard/BrokerClusterTest.java +++ /dev/null @@ -1,141 +0,0 @@ -/* - * Copyright 2016-2018, EnMasse authors. - * License: Apache License 2.0 (see the file LICENSE or http://apache.org/licenses/LICENSE-2.0.html). - */ -package io.enmasse.controller.standard; - -import io.enmasse.admin.model.v1.StandardInfraConfig; -import io.fabric8.kubernetes.api.model.*; -import io.fabric8.kubernetes.api.model.apps.StatefulSet; -import io.fabric8.kubernetes.api.model.apps.StatefulSetBuilder; -import org.junit.jupiter.api.Test; - -import static org.junit.jupiter.api.Assertions.*; - -public class BrokerClusterTest { - @Test - public void testPvcStaysTheSameIfNotChanged() throws Exception { - BrokerCluster oldCluster = new BrokerCluster("broker", new KubernetesListBuilder() - .addToStatefulSetItems(new StatefulSetBuilder() - .editOrNewMetadata() - .withName("broker") - .endMetadata() - .editOrNewSpec() - .withReplicas(1) - .addNewVolumeClaimTemplate() - .editOrNewMetadata() - .withName("myclaim") - .endMetadata() - .editOrNewSpec() - .addToAccessModes("ReadWriteOnce") - .withNewResources() - .addToRequests("storage", new Quantity("2Gi")) - .endResources() - .endSpec() - .endVolumeClaimTemplate() - .endSpec() - .build()) - .addToItems(new PersistentVolumeClaimBuilder() - .editOrNewMetadata() - .withName("myclaim") - .endMetadata() - .editOrNewSpec() - .addToAccessModes("ReadWriteOnce") - .withNewResources() - .addToRequests("storage", new Quantity("2Gi")) - .endResources() - .endSpec() - .build()) - .build()); - - oldCluster.updateResources(oldCluster, new StandardInfraConfig()); - PersistentVolumeClaim newClaim = null; - StatefulSet newBroker = null; - for (HasMetadata resource : oldCluster.getResources().getItems()) { - if (resource instanceof PersistentVolumeClaim) { - newClaim = (PersistentVolumeClaim) resource; - } - if (resource instanceof StatefulSet) { - newBroker = (StatefulSet) resource; - } - } - - assertEquals("2Gi", newBroker.getSpec().getVolumeClaimTemplates().get(0).getSpec().getResources().getRequests().get("storage").getAmount()); - assertEquals("2Gi", newClaim.getSpec().getResources().getRequests().get("storage").getAmount()); - } - - @Test - public void testPvcIsModifiedStatefulStaysTheSameIfModified() throws Exception { - BrokerCluster oldCluster = new BrokerCluster("broker", new KubernetesListBuilder() - .addToStatefulSetItems(new StatefulSetBuilder() - .editOrNewMetadata() - .withName("broker") - .endMetadata() - .editOrNewSpec() - .withReplicas(1) - .addNewVolumeClaimTemplate() - .editOrNewMetadata() - .withName("myclaim") - .endMetadata() - .editOrNewSpec() - .addToAccessModes("ReadWriteOnce") - .withNewResources() - .addToRequests("storage", new Quantity("2Gi")) - .endResources() - .endSpec() - .endVolumeClaimTemplate() - .endSpec() - .build()) - .addToItems(new PersistentVolumeClaimBuilder() - .editOrNewMetadata() - .withName("myclaim") - .endMetadata() - .editOrNewSpec() - .addToAccessModes("ReadWriteOnce") - .withNewResources() - .addToRequests("storage", new Quantity("2Gi")) - .endResources() - .endSpec() - .build()) - .build()); - - BrokerCluster newCluster = new BrokerCluster("broker", new KubernetesListBuilder() - .addToStatefulSetItems(new StatefulSetBuilder() - .editOrNewMetadata() - .withName("broker") - .endMetadata() - .editOrNewSpec() - .withReplicas(1) - .addNewVolumeClaimTemplate() - .editOrNewMetadata() - .withName("myclaim") - .endMetadata() - .editOrNewSpec() - .addToAccessModes("ReadWriteOnce") - .withNewResources() - .addToRequests("storage", new Quantity("5Gi")) - .endResources() - .endSpec() - .endVolumeClaimTemplate() - .endSpec() - .build()) - .build()); - - oldCluster.updateResources(newCluster, new StandardInfraConfig()); - PersistentVolumeClaim newClaim = null; - StatefulSet newBroker = null; - for (HasMetadata resource : oldCluster.getResources().getItems()) { - if (resource instanceof PersistentVolumeClaim) { - newClaim = (PersistentVolumeClaim) resource; - } - if (resource instanceof StatefulSet) { - newBroker = (StatefulSet) resource; - } - } - - assertNotNull(newClaim); - assertNotNull(newBroker); - assertEquals("2Gi", newBroker.getSpec().getVolumeClaimTemplates().get(0).getSpec().getResources().getRequests().get("storage").getAmount()); - assertEquals("5Gi", newClaim.getSpec().getResources().getRequests().get("storage").getAmount()); - } -} diff --git a/standard-controller/src/test/java/io/enmasse/controller/standard/StandardControllerSchema.java b/standard-controller/src/test/java/io/enmasse/controller/standard/StandardControllerSchema.java deleted file mode 100644 index d6b32daaae5..00000000000 --- a/standard-controller/src/test/java/io/enmasse/controller/standard/StandardControllerSchema.java +++ /dev/null @@ -1,319 +0,0 @@ -/* - * Copyright 2018, EnMasse authors. - * License: Apache License 2.0 (see the file LICENSE or http://apache.org/licenses/LICENSE-2.0.html). - */ -package io.enmasse.controller.standard; - -import io.enmasse.address.model.AddressSpaceType; -import io.enmasse.address.model.AddressSpaceTypeBuilder; -import io.enmasse.address.model.AddressTypeBuilder; -import io.enmasse.address.model.EndpointSpecBuilder; -import io.enmasse.address.model.MessageTtlBuilder; -import io.enmasse.address.model.Schema; -import io.enmasse.address.model.SchemaBuilder; -import io.enmasse.admin.model.v1.AddressPlanBuilder; -import io.enmasse.admin.model.v1.AddressPlanSpecBuilder; -import io.enmasse.admin.model.v1.AddressSpacePlan; -import io.enmasse.admin.model.v1.AddressSpacePlanBuilder; -import io.enmasse.admin.model.v1.ResourceAllowance; -import io.enmasse.admin.model.v1.StandardInfraConfigBuilder; -import io.enmasse.config.AnnotationKeys; -import io.enmasse.k8s.api.SchemaProvider; -import io.fabric8.kubernetes.api.model.NodeSelectorRequirementBuilder; -import io.fabric8.kubernetes.api.model.ObjectMetaBuilder; -import io.fabric8.kubernetes.api.model.PodTemplateSpec; -import io.fabric8.kubernetes.api.model.PodTemplateSpecBuilder; -import io.fabric8.kubernetes.api.model.PreferredSchedulingTermBuilder; - -import java.util.Arrays; -import java.util.Collections; -import java.util.List; -import java.util.Map; -import java.util.stream.Collectors; - -public class StandardControllerSchema implements SchemaProvider { - - private AddressSpacePlan plan; - private AddressSpaceType type; - private Schema schema; - - public StandardControllerSchema() { - this(Map.of("router", 1.0, "broker", 3.0, "aggregate", 3.0)); - } - - public StandardControllerSchema(List resourceAllowanceList) { - this(resourceAllowanceList.stream().collect(Collectors.toMap(ResourceAllowance::getName, ResourceAllowance::getMax))); - } - - public StandardControllerSchema(Map resourceAllowanceList) { - plan = new AddressSpacePlanBuilder() - .withNewMetadata() - .withName("plan1") - .endMetadata() - - .editOrNewSpec() - .withInfraConfigRef("cfg1") - .withResourceLimits(resourceAllowanceList) - .withAddressSpaceType("standard") - .withAddressPlans(Arrays.asList( - "small-anycast", - "small-queue", - "small-queue-with-maxttl", - "small-queue-with-minttl", - "pooled-queue-larger", - "pooled-queue-small", - "pooled-queue-tiny", - "small-topic", - "small-subscription" - )) - .endSpec() - .build(); - - type = new AddressSpaceTypeBuilder() - .withName("standard") - .withDescription("standard") - .withPlans(Arrays.asList(plan)) - .withAvailableEndpoints(Collections.singletonList(new EndpointSpecBuilder() - .withName("messaging") - .withService("messaging") - .build())) - .withAddressTypes(Arrays.asList( - new AddressTypeBuilder() - .withName("anycast") - .withDescription("anycast") - .withPlans(Arrays.asList( - new AddressPlanBuilder() - .withMetadata(new ObjectMetaBuilder().withName("small-anycast").build()) - .editOrNewSpec() - .withAddressType("anycast") - .withResources(Map.of("router", 0.2000000000)) - .endSpec() - .build())) - .build(), - new AddressTypeBuilder() - .withName("queue") - .withDescription("queue") - .withPlans(Arrays.asList( - new AddressPlanBuilder() - .withMetadata(new ObjectMetaBuilder().withName("pooled-queue-large").build()) - .editOrNewSpec() - .withAddressType("queue") - .withResources(Map.of("broker", 0.6)) - .endSpec() - .build(), - new AddressPlanBuilder() - .withMetadata(new ObjectMetaBuilder().withName("pooled-queue-small").build()) - .editOrNewSpec() - .withAddressType("queue") - .withResources(Map.of("broker", 0.1)) - .endSpec() - .build(), - new AddressPlanBuilder() - .withMetadata(new ObjectMetaBuilder().withName("pooled-queue-tiny").build()) - .editOrNewSpec() - .withAddressType("queue") - .withResources(Map.of("broker", 0.049)) - .endSpec() - .build(), - new AddressPlanBuilder() - .withMetadata(new ObjectMetaBuilder().withName("small-queue").build()) - .editOrNewSpec() - .withAddressType("queue") - .withResources(Map.of("router", 0.2, "broker", 0.4)) - .endSpec() - .build(), - new AddressPlanBuilder() - .withMetadata(new ObjectMetaBuilder().withName("small-queue-with-maxttl").build()) - .withSpec(new AddressPlanSpecBuilder() - .withAddressType("queue") - .withResources(Map.of("router", 0.2, "broker", 0.4)) - .withMessageTtl(new MessageTtlBuilder().withMaximum(30000L).build()).build()) - .build(), - new AddressPlanBuilder() - .withMetadata(new ObjectMetaBuilder().withName("small-queue-with-minttl").build()) - .withSpec(new AddressPlanSpecBuilder() - .withAddressType("queue") - .withResources(Map.of("router", 0.2, "broker", 0.4)) - .withMessageTtl(new MessageTtlBuilder().withMinimum(10000L).build()).build()) - .build(), - new AddressPlanBuilder() - .withMetadata(new ObjectMetaBuilder().withName("small-sharded-queue").build()) - .withNewSpec() - .withAddressType("queue") - .withPartitions(3) - .withResources(Map.of("router", 0.2, "broker", 0.4)) - .endSpec() - .build(), - new AddressPlanBuilder() - .withMetadata(new ObjectMetaBuilder().withName("medium-sharded-queue").build()) - .withNewSpec() - .withAddressType("queue") - .withResources(Map.of("router", 0.2, "broker", 1.6)) - .withPartitions(2) - .endSpec() - .build(), - new AddressPlanBuilder() - .withMetadata(new ObjectMetaBuilder().withName("large-queue").build()) - .editOrNewSpec() - .withAddressType("queue") - .withResources(Map.of("router", 0.2, "broker", 1.0)) - .endSpec() - .build(), - new AddressPlanBuilder() - .withMetadata(new ObjectMetaBuilder().withName("xlarge-queue").build()) - .editOrNewSpec() - .withAddressType("queue") - .withResources(Map.of("router", 0.2, "broker", 2.0)) - .endSpec() - .build(), - new AddressPlanBuilder() - .withMetadata(new ObjectMetaBuilder().withName("mega-xlarge-queue").build()) - .editOrNewSpec() - .withAddressType("queue") - .withResources(Map.of("router", 0.2, "broker", 10.0)) - .endSpec() - .build())) - .build(), - new AddressTypeBuilder() - .withName("topic") - .withDescription("topic") - .withPlans(Arrays.asList( - new AddressPlanBuilder() - .withMetadata(new ObjectMetaBuilder().withName("small-topic").build()) - .editOrNewSpec() - .withAddressType("topic") - .withResources(Map.of("router", 0.1, "broker", 0.2)) - .endSpec() - .build(), - new AddressPlanBuilder() - .withMetadata(new ObjectMetaBuilder().withName("large-topic").build()) - .editOrNewSpec() - .withAddressType("topic") - .withResources(Map.of("router", 0.2, "broker", 1.0)) - .endSpec() - .build(), - new AddressPlanBuilder() - .withMetadata(new ObjectMetaBuilder().withName("xlarge-topic").build()) - .editOrNewSpec() - .withAddressType("topic") - .withResources(Map.of("router", 0.2, "broker", 2.0)) - .endSpec() - .build())) - .build(), - new AddressTypeBuilder() - .withName("subscription") - .withDescription("subscription") - .withPlans(Arrays.asList( - new AddressPlanBuilder() - .withMetadata(new ObjectMetaBuilder().withName("small-subscription").build()) - .editOrNewSpec() - .withAddressType("subscription") - .withResources(Map.of("router", 0.05, "broker", 0.1)) - .endSpec() - .build(), - new AddressPlanBuilder() - .withMetadata(new ObjectMetaBuilder().withName("large-subscription").build()) - .editOrNewSpec() - .withAddressType("subscription") - .withResources(Map.of("router", 0.1, "broker", 1.0)) - .endSpec() - .build())) - - .build())) - .withInfraConfigs(Arrays.asList( - new StandardInfraConfigBuilder() - .withNewMetadata() - .withName("cfg1") - .addToAnnotations(AnnotationKeys.QUEUE_TEMPLATE_NAME, "queuetemplate") - .endMetadata() - - .withNewSpec() - .withVersion("latest") - .withNewAdmin() - .editOrNewResources() - .withMemory("512Mi") - .endResources() - .endAdmin() - .withNewBroker() - .editOrNewResources() - .withMemory("512Mi") - .withStorage("2Gi") - .endResources() - .withAddressFullPolicy("FAIL") - .withStorageClassName("mysc") - .withUpdatePersistentVolumeClaim(false) - .withPodTemplate(createTemplateSpec(Collections.singletonMap("key", "value"), "myaff", "mykey", "prioclass")) - .endBroker() - .withNewRouter() - .editOrNewResources() - .withMemory("512Mi") - .endResources() - .withMinReplicas(1) - .withLinkCapacity(500) - .endRouter() - .endSpec() - .build() - )) - .build(); - - schema = new SchemaBuilder() - .withAddressSpaceTypes(type) - .build(); - } - - public static PodTemplateSpec createTemplateSpec(Map labels, String nodeAffinityValue, String tolerationKey, String priorityClassName) { - PodTemplateSpecBuilder builder = new PodTemplateSpecBuilder(); - if (labels != null) { - builder.editOrNewMetadata() - .withLabels(labels) - .endMetadata(); - } - - if (nodeAffinityValue != null) { - builder.editOrNewSpec() - .editOrNewAffinity() - .editOrNewNodeAffinity() - .addToPreferredDuringSchedulingIgnoredDuringExecution(new PreferredSchedulingTermBuilder() - .withNewPreference() - .addToMatchExpressions(new NodeSelectorRequirementBuilder() - .addToValues(nodeAffinityValue) - .build()) - .endPreference() - .build()) - .endNodeAffinity() - .endAffinity() - .endSpec(); - } - - if (tolerationKey != null) { - builder.editOrNewSpec() - .addNewToleration() - .withKey(tolerationKey) - .withOperator("Exists") - .withEffect("NoSchedule") - .endToleration() - .endSpec(); - } - - if (priorityClassName != null) { - builder.editOrNewSpec() - .withPriorityClassName(priorityClassName) - .endSpec(); - } - - return builder.build(); - } - - - public AddressSpacePlan getPlan() { - return plan; - } - - public AddressSpaceType getType() { - return type; - } - - public Schema getSchema() { - return schema; - } -} diff --git a/standard-controller/src/test/java/io/enmasse/controller/standard/TemplateBrokerSetGeneratorTest.java b/standard-controller/src/test/java/io/enmasse/controller/standard/TemplateBrokerSetGeneratorTest.java deleted file mode 100644 index 253bf326c0b..00000000000 --- a/standard-controller/src/test/java/io/enmasse/controller/standard/TemplateBrokerSetGeneratorTest.java +++ /dev/null @@ -1,210 +0,0 @@ -/* - * Copyright 2016-2018, EnMasse authors. - * License: Apache License 2.0 (see the file LICENSE or http://apache.org/licenses/LICENSE-2.0.html). - */ - -package io.enmasse.controller.standard; - -import io.enmasse.address.model.Address; -import io.enmasse.address.model.AddressBuilder; -import io.enmasse.admin.model.v1.StandardInfraConfig; -import io.enmasse.config.AnnotationKeys; -import io.fabric8.kubernetes.api.model.*; -import io.fabric8.kubernetes.api.model.apps.StatefulSet; -import io.fabric8.kubernetes.api.model.apps.StatefulSetSpec; -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Test; -import org.mockito.ArgumentCaptor; - -import java.util.Arrays; -import java.util.Collections; -import java.util.List; -import java.util.Map; - -import static org.hamcrest.CoreMatchers.*; -import static org.hamcrest.MatcherAssert.assertThat; -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertNotNull; -import static org.mockito.Mockito.*; - -public class TemplateBrokerSetGeneratorTest { - public static final String CONTAINER_NAME = "container1"; - private Kubernetes kubernetes; - private StandardControllerSchema standardControllerSchema; - private BrokerSetGenerator generator; - - @BeforeEach - public void setUp() { - kubernetes = mock(Kubernetes.class); - - standardControllerSchema = new StandardControllerSchema(); - generator = new TemplateBrokerSetGenerator(kubernetes, new StandardControllerOptions(), Collections.emptyMap()); - } - - @Test - public void testDirect() throws Exception { - Address dest = createAddress("foo_bar_FOO", "anycast"); - @SuppressWarnings("unchecked") - ArgumentCaptor> captor = ArgumentCaptor.forClass(Map.class); - BrokerCluster clusterList = generateCluster(dest, captor); - List resources = clusterList.getResources().getItems(); - assertThat(resources.size(), is(1)); - Map parameters = captor.getValue(); - assertThat(parameters.size(), is(12)); - } - - @Test - public void testStoreAndForward() throws Exception { - Address dest = createAddress("foo.bar", "queue"); - @SuppressWarnings("unchecked") - ArgumentCaptor> captor = ArgumentCaptor.forClass(Map.class); - BrokerCluster clusterList = generateCluster(dest, captor); - List resources = clusterList.getResources().getItems(); - assertThat(resources.size(), is(1)); - assertResourcesHaveClusterId(dest.getMetadata().getName(), resources); - StatefulSet set = (StatefulSet) resources.get(0); - assertThat(set.getSpec().getVolumeClaimTemplates().get(0).getSpec().getStorageClassName(), is("mysc")); - assertThat(set.getSpec().getReplicas(), is(1)); - - PodTemplateSpec templateSpec = standardControllerSchema.getSchema().findAddressSpaceType("standard").map(type -> (StandardInfraConfig) type.findInfraConfig("cfg1").orElse(null)).orElse(null).getSpec().getBroker().getPodTemplate(); - assertTemplateSpec(set.getSpec().getTemplate(), templateSpec); - Map parameters = captor.getValue(); - assertThat(parameters.size(), is(12)); - } - - @Test - public void testContainerEnv() throws Exception { - EnvVar myEnv = new EnvVarBuilder().withName("MYVAR1").withValue("NEWVALUE").build(); - - PodTemplateSpec templateSpec = standardControllerSchema.getSchema().findAddressSpaceType("standard").map(type -> (StandardInfraConfig) type.findInfraConfig("cfg1").orElse(null)).orElse(null).getSpec().getBroker().getPodTemplate(); - templateSpec.getSpec().setContainers(Collections.singletonList(new ContainerBuilder() - .withName(CONTAINER_NAME) - .withEnv(myEnv) - .build())); - - Address dest = createAddress("foo.bar", "queue"); - @SuppressWarnings("unchecked") - BrokerCluster clusterList = generateCluster(dest, ArgumentCaptor., Map>forClass(Map.class)); - List resources = clusterList.getResources().getItems(); - assertThat(resources.size(), is(1)); - assertResourcesHaveClusterId(dest.getMetadata().getName(), resources); - StatefulSet set = (StatefulSet) resources.get(0); - - StatefulSetSpec spec = set.getSpec(); - List containers = spec.getTemplate().getSpec().getContainers(); - assertThat(containers.size(), is(1)); - List envVars = containers.get(0).getEnv(); - - assertThat(envVars.size(), is(1)); - assertThat(envVars, hasItem(myEnv)); - } - - @Test - public void testSupplementedContainerEnv() throws Exception { - EnvVar original = new EnvVarBuilder().withName("MYVAR1").withValue("ORIGINAL").build(); - EnvVar replacement = new EnvVarBuilder().withName("MYVAR1").withValue("NEWVALUE").build(); - EnvVar other = new EnvVarBuilder().withName("OTHER").withValue("OTHERVAL").build(); - - PodTemplateSpec templateSpec = standardControllerSchema.getSchema().findAddressSpaceType("standard").map(type -> (StandardInfraConfig) type.findInfraConfig("cfg1").orElse(null)).orElse(null).getSpec().getBroker().getPodTemplate(); - templateSpec.getSpec().setContainers(Collections.singletonList(new ContainerBuilder() - .withName(CONTAINER_NAME) - .withEnv(replacement) - .build())); - - Address dest = createAddress("foo.bar", "queue"); - @SuppressWarnings("unchecked") - BrokerCluster clusterList = generateCluster(dest, ArgumentCaptor., Map>forClass(Map.class), Arrays.asList(original, other)); - List resources = clusterList.getResources().getItems(); - assertThat(resources.size(), is(1)); - assertResourcesHaveClusterId(dest.getMetadata().getName(), resources); - StatefulSet set = (StatefulSet) resources.get(0); - - StatefulSetSpec spec = set.getSpec(); - List containers = spec.getTemplate().getSpec().getContainers(); - List envVars = containers.get(0).getEnv(); - - assertThat(envVars.size(), is(2)); - assertThat(envVars, hasItems(replacement, other)); - } - - private void assertResourcesHaveClusterId(String expectedClusterId, List resources) { - for (HasMetadata resource : resources) { - Map annotations = resource.getMetadata().getAnnotations(); - assertNotNull(annotations.get(AnnotationKeys.CLUSTER_ID)); - assertThat(annotations.get(AnnotationKeys.CLUSTER_ID), is(expectedClusterId)); - } - } - - private Address createAddress(String address, String type) { - return new AddressBuilder() - .withNewMetadata() - .withName(address) - .endMetadata() - - .withNewSpec() - .withAddress(address) - .withAddressSpace("myinstance") - .withType(type) - .withPlan("plan1") - .endSpec() - - .build(); - } - - private BrokerCluster generateCluster(Address address, ArgumentCaptor> captor) throws Exception { - return generateCluster(address, captor, Collections.emptyList()); - } - - private BrokerCluster generateCluster(Address address, ArgumentCaptor> captor, List envVars) throws Exception { - when(kubernetes.processTemplate(anyString(), captor.capture())).thenReturn(new KubernetesListBuilder().addNewStatefulSetItem().withNewMetadata().withName("testset").endMetadata(). - withNewSpec() - .withReplicas(0) - .withNewTemplate() - .editOrNewMetadata() - .endMetadata() - .withNewSpec() - .addNewContainer() - .withName(CONTAINER_NAME) - .withEnv(envVars) - .endContainer() - .endSpec() - .endTemplate() - .withVolumeClaimTemplates(new PersistentVolumeClaimBuilder() - .withNewSpec() - .endSpec() - .build()) - .endSpec() - .endStatefulSetItem().build()); - - return generator.generateCluster(address.getMetadata().getName(), 1, address, null, - standardControllerSchema.getSchema().findAddressSpaceType("standard").map(type -> (StandardInfraConfig) type.findInfraConfig("cfg1").orElse(null)).orElse(null)); - } - - private void assertTemplateSpec(PodTemplateSpec pod, PodTemplateSpec templateSpec) { - if (templateSpec.getMetadata().getLabels() != null) { - for (Map.Entry labelPair : templateSpec.getMetadata().getLabels().entrySet()) { - assertEquals(labelPair.getValue(), pod.getMetadata().getLabels().get(labelPair.getKey()), "Labels do not match"); - } - } - - if (templateSpec.getSpec().getAffinity() != null) { - assertEquals(templateSpec.getSpec().getAffinity(), pod.getSpec().getAffinity(), "Affinity rules do not match"); - } - - if (templateSpec.getSpec().getPriorityClassName() != null) { - assertEquals(templateSpec.getSpec().getPriorityClassName(), pod.getSpec().getPriorityClassName(), "Priority class names do not match"); - } - - if (templateSpec.getSpec().getTolerations() != null) { - assertEquals(templateSpec.getSpec().getTolerations(), pod.getSpec().getTolerations(), "List of tolerations does not match"); - } - - for (Container expectedContainer : templateSpec.getSpec().getContainers()) { - for (Container actualContainer : pod.getSpec().getContainers()) { - if (expectedContainer.getName().equals(actualContainer.getName())) { - assertEquals(expectedContainer.getResources(), actualContainer.getResources()); - } - } - } - } -} diff --git a/standard-controller/src/test/resources/logback-test.xml b/standard-controller/src/test/resources/logback-test.xml deleted file mode 100644 index 0037f119770..00000000000 --- a/standard-controller/src/test/resources/logback-test.xml +++ /dev/null @@ -1,17 +0,0 @@ - - - - - - - %d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n - - - - - - - - - - \ No newline at end of file diff --git a/systemtests/pom.xml b/systemtests/pom.xml index 8d30417eeb4..6ae9bde3b93 100644 --- a/systemtests/pom.xml +++ b/systemtests/pom.xml @@ -5,7 +5,7 @@ io.enmasse enmasse - 0.32-SNAPSHOT + 1.0-SNAPSHOT 4.0.0 systemtests @@ -103,11 +103,6 @@ junit-jupiter-params test - - io.github.artsok - rerunner-jupiter - test - org.seleniumhq.selenium selenium-java @@ -135,6 +130,10 @@ org.eclipse.hono hono-service-base + + org.eclipse.hono + hono-service-device-registry-base + org.hawkular.agent prometheus-scraper @@ -160,6 +159,7 @@ ${groups} ${excludeGroups} ${skip.tests} + 0 ${release.version} ${application.docs} @@ -223,130 +223,21 @@ systemtests false - (shared-standard | shared-brokered | isolated | soak | shared-iot | isolated-iot | isolated-shared-infra) & !upgrade - - - - shared - - false - (shared-standard | shared-brokered) & !isolated & !smoke & !soak - & !shared-iot & !olm & !upgrade & !scale & !framework - - - - - isolated-shared-infra - - (isolated-shared-infra) & !soak & !shared-iot & !olm & !upgrade & !scale - false - - - - isolated - - (isolated | isolated-broker | isolated-standard) & !soak & !shared-iot & !olm & !upgrade & !scale & !framework - false + systemtests & !upgrade & !scale pr false - (shared-standard | shared-brokered | isolated) & !nonPR & !smoke & !soak - & !shared-iot & !olm & !upgrade & !scale & !framework - - - - - iot - - (isolated-iot | shared-iot) & !soak & !olm & !upgrade & !scale & !framework - false - - - - shared-iot - - shared-iot & !soak & !olm & !upgrade & !framework - false - - - - isolated-iot - - isolated-iot & !soak & !olm & !upgrade & !framework - false - - - - smoke-iot - - (shared-iot | isolated-iot) & smoke & !soak & !olm & !upgrade & !scale & !framework - false - - - - soak - - soak - false - - - - smoke - - smoke & !shared-iot & !isolated-iot & !soak & !olm & !upgrade & !scale & !framework - false - - - - upgrade - - upgrade & !soak & !olm & !scale & !framework - false - - - - olm - - olm - false - - - - olm-pr - - olm & !nonPR - false - - - - iot-release - - (shared-iot | isolated-iot) & !noneAuth & !smoke & !soak & !olm & !upgrade & !framework - false - - - - acceptance - - acceptance & !soak & !upgrade & !scale & !framework - false - - - - acceptance-no-iot - - acceptance & !soak & !upgrade & !shared-iot & !isolated-iot & !scale & !framework - false + systemtests & !upgrade scale - scale false + scale diff --git a/systemtests/scripts/build_pr_response.sh b/systemtests/scripts/build_pr_response.sh index 4180f103f5c..4ad1fc519e4 100755 --- a/systemtests/scripts/build_pr_response.sh +++ b/systemtests/scripts/build_pr_response.sh @@ -39,7 +39,7 @@ fi SUMMARY="**TEST_PROFILE**: ${TEST_PROFILE}\n**TEST_CASE:** ${TEST_CASE}\n**TOTAL:** ${TEST_COUNT}\n**PASS:** $((TEST_COUNT - TEST_ALL_FAILED_COUNT - TEST_SKIPPED_COUNT))\n**FAIL:** ${TEST_ALL_FAILED_COUNT}\n**SKIP:** ${TEST_SKIPPED_COUNT}\n**BUILD_NUMBER:** ${BUILD_ID}\n**BUILD_ENV:** ${BUILD_ENV}\n" -FAILED_TESTS=$(find "${RESULTS_PATH}" -name 'TEST*.xml' -type f -print0 | xargs -0 sed -n "s#\(\)#\1#p" | awk -F '"' '{print "\\n- " $2 " in " $4}') +FAILED_TESTS=$(find "${RESULTS_PATH}" -name 'TEST*.xml' -type f -print0 | xargs -0 awk '//{ getline x; if (x ~ " SHARED_TAGS = new HashSet<>(Arrays.asList(SHARED_BROKERED, SHARED_STANDARD, SHARED_IOT)); public static final Set IOT_TAGS = new HashSet<>(Arrays.asList(SHARED_IOT, ISOLATED_IOT)); - public static final Set SHARED_INFRA_TAGS = new HashSet<>(Arrays.asList(ISOLATED_SHARED_INFRA)); } diff --git a/systemtests/src/main/java/io/enmasse/systemtest/bases/ITestBase.java b/systemtests/src/main/java/io/enmasse/systemtest/bases/ITestBase.java deleted file mode 100644 index a5dad924b3c..00000000000 --- a/systemtests/src/main/java/io/enmasse/systemtest/bases/ITestBase.java +++ /dev/null @@ -1,44 +0,0 @@ -/* - * Copyright 2019, EnMasse authors. - * License: Apache License 2.0 (see the file LICENSE or http://apache.org/licenses/LICENSE-2.0.html). - */ -package io.enmasse.systemtest.bases; - -import io.enmasse.systemtest.clients.ClientUtils; -import io.enmasse.systemtest.logs.CustomLogger; -import io.enmasse.systemtest.manager.ResourceManager; -import io.enmasse.systemtest.model.address.AddressType; -import io.enmasse.systemtest.model.addressspace.AddressSpaceType; -import io.enmasse.systemtest.platform.Kubernetes; - -import org.slf4j.Logger; - -public interface ITestBase { - ClientUtils clientUtils = new ClientUtils(); - Logger LOGGER = CustomLogger.getLogger(); - Kubernetes kubernetes = Kubernetes.getInstance(); - default ClientUtils getClientUtils() { - return clientUtils; - } - - default AddressSpaceType getAddressSpaceType() { - return null; - } - - default String getDefaultPlan(AddressType addressType) { - return null; - } - - default String getDefaultAddressSpacePlan() { - return null; - } - - default String getDefaultAddrSpaceIdentifier() { - return "default"; - } - - default ResourceManager getResourceManager() { - return null; - } - -} diff --git a/systemtests/src/main/java/io/enmasse/systemtest/bases/ITestBaseBrokered.java b/systemtests/src/main/java/io/enmasse/systemtest/bases/ITestBaseBrokered.java deleted file mode 100644 index 3480956cb1f..00000000000 --- a/systemtests/src/main/java/io/enmasse/systemtest/bases/ITestBaseBrokered.java +++ /dev/null @@ -1,28 +0,0 @@ -/* - * Copyright 2019, EnMasse authors. - * License: Apache License 2.0 (see the file LICENSE or http://apache.org/licenses/LICENSE-2.0.html). - */ -package io.enmasse.systemtest.bases; - -import io.enmasse.systemtest.model.address.AddressType; -import io.enmasse.systemtest.model.addressplan.DestinationPlan; -import io.enmasse.systemtest.model.addressspace.AddressSpaceType; - -public interface ITestBaseBrokered extends ITestBase { - - @Override - default AddressSpaceType getAddressSpaceType() { - return AddressSpaceType.BROKERED; - } - - @Override - default String getDefaultPlan(AddressType addressType) { - switch (addressType) { - case QUEUE: - return DestinationPlan.BROKERED_QUEUE; - case TOPIC: - return DestinationPlan.BROKERED_TOPIC; - } - return null; - } -} diff --git a/systemtests/src/main/java/io/enmasse/systemtest/bases/ITestBaseStandard.java b/systemtests/src/main/java/io/enmasse/systemtest/bases/ITestBaseStandard.java deleted file mode 100644 index 07d7b1fe49a..00000000000 --- a/systemtests/src/main/java/io/enmasse/systemtest/bases/ITestBaseStandard.java +++ /dev/null @@ -1,32 +0,0 @@ -/* - * Copyright 2019, EnMasse authors. - * License: Apache License 2.0 (see the file LICENSE or http://apache.org/licenses/LICENSE-2.0.html). - */ -package io.enmasse.systemtest.bases; - -import io.enmasse.systemtest.model.address.AddressType; -import io.enmasse.systemtest.model.addressplan.DestinationPlan; -import io.enmasse.systemtest.model.addressspace.AddressSpaceType; - -public interface ITestBaseStandard extends ITestBase { - - @Override - default AddressSpaceType getAddressSpaceType() { - return AddressSpaceType.STANDARD; - } - - @Override - default String getDefaultPlan(AddressType addressType) { - switch (addressType) { - case QUEUE: - return DestinationPlan.STANDARD_SMALL_QUEUE; - case TOPIC: - return DestinationPlan.STANDARD_SMALL_TOPIC; - case ANYCAST: - return DestinationPlan.STANDARD_SMALL_ANYCAST; - case MULTICAST: - return DestinationPlan.STANDARD_SMALL_MULTICAST; - } - return null; - } -} diff --git a/systemtests/src/main/java/io/enmasse/systemtest/bases/JUnitWorkaround.java b/systemtests/src/main/java/io/enmasse/systemtest/bases/JUnitWorkaround.java deleted file mode 100644 index 3285dc09418..00000000000 --- a/systemtests/src/main/java/io/enmasse/systemtest/bases/JUnitWorkaround.java +++ /dev/null @@ -1,120 +0,0 @@ -/* - * Copyright 2020, EnMasse authors. - * License: Apache License 2.0 (see the file LICENSE or http://apache.org/licenses/LICENSE-2.0.html). - */ - -package io.enmasse.systemtest.bases; - -import java.util.LinkedList; -import java.util.List; - -import org.junit.jupiter.api.BeforeAll; -import org.junit.jupiter.api.extension.AfterAllCallback; -import org.junit.jupiter.api.extension.BeforeEachCallback; -import org.junit.jupiter.api.extension.ExtensionContext; -import org.junit.jupiter.api.extension.LifecycleMethodExecutionExceptionHandler; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -/** - * Workaround for some JUnit issue. - *

- * TODO: Remove it after upgrade to surefire plugin 3.0.0-M5. - *

- * To use this you need to: - *

    - *
  • Add {@link JUnitWorkaround} as a JUnit extension. - *
- */ -public class JUnitWorkaround implements BeforeEachCallback, AfterAllCallback, LifecycleMethodExecutionExceptionHandler { - - private static final Logger logger = LoggerFactory.getLogger(JUnitWorkaround.class); - - private List exceptions; - - @Override - public void handleBeforeAllMethodExecutionException(final ExtensionContext context, final Throwable throwable) throws Throwable { - logger.info("Exception caught - recording"); - addBeforeAllException(throwable); - } - - /** - * Record an exception from a {@code @BeforeAll} method. - * - * @param e The exception to store. - */ - private void addBeforeAllException(final Throwable e) { - if (this.exceptions == null) { - this.exceptions = new LinkedList<>(); - } - this.exceptions.add(e); - } - - /** - * Fetch all stored exceptions. - * - * @return All exception that had been recorded. May be {@code null}. - */ - private List getBeforeAllExceptions() { - return this.exceptions; - } - - /** - * Clear all stored exceptions. - */ - private void clearBeforeAllExceptions() { - this.exceptions = null; - } - - /** - * Check if we have recorded "before all" exceptions. - *

- * As we use {@link #beforeEach(ExtensionContext)} this means that all tests covered by the wrapping - * {@link BeforeAll} call will fail. Which is reasonable as they did not get set up properly. - * - * @throws Throwable if there was any recorded exception. - */ - @Override - public void beforeEach(final ExtensionContext context) throws Exception { - - // check if we have recorded exceptions ... - - final List exceptions = getBeforeAllExceptions(); - if (exceptions == null || exceptions.isEmpty()) { - // ... all good - logger.info("No recorded @BeforeAll errors"); - return; - } - - logger.warn("There are @BeforeAll errors - count = {}", exceptions.size()); - - // build up exception - - Throwable first = null; - for (Throwable e : exceptions) { - if (first == null) { - first = e; - } else { - first.addSuppressed(e); - } - } - - // throw it - - if (first instanceof Exception) { - // normal exception - throw (Exception) first; - } else { - // assert*() calls throw AssertionError's - throw new Exception(first); - } - } - - @Override - public void afterAll(ExtensionContext context) throws Exception { - // we get called when we are closing up the BeforeAll calls - // now we can clear the collected exceptions and start fresh - clearBeforeAllExceptions(); - } - -} diff --git a/systemtests/src/main/java/io/enmasse/systemtest/bases/iot/ITestIoTBase.java b/systemtests/src/main/java/io/enmasse/systemtest/bases/iot/ITestIoTBase.java deleted file mode 100644 index e27d19a441c..00000000000 --- a/systemtests/src/main/java/io/enmasse/systemtest/bases/iot/ITestIoTBase.java +++ /dev/null @@ -1,38 +0,0 @@ -/* - * Copyright 2019, EnMasse authors. - * License: Apache License 2.0 (see the file LICENSE or http://apache.org/licenses/LICENSE-2.0.html). - */ -package io.enmasse.systemtest.bases.iot; - -import org.junit.jupiter.api.BeforeAll; - -import io.enmasse.systemtest.iot.DeviceManagementApi; -import io.enmasse.systemtest.iot.IoTTestSession; - -public interface ITestIoTBase { - String IOT_ADDRESS_EVENT = "event"; - String IOT_ADDRESS_TELEMETRY = "telemetry"; - String IOT_ADDRESS_CONTROL = "control"; - String IOT_ADDRESS_COMMAND = "command"; - String IOT_ADDRESS_COMMAND_RESPONSE = "command_response"; - - String[] IOT_ADDRESSES = new String[] { - IOT_ADDRESS_TELEMETRY, - IOT_ADDRESS_EVENT, - IOT_ADDRESS_CONTROL, - IOT_ADDRESS_COMMAND, - IOT_ADDRESS_COMMAND_RESPONSE, - }; - - String IOT_PROJECT_NAMESPACE = "iot-project-ns"; - - @BeforeAll - public static void deployDefaultCerts() throws Exception { - IoTTestSession.deployDefaultCerts(); - } - - @BeforeAll - public static void createDeviceManager() throws Exception { - DeviceManagementApi.createManagementServiceAccount(); - } -} diff --git a/systemtests/src/main/java/io/enmasse/systemtest/bases/iot/ITestIoTIsolated.java b/systemtests/src/main/java/io/enmasse/systemtest/bases/iot/ITestIoTIsolated.java deleted file mode 100644 index 35218089b2f..00000000000 --- a/systemtests/src/main/java/io/enmasse/systemtest/bases/iot/ITestIoTIsolated.java +++ /dev/null @@ -1,45 +0,0 @@ -/* - * Copyright 2019, EnMasse authors. - * License: Apache License 2.0 (see the file LICENSE or http://apache.org/licenses/LICENSE-2.0.html). - */ -package io.enmasse.systemtest.bases.iot; - -import org.junit.jupiter.api.Tag; - -import io.enmasse.systemtest.TestTag; -import io.enmasse.systemtest.amqp.AmqpClientFactory; -import io.enmasse.systemtest.bases.ITestBase; -import io.enmasse.systemtest.manager.IsolatedIoTManager; -import io.enmasse.systemtest.manager.ResourceManager; -import io.enmasse.systemtest.model.addressspace.AddressSpacePlans; -import io.enmasse.systemtest.model.addressspace.AddressSpaceType; - -@Tag(TestTag.ISOLATED_IOT) -public interface ITestIoTIsolated extends ITestIoTBase, ITestBase { - - IsolatedIoTManager isolatedIoTManager = IsolatedIoTManager.getInstance(); - - default AmqpClientFactory getAmqpClientFactory() { - return isolatedIoTManager.getAmqpClientFactory(); - } - - @Override - default ResourceManager getResourceManager() { - return isolatedIoTManager; - } - - @Override - default String getDefaultAddressSpacePlan() { - return AddressSpacePlans.STANDARD_SMALL; - } - - @Override - default String getDefaultAddrSpaceIdentifier() { - return "standard"; - } - - @Override - default AddressSpaceType getAddressSpaceType() { - return AddressSpaceType.STANDARD; - } -} diff --git a/systemtests/src/main/java/io/enmasse/systemtest/bases/iot/ITestIoTShared.java b/systemtests/src/main/java/io/enmasse/systemtest/bases/iot/ITestIoTShared.java deleted file mode 100644 index d61b7d450d0..00000000000 --- a/systemtests/src/main/java/io/enmasse/systemtest/bases/iot/ITestIoTShared.java +++ /dev/null @@ -1,60 +0,0 @@ -/* - * Copyright 2019, EnMasse authors. - * License: Apache License 2.0 (see the file LICENSE or http://apache.org/licenses/LICENSE-2.0.html). - */ -package io.enmasse.systemtest.bases.iot; - -import org.junit.jupiter.api.Tag; - -import io.enmasse.address.model.AddressSpace; -import io.enmasse.iot.model.v1.IoTConfig; -import io.enmasse.iot.model.v1.IoTProject; -import io.enmasse.systemtest.TestTag; -import io.enmasse.systemtest.amqp.AmqpClientFactory; -import io.enmasse.systemtest.bases.ITestBase; -import io.enmasse.systemtest.manager.ResourceManager; -import io.enmasse.systemtest.manager.SharedIoTManager; -import io.enmasse.systemtest.model.addressspace.AddressSpacePlans; -import io.enmasse.systemtest.model.addressspace.AddressSpaceType; - -@Tag(TestTag.SHARED_IOT) -public interface ITestIoTShared extends ITestBase, ITestIoTBase { - - SharedIoTManager sharedIoTResourceManager = SharedIoTManager.getInstance(); - - default AddressSpace getSharedAddressSpace() { - return sharedIoTResourceManager.getSharedAddressSpace(); - } - - default AmqpClientFactory getAmqpClientFactory() { - return sharedIoTResourceManager.getAmqpClientFactory(); - } - - default IoTProject getSharedIoTProject() { - return sharedIoTResourceManager.getSharedIoTProject(); - } - - default IoTConfig getSharedIoTConfig() { - return sharedIoTResourceManager.getSharedIoTConfig(); - } - - @Override - default ResourceManager getResourceManager() { - return sharedIoTResourceManager; - } - - @Override - default String getDefaultAddressSpacePlan() { - return AddressSpacePlans.STANDARD_SMALL; - } - - @Override - default String getDefaultAddrSpaceIdentifier() { - return "shared-iot"; - } - - @Override - default AddressSpaceType getAddressSpaceType() { - return AddressSpaceType.STANDARD; - } -} diff --git a/systemtests/src/main/java/io/enmasse/systemtest/bases/isolated/ITestBaseIsolated.java b/systemtests/src/main/java/io/enmasse/systemtest/bases/isolated/ITestBaseIsolated.java deleted file mode 100644 index 4bdfa006f04..00000000000 --- a/systemtests/src/main/java/io/enmasse/systemtest/bases/isolated/ITestBaseIsolated.java +++ /dev/null @@ -1,32 +0,0 @@ -/* - * Copyright 2019, EnMasse authors. - * License: Apache License 2.0 (see the file LICENSE or http://apache.org/licenses/LICENSE-2.0.html). - */ -package io.enmasse.systemtest.bases.isolated; - -import static io.enmasse.systemtest.TestTag.ISOLATED; - -import java.util.List; - -import org.junit.jupiter.api.Tag; - -import io.enmasse.address.model.AddressSpace; -import io.enmasse.systemtest.amqp.AmqpClientFactory; -import io.enmasse.systemtest.bases.ITestBase; -import io.enmasse.systemtest.manager.IsolatedResourcesManager; -import io.enmasse.systemtest.manager.ResourceManager; - -@Tag(ISOLATED) -public interface ITestBaseIsolated extends ITestBase { - IsolatedResourcesManager isolatedResourcesManager = IsolatedResourcesManager.getInstance(); - List currentAddressSpaces = isolatedResourcesManager.getCurrentAddressSpaces(); - - default AmqpClientFactory getAmqpClientFactory() { - return isolatedResourcesManager.getAmqpClientFactory(); - } - - @Override - default ResourceManager getResourceManager() { - return isolatedResourcesManager; - } -} diff --git a/systemtests/src/main/java/io/enmasse/systemtest/bases/isolated/ITestIsolatedBrokered.java b/systemtests/src/main/java/io/enmasse/systemtest/bases/isolated/ITestIsolatedBrokered.java deleted file mode 100644 index 9a44fad0b42..00000000000 --- a/systemtests/src/main/java/io/enmasse/systemtest/bases/isolated/ITestIsolatedBrokered.java +++ /dev/null @@ -1,28 +0,0 @@ -/* - * Copyright 2019, EnMasse authors. - * License: Apache License 2.0 (see the file LICENSE or http://apache.org/licenses/LICENSE-2.0.html). - */ -package io.enmasse.systemtest.bases.isolated; - -import io.enmasse.systemtest.bases.ITestBase; -import io.enmasse.systemtest.bases.ITestBaseBrokered; -import io.enmasse.systemtest.model.addressspace.AddressSpacePlans; -import io.enmasse.systemtest.model.addressspace.AddressSpaceType; -import org.junit.jupiter.api.Tag; - -import static io.enmasse.systemtest.TestTag.ISOLATED_BROKER; - - -@Tag(ISOLATED_BROKER) -public interface ITestIsolatedBrokered extends ITestBaseBrokered, ITestBaseIsolated, ITestBase { - - @Override - default AddressSpaceType getAddressSpaceType() { - return AddressSpaceType.BROKERED; - } - - @Override - default String getDefaultAddressSpacePlan() { - return AddressSpacePlans.BROKERED; - } -} diff --git a/systemtests/src/main/java/io/enmasse/systemtest/bases/isolated/ITestIsolatedSharedInfra.java b/systemtests/src/main/java/io/enmasse/systemtest/bases/isolated/ITestIsolatedSharedInfra.java deleted file mode 100644 index 926e5a5cf13..00000000000 --- a/systemtests/src/main/java/io/enmasse/systemtest/bases/isolated/ITestIsolatedSharedInfra.java +++ /dev/null @@ -1,25 +0,0 @@ -/* - * Copyright 2020, EnMasse authors. - * License: Apache License 2.0 (see the file LICENSE or http://apache.org/licenses/LICENSE-2.0.html). - */ -package io.enmasse.systemtest.bases.isolated; - -import io.enmasse.systemtest.amqp.AmqpClientFactory; -import io.enmasse.systemtest.bases.ITestBase; -import io.enmasse.systemtest.manager.IsolatedResourcesManager; -import io.enmasse.systemtest.manager.ResourceManager; - - -public interface ITestIsolatedSharedInfra extends ITestBase { - - IsolatedResourcesManager isolatedResourcesManager = IsolatedResourcesManager.getInstance(); - - default AmqpClientFactory getAmqpClientFactory() { - return isolatedResourcesManager.getAmqpClientFactory(); - } - - @Override - default ResourceManager getResourceManager() { - return isolatedResourcesManager; - } -} diff --git a/systemtests/src/main/java/io/enmasse/systemtest/bases/isolated/ITestIsolatedStandard.java b/systemtests/src/main/java/io/enmasse/systemtest/bases/isolated/ITestIsolatedStandard.java deleted file mode 100644 index 0f2fdfdf261..00000000000 --- a/systemtests/src/main/java/io/enmasse/systemtest/bases/isolated/ITestIsolatedStandard.java +++ /dev/null @@ -1,26 +0,0 @@ -/* - * Copyright 2019, EnMasse authors. - * License: Apache License 2.0 (see the file LICENSE or http://apache.org/licenses/LICENSE-2.0.html). - */ -package io.enmasse.systemtest.bases.isolated; - -import io.enmasse.systemtest.bases.ITestBase; -import io.enmasse.systemtest.bases.ITestBaseStandard; -import io.enmasse.systemtest.model.addressspace.AddressSpacePlans; -import org.junit.jupiter.api.Tag; - -import static io.enmasse.systemtest.TestTag.ISOLATED_STANDARD; - -@Tag(ISOLATED_STANDARD) -public interface ITestIsolatedStandard extends ITestBaseStandard, ITestBaseIsolated, ITestBase { - - @Override - default String getDefaultAddressSpacePlan() { - return AddressSpacePlans.STANDARD_UNLIMITED; - } - - @Override - default String getDefaultAddrSpaceIdentifier() { - return "standard"; - } -} diff --git a/systemtests/src/main/java/io/enmasse/systemtest/bases/shared/ITestBaseShared.java b/systemtests/src/main/java/io/enmasse/systemtest/bases/shared/ITestBaseShared.java deleted file mode 100644 index 3e4046c946c..00000000000 --- a/systemtests/src/main/java/io/enmasse/systemtest/bases/shared/ITestBaseShared.java +++ /dev/null @@ -1,28 +0,0 @@ -/* - * Copyright 2019, EnMasse authors. - * License: Apache License 2.0 (see the file LICENSE or http://apache.org/licenses/LICENSE-2.0.html). - */ -package io.enmasse.systemtest.bases.shared; - -import io.enmasse.address.model.AddressSpace; -import io.enmasse.systemtest.amqp.AmqpClientFactory; -import io.enmasse.systemtest.bases.ITestBase; -import io.enmasse.systemtest.manager.ResourceManager; -import io.enmasse.systemtest.manager.SharedResourceManager; - -public interface ITestBaseShared extends ITestBase { - SharedResourceManager sharedResourceManager = SharedResourceManager.getInstance(); - - default AddressSpace getSharedAddressSpace() { - return sharedResourceManager.getSharedAddressSpace(); - } - - default AmqpClientFactory getAmqpClientFactory() { - return sharedResourceManager.getAmqpClientFactory(); - } - - @Override - default ResourceManager getResourceManager() { - return sharedResourceManager; - } -} diff --git a/systemtests/src/main/java/io/enmasse/systemtest/bases/shared/ITestSharedBrokered.java b/systemtests/src/main/java/io/enmasse/systemtest/bases/shared/ITestSharedBrokered.java deleted file mode 100644 index 2697d47c13e..00000000000 --- a/systemtests/src/main/java/io/enmasse/systemtest/bases/shared/ITestSharedBrokered.java +++ /dev/null @@ -1,26 +0,0 @@ -/* - * Copyright 2019, EnMasse authors. - * License: Apache License 2.0 (see the file LICENSE or http://apache.org/licenses/LICENSE-2.0.html). - */ -package io.enmasse.systemtest.bases.shared; - -import io.enmasse.systemtest.bases.ITestBase; -import io.enmasse.systemtest.bases.ITestBaseBrokered; -import io.enmasse.systemtest.model.addressspace.AddressSpacePlans; -import org.junit.jupiter.api.Tag; - -import static io.enmasse.systemtest.TestTag.SHARED_BROKERED; - -@Tag(SHARED_BROKERED) -public interface ITestSharedBrokered extends ITestBase, ITestBaseShared, ITestBaseBrokered { - - @Override - default String getDefaultAddressSpacePlan() { - return AddressSpacePlans.BROKERED; - } - - @Override - default String getDefaultAddrSpaceIdentifier() { - return "brokered"; - } -} diff --git a/systemtests/src/main/java/io/enmasse/systemtest/bases/shared/ITestSharedStandard.java b/systemtests/src/main/java/io/enmasse/systemtest/bases/shared/ITestSharedStandard.java deleted file mode 100644 index f2d4d9d72e4..00000000000 --- a/systemtests/src/main/java/io/enmasse/systemtest/bases/shared/ITestSharedStandard.java +++ /dev/null @@ -1,28 +0,0 @@ -/* - * Copyright 2019, EnMasse authors. - * License: Apache License 2.0 (see the file LICENSE or http://apache.org/licenses/LICENSE-2.0.html). - */ -package io.enmasse.systemtest.bases.shared; - -import io.enmasse.systemtest.bases.ITestBase; -import io.enmasse.systemtest.bases.ITestBaseStandard; -import io.enmasse.systemtest.model.addressspace.AddressSpacePlans; -import org.junit.jupiter.api.Tag; - -import static io.enmasse.systemtest.TestTag.SHARED_STANDARD; - -@Tag(SHARED_STANDARD) -public interface ITestSharedStandard extends ITestBaseStandard, ITestBaseShared, ITestBase { - - @Override - default String getDefaultAddressSpacePlan() { - return AddressSpacePlans.STANDARD_UNLIMITED; - } - - @Override - default String getDefaultAddrSpaceIdentifier() { - return "standard"; - } - - -} diff --git a/systemtests/src/main/java/io/enmasse/systemtest/clients/ClientUtils.java b/systemtests/src/main/java/io/enmasse/systemtest/clients/ClientUtils.java index 6d461dc1da6..2895c261547 100644 --- a/systemtests/src/main/java/io/enmasse/systemtest/clients/ClientUtils.java +++ b/systemtests/src/main/java/io/enmasse/systemtest/clients/ClientUtils.java @@ -13,13 +13,13 @@ import io.enmasse.systemtest.amqp.ReceiverStatus; import io.enmasse.systemtest.amqp.UnauthorizedAccessException; import io.enmasse.systemtest.logs.CustomLogger; -import io.enmasse.systemtest.manager.ResourceManager; import io.enmasse.systemtest.messagingclients.AbstractClient; import io.enmasse.systemtest.messagingclients.ClientArgument; import io.enmasse.systemtest.messagingclients.ExternalMessagingClient; import io.enmasse.systemtest.messagingclients.rhea.RheaClientConnector; import io.enmasse.systemtest.messagingclients.rhea.RheaClientReceiver; import io.enmasse.systemtest.messagingclients.rhea.RheaClientSender; +import io.enmasse.systemtest.messaginginfra.ResourceManager; import io.enmasse.systemtest.model.address.AddressType; import io.enmasse.systemtest.platform.apps.SystemtestsKubernetesApps; import io.enmasse.systemtest.utils.AddressSpaceUtils; @@ -33,7 +33,6 @@ import org.slf4j.Logger; import javax.security.sasl.AuthenticationException; - import java.nio.file.Files; import java.nio.file.Path; import java.util.ArrayList; diff --git a/systemtests/src/main/java/io/enmasse/systemtest/info/TestInfo.java b/systemtests/src/main/java/io/enmasse/systemtest/info/TestInfo.java index 993dcf2ae42..3c68720fdcb 100644 --- a/systemtests/src/main/java/io/enmasse/systemtest/info/TestInfo.java +++ b/systemtests/src/main/java/io/enmasse/systemtest/info/TestInfo.java @@ -185,10 +185,6 @@ public boolean isTestShared() { return false; } - public boolean isTestSharedInfra() { - return currentTestClass.getTags().stream().anyMatch(TestTag.SHARED_INFRA_TAGS::contains); - } - public boolean isTestIoT() { for (String tag : currentTest.getTags()) { if (TestTag.IOT_TAGS.contains(tag)) { diff --git a/systemtests/src/main/java/io/enmasse/systemtest/iot/CredentialsRegistryClient.java b/systemtests/src/main/java/io/enmasse/systemtest/iot/CredentialsRegistryClient.java index b8a4dde9a10..24699e17099 100644 --- a/systemtests/src/main/java/io/enmasse/systemtest/iot/CredentialsRegistryClient.java +++ b/systemtests/src/main/java/io/enmasse/systemtest/iot/CredentialsRegistryClient.java @@ -174,8 +174,7 @@ public static PasswordCredential createPlainPasswordCredentialsObject(final Stri // create credentials - var credentials = new PasswordCredential(); - credentials.setAuthId(authId); + var credentials = new PasswordCredential(authId); credentials.setSecrets(Collections.singletonList(secret)); return credentials; @@ -188,8 +187,7 @@ static PasswordCredential createCredentialsObject(final String authId, final Str // create credentials - var credentials = new PasswordCredential(); - credentials.setAuthId(authId); + var credentials = new PasswordCredential(authId); credentials.setSecrets(Collections.singletonList(secret)); return credentials; @@ -202,8 +200,7 @@ static PskCredential createPskCredentialsObject(final String authId, final byte[ // create credentials - var credentials = new PskCredential(); - credentials.setAuthId(authId); + var credentials = new PskCredential(authId); credentials.setSecrets(Collections.singletonList(secret)); return credentials; @@ -216,8 +213,7 @@ static X509CertificateCredential createX509CertificateCredentialsObject(final St // create credentials - var credentials = new X509CertificateCredential(); - credentials.setAuthId(authId); + var credentials = new X509CertificateCredential(authId); credentials.setSecrets(Collections.singletonList(secret)); return credentials; diff --git a/systemtests/src/main/java/io/enmasse/systemtest/iot/IoTTestSession.java b/systemtests/src/main/java/io/enmasse/systemtest/iot/IoTTestSession.java index 04d746384d8..cf4be27ebc1 100644 --- a/systemtests/src/main/java/io/enmasse/systemtest/iot/IoTTestSession.java +++ b/systemtests/src/main/java/io/enmasse/systemtest/iot/IoTTestSession.java @@ -5,8 +5,6 @@ package io.enmasse.systemtest.iot; -import static io.enmasse.systemtest.bases.iot.ITestIoTBase.IOT_ADDRESS_EVENT; -import static io.enmasse.systemtest.bases.iot.ITestIoTBase.IOT_ADDRESS_TELEMETRY; import static io.enmasse.systemtest.condition.OpenShiftVersion.OCP4; import static io.enmasse.systemtest.iot.IoTTestSession.Adapter.HTTP; import static io.enmasse.systemtest.iot.IoTTestSession.Adapter.MQTT; @@ -55,7 +53,6 @@ import io.enmasse.systemtest.UserCredentials; import io.enmasse.systemtest.amqp.AmqpClient; import io.enmasse.systemtest.amqp.AmqpClientFactory; -import io.enmasse.systemtest.bases.iot.ITestIoTBase; import io.enmasse.systemtest.executor.Exec; import io.enmasse.systemtest.info.TestInfo; import io.enmasse.systemtest.iot.IoTTestSession.Builder.PreDeployProcessor; @@ -581,10 +578,10 @@ public void addCleanup(final ThrowingCallable cleanupTask) { .withAuthorization( Collections.singletonList(new UserAuthorizationBuilder() .withAddresses( - IOT_ADDRESS_TELEMETRY + "/" + tenantId, - IOT_ADDRESS_TELEMETRY + "/" + tenantId + "/*", - IOT_ADDRESS_EVENT + "/" + tenantId, - IOT_ADDRESS_EVENT + "/" + tenantId + "/*") + "telemetry" + "/" + tenantId, + "telemetry" + "/" + tenantId + "/*", + "event" + "/" + tenantId, + "event" + "/" + tenantId + "/*") .withOperations(Operation.recv) .build())) .endSpec() @@ -616,7 +613,7 @@ public void addCleanup(final ThrowingCallable cleanupTask) { if (log.isDebugEnabled()) { log.debug("Caught exception during deployment", e); } else { - log.info("Caught exception during deployment, running exception handler"); + log.info("Caught exception during deployment, running exception handler", e); } // first run exception handler @@ -657,7 +654,7 @@ public static IoTTestSession.Builder create(final String namespace, final boolea var project = new IoTProjectBuilder( IoTUtils.getBasicIoTProjectObject( name, name, - ITestIoTBase.IOT_PROJECT_NAMESPACE, + "iot-project-ns", AddressSpacePlans.STANDARD_SMALL)); // done diff --git a/systemtests/src/main/java/io/enmasse/systemtest/iot/IoTTests.java b/systemtests/src/main/java/io/enmasse/systemtest/iot/IoTTests.java index 6fdd1e6485a..edea94dc6cc 100644 --- a/systemtests/src/main/java/io/enmasse/systemtest/iot/IoTTests.java +++ b/systemtests/src/main/java/io/enmasse/systemtest/iot/IoTTests.java @@ -5,7 +5,6 @@ package io.enmasse.systemtest.iot; -import static io.enmasse.systemtest.bases.iot.ITestIoTBase.IOT_PROJECT_NAMESPACE; import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.BeforeEach; @@ -15,7 +14,6 @@ import io.enmasse.systemtest.IndicativeSentences; import io.enmasse.systemtest.bases.ITestSeparator; -import io.enmasse.systemtest.bases.JUnitWorkaround; import io.enmasse.systemtest.listener.JunitCallbackListener; import io.enmasse.systemtest.platform.Kubernetes; @@ -23,24 +21,23 @@ * Marker interface for IoT tests, which provision their own IoT infrastructure. */ @ExtendWith(JunitCallbackListener.class) -@ExtendWith(JUnitWorkaround.class) @DisplayNameGeneration(IndicativeSentences.class) @TestInstance(TestInstance.Lifecycle.PER_CLASS) public interface IoTTests extends ITestSeparator { @BeforeAll - public static void deployDefaultCerts() throws Exception { + static void deployDefaultCerts() throws Exception { IoTTestSession.deployDefaultCerts(); } @BeforeAll - public static void createDeviceManager() throws Exception { + static void createDeviceManager() throws Exception { DeviceManagementApi.createManagementServiceAccount(); } @BeforeEach default void createNamespace() { - Kubernetes.getInstance().createNamespace(IOT_PROJECT_NAMESPACE); + Kubernetes.getInstance().createNamespace("iot-project-ns"); } } diff --git a/systemtests/src/main/java/io/enmasse/systemtest/listener/JunitCallbackListener.java b/systemtests/src/main/java/io/enmasse/systemtest/listener/JunitCallbackListener.java index 84a5ae10175..bbad09d7d59 100644 --- a/systemtests/src/main/java/io/enmasse/systemtest/listener/JunitCallbackListener.java +++ b/systemtests/src/main/java/io/enmasse/systemtest/listener/JunitCallbackListener.java @@ -4,31 +4,26 @@ */ package io.enmasse.systemtest.listener; -import org.junit.jupiter.api.extension.AfterAllCallback; -import org.junit.jupiter.api.extension.AfterEachCallback; -import org.junit.jupiter.api.extension.BeforeAllCallback; -import org.junit.jupiter.api.extension.BeforeEachCallback; -import org.junit.jupiter.api.extension.ExtensionContext; -import org.junit.jupiter.api.extension.LifecycleMethodExecutionExceptionHandler; -import org.junit.jupiter.api.extension.TestExecutionExceptionHandler; -import org.slf4j.Logger; - import io.enmasse.systemtest.EnmasseInstallType; import io.enmasse.systemtest.Environment; import io.enmasse.systemtest.bases.ThrowableRunner; import io.enmasse.systemtest.info.TestInfo; import io.enmasse.systemtest.logs.CustomLogger; import io.enmasse.systemtest.logs.GlobalLogCollector; -import io.enmasse.systemtest.manager.IsolatedIoTManager; -import io.enmasse.systemtest.manager.IsolatedResourcesManager; -import io.enmasse.systemtest.manager.SharedIoTManager; -import io.enmasse.systemtest.manager.SharedResourceManager; import io.enmasse.systemtest.messaginginfra.ResourceManager; import io.enmasse.systemtest.operator.EnmasseOperatorManager; import io.enmasse.systemtest.platform.KubeCMDClient; import io.enmasse.systemtest.platform.Kubernetes; import io.enmasse.systemtest.platform.cluster.KubeClusterManager; import io.enmasse.systemtest.utils.TestUtils; +import org.junit.jupiter.api.extension.AfterAllCallback; +import org.junit.jupiter.api.extension.AfterEachCallback; +import org.junit.jupiter.api.extension.BeforeAllCallback; +import org.junit.jupiter.api.extension.BeforeEachCallback; +import org.junit.jupiter.api.extension.ExtensionContext; +import org.junit.jupiter.api.extension.LifecycleMethodExecutionExceptionHandler; +import org.junit.jupiter.api.extension.TestExecutionExceptionHandler; +import org.slf4j.Logger; /** * This class implements a variety of junit callbacks and orchestates the full lifecycle of the operator installation @@ -40,12 +35,7 @@ public class JunitCallbackListener implements TestExecutionExceptionHandler, Lif private static final Environment env = Environment.getInstance(); private final Kubernetes kubernetes = Kubernetes.getInstance(); private final TestInfo testInfo = TestInfo.getInstance(); - private final IsolatedResourcesManager isolatedResourcesManager = IsolatedResourcesManager.getInstance(); - private final SharedResourceManager sharedResourcesManager = SharedResourceManager.getInstance(); - private final SharedIoTManager sharedIoTManager = SharedIoTManager.getInstance(); - private final IsolatedIoTManager isolatedIoTManager = IsolatedIoTManager.getInstance(); private final EnmasseOperatorManager operatorManager = EnmasseOperatorManager.getInstance(); - private static Exception beforeAllException; //TODO remove it after upgrade to surefire plugin 3.0.0-M5 @Override public void beforeAll(ExtensionContext context) throws Exception { @@ -54,57 +44,20 @@ public void beforeAll(ExtensionContext context) throws Exception { testInfo.setCurrentTestClass(context); ResourceManager.getInstance().setClassResources(); KubeClusterManager.getInstance().setClassConfigurations(); - try { //TODO remove it after upgrade to surefire plugin 3.0.0-M5 - handleCallBackError("Callback before all", context, () -> { - if (testInfo.isUpgradeTest()) { - if (operatorManager.isEnmasseBundleDeployed()) { - operatorManager.deleteEnmasseBundle(); - } - LOGGER.info("Enmasse is not installed because next test is {}", context.getDisplayName()); - } else if (testInfo.isOLMTest()) { - LOGGER.info("Test is OLM"); - if (operatorManager.isEnmasseOlmDeployed()) { - operatorManager.deleteEnmasseOlm(); - } - if (operatorManager.isEnmasseBundleDeployed()) { - operatorManager.deleteEnmasseBundle(); - } - operatorManager.installEnmasseOlm(testInfo.getOLMInstallationType()); - } else if (env.installType() == EnmasseInstallType.OLM) { - if (!operatorManager.isEnmasseOlmDeployed()) { - operatorManager.installEnmasseOlm(); - } - if (!operatorManager.areExamplesApplied()) { - operatorManager.installExamplesBundleOlm(); - operatorManager.waitUntilOperatorReadyOlm(); - } - } else if (testInfo.isTestSharedInfra()) { - if (operatorManager.isEnmasseBundleDeployed()) { - operatorManager.deleteEnmasseBundle(); - } else if (operatorManager.isEnmasseOlmDeployed()) { - operatorManager.deleteEnmasseOlm(); - } - operatorManager.installEnmasseSharedInfraBundle(); - } else { - if (!operatorManager.isEnmasseBundleDeployed()) { - operatorManager.installEnmasseBundle(); - } - if (testInfo.isClassIoT()) { - operatorManager.installIoTOperator(); - } - } - }); - } catch (Exception ex) { - beforeAllException = ex; //TODO remove it after upgrade to surefire plugin 3.0.0-M5 - operatorManager.deleteEnmasseOlm(); - } + handleCallBackError("Callback before all", context, () -> { + if (operatorManager.isEnmasseBundleDeployed()) { + operatorManager.deleteEnmasseBundle(); + } else if (operatorManager.isEnmasseOlmDeployed()) { + operatorManager.deleteEnmasseOlm(); + } + operatorManager.installEnmasseBundle(); + }); } @Override public void afterAll(ExtensionContext extensionContext) throws Exception { LOGGER.info("running - afterAll"); - beforeAllException = null; //TODO remove it after upgrade to surefire plugin 3.0.0-M5 handleCallBackError("Callback after all", extensionContext, () -> { if (!env.skipCleanup()) { ResourceManager.getInstance().deleteClassResources(); @@ -112,15 +65,7 @@ public void afterAll(ExtensionContext extensionContext) throws Exception { } if (env.skipCleanup() || env.skipUninstall()) { LOGGER.info("Skip cleanup/uninstall is set, enmasse and iot operators won't be deleted"); - } else if (testInfo.isOLMTest()) { - LOGGER.info("Test is OLM"); - if (operatorManager.isEnmasseOlmDeployed()) { - operatorManager.deleteEnmasseOlm(); - } } else if (env.installType() == EnmasseInstallType.BUNDLE) { - if (testInfo.isEndOfIotTests()) { - operatorManager.removeIoT(); - } if (operatorManager.isEnmasseOlmDeployed()) { operatorManager.deleteEnmasseOlm(); } @@ -129,14 +74,11 @@ public void afterAll(ExtensionContext extensionContext) throws Exception { } @Override - public void beforeEach(ExtensionContext context) throws Exception { + public void beforeEach(ExtensionContext context) { testInfo.setCurrentTest(context); ResourceManager.getInstance().setMethodResources(); KubeClusterManager.getInstance().setMethodConfigurations(); logPodsInInfraNamespace(); - if (beforeAllException != null) { - throw beforeAllException; - } } @Override @@ -145,44 +87,9 @@ public void afterEach(ExtensionContext extensionContext) throws Exception { LOGGER.info("Teardown section: "); ResourceManager.getInstance().deleteMethodResources(); KubeClusterManager.getInstance().restoreMethodConfigurations(); - if (testInfo.isTestShared()) { - tearDownSharedResources(); - } else if (testInfo.isTestIoT()) { - if (testInfo.needsIoTCleanup()) { - isolatedIoTManager.tearDown(testInfo.getActualTest()); - } - } else { - tearDownCommonResources(); - } }); } - private void tearDownCommonResources() throws Exception { - LOGGER.info("Admin resource manager teardown"); - isolatedResourcesManager.tearDown(testInfo.getActualTest()); - isolatedResourcesManager.unsetReuseAddressSpace(); - isolatedResourcesManager.deleteAddressspacesFromList(); - } - - private void tearDownSharedResources() throws Exception { - if (testInfo.isAddressSpaceDeleteable() || testInfo.getActualTest().getExecutionException().isPresent()) { - if (testInfo.isTestIoT()) { - if (testInfo.needsIoTCleanup()) { - LOGGER.info("Teardown shared IoT!"); - sharedIoTManager.tearDown(testInfo.getActualTest()); - } else { - LOGGER.info("Self-cleaning IoT test"); - sharedIoTManager.closeAmqpFactory(); - } - } else { - LOGGER.info("Teardown shared!"); - sharedResourcesManager.tearDown(testInfo.getActualTest()); - } - } else if (sharedResourcesManager.getSharedAddressSpace() != null) { - sharedResourcesManager.tearDownShared(); - } - } - @Override public void handleTestExecutionException(ExtensionContext context, Throwable throwable) throws Throwable { saveKubernetesState("Test execution", context, throwable); diff --git a/systemtests/src/main/java/io/enmasse/systemtest/listener/JunitExecutionListener.java b/systemtests/src/main/java/io/enmasse/systemtest/listener/JunitExecutionListener.java index 03c1a0b53c4..11984cd14e1 100644 --- a/systemtests/src/main/java/io/enmasse/systemtest/listener/JunitExecutionListener.java +++ b/systemtests/src/main/java/io/enmasse/systemtest/listener/JunitExecutionListener.java @@ -4,22 +4,19 @@ */ package io.enmasse.systemtest.listener; -import org.junit.platform.launcher.TestExecutionListener; -import org.junit.platform.launcher.TestPlan; -import org.slf4j.Logger; - import io.enmasse.systemtest.Environment; import io.enmasse.systemtest.TestTag; import io.enmasse.systemtest.info.TestInfo; import io.enmasse.systemtest.logs.CustomLogger; import io.enmasse.systemtest.logs.GlobalLogCollector; -import io.enmasse.systemtest.manager.IsolatedResourcesManager; import io.enmasse.systemtest.operator.EnmasseOperatorManager; import io.enmasse.systemtest.platform.Kubernetes; import io.enmasse.systemtest.platform.apps.SystemtestsKubernetesApps; import io.enmasse.systemtest.time.TimeMeasuringSystem; -import io.enmasse.systemtest.utils.AddressSpaceUtils; import io.enmasse.systemtest.utils.IoTUtils; +import org.junit.platform.launcher.TestExecutionListener; +import org.junit.platform.launcher.TestPlan; +import org.slf4j.Logger; /** * Execution listener useful for safety cleanups of the test environment after test suite execution @@ -117,8 +114,6 @@ private void performCleanup() { kube.getAddressSpaceClient().inAnyNamespace().list().getItems().forEach((addrSpace) -> { LOGGER.info("address space '{}' will be removed", addrSpace); try { - AddressSpaceUtils.deleteAddressSpaceAndWait(addrSpace, logCollector); - IsolatedResourcesManager.getInstance().tearDown(TestInfo.getInstance().getActualTest()); } catch (Exception e) { LOGGER.warn("Cleanup failed or no clean is needed"); } diff --git a/systemtests/src/main/java/io/enmasse/systemtest/logs/GlobalLogCollector.java b/systemtests/src/main/java/io/enmasse/systemtest/logs/GlobalLogCollector.java index f245d960018..2a4b76060e6 100644 --- a/systemtests/src/main/java/io/enmasse/systemtest/logs/GlobalLogCollector.java +++ b/systemtests/src/main/java/io/enmasse/systemtest/logs/GlobalLogCollector.java @@ -338,10 +338,14 @@ public static void saveInfraState(Path path, String infraNamespace, ThrowableRun } //resource specific logs - Files.writeString(path.resolve("describe_addressspaces.txt"), KubeCMDClient.runOnClusterWithoutLogger("describe", "addressspaces", "--all-namespaces").getStdOut()); - Files.writeString(path.resolve("describe_addresses.txt"), KubeCMDClient.runOnClusterWithoutLogger("describe", "addresses", "--all-namespaces").getStdOut()); - Files.writeString(path.resolve("addressspaces.yml"), KubeCMDClient.runOnClusterWithoutLogger("get", "addressspaces", "-o", "yaml", "--all-namespaces").getStdOut()); - Files.writeString(path.resolve("addresses.yml"), KubeCMDClient.runOnClusterWithoutLogger("get", "addresses", "-o", "yaml", "--all-namespaces").getStdOut()); + Files.writeString(path.resolve("describe_tenants.txt"), KubeCMDClient.runOnClusterWithoutLogger("describe", "messagingtenants", "--all-namespaces").getStdOut()); + Files.writeString(path.resolve("describe_addresses.txt"), KubeCMDClient.runOnClusterWithoutLogger("describe", "messagingaddresses", "--all-namespaces").getStdOut()); + Files.writeString(path.resolve("describe_endpoints.txt"), KubeCMDClient.runOnClusterWithoutLogger("describe", "messagingendpoints", "--all-namespaces").getStdOut()); + Files.writeString(path.resolve("describe_messaginginfras.txt"), KubeCMDClient.runOnClusterWithoutLogger("describe", "messaginginfrastructures", "--all-namespaces").getStdOut()); + Files.writeString(path.resolve("tenants.yml"), KubeCMDClient.runOnClusterWithoutLogger("get", "messagingtenants", "-o", "yaml", "--all-namespaces").getStdOut()); + Files.writeString(path.resolve("addresses.yml"), KubeCMDClient.runOnClusterWithoutLogger("get", "messagingaddresses", "-o", "yaml", "--all-namespaces").getStdOut()); + Files.writeString(path.resolve("endpoints.yml"), KubeCMDClient.runOnClusterWithoutLogger("get", "messagingendpoints", "-o", "yaml", "--all-namespaces").getStdOut()); + Files.writeString(path.resolve("messaginginfras.yml"), KubeCMDClient.runOnClusterWithoutLogger("get", "messaginginfrastructures", "-o", "yaml", "--all-namespaces").getStdOut()); Files.writeString(path.resolve("catalogsources.yml"), KubeCMDClient.runOnClusterWithoutLogger("get", "catalogsources", "-o", "yaml", "--all-namespaces").getStdOut()); Files.writeString(path.resolve("csvs.yml"), KubeCMDClient.runOnClusterWithoutLogger("get", "csvs", "-o", "yaml", "--all-namespaces").getStdOut()); Files.writeString(path.resolve("users.yml"), KubeCMDClient.runOnClusterWithoutLogger("get", "messaginguser", "-o", "yaml", "--all-namespaces").getStdOut()); diff --git a/systemtests/src/main/java/io/enmasse/systemtest/manager/IsolatedIoTManager.java b/systemtests/src/main/java/io/enmasse/systemtest/manager/IsolatedIoTManager.java deleted file mode 100644 index 23ea7ed430b..00000000000 --- a/systemtests/src/main/java/io/enmasse/systemtest/manager/IsolatedIoTManager.java +++ /dev/null @@ -1,185 +0,0 @@ -/* - * Copyright 2019, EnMasse authors. - * License: Apache License 2.0 (see the file LICENSE or http://apache.org/licenses/LICENSE-2.0.html). - */ -package io.enmasse.systemtest.manager; - -import static io.enmasse.systemtest.bases.iot.ITestIoTBase.IOT_PROJECT_NAMESPACE; - -import java.nio.file.Path; -import java.util.ArrayList; -import java.util.List; - -import org.junit.jupiter.api.extension.ExtensionContext; -import org.slf4j.Logger; - -import io.enmasse.address.model.AddressSpace; -import io.enmasse.iot.model.v1.IoTConfig; -import io.enmasse.iot.model.v1.IoTProject; -import io.enmasse.systemtest.UserCredentials; -import io.enmasse.systemtest.amqp.AmqpClientFactory; -import io.enmasse.systemtest.logs.CustomLogger; -import io.enmasse.systemtest.platform.apps.SystemtestsKubernetesApps; -import io.enmasse.systemtest.utils.IoTUtils; -import io.enmasse.systemtest.utils.TestUtils; -import io.enmasse.systemtest.utils.ThrowingCallable; - -public class IsolatedIoTManager extends ResourceManager { - - private Logger LOGGER = CustomLogger.getLogger(); - protected AmqpClientFactory amqpClientFactory; - protected List ioTProjects; - protected List ioTConfigs; - private static IsolatedIoTManager instance = null; - private UserCredentials defaultCredentials = environment.getDefaultCredentials(); - - private IsolatedIoTManager() { - ioTProjects = new ArrayList<>(); - ioTConfigs = new ArrayList<>(); - } - - public static synchronized IsolatedIoTManager getInstance() { - if (instance == null) { - instance = new IsolatedIoTManager(); - } - return instance; - } - - public void initFactories(AddressSpace addressSpace) { - amqpClientFactory = new AmqpClientFactory(addressSpace, defaultCredentials); - } - - public void initFactories(IoTProject project) { - String addSpaceName = project.getSpec().getDownstreamStrategy().getManagedStrategy().getAddressSpace().getName(); - this.initFactories(kubernetes.getAddressSpaceClient(project.getMetadata() - .getNamespace()).withName(addSpaceName).get()); - } - - @Override - public void setup() { - kubernetes.createNamespace(IOT_PROJECT_NAMESPACE); - } - - private static Throwable cleanup(ThrowingCallable callable, Throwable e) { - try { - callable.call(); - return e; - } catch (Throwable e1) { - if (e == null) { - return e1; - } else { - e.addSuppressed(e1); - return e; - } - } - } - - @Override - public void tearDown(ExtensionContext context) throws Exception { - - if (environment.skipCleanup()) { - - LOGGER.info("Skip cleanup is set, no cleanup process"); - - } else { - - // the next lines, using cleanup(...) prevent that the failure of - // one cleanup step prevents the next step from being executed. - // Also we must handle Throwables here, since some parts of the code - // call 'assert*', which throws a 'AssertionFailedError', which is - // is an 'Error' rather than an 'Exception'. - - Throwable e = null; - e = cleanup(() -> tearDownProjects(), e); - e = cleanup(() -> tearDownConfigs(), e); - if (context.getExecutionException().isPresent()) { - final Path path = TestUtils.getFailedTestLogsPath(context); - e = cleanup(() -> SystemtestsKubernetesApps.collectInfinispanServerLogs(path), e); - } - e = cleanup(() -> SystemtestsKubernetesApps.deleteInfinispanServer(), e); - e = cleanup(() -> SystemtestsKubernetesApps.deletePostgresqlServer(), e); - e = cleanup(() -> SystemtestsKubernetesApps.deleteH2Server(), e); - - if (e != null) { - LOGGER.error("Error tearing down IoT test: {}", e); - if (e instanceof Exception) { - throw (Exception) e; - } else { - throw new Exception(e); - } - } - - } - } - - private void tearDownProjects() throws Exception { - LOGGER.info("All IoTProjects will be removed"); - for (IoTProject project : ioTProjects) { - var iotProjectApiClient = kubernetes.getIoTProjectClient(project.getMetadata().getNamespace()); - if (iotProjectApiClient.withName(project.getMetadata().getName()).get() != null) { - IoTUtils.deleteIoTProjectAndWait(kubernetes, project); - } else { - LOGGER.info("IoTProject '{}' doesn't exists!", project.getMetadata().getName()); - } - } - ioTProjects.clear(); - } - - private void tearDownConfigs() throws Exception { - // delete configs - LOGGER.info("All IoTConfigs will be removed"); - var iotConfigApiClient = kubernetes.getIoTConfigClient(); - for (IoTConfig config : ioTConfigs) { - if (iotConfigApiClient.withName(config.getMetadata().getName()).get() != null) { - IoTUtils.deleteIoTConfigAndWait(kubernetes, config); - } else { - LOGGER.info("IoTConfig '{}' doesn't exists!", config.getMetadata().getName()); - } - } - ioTConfigs.clear(); - } - - @Override - public AmqpClientFactory getAmqpClientFactory() { - return amqpClientFactory; - } - - @Override - public void setAmqpClientFactory(AmqpClientFactory amqpClientFactory) { - this.amqpClientFactory = amqpClientFactory; - } - - public void createIoTProject(IoTProject project) throws Exception { - ioTProjects.add(project); - IoTUtils.createIoTProject(project); - initFactories(project); - } - - public void deleteIoTProject(IoTProject project) throws Exception { - IoTUtils.deleteIoTProjectAndWait(kubernetes, project); - ioTProjects.remove(project); - } - - public void createIoTConfig(IoTConfig ioTConfig) throws Exception { - ioTConfigs.add(ioTConfig); - IoTUtils.createIoTConfig(ioTConfig); - } - - public void deleteIoTConfig(IoTConfig ioTConfig) throws Exception { - IoTUtils.deleteIoTConfigAndWait(kubernetes, ioTConfig); - ioTConfigs.add(ioTConfig); - } - - public List getIoTProjects() { - return ioTProjects; - } - - public String getTenantId() { - return IoTUtils.getTenantId(ioTProjects.get(0)); - } - - public List getIoTConfigs() { - return ioTConfigs; - } - -} diff --git a/systemtests/src/main/java/io/enmasse/systemtest/manager/IsolatedResourcesManager.java b/systemtests/src/main/java/io/enmasse/systemtest/manager/IsolatedResourcesManager.java deleted file mode 100644 index f193013f345..00000000000 --- a/systemtests/src/main/java/io/enmasse/systemtest/manager/IsolatedResourcesManager.java +++ /dev/null @@ -1,362 +0,0 @@ -/* - * Copyright 2019, EnMasse authors. - * License: Apache License 2.0 (see the file LICENSE or http://apache.org/licenses/LICENSE-2.0.html). - */ -package io.enmasse.systemtest.manager; - -import java.util.ArrayList; -import java.util.Collections; -import java.util.List; -import java.util.Map; -import java.util.concurrent.TimeUnit; - -import org.junit.jupiter.api.extension.ExtensionContext; -import org.slf4j.Logger; - -import io.enmasse.address.model.AddressSpace; -import io.enmasse.admin.model.v1.AddressPlan; -import io.enmasse.admin.model.v1.AddressSpacePlan; -import io.enmasse.admin.model.v1.AuthenticationService; -import io.enmasse.admin.model.v1.BrokeredInfraConfig; -import io.enmasse.admin.model.v1.InfraConfig; -import io.enmasse.admin.model.v1.StandardInfraConfig; -import io.enmasse.systemtest.UserCredentials; -import io.enmasse.systemtest.amqp.AmqpClientFactory; -import io.enmasse.systemtest.logs.CustomLogger; -import io.enmasse.systemtest.platform.Kubernetes; -import io.enmasse.systemtest.time.SystemtestsOperation; -import io.enmasse.systemtest.time.TimeMeasuringSystem; -import io.enmasse.systemtest.time.TimeoutBudget; -import io.enmasse.systemtest.utils.AddressSpaceUtils; -import io.enmasse.systemtest.utils.TestUtils; - -public class IsolatedResourcesManager extends ResourceManager { - - private static IsolatedResourcesManager managerInstance = null; - private static Logger LOGGER; - protected AmqpClientFactory amqpClientFactory; - boolean reuseAddressSpace = false; - private ArrayList addressPlans; - private ArrayList addressSpacePlans; - private ArrayList standardInfraConfigs; - private ArrayList brokeredInfraConfigs; - private ArrayList authServices; - private UserCredentials defaultCredentials = environment.getDefaultCredentials(); - - private IsolatedResourcesManager() { - LOGGER = CustomLogger.getLogger(); - addressPlans = new ArrayList<>(); - addressSpacePlans = new ArrayList<>(); - standardInfraConfigs = new ArrayList<>(); - brokeredInfraConfigs = new ArrayList<>(); - authServices = new ArrayList<>(); - currentAddressSpaces = new ArrayList<>(); - } - - //------------------------------------------------------------------------------------------------ - // Singleton handling - //------------------------------------------------------------------------------------------------ - - public List getCurrentAddressSpaces() { - return currentAddressSpaces; - } - - public static synchronized IsolatedResourcesManager getInstance() { - if (managerInstance == null) { - managerInstance = new IsolatedResourcesManager(); - } - return managerInstance; - } - - - public void initFactories(AddressSpace addressSpace) { - amqpClientFactory = new AmqpClientFactory(addressSpace, defaultCredentials); - } - - public void initFactories(AddressSpace addressSpace, UserCredentials userCredentials) throws Exception { - closeClientFactories(amqpClientFactory); - amqpClientFactory = new AmqpClientFactory(addressSpace, userCredentials); - } - - @Override - public void setup() { - if (currentAddressSpaces.isEmpty()) { - initFactories(null); - } else { - initFactories(currentAddressSpaces.get(0)); - } - } - - @Override - public void tearDown(ExtensionContext context) throws Exception { - LOGGER.info("Reuse addressspace: " + reuseAddressSpace); - LOGGER.info("Environment cleanup: " + environment.skipCleanup()); - - if (!environment.skipCleanup() && !reuseAddressSpace) { - for (AddressSpacePlan addressSpacePlan : addressSpacePlans) { - Kubernetes.getInstance().getAddressSpacePlanClient().withName(addressSpacePlan.getMetadata().getName()).cascading(true).delete(); - TestUtils.waitUntilCondition("Delete addressspace plan", waitPhase -> Kubernetes.getInstance().getAddressSpacePlanClient().list().getItems().stream().noneMatch(plan -> plan.getMetadata().getName().equals(addressSpacePlan.getMetadata().getName())), - new TimeoutBudget(1, TimeUnit.MINUTES)); - LOGGER.info("AddressSpace plan {} deleted", addressSpacePlan.getMetadata().getName()); - } - addressSpacePlans.clear(); - - for (AddressPlan addressPlan : addressPlans) { - Kubernetes.getInstance().getAddressPlanClient().withName(addressPlan.getMetadata().getName()).cascading(true).delete(); - TestUtils.waitUntilCondition("Delete address plan", waitPhase -> Kubernetes.getInstance().getAddressPlanClient().list().getItems().stream().noneMatch(plan -> plan.getMetadata().getName().equals(addressPlan.getMetadata().getName())), - new TimeoutBudget(1, TimeUnit.MINUTES)); - LOGGER.info("Address plan {} deleted", addressPlan.getMetadata().getName()); - } - addressPlans.clear(); - - for (StandardInfraConfig infraConfigDefinition : standardInfraConfigs) { - Kubernetes.getInstance().getStandardInfraConfigClient().withName(infraConfigDefinition.getMetadata().getName()).cascading(true).delete(); - TestUtils.waitUntilCondition("Delete infraConfig plan", waitPhase -> Kubernetes.getInstance().getStandardInfraConfigClient().list().getItems().stream().noneMatch(plan -> plan.getMetadata().getName().equals(infraConfigDefinition.getMetadata().getName())), - new TimeoutBudget(1, TimeUnit.MINUTES)); - LOGGER.info("Standardinfraconfig {} deleted", infraConfigDefinition.getMetadata().getName()); - } - standardInfraConfigs.clear(); - - for (BrokeredInfraConfig infraConfigDefinition : brokeredInfraConfigs) { - Kubernetes.getInstance().getBrokeredInfraConfigClient().withName(infraConfigDefinition.getMetadata().getName()).cascading(true).delete(); - TestUtils.waitUntilCondition("Delete infraConfig plan", waitPhase -> Kubernetes.getInstance().getBrokeredInfraConfigClient().list().getItems().stream().noneMatch(plan -> plan.getMetadata().getName().equals(infraConfigDefinition.getMetadata().getName())), - new TimeoutBudget(1, TimeUnit.MINUTES)); - LOGGER.info("Brokeredinfraconfig {} deleted", infraConfigDefinition.getMetadata().getName()); - } - brokeredInfraConfigs.clear(); - - for (AuthenticationService authService : authServices) { - Kubernetes.getInstance().getAuthenticationServiceClient().withName(authService.getMetadata().getName()).cascading(true).delete(); - TestUtils.waitForNReplicas(0, false, authService.getMetadata().getNamespace(), Map.of("name", authService.getMetadata().getName()), Collections.emptyMap(), new TimeoutBudget(1, TimeUnit.MINUTES), 5000); - LOGGER.info("AuthService {} deleted", authService.getMetadata().getName()); - } - authServices.clear(); - - - closeClientFactories(amqpClientFactory); - amqpClientFactory = null; - } else { - LOGGER.warn("No custom resources are deleted, SKIP_CLEANUP is set"); - } - - } - - - //------------------------------------------------------------------------------------------------ - // Address plans - //------------------------------------------------------------------------------------------------ - - @Override - public void createAddressPlan(AddressPlan addressPlan) throws Exception { - addressPlans.add(addressPlan); - super.createAddressPlan(addressPlan); - } - - @Override - public void removeAddressPlan(AddressPlan addressPlan) throws Exception { - super.removeAddressPlan(addressPlan); - addressPlans.removeIf(addressPlanIter -> addressPlanIter.getMetadata().getName().equals(addressPlan.getMetadata().getName())); - } - - @Override - public void replaceAddressPlan(AddressPlan plan) throws InterruptedException { - super.replaceAddressPlan(plan); - } - - @Override - public AddressPlan getAddressPlan(String name) throws Exception { - return super.getAddressPlan(name); - } - - //------------------------------------------------------------------------------------------------ - // Address space plans - //------------------------------------------------------------------------------------------------ - - @Override - public void createAddressSpacePlan(AddressSpacePlan addressSpacePlan) throws Exception { - addressSpacePlans.add(addressSpacePlan); - super.createAddressSpacePlan(addressSpacePlan); - } - - @Override - public void createAddressSpacePlan(AddressSpacePlan addressSpacePlan, boolean wait) throws Exception { - addressSpacePlans.add(addressSpacePlan); - super.createAddressSpacePlan(addressSpacePlan, wait); - } - - @Override - public void removeAddressSpacePlan(AddressSpacePlan addressSpacePlan) throws Exception { - super.removeAddressSpacePlan(addressSpacePlan); - addressSpacePlans.removeIf(spacePlanIter -> spacePlanIter.getMetadata().getName().equals(addressSpacePlan.getMetadata().getName())); - } - - @Override - public AddressSpacePlan getAddressSpacePlan(String config) throws Exception { - return super.getAddressSpacePlan(config); - } - - //------------------------------------------------------------------------------------------------ - // Infra configs - //------------------------------------------------------------------------------------------------ - - @Override - public BrokeredInfraConfig getBrokeredInfraConfig(String name) throws Exception { - return Kubernetes.getInstance().getBrokeredInfraConfigClient().withName(name).get(); - } - - @Override - public StandardInfraConfig getStandardInfraConfig(String name) throws Exception { - return Kubernetes.getInstance().getStandardInfraConfigClient().withName(name).get(); - } - - @Override - public void createInfraConfig(StandardInfraConfig standardInfraConfig) { - standardInfraConfigs.add(standardInfraConfig); - super.createInfraConfig(standardInfraConfig); - } - - @Override - public void createInfraConfig(BrokeredInfraConfig brokeredInfraConfig) { - brokeredInfraConfigs.add(brokeredInfraConfig); - super.createInfraConfig(brokeredInfraConfig); - } - - public void createInfraConfig(InfraConfig infraConfigDefinition) throws Exception { - if (infraConfigDefinition instanceof StandardInfraConfig) { - standardInfraConfigs.add((StandardInfraConfig) infraConfigDefinition); - super.createInfraConfig((StandardInfraConfig) infraConfigDefinition); - } else { - brokeredInfraConfigs.add((BrokeredInfraConfig) infraConfigDefinition); - super.createInfraConfig((BrokeredInfraConfig) infraConfigDefinition); - } - } - - public void removeInfraConfig(InfraConfig infraConfigDefinition) throws Exception { - if (infraConfigDefinition instanceof StandardInfraConfig) { - super.removeInfraConfig((StandardInfraConfig) infraConfigDefinition); - standardInfraConfigs.removeIf(infraId -> infraId.getMetadata().getName().equals(infraConfigDefinition.getMetadata().getName())); - } else { - super.removeInfraConfig((BrokeredInfraConfig) infraConfigDefinition); - brokeredInfraConfigs.removeIf(infraId -> infraId.getMetadata().getName().equals(infraConfigDefinition.getMetadata().getName())); - } - } - - //------------------------------------------------------------------------------------------------ - // Authentication services - //------------------------------------------------------------------------------------------------ - - @Override - public void replaceAuthService(AuthenticationService authService) throws Exception { - replaceAuthService(authService, false); - } - - public void replaceAuthService(AuthenticationService authenticationService, boolean replaceExisting) throws Exception { - if (replaceExisting) { - super.replaceAuthService(authenticationService); - } else { - super.createAuthService(authenticationService); - authServices.add(authenticationService); - } - } - - @Override - public void createAuthService(AuthenticationService authenticationService) throws Exception { - authServices.add(authenticationService); - super.createAuthService(authenticationService); - } - - @Override - public void createAuthService(AuthenticationService authenticationService, boolean wait) throws Exception { - authServices.add(authenticationService); - super.createAuthService(authenticationService, wait); - } - - @Override - public void removeAuthService(AuthenticationService authService) throws Exception { - super.removeAuthService(authService); - authServices.removeIf(authserviceId -> authserviceId.getMetadata().getName().equals(authService.getMetadata().getName())); - } - - @Override - public void createAddressSpace(AddressSpace addressSpace) throws Exception { - createAddressSpace(addressSpace, true); - } - - @Override - public void createAddressSpace(AddressSpace addressSpace, boolean waitForReady) throws Exception { - if (!AddressSpaceUtils.existAddressSpace(addressSpace.getMetadata().getNamespace(), addressSpace.getMetadata().getName())) { - currentAddressSpaces.add(addressSpace); - super.createAddressSpace(addressSpace, waitForReady); - } else { - if (waitForReady) { - super.waitForAddressSpaceReady(addressSpace); - } - } - } - - public void setReuseAddressSpace() { - reuseAddressSpace = true; - } - - public void unsetReuseAddressSpace() { - reuseAddressSpace = false; - } - - //================================================================================================ - //==================================== AddressSpace methods ====================================== - //================================================================================================ - - public void createAddressSpaceList(AddressSpace... addressSpaces) throws Exception { - String operationID = TimeMeasuringSystem.startOperation(SystemtestsOperation.CREATE_ADDRESS_SPACE); - ArrayList spaces = new ArrayList<>(); - for (AddressSpace addressSpace : addressSpaces) { - if (!AddressSpaceUtils.existAddressSpace(addressSpace.getMetadata().getNamespace(), addressSpace.getMetadata().getName())) { - LOGGER.info("Address space '" + addressSpace + "' doesn't exist and will be created."); - spaces.add(addressSpace); - } else { - LOGGER.warn("Address space '" + addressSpace + "' already exists."); - AddressSpaceUtils.syncAddressSpaceObject(addressSpace); - currentAddressSpaces.add(addressSpace); - } - } - currentAddressSpaces.addAll(spaces); - super.createAddressSpaces(spaces, operationID); - } - - public void replaceAddressSpace(AddressSpace addressSpace) throws Exception { - replaceAddressSpace(addressSpace, true, null); - } - - public void deleteAddressspacesFromList() throws Exception { - if (environment.skipCleanup()) { - LOGGER.warn("No address space is deleted, SKIP_CLEANUP is set"); - } else { - LOGGER.info("All addressspaces will be removed"); - for (AddressSpace addressSpace : currentAddressSpaces) { - deleteAddressSpace(addressSpace); - } - currentAddressSpaces.clear(); - } - } - - @Override - public void addToAddressSpaces(AddressSpace addressSpace) { - this.currentAddressSpaces.add(addressSpace); - } - - @Override - public void deleteAddressSpaceCreatedBySC(AddressSpace addressSpace) throws Exception { - TestUtils.deleteAddressSpaceCreatedBySC(kubernetes, addressSpace, logCollector); - } - - @Override - public AmqpClientFactory getAmqpClientFactory() { - return amqpClientFactory; - } - - @Override - public void setAmqpClientFactory(AmqpClientFactory amqpClientFactory) { - this.amqpClientFactory = amqpClientFactory; - } - -} diff --git a/systemtests/src/main/java/io/enmasse/systemtest/manager/ResourceManager.java b/systemtests/src/main/java/io/enmasse/systemtest/manager/ResourceManager.java deleted file mode 100644 index d1bb14d3e33..00000000000 --- a/systemtests/src/main/java/io/enmasse/systemtest/manager/ResourceManager.java +++ /dev/null @@ -1,532 +0,0 @@ -/* - * Copyright 2019, EnMasse authors. - * License: Apache License 2.0 (see the file LICENSE or http://apache.org/licenses/LICENSE-2.0.html). - */ - -package io.enmasse.systemtest.manager; - -import static io.enmasse.systemtest.time.TimeoutBudget.ofDuration; -import static java.time.Duration.ofMinutes; - -import java.util.List; -import java.util.Objects; -import java.util.concurrent.TimeUnit; -import java.util.regex.Pattern; -import java.util.stream.Collectors; - -import org.junit.jupiter.api.extension.ExtensionContext; -import org.slf4j.Logger; - -import io.enmasse.address.model.Address; -import io.enmasse.address.model.AddressSpace; -import io.enmasse.admin.model.v1.*; -import io.enmasse.config.AnnotationKeys; -import io.enmasse.systemtest.Environment; -import io.enmasse.systemtest.UserCredentials; -import io.enmasse.systemtest.amqp.AmqpClientFactory; -import io.enmasse.systemtest.logs.CustomLogger; -import io.enmasse.systemtest.logs.GlobalLogCollector; -import io.enmasse.systemtest.platform.Kubernetes; -import io.enmasse.systemtest.time.SystemtestsOperation; -import io.enmasse.systemtest.time.TimeMeasuringSystem; -import io.enmasse.systemtest.time.TimeoutBudget; -import io.enmasse.systemtest.utils.AddressSpaceUtils; -import io.enmasse.systemtest.utils.AddressUtils; -import io.enmasse.systemtest.utils.TestUtils; -import io.enmasse.systemtest.utils.UserUtils; -import io.enmasse.user.model.v1.Operation; -import io.enmasse.user.model.v1.User; -import io.enmasse.user.model.v1.UserAuthenticationType; -import io.enmasse.user.model.v1.UserBuilder; -import io.fabric8.kubernetes.api.model.Pod; - -public abstract class ResourceManager { - protected static final Environment environment = Environment.getInstance(); - protected static final Kubernetes kubernetes = Kubernetes.getInstance(); - protected static final GlobalLogCollector logCollector = new GlobalLogCollector(kubernetes, environment.testLogDir()); - private static Logger LOGGER = CustomLogger.getLogger(); - - protected String defaultAddSpaceIdentifier; - protected String addressSpaceType; - protected String addressSpacePlan; - protected List currentAddressSpaces; - - public void setDefaultAddSpaceIdentifier(String defaultAddSpaceIdentifier) { - this.defaultAddSpaceIdentifier = defaultAddSpaceIdentifier; - } - - public void setAddressSpaceType(String addressSpaceType) { - this.addressSpaceType = addressSpaceType; - } - - public void setAddressSpacePlan(String addressSpacePlan) { - this.addressSpacePlan = addressSpacePlan; - } - - public abstract void setup() throws Exception; - - public abstract void tearDown(ExtensionContext context) throws Exception; - - public abstract AmqpClientFactory getAmqpClientFactory(); - - public abstract void setAmqpClientFactory(AmqpClientFactory amqpClientFactory); - - public AddressSpace getSharedAddressSpace() { - return null; - } - - public void addToAddressSpaces(AddressSpace addressSpace) throws Exception { - throw new Exception("Not implemented in resource manager"); - } - - public void deleteAddressSpaceCreatedBySC(AddressSpace addressSpace) throws Exception { - throw new Exception("Not implemented in resource manager"); - } - - //------------------------------------------------------------------------------------------------ - // Client factories - //------------------------------------------------------------------------------------------------ - - public void closeClientFactories(AmqpClientFactory amqpClientFactory) throws Exception { - if (amqpClientFactory != null) { - amqpClientFactory.close(); - } - } - - //------------------------------------------------------------------------------------------------ - // Address plans - //------------------------------------------------------------------------------------------------ - - public void createAddressPlan(AddressPlan addressPlan) throws Exception { - LOGGER.info("Address plan {} will be created {}", addressPlan.getMetadata().getName(), addressPlan); - var client = Kubernetes.getInstance().getAddressPlanClient(); - client.create(addressPlan); - Thread.sleep(1000); - } - - public void replaceAddressPlan(AddressPlan addressPlan) throws InterruptedException { - LOGGER.info("Address plan {} will be replaced {}", addressPlan.getMetadata().getName(), addressPlan); - var client = Kubernetes.getInstance().getAddressPlanClient(); - client.createOrReplace(addressPlan); - Thread.sleep(1000); - } - - public void removeAddressPlan(AddressPlan addressPlan) throws Exception { - Kubernetes.getInstance().getAddressPlanClient().withName(addressPlan.getMetadata().getName()).cascading(true).delete(); - } - - public AddressPlan getAddressPlan(String name) throws Exception { - return Kubernetes.getInstance().getAddressPlanClient().withName(name).get(); - } - - //------------------------------------------------------------------------------------------------ - // Address space plans - //------------------------------------------------------------------------------------------------ - - public void createAddressSpacePlan(AddressSpacePlan addressSpacePlan) throws Exception { - createAddressSpacePlan(addressSpacePlan, true); - } - - public void createAddressSpacePlan(AddressSpacePlan addressSpacePlan, boolean wait) throws Exception { - LOGGER.info("AddressSpace plan {} will be created {}", addressSpacePlan.getMetadata().getName(), addressSpacePlan); - if (addressSpacePlan.getMetadata().getNamespace() == null || addressSpacePlan.getMetadata().getNamespace().equals("")) { - addressSpacePlan.getMetadata().setNamespace(Kubernetes.getInstance().getInfraNamespace()); - } - var client = Kubernetes.getInstance().getAddressSpacePlanClient(); - client.create(addressSpacePlan); - if (wait) { - TestUtils.waitForSchemaInSync(addressSpacePlan.getMetadata().getName()); - } - Thread.sleep(1000); - } - - public void removeAddressSpacePlan(AddressSpacePlan addressSpacePlan) throws Exception { - Kubernetes.getInstance().getAddressSpacePlanClient().withName(addressSpacePlan.getMetadata().getName()).cascading(true).delete(); - } - - public AddressSpacePlan getAddressSpacePlan(String config) throws Exception { - return Kubernetes.getInstance().getAddressSpacePlanClient().withName(config).get(); - } - - //------------------------------------------------------------------------------------------------ - // Infra configs - //------------------------------------------------------------------------------------------------ - - public BrokeredInfraConfig getBrokeredInfraConfig(String name) throws Exception { - return Kubernetes.getInstance().getBrokeredInfraConfigClient().withName(name).get(); - } - - public StandardInfraConfig getStandardInfraConfig(String name) throws Exception { - return Kubernetes.getInstance().getStandardInfraConfigClient().withName(name).get(); - } - - public void createInfraConfig(StandardInfraConfig standardInfraConfig) { - LOGGER.info("StandardInfraConfig {} will be created {}", standardInfraConfig.getMetadata().getName(), standardInfraConfig); - var client = Kubernetes.getInstance().getStandardInfraConfigClient(); - client.createOrReplace(standardInfraConfig); - } - - public void createInfraConfig(BrokeredInfraConfig brokeredInfraConfig) { - LOGGER.info("BrokeredInfraConfig {} will be created {}", brokeredInfraConfig.getMetadata().getName(), brokeredInfraConfig); - var client = Kubernetes.getInstance().getBrokeredInfraConfigClient(); - client.createOrReplace(brokeredInfraConfig); - } - - public void removeInfraConfig(StandardInfraConfig infraConfig) { - var client = Kubernetes.getInstance().getStandardInfraConfigClient(); - client.withName(infraConfig.getMetadata().getName()).cascading(true).delete(); - } - - public void removeInfraConfig(BrokeredInfraConfig infraConfig) { - var client = Kubernetes.getInstance().getBrokeredInfraConfigClient(); - client.withName(infraConfig.getMetadata().getName()).cascading(true).delete(); - } - - //------------------------------------------------------------------------------------------------ - // Authentication services - //------------------------------------------------------------------------------------------------ - - public AuthenticationService getAuthService(String name) { - return Kubernetes.getInstance().getAuthenticationServiceClient().withName(name).get(); - } - - public void createAuthService(AuthenticationService authenticationService) throws Exception { - createAuthService(authenticationService, true); - } - - public void createAuthService(AuthenticationService authenticationService, boolean wait) throws Exception { - var client = Kubernetes.getInstance().getAuthenticationServiceClient(); - LOGGER.info("AuthService {} will be created {}", authenticationService.getMetadata().getName(), authenticationService); - client.create(authenticationService); - if (wait) { - waitForAuthPods(authenticationService); - } - } - - - public void replaceAuthService(AuthenticationService authenticationService) throws Exception { - LOGGER.info("AuthService {} will be created {}", authenticationService.getMetadata().getName(), authenticationService); - var client = Kubernetes.getInstance().getAuthenticationServiceClient(); - client.createOrReplace(authenticationService); - waitForAuthPods(authenticationService); - } - - public void waitForAuthPods(AuthenticationService authenticationService) throws Exception { - String desiredPodName = authenticationService.getMetadata().getName(); - int expectedMatches = 1; - if (authenticationService.getSpec().getType().equals(AuthenticationServiceType.none) && - authenticationService.getSpec().getNone() != null && - authenticationService.getSpec().getNone().getReplicas() != null) { - expectedMatches = authenticationService.getSpec().getNone().getReplicas(); - } else if (authenticationService.getSpec().getType().equals(AuthenticationServiceType.standard) && - authenticationService.getSpec().getStandard() != null && - authenticationService.getSpec().getStandard().getReplicas() != null) { - expectedMatches = authenticationService.getSpec().getStandard().getReplicas(); - } - int finalExpectedMatches = expectedMatches; - TestUtils.waitUntilCondition("Auth service is deployed: " + desiredPodName, phase -> { - List pods = TestUtils.listReadyPods(Kubernetes.getInstance(), authenticationService.getMetadata().getNamespace()); - long matching = pods.stream().filter(pod -> - pod.getMetadata().getName().contains(desiredPodName)).count(); - if (matching != finalExpectedMatches) { - List podNames = pods.stream().map(p -> p.getMetadata().getName()).collect(Collectors.toList()); - LOGGER.info("Still awaiting pod with name : {}, matching : {}, current pods {}", - desiredPodName, matching, podNames); - } - - return matching == finalExpectedMatches; - }, - new TimeoutBudget(5, TimeUnit.MINUTES)); - } - - public void removeAuthService(AuthenticationService authService) throws Exception { - Kubernetes.getInstance().getAuthenticationServiceClient(authService.getMetadata().getNamespace()) - .withName(authService.getMetadata().getName()).cascading(true).delete(); - TestUtils.waitUntilCondition("Auth service is deleted: " + authService.getMetadata().getName(), (phase) -> - TestUtils.listReadyPods(Kubernetes.getInstance(), authService.getMetadata().getNamespace()).stream().noneMatch(pod -> - pod.getMetadata().getName().contains(authService.getMetadata().getName())), - new TimeoutBudget(1, TimeUnit.MINUTES)); - } - - public void createAddressSpace(AddressSpace addressSpace) throws Exception { - createAddressSpace(addressSpace, true); - } - - public void createAddressSpace(AddressSpace addressSpace, boolean waitUntilReady) throws Exception { - String operationID = TimeMeasuringSystem.startOperation(SystemtestsOperation.CREATE_ADDRESS_SPACE); - if (!AddressSpaceUtils.existAddressSpace(addressSpace.getMetadata().getNamespace(), addressSpace.getMetadata().getName())) { - LOGGER.info("Address space '{}' doesn't exist and will be created.", addressSpace); - kubernetes.getAddressSpaceClient(addressSpace.getMetadata().getNamespace()).createOrReplace(addressSpace); - if (waitUntilReady) { - AddressSpaceUtils.waitForAddressSpaceReady(addressSpace); - } - } else { - if (waitUntilReady) { - AddressSpaceUtils.waitForAddressSpaceReady(addressSpace); - } - LOGGER.info("Address space '" + addressSpace + "' already exists."); - } - AddressSpaceUtils.syncAddressSpaceObject(addressSpace); - TimeMeasuringSystem.stopOperation(operationID); - } - - public void createAddressSpace(AddressSpace... addressSpaces) throws Exception { - String operationID = TimeMeasuringSystem.startOperation(SystemtestsOperation.CREATE_ADDRESS_SPACE); - for (AddressSpace addressSpace : addressSpaces) { - if (!AddressSpaceUtils.existAddressSpace(addressSpace.getMetadata().getNamespace(), addressSpace.getMetadata().getName())) { - LOGGER.info("Address space '{}' doesn't exist and will be created.", addressSpace); - kubernetes.getAddressSpaceClient(addressSpace.getMetadata().getNamespace()).createOrReplace(addressSpace); - } else { - LOGGER.info("Address space '" + addressSpace + "' already exists."); - } - } - for (AddressSpace addressSpace : addressSpaces) { - AddressSpaceUtils.waitForAddressSpaceReady(addressSpace); - AddressSpaceUtils.syncAddressSpaceObject(addressSpace); - } - TimeMeasuringSystem.stopOperation(operationID); - } - - public void waitForAddressSpaceReady(AddressSpace addressSpace) throws Exception { - LOGGER.info("Waiting for address space ready"); - String operationID = TimeMeasuringSystem.startOperation(SystemtestsOperation.CREATE_ADDRESS_SPACE); - AddressSpaceUtils.waitForAddressSpaceReady(addressSpace); - AddressSpaceUtils.syncAddressSpaceObject(addressSpace); - TimeMeasuringSystem.stopOperation(operationID); - } - - //------------------------------------------------------------------------------------------------ - // Address space methods - //------------------------------------------------------------------------------------------------ - - public void deleteAddressSpace(AddressSpace addressSpace) throws Exception { - if (AddressSpaceUtils.existAddressSpace(addressSpace.getMetadata().getNamespace(), addressSpace.getMetadata().getName())) { - AddressSpaceUtils.deleteAddressSpaceAndWait(addressSpace, logCollector); - } else { - LOGGER.info("Address space '" + addressSpace.getMetadata().getName() + "' doesn't exists!"); - } - } - - public void deleteAddressSpaceWithoutWait(AddressSpace addressSpace) throws Exception { - if (AddressSpaceUtils.existAddressSpace(addressSpace.getMetadata().getNamespace(), addressSpace.getMetadata().getName())) { - AddressSpaceUtils.deleteAddressSpace(addressSpace, logCollector); - } else { - LOGGER.info("Address space '" + addressSpace.getMetadata().getName() + "' doesn't exists!"); - } - } - - protected void createAddressSpaces(List addressSpaces, String operationID) throws Exception { - addressSpaces.forEach(addressSpace -> - kubernetes.getAddressSpaceClient(addressSpace.getMetadata().getNamespace()).createOrReplace(addressSpace)); - for (AddressSpace addressSpace : addressSpaces) { - AddressSpaceUtils.waitForAddressSpaceReady(addressSpace); - AddressSpaceUtils.syncAddressSpaceObject(addressSpace); - } - TimeMeasuringSystem.stopOperation(operationID); - } - - public void replaceAddressSpace(AddressSpace addressSpace, boolean waitForConfigApplied, TimeoutBudget waitBudget) throws Exception { - String operationID = TimeMeasuringSystem.startOperation(SystemtestsOperation.UPDATE_ADDRESS_SPACE); - var client = kubernetes.getAddressSpaceClient(addressSpace.getMetadata().getNamespace()); - if (AddressSpaceUtils.existAddressSpace(addressSpace.getMetadata().getNamespace(), addressSpace.getMetadata().getName())) { - LOGGER.info("Address space '{}' exists and will be updated.", addressSpace); - final AddressSpace current = client.withName(addressSpace.getMetadata().getName()).get(); - final String currentResourceVersion = current.getMetadata().getResourceVersion(); - final String currentConfig = current.getAnnotation(AnnotationKeys.APPLIED_CONFIGURATION); - client.createOrReplace(addressSpace); - Thread.sleep(10_000); - TestUtils.waitForChangedResourceVersion(ofDuration(ofMinutes(5)), addressSpace.getMetadata().getNamespace(), addressSpace.getMetadata().getName(), currentResourceVersion); - if (waitForConfigApplied) { - AddressSpaceUtils.waitForAddressSpaceConfigurationApplied(addressSpace, currentConfig); - } - if (waitBudget == null) { - AddressSpaceUtils.waitForAddressSpaceReady(addressSpace); - } else { - AddressSpaceUtils.waitForAddressSpaceReady(addressSpace, waitBudget); - } - - AddressSpaceUtils.syncAddressSpaceObject(addressSpace); - currentAddressSpaces.add(addressSpace); - } else { - LOGGER.info("Address space '{}' does not exists.", addressSpace.getMetadata().getName()); - } - LOGGER.info("Address space updated: {}", addressSpace); - TimeMeasuringSystem.stopOperation(operationID); - } - - public AddressSpace getAddressSpace(String namespace, String addressSpaceName) { - return kubernetes.getAddressSpaceClient(namespace).withName(addressSpaceName).get(); - } - - public AddressSpace getAddressSpace(String addressSpaceName) { - return kubernetes.getAddressSpaceClient().withName(addressSpaceName).get(); - } - - - //================================================================================================ - //====================================== Address methods ========================================= - //================================================================================================ - - public void deleteAddresses(Address... destinations) { - logCollector.collectRouterState("deleteAddresses"); - AddressUtils.delete(destinations); - } - - public void deleteAddresses(AddressSpace addressSpace) throws Exception { - LOGGER.info("Addresses in " + addressSpace.getMetadata().getName() + " will be deleted!"); - logCollector.collectRouterState("deleteAddresses"); - AddressUtils.delete(addressSpace); - } - - - public void appendAddresses(Address... destinations) throws Exception { - TimeoutBudget budget = new TimeoutBudget(15, TimeUnit.MINUTES); - appendAddresses(budget, destinations); - } - - public void appendAddresses(TimeoutBudget timeout, Address... destinations) throws Exception { - appendAddresses(true, timeout, destinations); - } - - public void appendAddresses(boolean wait, Address... destinations) throws Exception { - TimeoutBudget budget = new TimeoutBudget(15, TimeUnit.MINUTES); - appendAddresses(wait, budget, destinations); - } - - private void appendAddresses(boolean wait, TimeoutBudget timeout, Address... destinations) throws Exception { - AddressUtils.appendAddresses(timeout, wait, destinations); - } - - public void setAddresses(TimeoutBudget budget, Address... addresses) throws Exception { - logCollector.collectRouterState("setAddresses"); - AddressUtils.setAddresses(budget, true, addresses); - } - - public void setAddresses(boolean wait, Address... addresses) throws Exception { - logCollector.collectRouterState("setAddresses"); - AddressUtils.setAddresses(null, wait, addresses); - } - - public void setAddresses(Address... addresses) throws Exception { - setAddresses(new TimeoutBudget(15, TimeUnit.MINUTES), addresses); - } - - public void replaceAddress(Address destination) throws Exception { - AddressUtils.replaceAddress(destination, true, new TimeoutBudget(10, TimeUnit.MINUTES)); - } - - public Address getAddress(String namespace, Address destination) { - return kubernetes.getAddressClient().inNamespace(namespace).withName(destination.getMetadata().getName()).get(); - } - - //================================================================================================ - //======================================= User methods =========================================== - //================================================================================================ - - public User createOrUpdateUser(AddressSpace addressSpace, UserCredentials credentials) throws Exception { - return createOrUpdateUser(addressSpace, credentials, true); - } - - public User createOrUpdateUser(AddressSpace addressSpace, UserCredentials credentials, boolean wait) throws Exception { - Objects.requireNonNull(addressSpace); - Objects.requireNonNull(credentials); - User user = new UserBuilder() - .withNewMetadata() - .withName(addressSpace.getMetadata().getName() + "." + credentials.getUsername()) - .endMetadata() - .withNewSpec() - .withUsername(credentials.getUsername()) - .withNewAuthentication() - .withType(UserAuthenticationType.password) - .withNewPassword(UserUtils.passwordToBase64(credentials.getPassword())) - .endAuthentication() - .addNewAuthorization() - .withAddresses("*") - .addToOperations(Operation.send) - .addToOperations(Operation.recv) - .endAuthorization() - .addNewAuthorization() - .addToOperations(Operation.manage) - .endAuthorization() - .endSpec() - .build(); - return createOrUpdateUser(addressSpace, user, wait); - } - - public User createOrUpdateUser(AddressSpace addressSpace, User user) throws Exception { - return createOrUpdateUser(addressSpace, user, true); - } - - public User createOrUpdateUser(AddressSpace addressSpace, User user, boolean wait) throws Exception { - if (user.getMetadata().getName() == null || !user.getMetadata().getName().contains(addressSpace.getMetadata().getName())) { - user.getMetadata().setName(addressSpace.getMetadata().getName() + "." + user.getSpec().getUsername()); - } - if (user.getMetadata().getNamespace() == null) { - user.getMetadata().setNamespace(addressSpace.getMetadata().getNamespace()); - } - LOGGER.info("User {} in address space {} will be created/replaced", user, addressSpace.getMetadata().getName()); - User existing = kubernetes.getUserClient(user.getMetadata().getNamespace()).withName(user.getMetadata().getName()).get(); - if (existing != null) { - existing.setSpec(user.getSpec()); - user = existing; - } - kubernetes.getUserClient(user.getMetadata().getNamespace()).createOrReplace(user); - if (wait) { - return UserUtils.waitForUserActive(user, new TimeoutBudget(1, TimeUnit.MINUTES)); - } - return kubernetes.getUserClient(user.getMetadata().getNamespace()).withName(user.getMetadata().getName()).get(); - } - - public User createUserServiceAccount(AddressSpace addressSpace, UserCredentials cred) throws Exception { - LOGGER.info("ServiceAccount user {} in address space {} will be created", cred.getUsername(), addressSpace.getMetadata().getName()); - String serviceaccountName = kubernetes.createServiceAccount(cred.getUsername(), addressSpace.getMetadata().getNamespace()); - User user = new UserBuilder() - .withNewMetadata() - .withName(String.format("%s.%s", addressSpace.getMetadata().getName(), - Pattern.compile(".*:").matcher(serviceaccountName).replaceAll(""))) - .endMetadata() - .withNewSpec() - .withUsername(serviceaccountName) - .withNewAuthentication() - .withType(UserAuthenticationType.serviceaccount) - .endAuthentication() - .addNewAuthorization() - .withAddresses("*") - .addToOperations(Operation.send) - .addToOperations(Operation.recv) - .endAuthorization() - .endSpec() - .build(); - return createOrUpdateUser(addressSpace, user); - } - - public void removeUser(AddressSpace addressSpace, User user) throws InterruptedException { - Objects.requireNonNull(addressSpace); - Objects.requireNonNull(user); - LOGGER.info("User {} in address space {} will be removed", user.getMetadata().getName(), addressSpace.getMetadata().getName()); - kubernetes.getUserClient(addressSpace.getMetadata().getNamespace()).withName(user.getMetadata().getName()).cascading(true).delete(); - UserUtils.waitForUserDeleted(user.getMetadata().getNamespace(), user.getMetadata().getName(), new TimeoutBudget(1, TimeUnit.MINUTES)); - } - - public void removeUser(AddressSpace addressSpace, String userName) throws InterruptedException { - Objects.requireNonNull(addressSpace); - LOGGER.info("User {} in address space {} will be removed", userName, addressSpace.getMetadata().getName()); - String name = String.format("%s.%s", addressSpace.getMetadata().getName(), userName); - kubernetes.getUserClient(addressSpace.getMetadata().getNamespace()).withName(name).cascading(true).delete(); - UserUtils.waitForUserDeleted(addressSpace.getMetadata().getNamespace(), name, new TimeoutBudget(1, TimeUnit.MINUTES)); - } - - public User getUser(AddressSpace addressSpace, String username) { - String id = String.format("%s.%s", addressSpace.getMetadata().getName(), username); - List response = kubernetes.getUserClient(addressSpace.getMetadata().getNamespace()).list().getItems(); - LOGGER.info("User list for {}: {}", addressSpace.getMetadata().getName(), response); - for (User user : response) { - if (user.getMetadata().getName().equals(id)) { - LOGGER.info("User {} in addressspace {} already exists", username, addressSpace.getMetadata().getName()); - return user; - } - } - return null; - } - -} diff --git a/systemtests/src/main/java/io/enmasse/systemtest/manager/SharedIoTManager.java b/systemtests/src/main/java/io/enmasse/systemtest/manager/SharedIoTManager.java deleted file mode 100644 index 8e282c6319f..00000000000 --- a/systemtests/src/main/java/io/enmasse/systemtest/manager/SharedIoTManager.java +++ /dev/null @@ -1,163 +0,0 @@ -/* - * Copyright 2019-2020, EnMasse authors. - * License: Apache License 2.0 (see the file LICENSE or http://apache.org/licenses/LICENSE-2.0.html). - */ -package io.enmasse.systemtest.manager; - -import static io.enmasse.systemtest.bases.iot.ITestIoTBase.IOT_PROJECT_NAMESPACE; -import static io.enmasse.systemtest.iot.DefaultDeviceRegistry.newDefaultInstance; -import static io.enmasse.systemtest.utils.IoTUtils.createIoTConfig; -import static io.enmasse.systemtest.utils.IoTUtils.createIoTProject; - -import java.nio.file.Path; -import java.util.UUID; - -import org.junit.jupiter.api.extension.ExtensionContext; -import org.slf4j.Logger; - -import io.enmasse.address.model.AddressSpace; -import io.enmasse.iot.model.v1.IoTConfig; -import io.enmasse.iot.model.v1.IoTProject; -import io.enmasse.systemtest.UserCredentials; -import io.enmasse.systemtest.amqp.AmqpClient; -import io.enmasse.systemtest.amqp.AmqpClientFactory; -import io.enmasse.systemtest.iot.IoTTestSession; -import io.enmasse.systemtest.logs.CustomLogger; -import io.enmasse.systemtest.platform.apps.SystemtestsKubernetesApps; -import io.enmasse.systemtest.utils.IoTUtils; -import io.enmasse.systemtest.utils.TestUtils; - - -public class SharedIoTManager extends ResourceManager { - - private static final Logger LOGGER = CustomLogger.getLogger(); - private static SharedIoTManager instance; - protected AmqpClientFactory amqpClientFactory = null; - private IoTProject sharedIoTProject = null; - private IoTConfig sharedIoTConfig = null; - private AmqpClient amqpClient; - - private SharedIoTManager() { - } - - public static synchronized SharedIoTManager getInstance() { - if (instance == null) { - instance = new SharedIoTManager(); - } - return instance; - } - - @Override - public AddressSpace getSharedAddressSpace() { - if (sharedIoTProject == null) { - return null; - } - String addSpaceName = sharedIoTProject.getSpec().getDownstreamStrategy().getManagedStrategy().getAddressSpace().getName(); - return kubernetes.getAddressSpaceClient(sharedIoTProject.getMetadata().getNamespace()).withName(addSpaceName).get(); - } - - @Override - public void tearDown(ExtensionContext context) throws Exception { - closeAmqpFactory(); - if (environment.skipCleanup()) { - LOGGER.info("Skip cleanup is set, no cleanup process"); - } else { - if (sharedIoTProject != null) { - LOGGER.info("Shared IoTProject will be removed"); - var iotProjectApiClient = kubernetes.getIoTProjectClient(sharedIoTProject.getMetadata().getNamespace()); - if (iotProjectApiClient.withName(sharedIoTProject.getMetadata().getName()).get() != null) { - IoTUtils.deleteIoTProjectAndWait(kubernetes, sharedIoTProject); - sharedIoTProject = null; - } else { - LOGGER.info("IoTProject '{}' doesn't exists!", sharedIoTProject.getMetadata().getName()); - } - } - tearDownSharedIoTConfig(); - if (context.getExecutionException().isPresent()) { - Path path = TestUtils.getFailedTestLogsPath(context); - SystemtestsKubernetesApps.collectInfinispanServerLogs(path); - } - SystemtestsKubernetesApps.deleteInfinispanServer(); - SystemtestsKubernetesApps.deletePostgresqlServer(); - SystemtestsKubernetesApps.deleteH2Server(); - } - } - - public void tearDownSharedIoTConfig() throws Exception { - if (sharedIoTConfig != null) { - LOGGER.info("Shared IoTConfig will be removed"); - var iotConfigApiClient = kubernetes.getIoTConfigClient(); - if (iotConfigApiClient.withName(sharedIoTConfig.getMetadata().getName()).get() != null) { - IoTUtils.deleteIoTConfigAndWait(kubernetes, sharedIoTConfig); - sharedIoTConfig = null; - } else { - LOGGER.info("IoTConfig '{}' doesn't exists!", sharedIoTConfig.getMetadata().getName()); - } - } - } - - void initFactories(UserCredentials credentials) { - amqpClientFactory = new AmqpClientFactory(getSharedAddressSpace(), credentials); - } - - @Override - public void setup() throws Exception { - kubernetes.createNamespace(IOT_PROJECT_NAMESPACE); - - UserCredentials credentials = new UserCredentials(UUID.randomUUID().toString(), UUID.randomUUID().toString()); - - if (sharedIoTConfig == null) { - createNewIoTConfig(); - } - - if (sharedIoTProject == null) { - sharedIoTProject = IoTUtils.getBasicIoTProjectObject("shared-iot-project", defaultAddSpaceIdentifier, IOT_PROJECT_NAMESPACE, addressSpacePlan); - createIoTProject(sharedIoTProject); - } - initFactories(credentials); - createOrUpdateUser(getSharedAddressSpace(), credentials); - this.amqpClient = amqpClientFactory.createQueueClient(); - } - - private void createNewIoTConfig() throws Exception { - sharedIoTConfig = IoTTestSession - .createDefaultConfig() - .editOrNewSpec().withServices(newDefaultInstance()).endSpec() - .build(); - - createIoTConfig(sharedIoTConfig); - } - - @Override - public AmqpClientFactory getAmqpClientFactory() { - return amqpClientFactory; - } - - @Override - public void setAmqpClientFactory(AmqpClientFactory amqpClientFactory) { - this.amqpClientFactory = amqpClientFactory; - } - - public IoTProject getSharedIoTProject() { - return sharedIoTProject; - } - - public IoTConfig getSharedIoTConfig() { - return sharedIoTConfig; - } - - public String getTenantId() { - return IoTUtils.getTenantId(sharedIoTProject); - } - - public AmqpClient getAmqpClient() { - return amqpClient; - } - - public void closeAmqpFactory() throws Exception { - if (amqpClientFactory != null) { - amqpClientFactory.close(); - amqpClientFactory = null; - } - } -} diff --git a/systemtests/src/main/java/io/enmasse/systemtest/manager/SharedResourceManager.java b/systemtests/src/main/java/io/enmasse/systemtest/manager/SharedResourceManager.java deleted file mode 100644 index 8140c7157c1..00000000000 --- a/systemtests/src/main/java/io/enmasse/systemtest/manager/SharedResourceManager.java +++ /dev/null @@ -1,138 +0,0 @@ -/* - * Copyright 2019, EnMasse authors. - * License: Apache License 2.0 (see the file LICENSE or http://apache.org/licenses/LICENSE-2.0.html). - */ - -package io.enmasse.systemtest.manager; - -import java.util.HashMap; -import java.util.Map; - -import org.junit.jupiter.api.extension.ExtensionContext; -import org.slf4j.Logger; - -import io.enmasse.address.model.AddressSpace; -import io.enmasse.address.model.AddressSpaceBuilder; -import io.enmasse.systemtest.UserCredentials; -import io.enmasse.systemtest.amqp.AmqpClientFactory; -import io.enmasse.systemtest.logs.CustomLogger; -import io.enmasse.systemtest.platform.Kubernetes; - -public class SharedResourceManager extends ResourceManager { - private static final Logger LOGGER = CustomLogger.getLogger(); - private static SharedResourceManager instance; - private static Map spaceCountMap = new HashMap<>(); - protected AmqpClientFactory amqpClientFactory = null; - private AddressSpace sharedAddressSpace = null; - private static final String DEFAULT_ADDRESS_TEMPLATE = "-shared-"; - private UserCredentials defaultCredentials = environment.getSharedDefaultCredentials(); - private UserCredentials managementCredentials = environment.getSharedManagementCredentials(); - - public static synchronized SharedResourceManager getInstance() { - if (instance == null) { - instance = new SharedResourceManager(); - } - return instance; - } - - @Override - public AddressSpace getSharedAddressSpace() { - return sharedAddressSpace; - } - - @Override - public void tearDown(ExtensionContext context) throws Exception { - if (context.getExecutionException().isPresent()) { //test failed - if (environment.skipCleanup()) { - LOGGER.warn("No address space is deleted, SKIP_CLEANUP is set"); - } else { - LOGGER.info(String.format("test failed: %s.%s", - context.getRequiredTestClass().getName(), - context.getRequiredTestMethod().getName())); - LOGGER.info("shared address space '{}' will be removed", sharedAddressSpace); - try { - if (sharedAddressSpace != null) { - super.deleteAddressSpace(sharedAddressSpace); - } - } catch (Exception ex) { - LOGGER.warn("Failed to delete shared address space (ignored)", ex); - } finally { - spaceCountMap.compute(defaultAddSpaceIdentifier, (k, count) -> count == null ? null : count + 1); - } - } - } else { //succeed - try { - if (environment.skipCleanup()) { - LOGGER.warn("No address space is deleted, SKIP_CLEANUP is set"); - } else { - LOGGER.info("Shared address space will be deleted!"); - super.deleteAddressSpace(sharedAddressSpace); - } - } catch (Exception e) { - LOGGER.warn("Failed to delete addresses from shared address space (ignored)", e); - } - } - closeClientFactories(amqpClientFactory); - amqpClientFactory = null; - sharedAddressSpace = null; - } - - void initFactories(AddressSpace addressSpace) { - amqpClientFactory = new AmqpClientFactory(sharedAddressSpace, defaultCredentials); - } - - @Override - public void setup() throws Exception { - LOGGER.info("Shared setup"); - if (spaceCountMap == null) { - spaceCountMap = new HashMap<>(); - } - spaceCountMap.putIfAbsent(defaultAddSpaceIdentifier, 0); - sharedAddressSpace = new AddressSpaceBuilder() - .withNewMetadata() - .withName(defaultAddSpaceIdentifier + DEFAULT_ADDRESS_TEMPLATE + spaceCountMap.get(defaultAddSpaceIdentifier)) - .withNamespace(Kubernetes.getInstance().getInfraNamespace()) - .endMetadata() - .withNewSpec() - .withType(addressSpaceType) - .withPlan(addressSpacePlan) - .withNewAuthenticationService() - .withName("standard-authservice") - .endAuthenticationService() - .endSpec() - .build(); - createSharedAddressSpace(sharedAddressSpace); - createOrUpdateUser(sharedAddressSpace, managementCredentials); - createOrUpdateUser(sharedAddressSpace, defaultCredentials); - initFactories(sharedAddressSpace); - } - - public void createSharedAddressSpace(AddressSpace addressSpace) throws Exception { - super.createAddressSpace(addressSpace); - sharedAddressSpace = addressSpace; - } - - @Override - public void createAddressSpace(AddressSpace addressSpace) throws Exception { - super.createAddressSpace(addressSpace); - } - - public void tearDownShared() throws Exception { - LOGGER.info("Deleting addresses"); - deleteAddresses(sharedAddressSpace); - LOGGER.info("Closing clients"); - closeClientFactories(amqpClientFactory); - initFactories(sharedAddressSpace); - } - - @Override - public AmqpClientFactory getAmqpClientFactory() { - return amqpClientFactory; - } - - @Override - public void setAmqpClientFactory(AmqpClientFactory amqpClientFactory) { - this.amqpClientFactory = amqpClientFactory; - } - -} diff --git a/systemtests/src/main/java/io/enmasse/systemtest/messaginginfra/ResourceManager.java b/systemtests/src/main/java/io/enmasse/systemtest/messaginginfra/ResourceManager.java index c9e47b8100e..14b6a2f8239 100644 --- a/systemtests/src/main/java/io/enmasse/systemtest/messaginginfra/ResourceManager.java +++ b/systemtests/src/main/java/io/enmasse/systemtest/messaginginfra/ResourceManager.java @@ -142,9 +142,6 @@ public void setDefaultMessagingTenant(MessagingTenant tenant) { // Client factories //------------------------------------------------------------------------------------------------ - public void initFactories(AddressSpace addressSpace) { - amqpClientFactory = new AmqpClientFactory(addressSpace, environment.getSharedDefaultCredentials()); - } public void initFactories(AddressSpace addressSpace, UserCredentials cred) { amqpClientFactory = new AmqpClientFactory(addressSpace, cred); diff --git a/systemtests/src/main/java/io/enmasse/systemtest/messaginginfra/resources/MessagingEndpointResourceType.java b/systemtests/src/main/java/io/enmasse/systemtest/messaginginfra/resources/MessagingEndpointResourceType.java index 6fd4f76c18b..8cc2523fd14 100644 --- a/systemtests/src/main/java/io/enmasse/systemtest/messaginginfra/resources/MessagingEndpointResourceType.java +++ b/systemtests/src/main/java/io/enmasse/systemtest/messaginginfra/resources/MessagingEndpointResourceType.java @@ -71,4 +71,10 @@ public static MessagingEndpointCondition getCondition(List p.getProtocol().equals(protocol)) + .findAny().orElseThrow().getPort(); + } } diff --git a/systemtests/src/main/java/io/enmasse/systemtest/operator/EnmasseOperatorManager.java b/systemtests/src/main/java/io/enmasse/systemtest/operator/EnmasseOperatorManager.java index 08ba9d557f5..3adced3aa7f 100644 --- a/systemtests/src/main/java/io/enmasse/systemtest/operator/EnmasseOperatorManager.java +++ b/systemtests/src/main/java/io/enmasse/systemtest/operator/EnmasseOperatorManager.java @@ -4,22 +4,6 @@ */ package io.enmasse.systemtest.operator; -import static org.junit.jupiter.api.Assertions.assertTrue; -import static io.enmasse.systemtest.condition.OpenShiftVersion.OCP4; - -import java.nio.file.Files; -import java.nio.file.Path; -import java.nio.file.Paths; -import java.time.Duration; -import java.util.Arrays; -import java.util.Collections; -import java.util.List; -import java.util.concurrent.TimeUnit; -import java.util.function.Consumer; -import java.util.stream.Collectors; - -import org.slf4j.Logger; - import io.enmasse.admin.model.v1.ConsoleService; import io.enmasse.admin.model.v1.ConsoleServiceSpec; import io.enmasse.systemtest.Environment; @@ -33,8 +17,23 @@ import io.enmasse.systemtest.platform.OpenShift; import io.enmasse.systemtest.time.TimeoutBudget; import io.enmasse.systemtest.utils.TestUtils; -import io.fabric8.kubernetes.api.model.EnvVar; import io.fabric8.kubernetes.api.model.ConfigMapBuilder; +import io.fabric8.kubernetes.api.model.EnvVar; +import org.slf4j.Logger; + +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.time.Duration; +import java.util.Arrays; +import java.util.Collections; +import java.util.List; +import java.util.concurrent.TimeUnit; +import java.util.function.Consumer; +import java.util.stream.Collectors; + +import static io.enmasse.systemtest.condition.OpenShiftVersion.OCP4; +import static org.junit.jupiter.api.Assertions.assertTrue; public class EnmasseOperatorManager { @@ -62,22 +61,12 @@ public OLMOperatorManager olm() { } public void installEnmasseBundle() throws Exception { - LOGGER.info("***********************************************************"); - LOGGER.info(" Enmasse operator install"); - LOGGER.info("***********************************************************"); - installOperators(); - installExamplesBundle(kube.getInfraNamespace()); - waitUntilOperatorReady(kube.getInfraNamespace()); - LOGGER.info("***********************************************************"); - } - - public void installEnmasseSharedInfraBundle() throws Exception { LOGGER.info("***********************************************************"); LOGGER.info(" Enmasse operator shared infra install"); LOGGER.info("***********************************************************"); generateTemplates(); kube.createNamespace(kube.getInfraNamespace(), Collections.singletonMap("allowed", "true")); - KubeCMDClient.applyFromFile(kube.getInfraNamespace(), Paths.get(Environment.getInstance().getTemplatesPath(), "install", "preview-bundles", "enmasse")); + KubeCMDClient.applyFromFile(kube.getInfraNamespace(), Paths.get(Environment.getInstance().getTemplatesPath(), "install", "bundles", env.getProductName())); TestUtils.waitUntilDeployed(kube.getInfraNamespace()); LOGGER.info("***********************************************************"); } @@ -91,15 +80,6 @@ public void installEnmasseOlm(OLMInstallationType installation) throws Exception LOGGER.info("***********************************************************"); } - public void installIoTOperator() { - LOGGER.info("***********************************************************"); - LOGGER.info(" Enmasse IoT operator install"); - LOGGER.info("***********************************************************"); - LOGGER.info("Installing enmasse IoT operator from: {}", Environment.getInstance().getTemplatesPath()); - KubeCMDClient.applyFromFile(kube.getInfraNamespace(), Paths.get(Environment.getInstance().getTemplatesPath(), "install", "preview-bundles", "iot")); - LOGGER.info("***********************************************************"); - } - public void enableMonitoring() throws Exception { LOGGER.info("***********************************************************"); LOGGER.info(" Enmasse enable monitoring"); @@ -352,7 +332,6 @@ public void removeServiceCatalog(String namespace) { public void removeIoT() { LOGGER.info("Delete enmasse IoT from: {}", Environment.getInstance().getTemplatesPath()); - KubeCMDClient.deleteFromFile(kube.getInfraNamespace(), Paths.get(Environment.getInstance().getTemplatesPath(), "install", "preview-bundles", "iot")); KubeCMDClient.runOnCluster("delete", "iotconfigs", "--all", "-n", kube.getInfraNamespace()); } @@ -437,8 +416,7 @@ private void awaitConsoleReadiness(String namespace) throws Exception { public boolean isEnmasseBundleDeployed() { return kube.namespaceExists(kube.getInfraNamespace()) - && kube.listPods(kube.getInfraNamespace()).stream().filter(pod -> pod.getMetadata().getName().contains("enmasse-operator")).count() == 1 - && kube.listPods(kube.getInfraNamespace()).stream().filter(pod -> pod.getMetadata().getName().contains("address-space-controller")).count() == 1; + && kube.listPods(kube.getInfraNamespace()).stream().filter(pod -> pod.getMetadata().getName().contains("enmasse-operator")).count() == 1; } public boolean isEnmasseOlmDeployed() { diff --git a/systemtests/src/main/java/io/enmasse/systemtest/operator/OLMOperatorManager.java b/systemtests/src/main/java/io/enmasse/systemtest/operator/OLMOperatorManager.java index 7c50c2b39b8..e30fccffbd4 100644 --- a/systemtests/src/main/java/io/enmasse/systemtest/operator/OLMOperatorManager.java +++ b/systemtests/src/main/java/io/enmasse/systemtest/operator/OLMOperatorManager.java @@ -11,6 +11,7 @@ import java.util.Arrays; import java.util.Collections; import java.util.List; + import org.slf4j.Logger; import com.fasterxml.jackson.dataformat.yaml.YAMLMapper; @@ -73,7 +74,7 @@ public void install(OLMInstallationType installation, String manifestsImage, Str if (installation == OLMInstallationType.SPECIFIC) { Path operatorGroupFile = Files.createTempFile("operatorgroup", ".yaml"); - String operatorGroup = Files.readString(Paths.get("custom-operator-registry", "operator-group.yaml")); + String operatorGroup = Files.readString(Paths.get(System.getProperty("user.dir"), "custom-operator-registry", "operator-group.yaml")); Files.writeString(operatorGroupFile, operatorGroup.replaceAll("\\$\\{OPERATOR_NAMESPACE}", namespace)); KubeCMDClient.applyFromFile(namespace, operatorGroupFile); } @@ -91,31 +92,31 @@ public void clean() throws Exception { public void applySubscription(String installationNamespace, String catalogSourceName, String catalogNamespace, String csvName, String operatorName, String operatorChannel) throws IOException { Path subscriptionFile = Files.createTempFile("subscription", ".yaml"); - String subscription = Files.readString(Paths.get("custom-operator-registry", "subscription.yaml")); + String subscription = Files.readString(Paths.get(System.getProperty("user.dir"), "custom-operator-registry", "subscription.yaml")); Files.writeString(subscriptionFile, subscription - .replaceAll("\\$\\{OPERATOR_NAME}", operatorName) - .replaceAll("\\$\\{OPERATOR_CHANNEL}", operatorChannel) - .replaceAll("\\$\\{OPERATOR_NAMESPACE}", installationNamespace) - .replaceAll("\\$\\{CATALOG_SOURCE_NAME}", catalogSourceName) - .replaceAll("\\$\\{CATALOG_NAMESPACE}", catalogNamespace) - .replaceAll("\\$\\{CSV}", csvName)); + .replaceAll("\\$\\{OPERATOR_NAME}", operatorName) + .replaceAll("\\$\\{OPERATOR_CHANNEL}", operatorChannel) + .replaceAll("\\$\\{OPERATOR_NAMESPACE}", installationNamespace) + .replaceAll("\\$\\{CATALOG_SOURCE_NAME}", catalogSourceName) + .replaceAll("\\$\\{CATALOG_NAMESPACE}", catalogNamespace) + .replaceAll("\\$\\{CSV}", csvName)); KubeCMDClient.applyFromFile(installationNamespace, subscriptionFile); } public void deployCatalogSource(String catalogSourceName, String catalogNamespace, String customRegistryImageToUse) throws IOException { Path catalogSourceFile = Files.createTempFile("catalogsource", ".yaml"); - String catalogSource = Files.readString(Paths.get("custom-operator-registry", "catalog-source.yaml")); + String catalogSource = Files.readString(Paths.get(System.getProperty("user.dir"), "custom-operator-registry", "catalog-source.yaml")); Files.writeString(catalogSourceFile, catalogSource - .replaceAll("\\$\\{CATALOG_SOURCE_NAME}", catalogSourceName) - .replaceAll("\\$\\{OPERATOR_NAMESPACE}", catalogNamespace) - .replaceAll("\\$\\{REGISTRY_IMAGE}", customRegistryImageToUse)); + .replaceAll("\\$\\{CATALOG_SOURCE_NAME}", catalogSourceName) + .replaceAll("\\$\\{OPERATOR_NAMESPACE}", catalogNamespace) + .replaceAll("\\$\\{REGISTRY_IMAGE}", customRegistryImageToUse)); KubeCMDClient.applyFromFile(catalogNamespace, catalogSourceFile); } public void buildPushCustomOperatorRegistry(String namespace, String manifestsImage) throws Exception { - String customRegistryImageToPush = clusterExternalImageRegistry+"/"+namespace+"/systemtests-operator-registry:latest"; + String customRegistryImageToPush = clusterExternalImageRegistry + "/" + namespace + "/systemtests-operator-registry:latest"; String olmManifestsImage = manifestsImage.replace(clusterInternalImageRegistry, clusterExternalImageRegistry); @@ -154,7 +155,7 @@ private boolean saysDoingNothing(String text) { } public String getCustomOperatorRegistryInternalImage(String namespace) { - return clusterInternalImageRegistry+"/"+namespace+"/systemtests-operator-registry:latest"; + return clusterInternalImageRegistry + "/" + namespace + "/systemtests-operator-registry:latest"; } public String getLatestStartingCsv() throws Exception { diff --git a/systemtests/src/main/java/io/enmasse/systemtest/platform/GenericKubernetes.java b/systemtests/src/main/java/io/enmasse/systemtest/platform/GenericKubernetes.java index 832d5906a29..44a60e9a268 100644 --- a/systemtests/src/main/java/io/enmasse/systemtest/platform/GenericKubernetes.java +++ b/systemtests/src/main/java/io/enmasse/systemtest/platform/GenericKubernetes.java @@ -71,11 +71,6 @@ public void deleteExternalEndpoint(String namespace, String name) { throw new UnsupportedOperationException(); } - @Override - public String getHost() { - return getNodeHost(); - } - @Override public String getOlmNamespace() { return OLM_NAMESPACE; diff --git a/systemtests/src/main/java/io/enmasse/systemtest/platform/KubeCMDClient.java b/systemtests/src/main/java/io/enmasse/systemtest/platform/KubeCMDClient.java index 2430b17f0a1..5acab5f0c2e 100644 --- a/systemtests/src/main/java/io/enmasse/systemtest/platform/KubeCMDClient.java +++ b/systemtests/src/main/java/io/enmasse/systemtest/platform/KubeCMDClient.java @@ -18,7 +18,6 @@ import java.util.Objects; import java.util.Optional; -import io.enmasse.systemtest.bases.ITestBase; import io.enmasse.systemtest.time.TimeoutBudget; import io.enmasse.systemtest.utils.TestUtils; import org.slf4j.Logger; @@ -419,13 +418,4 @@ public static ExecutionResultData runOnClusterWithoutLogger(String... args) { command.addAll(Arrays.asList(args)); return Exec.execute(command, ONE_MINUTE_TIMEOUT, false); } - - public static void awaitRollout(TimeoutBudget budget, String namespace, String deploymentName) { - TestUtils.waitUntilCondition(String.format("Wait for deployment %s in namespace %s rollout to complete", deploymentName, namespace), waitPhase -> { - ExecutionResultData rollout = Exec.execute(Arrays.asList(ITestBase.kubernetes.getCluster().getKubeCmd(), "rollout", - "status", "deployment", deploymentName, - "--namespace", namespace), true); - return rollout.getRetCode(); - }, budget); - } } diff --git a/systemtests/src/main/java/io/enmasse/systemtest/platform/Kubernetes.java b/systemtests/src/main/java/io/enmasse/systemtest/platform/Kubernetes.java index 8b19c395dc2..5de44e4e361 100644 --- a/systemtests/src/main/java/io/enmasse/systemtest/platform/Kubernetes.java +++ b/systemtests/src/main/java/io/enmasse/systemtest/platform/Kubernetes.java @@ -35,6 +35,7 @@ import java.util.function.Supplier; import java.util.stream.Collectors; +import io.enmasse.api.model.MessagingEndpoint; import io.enmasse.systemtest.platform.cluster.KubernetesCluster; import io.fabric8.kubernetes.api.model.ConfigMap; import io.fabric8.kubernetes.api.model.ConfigMapList; @@ -188,13 +189,6 @@ public static Kubernetes getInstance() { public abstract String getOlmNamespace(); - /** - * Retrieve host or ip address of Kubernetes node. - */ - public String getHost() { - return "localhost"; - } - public List listRoutes(String namespace, Map labels) { throw new UnsupportedOperationException(); } @@ -1245,7 +1239,10 @@ public FileVisitResult visitFile(final Path file, final BasicFileAttributes attr /** * Return the external IP for the first node found in the cluster. */ - public String getNodeHost() { + public String getHost() { + if (isCRC()) { + return "api.crc.testing"; + } List addresses = client.nodes().list().getItems().stream() .peek(n -> CustomLogger.getLogger().info("Found node: {}", n)) .flatMap(n -> n.getStatus().getAddresses().stream() diff --git a/systemtests/src/main/java/io/enmasse/systemtest/platform/OpenShift.java b/systemtests/src/main/java/io/enmasse/systemtest/platform/OpenShift.java index 85d46ef125b..a9235019487 100644 --- a/systemtests/src/main/java/io/enmasse/systemtest/platform/OpenShift.java +++ b/systemtests/src/main/java/io/enmasse/systemtest/platform/OpenShift.java @@ -160,15 +160,6 @@ public String getOlmNamespace() { return OLM_NAMESPACE; } - @Override - public String getHost() { - if (isCRC()) { - return "api.crc.testing"; - } else { - throw new UnsupportedOperationException(); - } - } - @Override public List listRoutes(String namespace, Map labels) { return client.adapt(OpenShiftClient.class).routes().inNamespace(namespace).withLabels(labels).list().getItems(); diff --git a/systemtests/src/main/java/io/enmasse/systemtest/platform/apps/SystemtestsKubernetesApps.java b/systemtests/src/main/java/io/enmasse/systemtest/platform/apps/SystemtestsKubernetesApps.java index 12bca650709..91018990ec8 100644 --- a/systemtests/src/main/java/io/enmasse/systemtest/platform/apps/SystemtestsKubernetesApps.java +++ b/systemtests/src/main/java/io/enmasse/systemtest/platform/apps/SystemtestsKubernetesApps.java @@ -216,7 +216,7 @@ public static String getMessagingAppPodName(String namespace) throws Exception { public static String deployMessagingClientApp() throws Exception { kube.createNamespace(MESSAGING_PROJECT); kube.createDeploymentFromResource(MESSAGING_PROJECT, getMessagingAppDeploymentResource()); - TestUtils.waitForExpectedReadyPods(kube, MESSAGING_PROJECT, 1, new TimeoutBudget(1, TimeUnit.MINUTES)); + TestUtils.waitForExpectedReadyPods(kube, MESSAGING_PROJECT, 1, new TimeoutBudget(5, TimeUnit.MINUTES)); return getMessagingAppPodName(); } @@ -928,11 +928,11 @@ public static void buildOperatorRegistryImage(Kubernetes kubeClient, String olmM } catch (Exception ex) { return false; } - }, new TimeoutBudget(15, TimeUnit.SECONDS)); + }, new TimeoutBudget(60, TimeUnit.SECONDS)); kubeClient.waitPodUntilCondition(pod, p -> { String reason = containerReasonGetter.apply(p); return reason.equals("Completed") || reason.equals("Error"); - }, 3, TimeUnit.MINUTES); + }, 5, TimeUnit.MINUTES); Pod podRes = kubeClient.getPod(CONTAINER_BUILDS_PROJECT, pod.getMetadata().getName()); String reason = containerReasonGetter.apply(podRes); if (reason.equals("Error")) { @@ -1051,6 +1051,7 @@ private static Deployment getMessagingAppDeploymentResource() { .addNewContainer() .withName(MESSAGING_CLIENTS) .withImage("quay.io/enmasse/systemtests-clients:latest") + .withImagePullPolicy(env.getImagePullPolicy()) .withCommand("sleep") .withArgs("infinity") .withEnv(new EnvVarBuilder().withName("PN_TRACE_FRM").withValue("true").build()) diff --git a/systemtests/src/main/java/io/enmasse/systemtest/resources/CliOutputData.java b/systemtests/src/main/java/io/enmasse/systemtest/resources/CliOutputData.java index 89e5685ab11..8b3726ebe69 100644 --- a/systemtests/src/main/java/io/enmasse/systemtest/resources/CliOutputData.java +++ b/systemtests/src/main/java/io/enmasse/systemtest/resources/CliOutputData.java @@ -172,7 +172,7 @@ public static class AddressSpaceRow extends CliDataRow { private String phase; private String status; - AddressSpaceRow(String[] parsedData) { + public AddressSpaceRow(String[] parsedData) { expectColumns(7, parsedData); this.name = parsedData[0]; this.type = parsedData[1]; @@ -242,7 +242,7 @@ public static class AddressRow extends CliDataRow { private String phase; private String status; - AddressRow(String[] parsedData) { + public AddressRow(String[] parsedData) { expectColumns(8, parsedData); this.name = parsedData[0]; this.address = parsedData[1]; @@ -317,7 +317,7 @@ public static class UserRow extends CliDataRow { private String username; private String phase; - UserRow(String[] parsedData) { + public UserRow(String[] parsedData) { expectColumns(5, parsedData); this.name = parsedData[0]; this.username = parsedData[1]; diff --git a/systemtests/src/main/java/io/enmasse/systemtest/utils/JmsProvider.java b/systemtests/src/main/java/io/enmasse/systemtest/utils/JmsProvider.java index de5894ec8a5..24bfcf55944 100644 --- a/systemtests/src/main/java/io/enmasse/systemtest/utils/JmsProvider.java +++ b/systemtests/src/main/java/io/enmasse/systemtest/utils/JmsProvider.java @@ -5,6 +5,7 @@ package io.enmasse.systemtest.utils; import io.enmasse.address.model.Address; +import io.enmasse.api.model.MessagingAddress; import io.enmasse.systemtest.UserCredentials; import io.enmasse.systemtest.logs.CustomLogger; import io.enmasse.systemtest.model.address.AddressType; @@ -26,7 +27,6 @@ import javax.naming.NamingException; import java.util.ArrayList; import java.util.Collections; -import java.util.HashMap; import java.util.Hashtable; import java.util.LinkedList; import java.util.List; @@ -52,7 +52,25 @@ public javax.jms.Destination getDestination(String address) throws NamingExcepti return (javax.jms.Destination) context.lookup(address); } - private HashMap createAddressMap(Address destination) { + private String getAddress(MessagingAddress address) { + if (address.getSpec().getAddress() != null) { + return address.getSpec().getAddress(); + } + return address.getMetadata().getName(); + } + + private Map createAddressMap(MessagingAddress destination) { + String identification; + if (destination.getSpec().getQueue() != null) { + identification = "queue."; + } else { + identification = "topic."; + } + + return Map.of(identification + getAddress(destination), getAddress(destination)); + } + + private Map createAddressMap(Address destination) { String identification; if (destination.getSpec().getType().equals(AddressType.QUEUE.toString())) { identification = "queue."; @@ -60,13 +78,17 @@ private HashMap createAddressMap(Address destination) { identification = "topic."; } - return new HashMap() {{ - put(identification + destination.getSpec().getAddress(), destination.getSpec().getAddress()); - }}; + return Map.of(identification + destination.getSpec().getAddress(), destination.getSpec().getAddress()); + } + + public Context createContext(String host, int port, boolean tls, String username, String password, String clientID, MessagingAddress address) throws NamingException { + Hashtable env = setUpEnv(host, port, tls, username, password, clientID, createAddressMap(address)); + context = new InitialContext(env); + return context; } public Context createContext(String route, UserCredentials credentials, String cliID, Address address) throws Exception { - Hashtable env = setUpEnv("amqps://" + route, credentials.getUsername(), credentials.getPassword(), cliID, + Hashtable env = setUpEnv("amqps://" + route, 0, true, credentials.getUsername(), credentials.getPassword(), cliID, createAddressMap(address)); context = new InitialContext(env); return context; @@ -94,21 +116,45 @@ public Connection createConnection(Context context) throws Exception { public Hashtable setUpEnv(String url, String username, String password, Map prop) { - return setUpEnv(url, username, password, "", prop); + return setUpEnv(url, 0, true, username, password, "", prop); } - public Hashtable setUpEnv(String url, String username, String password, String clientID, Map prop) { + public Hashtable setUpEnv(String host, int port, boolean tls, String username, String password, String clientID, Map prop) { Hashtable env = new Hashtable<>(); env.put(Context.INITIAL_CONTEXT_FACTORY, "org.apache.qpid.jms.jndi.JmsInitialContextFactory"); - StringBuilder urlParam = new StringBuilder(); - urlParam.append("?transport.trustAll=true") - .append("&jms.password=").append(username) - .append("&jms.username=").append(password) - .append("&transport.verifyHost=false") - .append("&amqp.saslMechanisms=PLAIN"); - urlParam.append(clientID.isEmpty() ? clientID : "&jms.clientID=" + clientID); - - env.put("connectionfactory.qpidConnectionFactory", url + urlParam); + + String scheme = "amqp://"; + List params = new ArrayList<>(); + if (tls) { + scheme = "amqps://"; + params.add("?transport.trustAll=true"); + params.add("?transport.verifyHost=false"); + } + + if (username != null && password != null) { + params.add("amqp.saslMechanisms=PLAIN"); + params.add(String.format("jms.username=%s", username)); + params.add(String.format("jms.password=%s", password)); + } else { + params.add("amqp.saslMechanisms=ANONYMOUS"); + } + + if (clientID != null && !clientID.isBlank()) { + params.add(String.format("jms.clientID=%s", clientID)); + } + + StringBuilder url = new StringBuilder(String.format("%s%s", scheme, host)); + if (port != 0) { + url.append(":").append(port); + } + + char sep = '?'; + for (String param : params) { + url.append(sep).append(param); + sep = '&'; + } + + env.put("connectionfactory.qpidConnectionFactory", url.toString()); for (Map.Entry entry : prop.entrySet()) { env.put(entry.getKey(), entry.getValue()); } diff --git a/systemtests/src/main/java/io/enmasse/systemtest/utils/UserUtils.java b/systemtests/src/main/java/io/enmasse/systemtest/utils/UserUtils.java index 21dd1738478..9739c209941 100644 --- a/systemtests/src/main/java/io/enmasse/systemtest/utils/UserUtils.java +++ b/systemtests/src/main/java/io/enmasse/systemtest/utils/UserUtils.java @@ -5,13 +5,9 @@ package io.enmasse.systemtest.utils; import com.fasterxml.jackson.databind.ObjectMapper; -import io.enmasse.address.model.AddressSpace; import io.enmasse.address.model.Phase; import io.enmasse.systemtest.UserCredentials; import io.enmasse.systemtest.logs.CustomLogger; -import io.enmasse.systemtest.manager.IsolatedIoTManager; -import io.enmasse.systemtest.manager.IsolatedResourcesManager; -import io.enmasse.systemtest.manager.ResourceManager; import io.enmasse.systemtest.platform.Kubernetes; import io.enmasse.systemtest.time.TimeoutBudget; import io.enmasse.user.model.v1.DoneableUser; @@ -108,8 +104,4 @@ public static String base64ToPassword(String password) { public static UserCredentials getCredentialsFromUser(User user) { return new UserCredentials(user.getSpec().getUsername(), base64ToPassword(user.getSpec().getAuthentication().getPassword())); } - - public static boolean userExist(AddressSpace addressSpace, String username) { - return IsolatedResourcesManager.getInstance().getUser(addressSpace, username) != null; - } } diff --git a/systemtests/src/test/java/io/enmasse/systemtest/bases/TestBase.java b/systemtests/src/test/java/io/enmasse/systemtest/bases/TestBase.java index bf5dedb2750..9600fccbf27 100644 --- a/systemtests/src/test/java/io/enmasse/systemtest/bases/TestBase.java +++ b/systemtests/src/test/java/io/enmasse/systemtest/bases/TestBase.java @@ -5,70 +5,25 @@ package io.enmasse.systemtest.bases; -import static org.hamcrest.CoreMatchers.is; -import static org.hamcrest.MatcherAssert.assertThat; -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertTrue; -import static org.junit.jupiter.api.Assertions.fail; - -import java.nio.charset.StandardCharsets; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Collections; -import java.util.Comparator; -import java.util.HashMap; -import java.util.LinkedList; -import java.util.List; -import java.util.Map; -import java.util.Objects; -import java.util.concurrent.Callable; -import java.util.concurrent.CompletableFuture; -import java.util.concurrent.Future; -import java.util.concurrent.TimeUnit; -import java.util.stream.Collectors; -import java.util.stream.IntStream; - -import javax.jms.DeliveryMode; -import javax.jms.MessageConsumer; -import javax.jms.MessageProducer; -import javax.jms.Session; - -import org.apache.qpid.proton.amqp.messaging.AmqpValue; -import org.apache.qpid.proton.message.Message; -import org.eclipse.paho.client.mqttv3.IMqttClient; -import org.eclipse.paho.client.mqttv3.MqttMessage; -import org.hamcrest.Matchers; -import org.junit.jupiter.api.AfterEach; -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.DisplayNameGeneration; -import org.junit.jupiter.api.TestInstance; -import org.junit.jupiter.api.extension.ExtendWith; -import org.slf4j.Logger; - -import com.google.common.collect.Ordering; - -import io.enmasse.address.model.Address; -import io.enmasse.address.model.AddressSpace; import io.enmasse.systemtest.Environment; import io.enmasse.systemtest.IndicativeSentences; +import io.enmasse.systemtest.TestTag; import io.enmasse.systemtest.UserCredentials; -import io.enmasse.systemtest.amqp.AmqpClient; -import io.enmasse.systemtest.info.TestInfo; +import io.enmasse.systemtest.clients.ClientUtils; import io.enmasse.systemtest.listener.JunitCallbackListener; -import io.enmasse.systemtest.logs.GlobalLogCollector; import io.enmasse.systemtest.messaginginfra.ResourceManager; -import io.enmasse.systemtest.mqtt.MqttUtils; import io.enmasse.systemtest.platform.KubeCMDClient; -import io.enmasse.systemtest.time.TimeoutBudget; -import io.enmasse.systemtest.utils.AddressSpaceUtils; -import io.enmasse.systemtest.utils.AddressUtils; -import io.enmasse.systemtest.utils.JmsProvider; -import io.enmasse.systemtest.utils.TestUtils; +import io.enmasse.systemtest.platform.Kubernetes; import io.enmasse.systemtest.utils.ThrowingCallable; import io.enmasse.systemtest.utils.ThrowingConsumer; -import io.enmasse.systemtest.utils.UserUtils; -import io.enmasse.user.model.v1.Operation; -import io.enmasse.user.model.v1.UserAuthorizationBuilder; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.DisplayNameGeneration; +import org.junit.jupiter.api.Tag; +import org.junit.jupiter.api.TestInstance; +import org.junit.jupiter.api.extension.ExtendWith; + +import java.util.LinkedList; +import java.util.List; /** * Base class for all tests @@ -76,23 +31,25 @@ @ExtendWith(JunitCallbackListener.class) @DisplayNameGeneration(IndicativeSentences.class) @TestInstance(TestInstance.Lifecycle.PER_CLASS) -public abstract class TestBase implements ITestBase, ITestSeparator { +@Tag(TestTag.SYSTEMTEST) +public abstract class TestBase implements ITestSeparator { + ClientUtils clientUtils = new ClientUtils(); + protected static final Kubernetes kubernetes = Kubernetes.getInstance(); protected static final UserCredentials clusterUser = new UserCredentials(KubeCMDClient.getOCUser()); protected static final Environment environment = Environment.getInstance(); - protected static final GlobalLogCollector logCollector = new GlobalLogCollector(kubernetes, environment.testLogDir()); - protected io.enmasse.systemtest.manager.ResourceManager resourcesManager; - protected UserCredentials defaultCredentials = null; - protected UserCredentials managementCredentials = null; - protected ResourceManager infraResourceManager = ResourceManager.getInstance(); + protected UserCredentials defaultCredentials = environment.getDefaultCredentials(); + protected UserCredentials managementCredentials = environment.getManagementCredentials(); + protected ResourceManager resourceManager = ResourceManager.getInstance(); + protected List cleanup = new LinkedList<>(); /** * Close any provided resource at the end of test, by calling the cleaner. * - * @param The type of the closable. + * @param The type of the closable. * @param resource The closable to close. - * @param cleaner The cleaner, which will be called. + * @param cleaner The cleaner, which will be called. * @return The input value, for chained calls. */ protected T cleanup(final T resource, final ThrowingConsumer cleaner) { @@ -103,36 +60,16 @@ protected T cleanup(final T resource, final ThrowingConsumer cleaner) { /** * Close an {@link AutoCloseable} at the end of the test. * - * @param The type of the closable. + * @param The type of the closable. * @param closeable The closable to close. * @return The input value, for chained calls. */ - protected T cleanup (final T closeable) { + protected T cleanup(final T closeable) { return cleanup(closeable, AutoCloseable::close); } - @BeforeEach - public void initTest() throws Exception { - LOGGER.info("Test init"); - resourcesManager = getResourceManager(); - if (TestInfo.getInstance().isTestShared()) { - defaultCredentials = environment.getSharedDefaultCredentials(); - managementCredentials = environment.getSharedManagementCredentials(); - resourcesManager.setAddressSpacePlan(getDefaultAddressSpacePlan()); - resourcesManager.setAddressSpaceType(getAddressSpaceType().toString()); - resourcesManager.setDefaultAddSpaceIdentifier(getDefaultAddrSpaceIdentifier()); - if (resourcesManager.getSharedAddressSpace() == null) { - resourcesManager.setup(); - } - } else { - defaultCredentials = environment.getDefaultCredentials(); - managementCredentials = environment.getManagementCredentials(); - resourcesManager.setup(); - } - } - @AfterEach - private void cleanup() throws Exception { + void cleanup() throws Exception { Exception exception = null; for (ThrowingCallable cleanup : this.cleanup) { try { @@ -151,243 +88,7 @@ private void cleanup() throws Exception { } } - //================================================================================================ - //======================================= Help methods =========================================== - //================================================================================================ - - protected void waitForBrokerReplicas(AddressSpace addressSpace, Address destination, int expectedReplicas) throws - Exception { - TimeoutBudget budget = new TimeoutBudget(10, TimeUnit.MINUTES); - TestUtils.waitForNBrokerReplicas(addressSpace, expectedReplicas, true, destination, budget, 5000); - } - - protected void waitForRouterReplicas(AddressSpace addressSpace, int expectedReplicas) throws - Exception { - TimeoutBudget budget = new TimeoutBudget(3, TimeUnit.MINUTES); - Map labels = new HashMap<>(); - labels.put("name", "qdrouterd"); - labels.put("infraUuid", AddressSpaceUtils.getAddressSpaceInfraUuid(addressSpace)); - TestUtils.waitForNReplicas(expectedReplicas, kubernetes.getInfraNamespace(), labels, budget); - } - - protected void waitForPodsToTerminate(List uids) throws Exception { - LOGGER.info("Waiting for following pods to be deleted {}", uids); - assertWaitForValue(true, () -> (kubernetes.listPods(kubernetes.getInfraNamespace()).stream() - .noneMatch(pod -> uids.contains(pod.getMetadata().getUid()))), new TimeoutBudget(2, TimeUnit.MINUTES)); - } - - protected void logWithSeparator(Logger logger, String... messages) { - logger.info("--------------------------------------------------------------------------------"); - for (String message : messages) { - logger.info(message); - } - } - - protected List extractBodyAsString(Future> msgs) throws Exception { - return msgs.get(1, TimeUnit.MINUTES).stream().map(m -> (String) ((AmqpValue) m.getBody()).getValue()).collect(Collectors.toList()); - } - - //================================================================================================ - //==================================== Asserts methods =========================================== - //================================================================================================ - - protected static void assertSimpleMQTTSendReceive(Address dest, IMqttClient client, int msgCount) throws Exception { - List messages = IntStream.range(0, msgCount).boxed().map(i -> { - MqttMessage m = new MqttMessage(); - m.setPayload(String.format("mqtt-simple-send-receive-%s", i).getBytes(StandardCharsets.UTF_8)); - m.setQos(1); - return m; - }).collect(Collectors.toList()); - - List> receiveFutures = MqttUtils.subscribeAndReceiveMessages(client, dest.getSpec().getAddress(), messages.size(), 1); - List> publishFutures = MqttUtils.publish(client, dest.getSpec().getAddress(), messages); - - int publishCount = MqttUtils.awaitAndReturnCode(publishFutures, 1, TimeUnit.MINUTES); - assertThat("Incorrect count of messages published", - publishCount, is(messages.size())); - - int receivedCount = MqttUtils.awaitAndReturnCode(receiveFutures, 1, TimeUnit.MINUTES); - assertThat("Incorrect count of messages received", - receivedCount, is(messages.size())); - } - - protected > void assertSorted(String message, Iterable list) throws Exception { - assertSorted(message, list, false); - } - - protected void assertSorted(String message, Iterable list, Comparator comparator) throws Exception { - assertSorted(message, list, false, comparator); - } - - protected > void assertSorted(String message, Iterable list, boolean reverse) { - LOGGER.info("Assert sort reverse: " + reverse); - if (!reverse) { - assertTrue(Ordering.natural().isOrdered(list), message); - } else { - assertTrue(Ordering.natural().reverse().isOrdered(list), message); - } - } - - protected void assertSorted(String message, Iterable list, boolean reverse, Comparator comparator) { - LOGGER.info("Assert sort reverse: " + reverse); - if (!reverse) { - assertTrue(Ordering.from(comparator).isOrdered(list), message); - } else { - assertTrue(Ordering.from(comparator).reverse().isOrdered(list), message); - } - } - - protected void assertWaitForValue(T expected, Callable fn, TimeoutBudget budget) throws Exception { - T got = null; - LOGGER.info("waiting for expected value '{}' ...", expected); - while (budget.timeLeft() >= 0) { - got = fn.call(); - if (Objects.equals(expected, got)) { - return; - } - Thread.sleep(100); - } - fail(String.format("Incorrect result value! expected: '%s', got: '%s'", expected, Objects.requireNonNull(got))); - } - - protected static void assertDefaultEnabled(final Boolean enabled) { - if (enabled != null && !Boolean.TRUE.equals(enabled)) { - fail("Default value must be 'null' or 'true'"); - } - } - - protected void assertConcurentMessaging(List

dest, List users, String destNamePrefix, int customerIndex, int messageCount) throws Exception { - ArrayList clients = new ArrayList<>(users.size()); - String sufix = AddressSpaceUtils.isBrokered(resourcesManager.getSharedAddressSpace()) ? "#" : "*"; - users.forEach((user) -> { - try { - resourcesManager.createOrUpdateUser(resourcesManager.getSharedAddressSpace(), - UserUtils.createUserResource(user) - .editSpec() - .withAuthorization(Collections.singletonList( - new UserAuthorizationBuilder() - .withAddresses(String.format("%s.%s.%s", destNamePrefix, customerIndex, sufix)) - .withOperations(Operation.send, Operation.recv).build())) - .endSpec() - .done()); - AmqpClient queueClient = resourcesManager.getAmqpClientFactory().createQueueClient(); - queueClient.getConnectOptions().setCredentials(user); - clients.add(queueClient); - } catch (Exception e) { - e.printStackTrace(); - } - }); - - AddressUtils.waitForDestinationsReady(dest.toArray(new Address[0])); - //start sending messages - int everyN = 3; - for (AmqpClient client : clients) { - for (int i = 0; i < dest.size(); i++) { - if (i % everyN == 0) { - Future sent = client.sendMessages(dest.get(i).getSpec().getAddress(), TestUtils.generateMessages(messageCount)); - //wait for messages sent - assertEquals(messageCount, sent.get(1, TimeUnit.MINUTES), - "Incorrect count of messages send"); - } - } - } - - //receive messages - for (AmqpClient client : clients) { - for (int i = 0; i < dest.size(); i++) { - if (i % everyN == 0) { - Future> received = client.recvMessages(dest.get(i).getSpec().getAddress(), messageCount); - //wait for messages received - assertEquals(messageCount, received.get(1, TimeUnit.MINUTES).size(), - "Incorrect count of messages received"); - } - } - client.close(); - } - } - - protected void assertSendReceiveLargeMessageQueue(JmsProvider jmsProvider, double sizeInMB, Address dest, int count) throws Exception { - assertSendReceiveLargeMessageQueue(jmsProvider, sizeInMB, dest, count, DeliveryMode.NON_PERSISTENT); - } - - protected void assertSendReceiveLargeMessageQueue(JmsProvider jmsProvider, double sizeInMB, Address dest, int count, int mode) throws Exception { - int size = (int) (sizeInMB * 1024 * 1024); - - Session session = jmsProvider.getConnection().createSession(false, Session.AUTO_ACKNOWLEDGE); - javax.jms.Queue testQueue = (javax.jms.Queue) jmsProvider.getDestination(dest.getSpec().getAddress()); - List messages = jmsProvider.generateMessages(session, count, size); - - MessageProducer sender = session.createProducer(testQueue); - MessageConsumer receiver = session.createConsumer(testQueue); - - assertSendReceiveLargeMessage(jmsProvider, sender, receiver, sizeInMB, mode, count, messages); - - } - - protected void assertSendReceiveLargeMessageTopic(JmsProvider jmsProvider, double sizeInMB, Address dest, int count) throws Exception { - assertSendReceiveLargeMessageTopic(jmsProvider, sizeInMB, dest, count, DeliveryMode.NON_PERSISTENT); - } - - protected void assertSendReceiveLargeMessageTopic(JmsProvider jmsProvider, double sizeInMB, Address dest, int count, int mode) throws Exception { - int size = (int) (sizeInMB * 1024 * 1024); - - Session session = jmsProvider.getConnection().createSession(false, Session.AUTO_ACKNOWLEDGE); - javax.jms.Topic testTopic = (javax.jms.Topic) jmsProvider.getDestination(dest.getSpec().getAddress()); - List messages = jmsProvider.generateMessages(session, count, size); - - MessageProducer sender = session.createProducer(testTopic); - MessageConsumer receiver = session.createConsumer(testTopic); - - assertSendReceiveLargeMessage(jmsProvider, sender, receiver, sizeInMB, mode, count, messages); - session.close(); - sender.close(); - receiver.close(); - } - - private void assertSendReceiveLargeMessage(JmsProvider jmsProvider, MessageProducer sender, MessageConsumer receiver, double sizeInMB, int mode, int count, List messages) { - List recvd; - - jmsProvider.sendMessages(sender, messages, mode, javax.jms.Message.DEFAULT_PRIORITY, javax.jms.Message.DEFAULT_TIME_TO_LIVE); - LOGGER.info("{}MB {} message sent", sizeInMB, mode == DeliveryMode.PERSISTENT ? "durable" : "non-durable"); - - recvd = jmsProvider.receiveMessages(receiver, count, 2000); - assertThat("Wrong count of received messages", recvd.size(), Matchers.is(count)); - LOGGER.info("{}MB {} message received", sizeInMB, mode == DeliveryMode.PERSISTENT ? "durable" : "non-durable"); - } - - protected void assertAddressApi(AddressSpace addressSpace, Address d1, Address d2) throws Exception { - List destinationsNames = Arrays.asList(d1.getSpec().getAddress(), d2.getSpec().getAddress()); - resourcesManager.setAddresses(d1); - resourcesManager.appendAddresses(d2); - - //d1, d2 - List response = AddressUtils.getAddresses(addressSpace).stream().map(address -> address.getSpec().getAddress()).collect(Collectors.toList()); - assertThat("Rest api does not return all addresses", response, is(destinationsNames)); - LOGGER.info("addresses {} successfully created", Arrays.toString(destinationsNames.toArray())); - - //get specific address d2 - Address res = kubernetes.getAddressClient(addressSpace.getMetadata().getNamespace()).withName(d2.getMetadata().getName()).get(); - assertThat("Rest api does not return specific address", res.getSpec().getAddress(), is(d2.getSpec().getAddress())); - - resourcesManager.deleteAddresses(d1); - - //d2 - response = AddressUtils.getAddresses(addressSpace).stream().map(address -> address.getSpec().getAddress()).collect(Collectors.toList()); - assertThat("Rest api does not return right addresses", response, is(destinationsNames.subList(1, 2))); - LOGGER.info("address {} successfully deleted", d1.getSpec().getAddress()); - - resourcesManager.deleteAddresses(d2); - - //empty - List
listRes = AddressUtils.getAddresses(addressSpace); - assertThat("Rest api returns addresses", listRes, is(Collections.emptyList())); - LOGGER.info("addresses {} successfully deleted", d2.getSpec().getAddress()); - - resourcesManager.setAddresses(d1, d2); - resourcesManager.deleteAddresses(d1, d2); - - listRes = AddressUtils.getAddresses(addressSpace); - assertThat("Rest api returns addresses", listRes, is(Collections.emptyList())); - LOGGER.info("addresses {} successfully deleted", Arrays.toString(destinationsNames.toArray())); + protected ClientUtils getClientUtils() { + return clientUtils; } } diff --git a/systemtests/src/test/java/io/enmasse/systemtest/bases/authz/AuthorizationTestBase.java b/systemtests/src/test/java/io/enmasse/systemtest/bases/authz/AuthorizationTestBase.java deleted file mode 100644 index 1abcb750b99..00000000000 --- a/systemtests/src/test/java/io/enmasse/systemtest/bases/authz/AuthorizationTestBase.java +++ /dev/null @@ -1,521 +0,0 @@ -/* - * Copyright 2018, EnMasse authors. - * License: Apache License 2.0 (see the file LICENSE or http://apache.org/licenses/LICENSE-2.0.html). - */ -package io.enmasse.systemtest.bases.authz; - -import io.enmasse.address.model.Address; -import io.enmasse.address.model.AddressBuilder; -import io.enmasse.address.model.AddressSpace; -import io.enmasse.systemtest.UserCredentials; -import io.enmasse.systemtest.amqp.AmqpClient; -import io.enmasse.systemtest.amqp.UnauthorizedAccessException; -import io.enmasse.systemtest.bases.TestBase; -import io.enmasse.systemtest.bases.shared.ITestBaseShared; -import io.enmasse.systemtest.logs.CustomLogger; -import io.enmasse.systemtest.model.address.AddressType; -import io.enmasse.systemtest.model.addressplan.DestinationPlan; -import io.enmasse.systemtest.model.addressspace.AddressSpaceType; -import io.enmasse.systemtest.utils.AddressUtils; -import io.enmasse.systemtest.utils.UserUtils; -import io.enmasse.user.model.v1.Operation; -import io.enmasse.user.model.v1.User; -import io.enmasse.user.model.v1.UserAuthorizationBuilder; -import io.vertx.proton.sasl.SaslSystemException; -import org.apache.qpid.proton.message.Message; -import org.slf4j.Logger; - -import javax.security.sasl.AuthenticationException; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Collections; -import java.util.List; -import java.util.Objects; -import java.util.concurrent.ExecutionException; -import java.util.concurrent.Future; -import java.util.concurrent.TimeUnit; -import java.util.stream.Collectors; -import java.util.stream.Stream; - -import static org.junit.jupiter.api.Assertions.assertFalse; -import static org.junit.jupiter.api.Assertions.assertTrue; - -public abstract class AuthorizationTestBase extends TestBase implements ITestBaseShared { - - private static Logger log = CustomLogger.getLogger(); - - private Address queue; - private Address topic; - private Address anycast; - private Address multicast; - private List
addresses; - - private void initAddresses() throws Exception { - queue = new AddressBuilder() - .withNewMetadata() - .withNamespace(getSharedAddressSpace().getMetadata().getNamespace()) - .withName(AddressUtils.generateAddressMetadataName(getSharedAddressSpace(), "authz_queue")) - .endMetadata() - .withNewSpec() - .withType("queue") - .withAddress("authz_queue") - .withPlan(getDefaultPlan(AddressType.QUEUE)) - .endSpec() - .build(); - - topic = new AddressBuilder() - .withNewMetadata() - .withNamespace(getSharedAddressSpace().getMetadata().getNamespace()) - .withName(AddressUtils.generateAddressMetadataName(getSharedAddressSpace(), "authz-topic")) - .endMetadata() - .withNewSpec() - .withType("topic") - .withAddress("authz-topic") - .withPlan(getDefaultPlan(AddressType.TOPIC)) - .endSpec() - .build(); - - anycast = new AddressBuilder() - .withNewMetadata() - .withNamespace(getSharedAddressSpace().getMetadata().getNamespace()) - .withName(AddressUtils.generateAddressMetadataName(getSharedAddressSpace(), "authz-anycast")) - .endMetadata() - .withNewSpec() - .withType("anycast") - .withAddress("authz-anycast") - .withPlan(DestinationPlan.STANDARD_SMALL_ANYCAST) - .endSpec() - .build(); - - multicast = new AddressBuilder() - .withNewMetadata() - .withNamespace(getSharedAddressSpace().getMetadata().getNamespace()) - .withName(AddressUtils.generateAddressMetadataName(getSharedAddressSpace(), "authz-multicast")) - .endMetadata() - .withNewSpec() - .withType("multicast") - .withAddress("authz-multicast") - .withPlan(DestinationPlan.STANDARD_SMALL_MULTICAST) - .endSpec() - .build(); - - addresses = new ArrayList<>(); - addresses.add(queue); - addresses.add(topic); - if (getAddressSpaceType() == AddressSpaceType.STANDARD) { - addresses.add(anycast); - addresses.add(multicast); - } - resourcesManager.setAddresses(addresses.toArray(new Address[0])); - } - - protected void doTestSendAuthz() throws Exception { - initAddresses(); - UserCredentials allowedUser = new UserCredentials("sender", "senderPa55"); - UserCredentials noAllowedUser = new UserCredentials("notallowedsender", "nobodyPa55"); - - resourcesManager.createOrUpdateUser(getSharedAddressSpace(), UserUtils.createUserResource(allowedUser) - .editSpec() - .withAuthorization( - Collections.singletonList(new UserAuthorizationBuilder().withAddresses("*").withOperations(Operation.send).build())) - .endSpec() - .done()); - Thread.sleep(100); - assertSend(allowedUser); - resourcesManager.removeUser(getSharedAddressSpace(), allowedUser.getUsername()); - - resourcesManager.createOrUpdateUser(getSharedAddressSpace(), UserUtils.createUserResource(allowedUser) - .editSpec() - .withAuthorization( - Collections.singletonList(new UserAuthorizationBuilder() - .withAddresses(addresses.stream().map(address -> address.getSpec().getAddress()).collect(Collectors.toList())) - .withOperations(Operation.send).build())) - .endSpec() - .done()); - Thread.sleep(100); - assertSend(allowedUser); - resourcesManager.removeUser(getSharedAddressSpace(), allowedUser.getUsername()); - - resourcesManager.createOrUpdateUser(getSharedAddressSpace(), UserUtils.createUserResource(noAllowedUser).done()); - assertCannotSend(noAllowedUser); - resourcesManager.removeUser(getSharedAddressSpace(), noAllowedUser.getUsername()); - - resourcesManager.createOrUpdateUser(getSharedAddressSpace(), UserUtils.createUserResource(noAllowedUser) - .editSpec() - .withAuthorization( - Collections.singletonList(new UserAuthorizationBuilder() - .withAddresses("*") - .withOperations(Operation.recv).build())) - .endSpec() - .done()); - assertCannotSend(noAllowedUser); - resourcesManager.removeUser(getSharedAddressSpace(), noAllowedUser.getUsername()); - } - - protected void doTestReceiveAuthz() throws Exception { - initAddresses(); - UserCredentials allowedUser = new UserCredentials("receiver", "receiverPa55"); - UserCredentials noAllowedUser = new UserCredentials("notallowedreceiver", "nobodyPa55"); - - resourcesManager.createOrUpdateUser(getSharedAddressSpace(), UserUtils.createUserResource(allowedUser) - .editSpec() - .withAuthorization( - Collections.singletonList(new UserAuthorizationBuilder().withAddresses("*").withOperations(Operation.recv).build())) - .endSpec() - .done()); - assertReceive(allowedUser); - resourcesManager.removeUser(getSharedAddressSpace(), allowedUser.getUsername()); - - resourcesManager.createOrUpdateUser(getSharedAddressSpace(), UserUtils.createUserResource(allowedUser) - .editSpec() - .withAuthorization( - Collections.singletonList(new UserAuthorizationBuilder() - .withAddresses(addresses.stream().map(address -> address.getSpec().getAddress()).collect(Collectors.toList())) - .withOperations(Operation.recv).build())) - .endSpec() - .done()); - assertReceive(allowedUser); - resourcesManager.removeUser(getSharedAddressSpace(), allowedUser.getUsername()); - - resourcesManager.createOrUpdateUser(getSharedAddressSpace(), UserUtils.createUserResource(noAllowedUser) - .editSpec() - .withAuthorization( - Collections.singletonList(new UserAuthorizationBuilder() - .withAddresses("*") - .withOperations(Operation.send).build())) - .endSpec() - .done()); - assertCannotReceive(noAllowedUser); - resourcesManager.removeUser(getSharedAddressSpace(), noAllowedUser.getUsername()); - } - - protected void doTestUserPermissionAfterRemoveAuthz() throws Exception { - initAddresses(); - UserCredentials user = new UserCredentials("pepa", "pepaPa55"); - - resourcesManager.createOrUpdateUser(getSharedAddressSpace(), UserUtils.createUserResource(user) - .editSpec() - .withAuthorization( - Collections.singletonList(new UserAuthorizationBuilder() - .withOperations(Operation.recv) - .withAddresses("*").build())) - .endSpec() - .done()); - assertReceive(user); - resourcesManager.removeUser(getSharedAddressSpace(), user.getUsername()); - Thread.sleep(5000); - - resourcesManager.createOrUpdateUser(getSharedAddressSpace(), UserUtils.createUserResource(user) - .editSpec() - .withAuthorization( - Collections.singletonList(new UserAuthorizationBuilder() - .withOperations(Operation.recv) - .withAddresses("pepa_address").build())) - .endSpec() - .done()); - assertCannotReceive(user); - resourcesManager.removeUser(getSharedAddressSpace(), user.getUsername()); - } - - protected void doTestSendAuthzWithWIldcards() throws Exception { - List
addresses = getAddressesWildcard(getSharedAddressSpace()); - List users = createUsersWildcard(getSharedAddressSpace(), Operation.send); - - resourcesManager.setAddresses(addresses.toArray(new Address[0])); - - for (User user : users) { - for (Address destination : addresses) { - assertSendWildcard(user, destination); - } - resourcesManager.removeUser(getSharedAddressSpace(), user.getSpec().getUsername()); - } - } - - protected void doTestReceiveAuthzWithWIldcards() throws Exception { - List
addresses = getAddressesWildcard(getSharedAddressSpace()); - List users = createUsersWildcard(getSharedAddressSpace(), Operation.recv); - - resourcesManager.setAddresses(addresses.toArray(new Address[0])); - - for (User user : users) { - for (Address destination : addresses) { - assertReceiveWildcard(user, destination); - } - resourcesManager.removeUser(getSharedAddressSpace(), user.getSpec().getUsername()); - } - } - - //=========================================================================================================== - // Help methods - //=========================================================================================================== - - private void assertSendWildcard(User user, Address destination) throws Exception { - List addresses = user.getSpec().getAuthorization().stream() - .map(authz -> authz.getAddresses().stream()) - .flatMap(Stream::distinct) - .collect(Collectors.toList()); - - UserCredentials credentials = UserUtils.getCredentialsFromUser(user); - if (addresses.stream().filter(address -> destination.getSpec().getAddress().contains(address.replace("*", ""))).count() > 0) { - assertTrue(canSend(destination, credentials), - String.format("Authz failed, user %s cannot send message to destination %s", credentials, - destination.getSpec().getAddress())); - } else { - assertFalse(canSend(destination, credentials), - String.format("Authz failed, user %s can send message to destination %s", credentials, - destination.getSpec().getAddress())); - } - } - - private void assertReceiveWildcard(User user, Address destination) throws Exception { - List addresses = user.getSpec().getAuthorization().stream() - .map(authz -> authz.getAddresses().stream()) - .flatMap(Stream::distinct) - .collect(Collectors.toList()); - - UserCredentials credentials = UserUtils.getCredentialsFromUser(user); - if (addresses.stream().filter(address -> destination.getSpec().getAddress().contains(address.replace("*", ""))).count() > 0) { - assertTrue(canReceive(destination, credentials), - String.format("Authz failed, user %s cannot receive message from destination %s", credentials, - destination.getSpec().getAddress())); - } else { - assertFalse(canReceive(destination, credentials), - String.format("Authz failed, user %s can receive message from destination %s", credentials, - destination.getSpec().getAddress())); - } - } - - private void assertSend(UserCredentials credentials) throws Exception { - log.info("Testing if client is authorized to send messages"); - String message = String.format("Authz failed, user %s cannot send message", credentials); - assertTrue(canSend(queue, credentials), message); - assertTrue(canSend(topic, credentials), message); - - if (getAddressSpaceType() == AddressSpaceType.STANDARD) { - assertTrue(canSend(multicast, credentials), message); - assertTrue(canSend(anycast, credentials), message); - } - } - - private void assertCannotSend(UserCredentials credentials) throws Exception { - log.info("Testing if client is NOT authorized to send messages"); - String message = String.format("Authz failed, user %s can send message", credentials); - assertFalse(canSend(queue, credentials), message); - assertFalse(canSend(topic, credentials), message); - - if (getAddressSpaceType() == AddressSpaceType.STANDARD) { - assertFalse(canSend(multicast, credentials), message); - assertFalse(canSend(anycast, credentials), message); - } - } - - private void assertReceive(UserCredentials credentials) throws Exception { - log.info("Testing if client is authorized to receive messages"); - String message = String.format("Authz failed, user %s cannot receive message", credentials); - assertTrue(canReceive(queue, credentials), message); - assertTrue(canReceive(topic, credentials), message); - - if (getAddressSpaceType() == AddressSpaceType.STANDARD) { - assertTrue(canReceive(multicast, credentials), message); - assertTrue(canReceive(anycast, credentials), message); - } - } - - private void assertCannotReceive(UserCredentials credentials) throws Exception { - log.info("Testing if client is NOT authorized to receive messages"); - String message = String.format("Authz failed, user %s can receive message", credentials); - assertFalse(canReceive(queue, credentials), message); - assertFalse(canReceive(topic, credentials), message); - - if (getAddressSpaceType() == AddressSpaceType.STANDARD) { - assertFalse(canReceive(multicast, credentials), message); - assertFalse(canReceive(anycast, credentials), message); - } - } - - private boolean canSend(Address destination, UserCredentials credentials) throws Exception { - logWithSeparator(log, - String.format("Try send message under user %s from %s %s", credentials, destination.getSpec().getType(), destination.getSpec().getAddress()), - String.format("***** Try to open sender client under user %s", credentials), - String.format("***** Try to open receiver client under user %s", defaultCredentials)); - AmqpClient sender = createClient(destination, credentials); - AmqpClient receiver = createClient(destination, defaultCredentials); - logWithSeparator(log); - return canAuth(sender, receiver, destination, true); - } - - private boolean canReceive(Address destination, UserCredentials credentials) throws Exception { - logWithSeparator(log, - String.format("Try receive message under user %s from %s %s", credentials, destination.getSpec().getType(), destination.getSpec().getAddress()), - String.format("***** Try to open sender client under user %s", defaultCredentials), - String.format("***** Try to open receiver client under user %s", credentials)); - - AmqpClient sender = createClient(destination, defaultCredentials); - AmqpClient receiver = createClient(destination, credentials); - logWithSeparator(log); - return canAuth(sender, receiver, destination, false); - } - - private boolean canAuth(AmqpClient sender, AmqpClient receiver, Address destination, boolean checkSender) throws Exception { - try { - log.info("Staring receiver for " + destination.getSpec().getAddress()); - Future> received = receiver.recvMessages(destination.getSpec().getAddress(), 1); - - log.info("Staring sender for " + destination.getSpec().getAddress()); - Future sent = sender.sendMessages(destination.getSpec().getAddress(), Collections.singletonList("msg1")); - - if (checkSender) { - int numSent = sent.get(1, TimeUnit.MINUTES); - log.info("Sent {}", numSent); - int numReceived = received.get(1, TimeUnit.MINUTES).size(); - return numSent == numReceived; - } else { - int numReceived = received.get(1, TimeUnit.MINUTES).size(); - int numSent = sent.get(1, TimeUnit.MINUTES); - return numSent == numReceived; - } - } catch (ExecutionException | SecurityException | UnauthorizedAccessException ex) { - Throwable cause = ex; - if (ex instanceof ExecutionException) { - cause = ex.getCause(); - } - - if (cause instanceof SecurityException || cause instanceof SaslSystemException || cause instanceof AuthenticationException || cause instanceof UnauthorizedAccessException) { - log.info("canAuth {} ({}): {}", destination.getSpec().getAddress(), destination.getSpec().getType(), ex.getMessage()); - return false; - } else { - log.warn("canAuth {} ({}) exception", destination.getSpec().getAddress(), destination.getSpec().getType(), ex); - throw ex; - } - } finally { - sender.close(); - receiver.close(); - } - } - - private AmqpClient createClient(Address dest, UserCredentials credentials) throws Exception { - AmqpClient client = null; - - switch (dest.getSpec().getType()) { - case "queue": - case "anycast": - client = getAmqpClientFactory().createQueueClient(getSharedAddressSpace()); - break; - case "topic": - client = getAmqpClientFactory().createTopicClient(getSharedAddressSpace()); - break; - case "multicast": - client = getAmqpClientFactory().createBroadcastClient(getSharedAddressSpace()); - break; - } - - Objects.requireNonNull(client).getConnectOptions().setCredentials(credentials); - return client; - } - - protected List
getAddressesWildcard(AddressSpace addressspace) { - Address queue = new AddressBuilder() - .withNewMetadata() - .withNamespace(addressspace.getMetadata().getNamespace()) - .withName(AddressUtils.generateAddressMetadataName(addressspace, "queue/1234")) - .endMetadata() - .withNewSpec() - .withType("queue") - .withAddress("queue/1234") - .withPlan(getDefaultPlan(AddressType.QUEUE)) - .endSpec() - .build(); - - Address queue2 = new AddressBuilder() - .withNewMetadata() - .withNamespace(addressspace.getMetadata().getNamespace()) - .withName(AddressUtils.generateAddressMetadataName(addressspace, "queue/ABCD")) - .endMetadata() - .withNewSpec() - .withType("queue") - .withAddress("queue/ABCD") - .withPlan(getDefaultPlan(AddressType.QUEUE)) - .endSpec() - .build(); - - Address topic = new AddressBuilder() - .withNewMetadata() - .withNamespace(addressspace.getMetadata().getNamespace()) - .withName(AddressUtils.generateAddressMetadataName(addressspace, "topic/2345")) - .endMetadata() - .withNewSpec() - .withType("topic") - .withAddress("topic/2345") - .withPlan(getDefaultPlan(AddressType.TOPIC)) - .endSpec() - .build(); - - Address topic2 = new AddressBuilder() - .withNewMetadata() - .withNamespace(addressspace.getMetadata().getNamespace()) - .withName(AddressUtils.generateAddressMetadataName(addressspace, "topic/ABCD")) - .endMetadata() - .withNewSpec() - .withType("topic") - .withAddress("topic/ABCD") - .withPlan(getDefaultPlan(AddressType.TOPIC)) - .endSpec() - .build(); - - return Arrays.asList(queue, queue2, topic, topic2); - } - - protected List createUsersWildcard(AddressSpace addressSpace, Operation operation) throws - Exception { - List users = new ArrayList<>(); - users.add(UserUtils.createUserResource(new UserCredentials("user1", "password")) - .editSpec() - .withAuthorization(Collections.singletonList(new UserAuthorizationBuilder() - .withAddresses("*") - .withOperations(operation) - .build())) - .endSpec() - .done()); - - users.add(UserUtils.createUserResource(new UserCredentials("user2", "password")) - .editSpec() - .withAuthorization(Collections.singletonList(new UserAuthorizationBuilder() - .withAddresses("queue/*") - .withOperations(operation) - .build())) - .endSpec() - .done()); - - users.add(UserUtils.createUserResource(new UserCredentials("user3", "password")) - .editSpec() - .withAuthorization(Collections.singletonList(new UserAuthorizationBuilder() - .withAddresses("topic/*") - .withOperations(operation) - .build())) - .endSpec() - .done()); - - users.add(UserUtils.createUserResource(new UserCredentials("user4", "password")) - .editSpec() - .withAuthorization(Collections.singletonList(new UserAuthorizationBuilder() - .withAddresses("queueA*") - .withOperations(operation) - .build())) - .endSpec() - .done()); - - users.add(UserUtils.createUserResource(new UserCredentials("user5", "password")) - .editSpec() - .withAuthorization(Collections.singletonList(new UserAuthorizationBuilder() - .withAddresses("topicA*") - .withOperations(operation) - .build())) - .endSpec() - .done()); - - for (User user : users) { - resourcesManager.createOrUpdateUser(addressSpace, user); - } - return users; - } -} diff --git a/systemtests/src/test/java/io/enmasse/systemtest/bases/bridging/BridgingBase.java b/systemtests/src/test/java/io/enmasse/systemtest/bases/bridging/BridgingBase.java deleted file mode 100644 index 9907442bd60..00000000000 --- a/systemtests/src/test/java/io/enmasse/systemtest/bases/bridging/BridgingBase.java +++ /dev/null @@ -1,226 +0,0 @@ -/* - * Copyright 2019, EnMasse authors. - * License: Apache License 2.0 (see the file LICENSE or http://apache.org/licenses/LICENSE-2.0.html). - */ -package io.enmasse.systemtest.bases.bridging; - -import io.enmasse.address.model.AddressSpace; -import io.enmasse.address.model.AddressSpaceBuilder; -import io.enmasse.address.model.AddressSpaceSpecConnectorAddressRuleBuilder; -import io.enmasse.address.model.AddressSpaceSpecConnectorBuilder; -import io.enmasse.address.model.AddressSpaceSpecConnectorCredentials; -import io.enmasse.address.model.AddressSpaceSpecConnectorCredentialsBuilder; -import io.enmasse.address.model.AddressSpaceSpecConnectorEndpointBuilder; -import io.enmasse.address.model.AddressSpaceSpecConnectorTls; -import io.enmasse.address.model.AddressSpaceSpecConnectorTlsBuilder; -import io.enmasse.address.model.StringOrSecretSelectorBuilder; -import io.enmasse.systemtest.Endpoint; -import io.enmasse.systemtest.amqp.AmqpClient; -import io.enmasse.systemtest.amqp.AmqpConnectOptions; -import io.enmasse.systemtest.amqp.QueueTerminusFactory; -import io.enmasse.systemtest.bases.TestBase; -import io.enmasse.systemtest.bases.isolated.ITestIsolatedStandard; -import io.enmasse.systemtest.certs.BrokerCertBundle; -import io.enmasse.systemtest.certs.openssl.OpenSSLUtil; -import io.enmasse.systemtest.logs.CustomLogger; -import io.enmasse.systemtest.messagingclients.AbstractClient; -import io.enmasse.systemtest.messagingclients.ClientArgument; -import io.enmasse.systemtest.messagingclients.ExternalMessagingClient; -import io.enmasse.systemtest.model.addressspace.AddressSpacePlans; -import io.enmasse.systemtest.model.addressspace.AddressSpaceType; -import io.enmasse.systemtest.platform.Kubernetes; -import io.enmasse.systemtest.platform.apps.SystemtestsKubernetesApps; -import io.enmasse.systemtest.utils.AddressSpaceUtils; -import io.enmasse.systemtest.utils.TestUtils; -import io.fabric8.kubernetes.api.model.SecretKeySelectorBuilder; -import io.vertx.proton.ProtonClientOptions; -import io.vertx.proton.ProtonQoS; -import org.junit.jupiter.api.AfterEach; -import org.junit.jupiter.api.Assertions; -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.extension.ExtensionContext; -import org.slf4j.Logger; - -import java.nio.file.Path; -import java.util.Base64; - -public abstract class BridgingBase extends TestBase implements ITestIsolatedStandard { - - private static Logger log = CustomLogger.getLogger(); - - protected static final String REMOTE_NAME = "remote1"; - - protected final String remoteBrokerName = "amq-broker"; - protected final String remoteBrokerNamespace = "systemtests-external-broker"; - protected final String remoteBrokerUsername = "test-user"; - protected final String remoteBrokerPassword = "test-password"; - protected Endpoint clientBrokerEndpoint; - protected Endpoint clientBrokerEndpointExternal; - protected Endpoint remoteBrokerEndpoint; - protected Endpoint remoteBrokerEndpointSSL; - protected Endpoint remoteBrokerEndpointMutualTLS; - protected BrokerCertBundle certBundle; - - @BeforeEach - void deployBroker() throws Exception { - String serviceName = String.format("%s.%s.svc.cluster.local", remoteBrokerName, remoteBrokerNamespace); - certBundle = OpenSSLUtil.createBrokerCertBundle(serviceName); - SystemtestsKubernetesApps.deployAMQBroker(remoteBrokerNamespace, remoteBrokerName, remoteBrokerUsername, remoteBrokerPassword, certBundle); - remoteBrokerEndpoint = new Endpoint(serviceName, 5672); - remoteBrokerEndpointSSL = new Endpoint(serviceName, 5671); - remoteBrokerEndpointMutualTLS = new Endpoint(serviceName, 55671); - clientBrokerEndpointExternal = Kubernetes.getInstance().getExternalEndpoint(remoteBrokerName, remoteBrokerNamespace); - clientBrokerEndpoint = remoteBrokerEndpoint; - log.info("Broker endpoint: {}", remoteBrokerEndpoint); - log.info("Broker SSL endpoint: {}", remoteBrokerEndpointSSL); - log.info("Client broker endpoint: {}", clientBrokerEndpoint); - } - - @AfterEach - void undeployBroker(ExtensionContext context) throws Exception { - if (context.getExecutionException().isPresent()) { //test failed - Path path = TestUtils.getFailedTestLogsPath(context); - SystemtestsKubernetesApps.collectAMQBrokerLogs(path, remoteBrokerNamespace); - } - SystemtestsKubernetesApps.deleteAMQBroker(remoteBrokerNamespace, remoteBrokerName); - } - - protected void scaleDownBroker() throws Exception { - log.info("Scaling down broker"); - SystemtestsKubernetesApps.scaleDownDeployment(remoteBrokerNamespace, remoteBrokerName); - } - - protected void scaleUpBroker() throws Exception { - log.info("Scaling up broker"); - SystemtestsKubernetesApps.scaleUpDeployment(remoteBrokerNamespace, remoteBrokerName); - } - - protected AddressSpace createAddressSpace(String name, String addressRule, AddressSpaceSpecConnectorTls tlsSettings, AddressSpaceSpecConnectorCredentials credentials) throws Exception { - var endpoint = tlsSettings != null ? tlsSettings.getClientCert() != null ? remoteBrokerEndpointMutualTLS : remoteBrokerEndpointSSL : remoteBrokerEndpoint; - var connectorBuilder = new AddressSpaceSpecConnectorBuilder() - .withName(REMOTE_NAME) - .addToEndpointHosts(new AddressSpaceSpecConnectorEndpointBuilder() - .withHost(endpoint.getHost()) - .withPort(endpoint.getPort()) - .build()) - .addToAddresses(new AddressSpaceSpecConnectorAddressRuleBuilder() - .withName("queuesrule") - .withPattern(addressRule) - .build()); - - if (tlsSettings != null) { - connectorBuilder.withTls(tlsSettings); - } - - //only set credentials when mutual tls isn't configured - if (tlsSettings == null || tlsSettings.getClientCert() == null) { - if (credentials == null) { - Assertions.fail("Connector wrongly configured, missing connector credentials"); - } - connectorBuilder.withCredentials(credentials); - } - - AddressSpace space = new AddressSpaceBuilder() - .withNewMetadata() - .withNamespace(remoteBrokerNamespace) - .withName(name) - .endMetadata() - .withNewSpec() - .withType(AddressSpaceType.STANDARD.toString()) - .withPlan(AddressSpacePlans.STANDARD_SMALL) - .withConnectors(connectorBuilder.build()) - .endSpec() - .build(); - resourcesManager.createAddressSpace(space); - AddressSpaceUtils.waitForAddressSpaceConnectorsReady(space); - return space; - } - - protected AmqpClient createClientToRemoteBroker() { - ProtonClientOptions clientOptions = new ProtonClientOptions(); - clientOptions.setSsl(true); - clientOptions.setTrustAll(true); - clientOptions.setHostnameVerificationAlgorithm(""); - - AmqpConnectOptions connectOptions = new AmqpConnectOptions() - .setTerminusFactory(new QueueTerminusFactory()) - .setEndpoint(clientBrokerEndpointExternal) - .setProtonClientOptions(clientOptions) - .setQos(ProtonQoS.AT_LEAST_ONCE) - .setUsername(remoteBrokerUsername) - .setPassword(remoteBrokerPassword); - return getAmqpClientFactory().createClient(connectOptions); - } - - protected ExternalMessagingClient createOnClusterClientToRemoteBroker(AbstractClient client, int messageCount) { - return new ExternalMessagingClient(false) - .withMessagingRoute(clientBrokerEndpoint) - .withMessageBody("msg no. %d") - .withCount(messageCount) - .withClientEngine(client) - .withAdditionalArgument(ClientArgument.CONN_AUTH_MECHANISM, "PLAIN") - .withCredentials(remoteBrokerUsername, remoteBrokerPassword); - } - - protected AddressSpaceSpecConnectorCredentials defaultCredentials() { - return new AddressSpaceSpecConnectorCredentialsBuilder() - .withNewUsername() - .withValue(remoteBrokerUsername) - .endUsername() - .withNewPassword() - .withValue(remoteBrokerPassword) - .endPassword() - .build(); - } - - protected AddressSpaceSpecConnectorCredentials credentialsInSecret() { - return new AddressSpaceSpecConnectorCredentialsBuilder() - .withNewUsername() - .withValueFromSecret(new SecretKeySelectorBuilder() - .withName(remoteBrokerName) - .withKey("user") - .build()) - .endUsername() - .withNewPassword() - .withValueFromSecret(new SecretKeySelectorBuilder() - .withName(remoteBrokerName) - .withKey("password") - .build()) - .endPassword() - .build(); - } - - protected AddressSpaceSpecConnectorTls defaultTls() { - return new AddressSpaceSpecConnectorTlsBuilder() - .withCaCert(new StringOrSecretSelectorBuilder() - .withValue(Base64.getEncoder().encodeToString(certBundle.getCaCert())) - .build()) - .build(); - } - - protected AddressSpaceSpecConnectorTls tlsInSecret() { - return new AddressSpaceSpecConnectorTlsBuilder() - .withCaCert(new StringOrSecretSelectorBuilder() - .withValueFromSecret(new SecretKeySelectorBuilder() - .withName(remoteBrokerName) - .withKey("ca.crt") - .build()) - .build()) - .build(); - } - - protected AddressSpaceSpecConnectorTls defaultMutualTls() { - return new AddressSpaceSpecConnectorTlsBuilder() - .withCaCert(new StringOrSecretSelectorBuilder() - .withValue(Base64.getEncoder().encodeToString(certBundle.getCaCert())) - .build()) - .withClientCert(new StringOrSecretSelectorBuilder() - .withValue(Base64.getEncoder().encodeToString(certBundle.getClientCert())) - .build()) - .withClientKey(new StringOrSecretSelectorBuilder() - .withValue(Base64.getEncoder().encodeToString(certBundle.getClientKey())) - .build()) - .build(); - } - -} diff --git a/systemtests/src/test/java/io/enmasse/systemtest/bases/clients/ClientTestBase.java b/systemtests/src/test/java/io/enmasse/systemtest/bases/clients/ClientTestBase.java deleted file mode 100644 index 531d19ec96c..00000000000 --- a/systemtests/src/test/java/io/enmasse/systemtest/bases/clients/ClientTestBase.java +++ /dev/null @@ -1,591 +0,0 @@ -/* - * Copyright 2018, EnMasse authors. - * License: Apache License 2.0 (see the file LICENSE or http://apache.org/licenses/LICENSE-2.0.html). - */ -package io.enmasse.systemtest.bases.clients; - -import io.enmasse.address.model.Address; -import io.enmasse.address.model.AddressBuilder; -import io.enmasse.address.model.AddressSpace; -import io.enmasse.systemtest.Endpoint; -import io.enmasse.systemtest.UserCredentials; -import io.enmasse.systemtest.bases.TestBase; -import io.enmasse.systemtest.bases.shared.ITestBaseShared; -import io.enmasse.systemtest.messagingclients.AbstractClient; -import io.enmasse.systemtest.messagingclients.ClientArgument; -import io.enmasse.systemtest.messagingclients.ClientType; -import io.enmasse.systemtest.annotations.ExternalClients; -import io.enmasse.systemtest.messagingclients.ExternalMessagingClient; -import io.enmasse.systemtest.model.address.AddressType; -import io.enmasse.systemtest.model.addressspace.AddressSpaceType; -import io.enmasse.systemtest.utils.AddressSpaceUtils; -import io.enmasse.systemtest.utils.AddressUtils; -import io.enmasse.systemtest.utils.UserUtils; -import io.enmasse.user.model.v1.Operation; -import io.enmasse.user.model.v1.User; -import io.enmasse.user.model.v1.UserAuthorizationBuilder; -import org.junit.jupiter.api.AfterEach; -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.TestInfo; -import org.junit.jupiter.api.function.Executable; - -import java.nio.file.Path; -import java.nio.file.Paths; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Collections; -import java.util.List; -import java.util.concurrent.Future; -import java.util.concurrent.TimeUnit; - -import static org.junit.jupiter.api.Assertions.assertAll; -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertFalse; -import static org.junit.jupiter.api.Assertions.assertTrue; - -@ExternalClients -public abstract class ClientTestBase extends TestBase implements ITestBaseShared { - protected Path logPath = null; - private List clients; - - @BeforeEach - public void setUpClientBase(TestInfo info) { - clients = new ArrayList<>(); - String clientFolder = "clients_tests"; - logPath = environment.testLogDir().resolve( - Paths.get( - clientFolder, - info.getTestClass().get().getName(), - info.getDisplayName())); - } - - @AfterEach - public void teardownClient() { - if (clients != null) { - clients.forEach(AbstractClient::stop); - clients.clear(); - } - } - - private Endpoint getMessagingRoute(AddressSpace addressSpace, boolean websocket) throws Exception { - return websocket ? AddressSpaceUtils.getMessagingWssRoute(addressSpace) : AddressSpaceUtils.getMessagingRoute(addressSpace); - } - - protected void doBasicMessageTest(AbstractClient sender, AbstractClient receiver) throws Exception { - doBasicMessageTest(sender, receiver, false); - } - - protected void doBasicMessageTest(AbstractClient sender, AbstractClient receiver, boolean websocket) throws Exception { - clients.addAll(Arrays.asList(sender, receiver)); - int expectedMsgCount = 10; - - Address dest = new AddressBuilder() - .withNewMetadata() - .withNamespace(getSharedAddressSpace().getMetadata().getNamespace()) - .withName(AddressUtils.generateAddressMetadataName(getSharedAddressSpace(), "message-basic" + ClientType.getAddressName(sender))) - .endMetadata() - .withNewSpec() - .withType("queue") - .withAddress("message-basic" + ClientType.getAddressName(sender)) - .withPlan(getDefaultPlan(AddressType.QUEUE)) - .endSpec() - .build(); - resourcesManager.setAddresses(dest); - - ExternalMessagingClient senderClient = new ExternalMessagingClient() - .withClientEngine(sender) - .withMessagingRoute(getMessagingRoute(getSharedAddressSpace(), websocket)) - .withAddress(dest) - .withCredentials(defaultCredentials) - .withCount(expectedMsgCount) - .withMessageBody("msg no. %d") - .withTimeout(30) - .withAdditionalArgument(ClientArgument.CONN_WEB_SOCKET, websocket) - .withAdditionalArgument(ClientArgument.CONN_WEB_SOCKET_PROTOCOLS, getSharedAddressSpace().getSpec().getType().equals(AddressSpaceType.STANDARD.toString()) ? "binary" : "") - .withAdditionalArgument(ClientArgument.DEST_TYPE, "ANYCAST"); - - ExternalMessagingClient receiverClient = new ExternalMessagingClient() - .withClientEngine(receiver) - .withMessagingRoute(getMessagingRoute(getSharedAddressSpace(), websocket)) - .withAddress(dest) - .withCredentials(defaultCredentials) - .withCount(expectedMsgCount) - .withTimeout(30) - .withAdditionalArgument(ClientArgument.CONN_WEB_SOCKET, websocket) - .withAdditionalArgument(ClientArgument.CONN_WEB_SOCKET_PROTOCOLS, getSharedAddressSpace().getSpec().getType().equals(AddressSpaceType.STANDARD.toString()) ? "binary" : "") - .withAdditionalArgument(ClientArgument.DEST_TYPE, "ANYCAST"); - - - assertTrue(senderClient.run(), "Sender failed, expected return code 0"); - assertTrue(receiverClient.run(), "Receiver failed, expected return code 0"); - - assertEquals(expectedMsgCount, senderClient.getMessages().size(), - String.format("Expected %d sent messages", expectedMsgCount)); - assertEquals(expectedMsgCount, receiverClient.getMessages().size(), - String.format("Expected %d received messages", expectedMsgCount)); - } - - protected void doRoundRobinReceiverTest(AbstractClient sender, AbstractClient receiver, AbstractClient receiver2) - throws Exception { - clients.addAll(Arrays.asList(sender, receiver, receiver2)); - int expectedMsgCount = 10; - - Address dest = new AddressBuilder() - .withNewMetadata() - .withNamespace(getSharedAddressSpace().getMetadata().getNamespace()) - .withName(AddressUtils.generateAddressMetadataName(getSharedAddressSpace(), "round-robin" + ClientType.getAddressName(sender))) - .endMetadata() - .withNewSpec() - .withType("queue") - .withAddress("round-robin" + ClientType.getAddressName(sender)) - .withPlan(getDefaultPlan(AddressType.QUEUE)) - .endSpec() - .build(); - resourcesManager.setAddresses(dest); - - ExternalMessagingClient senderClient = new ExternalMessagingClient() - .withClientEngine(sender) - .withMessagingRoute(AddressSpaceUtils.getMessagingRoute(getSharedAddressSpace())) - .withAddress(dest) - .withCredentials(defaultCredentials) - .withCount(expectedMsgCount) - .withMessageBody("msg no. %d") - .withTimeout(30); - - ExternalMessagingClient receiverClient1 = new ExternalMessagingClient() - .withClientEngine(receiver) - .withMessagingRoute(AddressSpaceUtils.getMessagingRoute(getSharedAddressSpace())) - .withAddress(dest) - .withCredentials(defaultCredentials) - .withCount(expectedMsgCount / 2) - .withTimeout(250); - - ExternalMessagingClient receiverClient2 = new ExternalMessagingClient() - .withClientEngine(receiver2) - .withMessagingRoute(AddressSpaceUtils.getMessagingRoute(getSharedAddressSpace())) - .withAddress(dest) - .withCredentials(defaultCredentials) - .withCount(expectedMsgCount / 2) - .withTimeout(250); - - Future recResult = receiverClient1.runAsync(); - Future rec2Result = receiverClient2.runAsync(); - - receiverClient1.getLinkAttachedProbe().get(15000, TimeUnit.MILLISECONDS); - receiverClient2.getLinkAttachedProbe().get(15000, TimeUnit.MILLISECONDS); - - assertTrue(senderClient.run(), "Sender failed, expected return code 0"); - assertTrue(recResult.get(), "Receiver failed, expected return code 0"); - assertTrue(rec2Result.get(), "Receiver failed, expected return code 0"); - - assertEquals(expectedMsgCount, senderClient.getMessages().size(), - String.format("Expected %d sent messages", expectedMsgCount)); - - assertAll( - () -> assertEquals(expectedMsgCount / 2, receiverClient1.getMessages().size(), - String.format("Expected %d received messages", expectedMsgCount / 2)), - () -> assertEquals(expectedMsgCount / 2, receiverClient2.getMessages().size(), - String.format("Expected %d sent messages", expectedMsgCount / 2))); - } - - protected void doTopicSubscribeTest(AbstractClient sender, AbstractClient subscriber, AbstractClient subscriber2) throws Exception { - clients.addAll(Arrays.asList(sender, subscriber, subscriber2)); - int expectedMsgCount = 10; - - Address dest = new AddressBuilder() - .withNewMetadata() - .withNamespace(getSharedAddressSpace().getMetadata().getNamespace()) - .withName(AddressUtils.generateAddressMetadataName(getSharedAddressSpace(), "topic-sub" + ClientType.getAddressName(sender))) - .endMetadata() - .withNewSpec() - .withType("topic") - .withAddress("topic-sub" + ClientType.getAddressName(sender)) - .withPlan(getDefaultPlan(AddressType.TOPIC)) - .endSpec() - .build(); - resourcesManager.setAddresses(dest); - - ExternalMessagingClient senderClient = new ExternalMessagingClient() - .withClientEngine(sender) - .withMessagingRoute(AddressSpaceUtils.getMessagingRoute(getSharedAddressSpace())) - .withAddress(dest) - .withCredentials(defaultCredentials) - .withCount(expectedMsgCount) - .withMessageBody("msg no. %d") - .withTimeout(30) - .withAdditionalArgument(ClientArgument.DEST_TYPE, "MULTICAST"); - - ExternalMessagingClient receiverClient1 = new ExternalMessagingClient() - .withClientEngine(subscriber) - .withMessagingRoute(AddressSpaceUtils.getMessagingRoute(getSharedAddressSpace())) - .withAddress(dest) - .withCredentials(defaultCredentials) - .withCount(expectedMsgCount) - .withTimeout(250) - .withAdditionalArgument(ClientArgument.DEST_TYPE, "MULTICAST"); - - ExternalMessagingClient receiverClient2 = new ExternalMessagingClient() - .withClientEngine(subscriber2) - .withMessagingRoute(AddressSpaceUtils.getMessagingRoute(getSharedAddressSpace())) - .withAddress(dest) - .withCredentials(defaultCredentials) - .withCount(expectedMsgCount) - .withTimeout(250) - .withAdditionalArgument(ClientArgument.DEST_TYPE, "MULTICAST"); - - Future recResult = receiverClient1.runAsync(); - Future recResult2 = receiverClient2.runAsync(); - - receiverClient1.getLinkAttachedProbe().get(15000, TimeUnit.MILLISECONDS); - receiverClient2.getLinkAttachedProbe().get(15000, TimeUnit.MILLISECONDS); - - assertAll( - () -> assertTrue(senderClient.run(), "Producer failed, expected return code 0"), - () -> assertEquals(expectedMsgCount, senderClient.getMessages().size(), - String.format("Expected %d sent messages", expectedMsgCount))); - assertAll( - () -> assertTrue(recResult.get(), "Subscriber failed, expected return code 0"), - () -> assertTrue(recResult2.get(), "Subscriber failed, expected return code 0"), - () -> assertEquals(expectedMsgCount, receiverClient1.getMessages().size(), - String.format("Expected %d received messages", expectedMsgCount)), - () -> assertEquals(expectedMsgCount, receiverClient2.getMessages().size(), - String.format("Expected %d received messages", expectedMsgCount))); - } - - protected void doMessageBrowseTest(AbstractClient sender, AbstractClient receiver_browse, AbstractClient receiver_receive) - throws Exception { - clients.addAll(Arrays.asList(sender, receiver_browse, receiver_receive)); - int expectedMsgCount = 10; - - Address dest = new AddressBuilder() - .withNewMetadata() - .withNamespace(getSharedAddressSpace().getMetadata().getNamespace()) - .withName(AddressUtils.generateAddressMetadataName(getSharedAddressSpace(), "browse" + ClientType.getAddressName(sender))) - .endMetadata() - .withNewSpec() - .withType("queue") - .withAddress("browse" + ClientType.getAddressName(sender)) - .withPlan(getDefaultPlan(AddressType.QUEUE)) - .endSpec() - .build(); - resourcesManager.setAddresses(dest); - - ExternalMessagingClient senderClient = new ExternalMessagingClient() - .withClientEngine(sender) - .withMessagingRoute(AddressSpaceUtils.getMessagingRoute(getSharedAddressSpace())) - .withAddress(dest) - .withCredentials(defaultCredentials) - .withCount(expectedMsgCount) - .withMessageBody("msg no. %d"); - - ExternalMessagingClient receiverClientBrowse = new ExternalMessagingClient() - .withClientEngine(receiver_browse) - .withMessagingRoute(AddressSpaceUtils.getMessagingRoute(getSharedAddressSpace())) - .withAddress(dest) - .withCredentials(defaultCredentials) - .withCount(expectedMsgCount) - .withAdditionalArgument(ClientArgument.RECV_BROWSE, "true"); - - ExternalMessagingClient receiverClientReceive = new ExternalMessagingClient() - .withClientEngine(receiver_receive) - .withMessagingRoute(AddressSpaceUtils.getMessagingRoute(getSharedAddressSpace())) - .withAddress(dest) - .withCredentials(defaultCredentials) - .withCount(expectedMsgCount) - .withAdditionalArgument(ClientArgument.RECV_BROWSE, "false"); - - assertAll( - () -> assertTrue(senderClient.run(), "Sender failed, expected return code 0"), - () -> assertEquals(expectedMsgCount, senderClient.getMessages().size(), - String.format("Expected %d sent messages", expectedMsgCount))); - assertAll( - () -> assertTrue(receiverClientBrowse.run(), "Browse receiver failed, expected return code 0"), - () -> assertTrue(receiverClientReceive.run(), "Receiver failed, expected return code 0"), - () -> assertEquals(expectedMsgCount, receiverClientBrowse.getMessages().size(), - String.format("Expected %d browsed messages", expectedMsgCount)), - () -> assertEquals(expectedMsgCount, receiverClientReceive.getMessages().size(), - String.format("Expected %d received messages", expectedMsgCount))); - } - - protected void doDrainQueueTest(AbstractClient sender, AbstractClient receiver) throws Exception { - Address dest = new AddressBuilder() - .withNewMetadata() - .withNamespace(getSharedAddressSpace().getMetadata().getNamespace()) - .withName(AddressUtils.generateAddressMetadataName(getSharedAddressSpace(), "drain" + ClientType.getAddressName(sender))) - .endMetadata() - .withNewSpec() - .withType("queue") - .withAddress("drain" + ClientType.getAddressName(sender)) - .withPlan(getDefaultPlan(AddressType.QUEUE)) - .endSpec() - .build(); - resourcesManager.setAddresses(dest); - - clients.addAll(Arrays.asList(sender, receiver)); - int expectedMsgCount = 50; - - ExternalMessagingClient senderClient = new ExternalMessagingClient() - .withClientEngine(sender) - .withMessagingRoute(AddressSpaceUtils.getMessagingRoute(getSharedAddressSpace())) - .withAddress(dest) - .withCredentials(defaultCredentials) - .withCount(expectedMsgCount) - .withMessageBody("msg no. %d"); - - ExternalMessagingClient receiverClient = new ExternalMessagingClient() - .withClientEngine(receiver) - .withMessagingRoute(AddressSpaceUtils.getMessagingRoute(getSharedAddressSpace())) - .withAddress(dest) - .withCredentials(defaultCredentials) - .withCount(0) - .withTimeout(10); - - assertTrue(senderClient.run(), "Sender failed, expected return code 0"); - assertTrue(receiverClient.run(), "Drain receiver failed, expected return code 0"); - - assertEquals(expectedMsgCount, senderClient.getMessages().size(), - String.format("Expected %d sent messages", expectedMsgCount)); - assertEquals(expectedMsgCount, receiverClient.getMessages().size(), - String.format("Expected %d received messages", expectedMsgCount)); - } - - protected void doMessageSelectorQueueTest(AbstractClient sender, AbstractClient receiver) throws Exception { - int expectedMsgCount = 10; - - clients.addAll(Arrays.asList(sender, receiver)); - Address queue = new AddressBuilder() - .withNewMetadata() - .withNamespace(getSharedAddressSpace().getMetadata().getNamespace()) - .withName(AddressUtils.generateAddressMetadataName(getSharedAddressSpace(), "selector-queue" + ClientType.getAddressName(sender))) - .endMetadata() - .withNewSpec() - .withType("queue") - .withAddress("selector-queue" + ClientType.getAddressName(sender)) - .withPlan(getDefaultPlan(AddressType.QUEUE)) - .endSpec() - .build(); - resourcesManager.setAddresses(queue); - - ExternalMessagingClient senderClient = new ExternalMessagingClient() - .withClientEngine(sender) - .withMessagingRoute(AddressSpaceUtils.getMessagingRoute(getSharedAddressSpace())) - .withCount(expectedMsgCount) - .withAddress(queue) - .withCredentials(defaultCredentials) - .withAdditionalArgument(ClientArgument.MSG_PROPERTY, "colour~red") - .withAdditionalArgument(ClientArgument.MSG_PROPERTY, "number~12.65") - .withAdditionalArgument(ClientArgument.MSG_PROPERTY, "a~true") - .withAdditionalArgument(ClientArgument.MSG_PROPERTY, "b~false") - .withMessageBody("msg no. %d"); - - //send messages - assertTrue(senderClient.run(), "Sender failed, expected return code 0"); - assertEquals(expectedMsgCount, senderClient.getMessages().size(), - String.format("Expected %d sent messages", expectedMsgCount)); - - ExternalMessagingClient receiverClient = new ExternalMessagingClient() - .withClientEngine(receiver) - .withMessagingRoute(AddressSpaceUtils.getMessagingRoute(getSharedAddressSpace())) - .withCount(0) - .withCredentials(defaultCredentials) - .withAddress(queue) - .withAdditionalArgument(ClientArgument.RECV_BROWSE, "true"); - - //receiver with selector colour = red - receiverClient.withAdditionalArgument(ClientArgument.SELECTOR, "colour = 'red'"); - final Executable executable = () -> assertEquals(expectedMsgCount, receiverClient.getMessages().size(), - String.format("Expected %d received messages 'colour = red'", expectedMsgCount)); - assertAll( - () -> assertTrue(receiverClient.run(), "Receiver 'colour = red' failed, expected return code 0"), - executable); - - //receiver with selector number > 12.5 - receiverClient.withAdditionalArgument(ClientArgument.SELECTOR, "number > 12.5"); - assertAll( - () -> assertTrue(receiverClient.run(), "Receiver 'number > 12.5' failed, expected return code 0"), - executable); - - - //receiver with selector a AND b - receiverClient.withAdditionalArgument(ClientArgument.SELECTOR, "a AND b"); - assertAll( - () -> assertTrue(receiverClient.run(), "Receiver 'a AND b' failed, expected return code 0"), - () -> assertEquals(0, receiverClient.getMessages().size(), - String.format("Expected %d received messages 'a AND b'", 0))); - - //receiver with selector a OR b - receiverClient.withAdditionalArgument(ClientArgument.RECV_BROWSE, "false"); - receiverClient.withAdditionalArgument(ClientArgument.SELECTOR, "a OR b"); - - assertAll( - () -> assertTrue(receiverClient.run(), "Receiver 'a OR b' failed, expected return code 0"), - () -> assertEquals(expectedMsgCount, receiverClient.getMessages().size(), - String.format("Expected %d received messages 'a OR b'", expectedMsgCount))); - } - - protected void doMessageSelectorTopicTest(AbstractClient sender, AbstractClient sender2, - AbstractClient subscriber, AbstractClient subscriber2) throws Exception { - clients.addAll(Arrays.asList(sender, sender2, subscriber, subscriber2)); - int expectedMsgCount = 5; - - Address topic = new AddressBuilder() - .withNewMetadata() - .withNamespace(getSharedAddressSpace().getMetadata().getNamespace()) - .withName(AddressUtils.generateAddressMetadataName(getSharedAddressSpace(), "selector-topic" + ClientType.getAddressName(sender))) - .endMetadata() - .withNewSpec() - .withType("topic") - .withAddress("selector-topic" + ClientType.getAddressName(sender)) - .withPlan(getDefaultPlan(AddressType.TOPIC)) - .endSpec() - .build(); - resourcesManager.setAddresses(topic); - - //set up senders - ExternalMessagingClient senderClient1 = new ExternalMessagingClient() - .withClientEngine(sender) - .withMessagingRoute(AddressSpaceUtils.getMessagingRoute(getSharedAddressSpace())) - .withCount(expectedMsgCount) - .withAddress(topic) - .withCredentials(defaultCredentials) - .withAdditionalArgument(ClientArgument.MSG_PROPERTY, "colour~red") - .withAdditionalArgument(ClientArgument.MSG_PROPERTY, "number~12.65") - .withAdditionalArgument(ClientArgument.MSG_PROPERTY, "a~true") - .withAdditionalArgument(ClientArgument.MSG_PROPERTY, "b~false") - .withTimeout(250) - .withMessageBody("msg no. %d"); - - ExternalMessagingClient senderClient2 = new ExternalMessagingClient() - .withClientEngine(sender2) - .withMessagingRoute(AddressSpaceUtils.getMessagingRoute(getSharedAddressSpace())) - .withCount(expectedMsgCount) - .withAddress(topic) - .withCredentials(defaultCredentials) - .withAdditionalArgument(ClientArgument.MSG_PROPERTY, "colour~blue") - .withAdditionalArgument(ClientArgument.MSG_PROPERTY, "number~11.65") - .withTimeout(250) - .withMessageBody("msg no. %d"); - - //set up subscriber1 - ExternalMessagingClient receiverClient1 = new ExternalMessagingClient() - .withClientEngine(subscriber) - .withMessagingRoute(AddressSpaceUtils.getMessagingRoute(getSharedAddressSpace())) - .withCount(expectedMsgCount) - .withCredentials(defaultCredentials) - .withAddress(topic) - .withTimeout(250) - .withAdditionalArgument(ClientArgument.SELECTOR, "colour = 'red' AND a"); - - //set up subscriber2 - ExternalMessagingClient receiverClient2 = new ExternalMessagingClient() - .withClientEngine(subscriber2) - .withMessagingRoute(AddressSpaceUtils.getMessagingRoute(getSharedAddressSpace())) - .withCount(expectedMsgCount) - .withCredentials(defaultCredentials) - .withAddress(topic) - .withTimeout(250) - .withAdditionalArgument(ClientArgument.SELECTOR, "number < 12.5"); - - Future result1 = receiverClient1.runAsync(); - Future result2 = receiverClient2.runAsync(); - - receiverClient1.getLinkAttachedProbe().get(15000, TimeUnit.MILLISECONDS); - receiverClient2.getLinkAttachedProbe().get(15000, TimeUnit.MILLISECONDS); - - assertTrue(senderClient1.run(), "Sender failed, expected return code 0"); - assertTrue(senderClient2.run(), "Sender2 failed, expected return code 0"); - assertTrue(result1.get(), "Receiver 'colour = red' failed, expected return code 0"); - assertTrue(result2.get(), "Receiver 'number < 12.5' failed, expected return code 0"); - - assertEquals(expectedMsgCount, senderClient1.getMessages().size(), - String.format("Expected %d sent messages", expectedMsgCount)); - assertEquals(expectedMsgCount, senderClient2.getMessages().size(), - String.format("Expected %d sent messages", expectedMsgCount)); - assertEquals(expectedMsgCount, receiverClient1.getMessages().size(), - String.format("Expected %d received messages 'colour = red' AND a", expectedMsgCount)); - assertEquals(expectedMsgCount, receiverClient2.getMessages().size(), - String.format("Expected %d received messages 'number < 12.5'", expectedMsgCount)); - } - - protected void doTestUserPermissions(AbstractClient sender, AbstractClient receiver) throws Exception { - int expectedMsgCount = 5; - UserCredentials publishCred = new UserCredentials("publisher", "publish"); - UserCredentials consumCred = new UserCredentials("consumer", "consume"); - createPublisherAndConsumer(publishCred, consumCred); - - Address dest = new AddressBuilder() - .withNewMetadata() - .withNamespace(getSharedAddressSpace().getMetadata().getNamespace()) - .withName(AddressUtils.generateAddressMetadataName(getSharedAddressSpace(), "message-basic" + ClientType.getAddressName(sender))) - .endMetadata() - .withNewSpec() - .withType("queue") - .withAddress("message-basic" + ClientType.getAddressName(sender)) - .withPlan(getDefaultPlan(AddressType.QUEUE)) - .endSpec() - .build(); - resourcesManager.setAddresses(dest); - - ExternalMessagingClient senderClient = new ExternalMessagingClient() - .withClientEngine(sender) - .withMessagingRoute(getMessagingRoute(getSharedAddressSpace(), false)) - .withAddress(dest) - .withCredentials(publishCred) - .withCount(expectedMsgCount) - .withMessageBody("msg no. %d") - .withTimeout(30) - .withCredentials(consumCred) - .withAdditionalArgument(ClientArgument.DEST_TYPE, "ANYCAST"); - - ExternalMessagingClient receiverClient = new ExternalMessagingClient() - .withClientEngine(receiver) - .withMessagingRoute(getMessagingRoute(getSharedAddressSpace(), false)) - .withAddress(dest) - .withCredentials(consumCred) - .withCount(expectedMsgCount) - .withTimeout(30) - .withCredentials(publishCred) - .withAdditionalArgument(ClientArgument.DEST_TYPE, "ANYCAST"); - - - assertAll( - () -> assertFalse(senderClient.run(), "Sender failed. Specified user is not allowed to write"), - () -> assertFalse(receiverClient.run(), "Receiver failed. Specified user is not allowed to read")); - - senderClient.withCredentials(publishCred); - - receiverClient.withCredentials(consumCred); - - assertTrue(senderClient.run(), "Sender failed, expected return code 0"); - assertTrue(receiverClient.run(), "Receiver failed, expected return code 0"); - - assertEquals(expectedMsgCount, senderClient.getMessages().size(), - String.format("Expected %d sent messages", expectedMsgCount)); - assertEquals(expectedMsgCount, receiverClient.getMessages().size(), - String.format("Expected %d received messages", expectedMsgCount)); - } - - private void createPublisherAndConsumer(UserCredentials publishCred, UserCredentials consumCred) throws Exception { - User publisher = (UserUtils.createUserResource(publishCred) - .editSpec() - .withAuthorization(Collections.singletonList(new UserAuthorizationBuilder() - .withAddresses("*") - .withOperations(Operation.send) - .build())) - .endSpec() - .done()); - - User consumer = (UserUtils.createUserResource(consumCred) - .editSpec() - .withAuthorization(Collections.singletonList(new UserAuthorizationBuilder() - .withAddresses("*") - .withOperations(Operation.recv) - .build())) - .endSpec() - .done()); - - resourcesManager.createOrUpdateUser(getSharedAddressSpace(), publisher); - resourcesManager.createOrUpdateUser(getSharedAddressSpace(), consumer); - } - -} diff --git a/systemtests/src/test/java/io/enmasse/systemtest/bases/clients/ClusterClientTestBase.java b/systemtests/src/test/java/io/enmasse/systemtest/bases/clients/ClusterClientTestBase.java deleted file mode 100644 index 1117b5dd400..00000000000 --- a/systemtests/src/test/java/io/enmasse/systemtest/bases/clients/ClusterClientTestBase.java +++ /dev/null @@ -1,145 +0,0 @@ -/* - * Copyright 2018, EnMasse authors. - * License: Apache License 2.0 (see the file LICENSE or http://apache.org/licenses/LICENSE-2.0.html). - */ -package io.enmasse.systemtest.bases.clients; - -import io.enmasse.address.model.Address; -import io.enmasse.address.model.AddressBuilder; -import io.enmasse.address.model.AddressSpace; -import io.enmasse.systemtest.Endpoint; -import io.enmasse.systemtest.bases.TestBase; -import io.enmasse.systemtest.bases.shared.ITestBaseShared; -import io.enmasse.systemtest.messagingclients.AbstractClient; -import io.enmasse.systemtest.messagingclients.ClientArgument; -import io.enmasse.systemtest.messagingclients.ClientType; -import io.enmasse.systemtest.annotations.ExternalClients; -import io.enmasse.systemtest.messagingclients.ExternalMessagingClient; -import io.enmasse.systemtest.messagingclients.mqtt.PahoMQTTClientReceiver; -import io.enmasse.systemtest.messagingclients.mqtt.PahoMQTTClientSender; -import io.enmasse.systemtest.model.address.AddressType; -import io.enmasse.systemtest.model.addressplan.DestinationPlan; -import io.enmasse.systemtest.model.addressspace.AddressSpaceType; -import io.enmasse.systemtest.utils.AddressSpaceUtils; -import io.enmasse.systemtest.utils.AddressUtils; - -import java.util.concurrent.Future; - -import static org.junit.jupiter.api.Assertions.assertAll; -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertTrue; - -@ExternalClients -public abstract class ClusterClientTestBase extends TestBase implements ITestBaseShared { - - private Endpoint getMessagingRoute(AddressSpace addressSpace, boolean websocket, boolean ssl, boolean mqtt) throws Exception { - int port = ssl ? 5671 : 5672; - if (addressSpace.getSpec().getType().equals(AddressSpaceType.STANDARD.toString()) && mqtt) { - port = ssl ? 8883 : 1883; - } - return new Endpoint(String.format("%s-%s.%s.svc.cluster.local", - (addressSpace.getSpec().getType().equals(AddressSpaceType.STANDARD.toString()) && mqtt) ? "mqtt" : "messaging", - AddressSpaceUtils.getAddressSpaceInfraUuid(addressSpace), - environment.namespace()), - websocket && addressSpace.getSpec().getType().equals(AddressSpaceType.STANDARD.toString()) ? 443 : port); - } - - protected void doBasicMessageTest(AbstractClient sender, AbstractClient receiver) throws Exception { - doBasicMessageTest(sender, receiver, false); - } - - protected void doBasicMessageTest(AbstractClient sender, AbstractClient receiver, boolean websocket) throws Exception { - int expectedMsgCount = 10; - - Address dest = new AddressBuilder() - .withNewMetadata() - .withNamespace(getSharedAddressSpace().getMetadata().getNamespace()) - .withName(AddressUtils.generateAddressMetadataName(getSharedAddressSpace(), "message-basic-" + ClientType.getAddressName(sender) + (websocket ? "-ws" : ""))) - .endMetadata() - .withNewSpec() - .withType("queue") - .withAddress("message-basic-" + ClientType.getAddressName(sender) + (websocket ? "-ws" : "")) - .withPlan(getDefaultPlan(AddressType.QUEUE)) - .endSpec() - .build(); - resourcesManager.setAddresses(dest); - - ExternalMessagingClient senderClient = new ExternalMessagingClient() - .withClientEngine(sender) - .withMessagingRoute(getMessagingRoute(getSharedAddressSpace(), websocket, true, false)) - .withAddress(dest) - .withCredentials(defaultCredentials) - .withCount(expectedMsgCount) - .withMessageBody("msg no. %d") - .withTimeout(30) - .withAdditionalArgument(ClientArgument.CONN_WEB_SOCKET, websocket) - .withAdditionalArgument(ClientArgument.CONN_WEB_SOCKET_PROTOCOLS, getSharedAddressSpace().getSpec().getType().equals(AddressSpaceType.STANDARD.toString()) ? "binary" : ""); - - ExternalMessagingClient receiverClient = new ExternalMessagingClient() - .withClientEngine(receiver) - .withMessagingRoute(getMessagingRoute(getSharedAddressSpace(), websocket, true, false)) - .withAddress(dest) - .withCredentials(defaultCredentials) - .withCount(expectedMsgCount) - .withTimeout(30) - .withAdditionalArgument(ClientArgument.CONN_WEB_SOCKET, websocket) - .withAdditionalArgument(ClientArgument.CONN_WEB_SOCKET_PROTOCOLS, getSharedAddressSpace().getSpec().getType().equals(AddressSpaceType.STANDARD.toString()) ? "binary" : ""); - - assertTrue(senderClient.run()); - assertTrue(receiverClient.run()); - - assertEquals(expectedMsgCount, senderClient.getMessages().size(), - String.format("Expected %d sent messages", expectedMsgCount)); - assertEquals(expectedMsgCount, receiverClient.getMessages().size(), - String.format("Expected %d received messages", expectedMsgCount)); - } - - protected void doMqttMessageTest() throws Exception { - int expectedMsgCount = 10; - AbstractClient sender = new PahoMQTTClientSender(); - AbstractClient receiver = new PahoMQTTClientReceiver(); - - Address dest = new AddressBuilder() - .withNewMetadata() - .withNamespace(getSharedAddressSpace().getMetadata().getNamespace()) - .withName(AddressUtils.generateAddressMetadataName(getSharedAddressSpace(), "basic-mqtt" + ClientType.getAddressName(sender))) - .endMetadata() - .withNewSpec() - .withType("topic") - .withAddress("basic-mqtt" + ClientType.getAddressName(sender)) - .withPlan(getSharedAddressSpace().getSpec().getType().equals(AddressSpaceType.STANDARD.toString()) ? DestinationPlan.STANDARD_LARGE_TOPIC : getDefaultPlan(AddressType.TOPIC)) - .endSpec() - .build(); - - resourcesManager.setAddresses(dest); - - ExternalMessagingClient senderClient = new ExternalMessagingClient() - .withClientEngine(sender) - .withMessagingRoute(getMessagingRoute(getSharedAddressSpace(), false, false, false)) - .withAddress(dest) - .withCredentials(defaultCredentials) - .withCount(expectedMsgCount) - .withMessageBody("msg no. %d") - .withTimeout(30); - - ExternalMessagingClient receiverClient = new ExternalMessagingClient() - .withClientEngine(receiver) - .withMessagingRoute(getMessagingRoute(getSharedAddressSpace(), false, false, false)) - .withAddress(dest) - .withCredentials(defaultCredentials) - .withCount(expectedMsgCount) - .withTimeout(40); - - Future recResult = receiverClient.runAsync(); - Thread.sleep(20_000); - - assertAll( - () -> assertTrue(senderClient.run(), "Producer failed, expected return code 0"), - () -> assertEquals(expectedMsgCount, senderClient.getMessages().size(), - String.format("Expected %d sent messages", expectedMsgCount))); - assertAll( - () -> assertTrue(recResult.get(), "Subscriber failed, expected return code 0"), - () -> assertEquals(expectedMsgCount, receiverClient.getMessages().size(), - String.format("Expected %d received messages", expectedMsgCount))); - } -} diff --git a/systemtests/src/test/java/io/enmasse/systemtest/bases/infra/InfraTestBase.java b/systemtests/src/test/java/io/enmasse/systemtest/bases/infra/InfraTestBase.java deleted file mode 100644 index 6e368c973d7..00000000000 --- a/systemtests/src/test/java/io/enmasse/systemtest/bases/infra/InfraTestBase.java +++ /dev/null @@ -1,209 +0,0 @@ -/* - * Copyright 2018, EnMasse authors. - * License: Apache License 2.0 (see the file LICENSE or http://apache.org/licenses/LICENSE-2.0.html). - */ -package io.enmasse.systemtest.bases.infra; - -import io.enmasse.address.model.AddressSpace; -import io.enmasse.admin.model.v1.AddressPlan; -import io.enmasse.systemtest.UserCredentials; -import io.enmasse.systemtest.bases.ITestBase; -import io.enmasse.systemtest.bases.TestBase; -import io.enmasse.systemtest.executor.ExecutionResultData; -import io.enmasse.systemtest.infra.InfraConfiguration; -import io.enmasse.systemtest.logs.CustomLogger; -import io.enmasse.systemtest.platform.KubeCMDClient; -import io.enmasse.systemtest.time.TimeoutBudget; -import io.enmasse.systemtest.utils.TestUtils; -import io.enmasse.user.model.v1.User; -import io.fabric8.kubernetes.api.model.Container; -import io.fabric8.kubernetes.api.model.PersistentVolumeClaim; -import io.fabric8.kubernetes.api.model.Pod; -import io.fabric8.kubernetes.api.model.PodTemplateSpec; -import io.fabric8.kubernetes.api.model.ResourceRequirements; -import io.fabric8.kubernetes.api.model.Toleration; -import io.fabric8.kubernetes.api.model.storage.StorageClass; -import org.opentest4j.AssertionFailedError; -import org.slf4j.Logger; - -import java.util.Arrays; -import java.util.List; -import java.util.Map; -import java.util.function.Supplier; -import java.util.stream.Collectors; - -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertTrue; - -public abstract class InfraTestBase extends TestBase implements ITestBase { - - private static final List resizingStorageProvisioners = Arrays.asList("kubernetes.io/aws-ebs", "kubernetes.io/gce-pd", - "kubernetes.io/azure-file", "kubernetes.io/azure-disk", "kubernetes.io/glusterfs", "kubernetes.io/cinder", - "kubernetes.io/portworx-volume", "kubernetes.io/rbd"); - private static Logger log = CustomLogger.getLogger(); - protected AddressPlan exampleAddressPlan; - protected AddressSpace exampleAddressSpace; - protected UserCredentials exampleUser = new UserCredentials("test", "test"); - - protected void assertBroker(InfraConfiguration brokerConfig) { - log.info("Checking broker infra"); - List brokerPods = TestUtils.listBrokerPods(kubernetes, exampleAddressSpace); - assertEquals(1, brokerPods.size()); - - Pod broker = brokerPods.stream().findFirst().get(); - - ResourceRequirements resources = broker.getSpec().getContainers().stream() - .filter(container -> container.getName().equals("broker")) - .findFirst() - .map(Container::getResources) - .get(); - assertEquals(brokerConfig.getMemory(), resources.getLimits().get("memory").getAmount(), - "Broker memory limit incorrect"); - assertEquals(brokerConfig.getMemory(), resources.getRequests().get("memory").getAmount(), - "Broker memory requests incorrect"); - if (brokerConfig.getCpu() != null) { - assertEquals(brokerConfig.getCpu(), resources.getLimits().get("cpu").getAmount(), - "Broker cpu limit incorrect"); - assertEquals(brokerConfig.getCpu(), resources.getRequests().get("cpu").getAmount(), - "Broker cpu requests incorrect"); - } - - if (brokerConfig.getBrokerStorage() != null) { - PersistentVolumeClaim brokerVolumeClaim = getBrokerPVCData(broker); - assertEquals(brokerConfig.getBrokerStorage(), brokerVolumeClaim.getSpec().getResources().getRequests().get("storage").getAmount(), - "Broker data storage request incorrect"); - } - - if (brokerConfig.getBrokerJavaOpts() != null) { - brokerPods.forEach(pod -> { - ExecutionResultData result = KubeCMDClient.runOnCluster("exec", pod.getMetadata().getName(), "-n", - pod.getMetadata().getNamespace(), "ps", "auxww"); - assertTrue(result.getRetCode(), result.getStdOut()); - assertTrue(result.getStdOut().contains(brokerConfig.getBrokerJavaOpts()), - "Unable to find expected java opts in process argument list: " + result.getStdOut()); - }); - } - - if (brokerConfig.getTemplateSpec() != null) { - assertTemplateSpec(broker, brokerConfig.getTemplateSpec()); - } - } - - protected void assertTemplateSpec(Pod pod, PodTemplateSpec templateSpec) { - if (templateSpec.getMetadata().getLabels() != null) { - for (Map.Entry labelPair : templateSpec.getMetadata().getLabels().entrySet()) { - assertEquals(labelPair.getValue(), pod.getMetadata().getLabels().get(labelPair.getKey()), "Labels do not match"); - } - } - - if (templateSpec.getSpec().getAffinity() != null) { - assertEquals(templateSpec.getSpec().getAffinity(), pod.getSpec().getAffinity(), "Affinity rules do not match"); - } - - if (templateSpec.getSpec().getPriorityClassName() != null) { - assertEquals(templateSpec.getSpec().getPriorityClassName(), pod.getSpec().getPriorityClassName(), "Priority class names do not match"); - } - - if (templateSpec.getSpec().getTolerations() != null) { - for (Toleration expected : templateSpec.getSpec().getTolerations()) { - boolean found = false; - for (Toleration actual : pod.getSpec().getTolerations()) { - if (actual.equals(expected)) { - found = true; - break; - } - } - assertTrue(found, "Did not find expected toleration " + expected); - } - } - - for (Container expectedContainer : templateSpec.getSpec().getContainers()) { - for (Container actualContainer : pod.getSpec().getContainers()) { - if (expectedContainer.getName().equals(actualContainer.getName())) { - assertEquals(expectedContainer.getResources(), actualContainer.getResources()); - } - } - } - } - - protected void assertAdminConsole(InfraConfiguration adminConfig) { - log.info("Checking admin console infra"); - List adminPods = TestUtils.listAdminConsolePods(kubernetes, exampleAddressSpace); - assertEquals(1, adminPods.size()); - - List adminResources = adminPods.stream().findFirst().get().getSpec().getContainers() - .stream().map(Container::getResources).collect(Collectors.toList()); - - for (ResourceRequirements requirements : adminResources) { - assertEquals(adminConfig.getMemory(), requirements.getLimits().get("memory").getAmount(), - "Admin console memory limit incorrect"); - assertEquals(adminConfig.getMemory(), requirements.getRequests().get("memory").getAmount(), - "Admin console memory requests incorrect"); - if (adminConfig.getCpu() != null) { - assertEquals(adminConfig.getCpu(), requirements.getLimits().get("cpu").getAmount(), - "Admin console cpu limit incorrect"); - assertEquals(adminConfig.getCpu(), requirements.getRequests().get("cpu").getAmount(), - "Admin console cpu requests incorrect"); - } - } - - if (adminConfig.getTemplateSpec() != null) { - assertTemplateSpec(adminPods.get(0), adminConfig.getTemplateSpec()); - } - } - - protected void waitUntilInfraReady(Supplier assertCall, TimeoutBudget timeout) throws InterruptedException { - log.info("Start waiting for infra ready"); - AssertionFailedError lastException = null; - while (!timeout.timeoutExpired()) { - try { - assertCall.get(); - log.info("assert infra ready succeed"); - return; - } catch (AssertionFailedError e) { - lastException = e; - } - log.debug("next iteration, remaining time: {}", timeout.timeLeft()); - Thread.sleep(5000); - } - log.error("Timeout assert infra expired"); - if (lastException != null) { - throw lastException; - } - } - - protected PersistentVolumeClaim getBrokerPVCData(Pod broker) { - String brokerVolumeClaimName = broker.getSpec().getVolumes().stream() - .filter(volume -> volume.getName().equals("data")) - .findFirst().get() - .getPersistentVolumeClaim().getClaimName(); - return TestUtils.listPersistentVolumeClaims(kubernetes, exampleAddressSpace).stream() - .filter(pvc -> pvc.getMetadata().getName().equals(brokerVolumeClaimName)) - .findFirst().get(); - } - - protected Boolean volumeResizingSupported() throws Exception { - List brokerPods = TestUtils.listBrokerPods(kubernetes, exampleAddressSpace); - assertEquals(1, brokerPods.size()); - Pod broker = brokerPods.stream().findFirst().get(); - PersistentVolumeClaim brokerVolumeClaim = getBrokerPVCData(broker); - String brokerStorageClassName = brokerVolumeClaim.getSpec().getStorageClassName(); - if (brokerStorageClassName != null) { - StorageClass brokerStorageClass = kubernetes.getStorageClass(brokerStorageClassName); - if (resizingStorageProvisioners.contains(brokerStorageClass.getProvisioner())) { - if (brokerStorageClass.getAllowVolumeExpansion() != null && brokerStorageClass.getAllowVolumeExpansion()) { - log.info("Testing broker volume resize because of {}:{}", brokerStorageClassName, brokerStorageClass.getProvisioner()); - return true; - } else { - log.info("Skipping broker volume resize due to allowVolumeExpansion in StorageClass {} disabled", brokerStorageClassName); - } - } else { - log.info("Skipping broker volume resize due to provisioner: {}", brokerStorageClass.getProvisioner()); - } - } else { - log.info("Skipping broker volume resize due to missing StorageClass name in PVC {}", brokerVolumeClaim.getMetadata().getName()); - } - return false; - } - -} diff --git a/systemtests/src/test/java/io/enmasse/systemtest/bases/mqtt/MqttPublishTestBase.java b/systemtests/src/test/java/io/enmasse/systemtest/bases/mqtt/MqttPublishTestBase.java deleted file mode 100644 index 6e04ec1a432..00000000000 --- a/systemtests/src/test/java/io/enmasse/systemtest/bases/mqtt/MqttPublishTestBase.java +++ /dev/null @@ -1,163 +0,0 @@ -/* - * Copyright 2019, EnMasse authors. - * License: Apache License 2.0 (see the file LICENSE or http://apache.org/licenses/LICENSE-2.0.html). - */ -package io.enmasse.systemtest.bases.mqtt; - -import static org.hamcrest.CoreMatchers.is; -import static org.hamcrest.MatcherAssert.assertThat; -import static org.junit.jupiter.api.Assertions.assertTrue; - -import java.nio.charset.StandardCharsets; -import java.util.List; -import java.util.UUID; -import java.util.concurrent.CompletableFuture; -import java.util.concurrent.TimeUnit; -import java.util.stream.Collectors; -import java.util.stream.Stream; - -import org.eclipse.paho.client.mqttv3.IMqttClient; -import org.eclipse.paho.client.mqttv3.MqttMessage; -import org.slf4j.Logger; - -import io.enmasse.address.model.Address; -import io.enmasse.address.model.AddressBuilder; -import io.enmasse.systemtest.bases.TestBase; -import io.enmasse.systemtest.bases.shared.ITestBaseShared; -import io.enmasse.systemtest.logs.CustomLogger; -import io.enmasse.systemtest.model.address.AddressType; -import io.enmasse.systemtest.mqtt.MqttClientFactory; -import io.enmasse.systemtest.mqtt.MqttClientFactory.Builder; -import io.enmasse.systemtest.mqtt.MqttUtils; -import io.enmasse.systemtest.utils.AddressUtils; - -public abstract class MqttPublishTestBase extends TestBase implements ITestBaseShared { - - private static final String MYTOPIC = "mytopic"; - private static final Logger log = CustomLogger.getLogger(); - - public void testPublishQoS0() throws Exception { - List messages = Stream.generate(MqttMessage::new).limit(3).collect(Collectors.toList()); - messages.forEach(m -> { - m.setPayload(UUID.randomUUID().toString().getBytes(StandardCharsets.UTF_8)); - m.setQos(0); - }); - - this.publish(messages, 0); - } - - public void testPublishQoS1() throws Exception { - List messages = Stream.generate(MqttMessage::new).limit(3).collect(Collectors.toList()); - messages.forEach(m -> { - m.setPayload(UUID.randomUUID().toString().getBytes(StandardCharsets.UTF_8)); - m.setQos(1); - }); - - this.publish(messages, 1); - } - - public void testPublishQoS2() throws Exception { - List messages = Stream.generate(MqttMessage::new).limit(3).collect(Collectors.toList()); - messages.forEach(m -> { - m.setPayload(UUID.randomUUID().toString().getBytes(StandardCharsets.UTF_8)); - m.setQos(2); - }); - - this.publish(messages, 2); - } - - public void testRetainedMessages() throws Exception { - Address topic = new AddressBuilder() - .withNewMetadata() - .withNamespace(getSharedAddressSpace().getMetadata().getNamespace()) - .withName(AddressUtils.generateAddressMetadataName(getSharedAddressSpace(), "test-topic1")) - .endMetadata() - .withNewSpec() - .withType("topic") - .withAddress("test-topic1") - .withPlan(topicPlan()) - .endSpec() - .build(); - resourcesManager.setAddresses(topic); - - MqttMessage retainedMessage = new MqttMessage(); - retainedMessage.setQos(1); - retainedMessage.setPayload("retained-message".getBytes(StandardCharsets.UTF_8)); - retainedMessage.setId(1); - retainedMessage.setRetained(true); - - // send retained message to the topic - Builder publisherBuilder = new MqttClientFactory.Builder() - .usernameAndPassword(defaultCredentials); - customizeClient(publisherBuilder); - try(IMqttClient publisher = publisherBuilder.create()) { - publisher.connect(); - publisher.publish(topic.getSpec().getAddress(), retainedMessage); - publisher.disconnect(); - } - - // each client which will subscribe to the topic should receive retained message! - Builder subscriberBuilder = new MqttClientFactory.Builder() - .usernameAndPassword(defaultCredentials); - customizeClient(subscriberBuilder); - try(IMqttClient subscriber = subscriberBuilder.create()) { - subscriber.connect(); - CompletableFuture messageFuture = new CompletableFuture<>(); - subscriber.subscribe(topic.getSpec().getAddress(), (topic1, message) -> messageFuture.complete(message)); - MqttMessage receivedMessage = messageFuture.get(1, TimeUnit.MINUTES); - assertTrue(receivedMessage.isRetained(), "Retained message expected"); - - subscriber.disconnect(); - } - } - - private void publish(List messages, int subscriberQos) throws Exception { - - Address dest = new AddressBuilder() - .withNewMetadata() - .withNamespace(getSharedAddressSpace().getMetadata().getNamespace()) - .withName(AddressUtils.generateAddressMetadataName(getSharedAddressSpace(), MYTOPIC)) - .endMetadata() - .withNewSpec() - .withType("topic") - .withAddress(MYTOPIC) - .withPlan(topicPlan()) - .endSpec() - .build(); - resourcesManager.setAddresses(dest); - - Builder clientBuilder = new MqttClientFactory.Builder() - .usernameAndPassword(defaultCredentials) - .mqttConnectionOptions(options -> { - options.setConnectionTimeout(options.getConnectionTimeout() * 2); // Default is 30 seconds, increase it to 1 min. - options.setAutomaticReconnect(true); - }); - customizeClient(clientBuilder); - - try (IMqttClient client = clientBuilder.create()) { - log.info("Connecting"); - client.connect(); - - List> receiveFutures = MqttUtils.subscribeAndReceiveMessages(client, dest.getSpec().getAddress(), messages.size(), subscriberQos); - List> publishFutures = MqttUtils.publish(client, dest.getSpec().getAddress(), messages); - - int publishCount = MqttUtils.awaitAndReturnCode(publishFutures, 1, TimeUnit.MINUTES); - assertThat("Incorrect count of messages published", - publishCount, is(messages.size())); - - int receivedCount = MqttUtils.awaitAndReturnCode(receiveFutures, 2, TimeUnit.MINUTES); - assertThat("Incorrect count of messages received", - receivedCount, is(messages.size())); - } - - } - - protected void customizeClient(Builder mqttClientBuilder) { - //optional to implement - } - - protected String topicPlan() { - return getDefaultPlan(AddressType.TOPIC); - } - -} diff --git a/systemtests/src/test/java/io/enmasse/systemtest/bases/olm/OLMTestBase.java b/systemtests/src/test/java/io/enmasse/systemtest/bases/olm/OLMTestBase.java deleted file mode 100644 index 2300d84d90d..00000000000 --- a/systemtests/src/test/java/io/enmasse/systemtest/bases/olm/OLMTestBase.java +++ /dev/null @@ -1,199 +0,0 @@ -/* - * Copyright 2019, EnMasse authors. - * License: Apache License 2.0 (see the file LICENSE or http://apache.org/licenses/LICENSE-2.0.html). - */ -package io.enmasse.systemtest.bases.olm; - -import io.enmasse.address.model.Address; -import io.enmasse.address.model.AddressSpace; -import io.enmasse.admin.model.v1.AuthenticationService; -import io.enmasse.systemtest.amqp.AmqpClient; -import io.enmasse.systemtest.amqp.AmqpConnectOptions; -import io.enmasse.systemtest.amqp.QueueTerminusFactory; -import io.enmasse.systemtest.bases.TestBase; -import io.enmasse.systemtest.bases.isolated.ITestIsolatedStandard; -import io.enmasse.systemtest.executor.ExecutionResultData; -import io.enmasse.systemtest.logs.CustomLogger; -import io.enmasse.systemtest.logs.GlobalLogCollector; -import io.enmasse.systemtest.platform.KubeCMDClient; -import io.enmasse.systemtest.platform.Kubernetes; -import io.enmasse.systemtest.platform.cluster.CRCCluster; -import io.enmasse.systemtest.time.TimeoutBudget; -import io.enmasse.systemtest.utils.AddressSpaceUtils; -import io.enmasse.systemtest.utils.AddressUtils; -import io.enmasse.systemtest.utils.AuthServiceUtils; -import io.enmasse.systemtest.utils.TestUtils; -import io.vertx.core.json.JsonArray; -import io.vertx.core.json.JsonObject; -import io.vertx.proton.ProtonQoS; -import org.apache.qpid.proton.message.Message; -import org.junit.jupiter.api.AfterAll; -import org.junit.jupiter.api.AfterEach; -import org.junit.jupiter.api.Assertions; -import org.junit.jupiter.api.BeforeAll; -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.extension.ExtensionContext; -import org.slf4j.Logger; - -import java.io.IOException; -import java.nio.charset.StandardCharsets; -import java.nio.file.Path; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Base64; -import java.util.List; -import java.util.Set; -import java.util.concurrent.Future; -import java.util.concurrent.TimeUnit; -import java.util.stream.Collectors; - -import static org.junit.jupiter.api.Assertions.assertEquals; - -public abstract class OLMTestBase extends TestBase implements ITestIsolatedStandard { - private static Logger log = CustomLogger.getLogger(); - private Exception ex = null; //TODO remove it after upgrade to surefire plugin 3.0.0-M5 - - private static final int CR_TIMEOUT_MILLIS = 30000; - private List exampleResources = new ArrayList<>(); - - protected abstract String getInstallationNamespace(); - - protected abstract String getDifferentAddressSpaceNamespace(); - - @BeforeAll - void setupExampleResources() throws Exception { - try { //TODO remove it after upgrade to surefire plugin 3.0.0-M5 - ExecutionResultData result = KubeCMDClient.runOnCluster("get", "csv", "-n", getInstallationNamespace(), "-o", "json", "-l", "app=enmasse"); - JsonObject csvList = new JsonObject(result.getStdOut()); - JsonObject csv = csvList.getJsonArray("items").getJsonObject(0); - String almExamples = csv.getJsonObject("metadata").getJsonObject("annotations").getString("alm-examples"); - JsonArray examples = new JsonArray(almExamples); - exampleResources = examples.stream().map(o -> (JsonObject) o).collect(Collectors.toList()); - - Set infraKinds = Set.of("StandardInfraConfig", "BrokeredInfraConfig", "AddressPlan", "AddressSpacePlan", "AuthenticationService"); - - for (JsonObject example : exampleResources) { - LOGGER.info("Example: {}", example); - String kind = example.getString("kind"); - if (kind.equals("AuthenticationService") && kubernetes.getCluster() instanceof CRCCluster) { - log.info("Creating standard-authservice with no persistence because of CRC cluster"); - AuthenticationService authService = AuthServiceUtils.createStandardAuthServiceObject("standard-authservice", false); - authService.getMetadata().setNamespace(getInstallationNamespace()); - kubernetes.getAuthenticationServiceClient(getInstallationNamespace()).create(authService); - } else if (infraKinds.contains(kind)) { - log.info("Creating {}", example.toString()); - createCR(getInstallationNamespace(), example); - } - } - TestUtils.waitUntilDeployed(getInstallationNamespace()); - TestUtils.waitForPodReady("standard-authservice", getInstallationNamespace()); - TestUtils.waitForSchemaInSync("standard-small"); - var addressSpacePlanClient = kubernetes.getAddressSpacePlanClient(getInstallationNamespace()); - TestUtils.waitUntilCondition("AddressSpacePlan standard-small visible", - phase -> addressSpacePlanClient.withName("standard-small").get() != null, - new TimeoutBudget(2, TimeUnit.MINUTES)); - } catch (Exception exception){ //TODO remove it after upgrade to surefire plugin 3.0.0-M5 - ex = exception; - } - } - - @BeforeEach - void removeMe() throws Exception { //TODO remove it after upgrade to surefire plugin 3.0.0-M5 - if (ex != null) { - throw ex; - } - } - - @AfterEach - void deleteAddressSpace(ExtensionContext context) throws Exception { - if (context.getExecutionException().isPresent()) { //test failed - Path path = TestUtils.getFailedTestLogsPath(context); - GlobalLogCollector collector = new GlobalLogCollector(Kubernetes.getInstance(), path, getInstallationNamespace()); - collector.collectLogsOfPodsInNamespace(getInstallationNamespace()); - collector.collectEvents(getInstallationNamespace()); - } - for (String namespace : Arrays.asList(getInstallationNamespace(), getDifferentAddressSpaceNamespace())) { - AddressSpace addressSpace = kubernetes.getAddressSpaceClient(namespace).withName("myspace").get(); - if (addressSpace != null) { - resourcesManager.deleteAddressSpace(addressSpace); - } - } - } - - @AfterAll - void teardownExampleResources() throws Exception { - ex = null; //TODO remove it after upgrade to surefire plugin 3.0.0-M5 - if (!environment.skipCleanup()) { - for (JsonObject example : exampleResources) { - log.info("Deleting {}", example.toString()); - KubeCMDClient.deleteCR(getInstallationNamespace(), example.toString(), CR_TIMEOUT_MILLIS); - } - kubernetes.deleteNamespace(getDifferentAddressSpaceNamespace()); - } - } - - protected void doTestExampleResourcesSameNamespaceAsOperator() throws Exception { - createdUserResources(getInstallationNamespace()); - } - - protected void doTestExampleResourcesDifferentNamespaceThanOperator() throws Exception { - kubernetes.createNamespace(getDifferentAddressSpaceNamespace()); - createdUserResources(getDifferentAddressSpaceNamespace()); - } - - private void createdUserResources(String addressSpaceNamespace) throws Exception { - JsonObject exampleAddressSpace = exampleResources.stream() - .filter(example -> example.getString("kind").equals("AddressSpace")) - .findFirst() - .orElseThrow(()-> new IllegalStateException("Example resources don't contain an address space")); - log.info("Creating {}", exampleAddressSpace.getString("kind")); - createCR(addressSpaceNamespace, exampleAddressSpace); - var client = kubernetes.getAddressSpaceClient(addressSpaceNamespace); - TestUtils.waitUntilCondition("Address space visible", - phase -> client.withName("myspace").get() != null,new TimeoutBudget(30, TimeUnit.SECONDS)); - resourcesManager.waitForAddressSpaceReady(client.withName("myspace").get()); - - - for(JsonObject example : exampleResources) { - String kind = example.getString("kind"); - if(kind.equals("Address") || kind.equals("MessagingUser")) { - log.info("Creating {}", kind); - createCR(addressSpaceNamespace, example); - } - } - Thread.sleep(10_000); - TestUtils.waitUntilDeployed(getInstallationNamespace()); - var addressClient = kubernetes.getAddressClient(addressSpaceNamespace); - TestUtils.waitUntilCondition("Address visible", - phase -> addressClient.withName("myspace.myqueue").get() != null, - new TimeoutBudget(30, TimeUnit.SECONDS)); - AddressUtils.waitForDestinationsReady(addressClient.withName("myspace.myqueue").get()); - - // Test basic messages - AddressSpace exampleSpace = kubernetes.getAddressSpaceClient(addressSpaceNamespace).withName("myspace").get(); - Address exampleAddress = kubernetes.getAddressClient(addressSpaceNamespace).withName("myspace.myqueue").get(); - - AmqpClient amqpClient = resourcesManager.getAmqpClientFactory().createClient(new AmqpConnectOptions() - .setTerminusFactory(new QueueTerminusFactory()) - .setQos(ProtonQoS.AT_LEAST_ONCE) - .setCert(new String(Base64.getDecoder().decode(exampleSpace.getStatus().getCaCert()), StandardCharsets.UTF_8)) - .setEndpoint(kubernetes.getExternalEndpoint("messaging-" + AddressSpaceUtils.getAddressSpaceInfraUuid(exampleSpace), getInstallationNamespace())) - .setUsername("user") - .setPassword("enmasse")); - - int messageCount = 10; - Future sent = amqpClient.sendMessages(exampleAddress.getSpec().getAddress(), TestUtils.generateMessages(messageCount)); - assertEquals(messageCount, sent.get(1, TimeUnit.MINUTES).intValue(), "Incorrect count of messages send"); - - Future> received = amqpClient.recvMessages(exampleAddress.getSpec().getAddress(), messageCount); - assertEquals(messageCount, received.get(1, TimeUnit.MINUTES).size(), "Incorrect count of messages received"); - } - - private void createCR(String namespace, JsonObject cr) throws IOException { - ExecutionResultData res = KubeCMDClient.createCR(namespace, cr.toString(), CR_TIMEOUT_MILLIS); - if(!res.getRetCode()) { - Assertions.fail(res.getStdErr()); - } - } - -} diff --git a/systemtests/src/test/java/io/enmasse/systemtest/bases/plans/PlansTestBase.java b/systemtests/src/test/java/io/enmasse/systemtest/bases/plans/PlansTestBase.java deleted file mode 100644 index 318ab8ad12b..00000000000 --- a/systemtests/src/test/java/io/enmasse/systemtest/bases/plans/PlansTestBase.java +++ /dev/null @@ -1,257 +0,0 @@ -/* - * Copyright 2020, EnMasse authors. - * License: Apache License 2.0 (see the file LICENSE or http://apache.org/licenses/LICENSE-2.0.html). - */ - -package io.enmasse.systemtest.bases.plans; - -import io.enmasse.address.model.Address; -import io.enmasse.address.model.AddressBuilder; -import io.enmasse.address.model.AddressSpace; -import io.enmasse.admin.model.v1.AddressPlan; -import io.enmasse.systemtest.UserCredentials; -import io.enmasse.systemtest.amqp.AmqpClient; -import io.enmasse.systemtest.amqp.AmqpClientFactory; -import io.enmasse.systemtest.bases.TestBase; -import io.enmasse.systemtest.bases.isolated.ITestBaseIsolated; -import io.enmasse.systemtest.broker.ArtemisUtils; -import io.enmasse.systemtest.logs.CustomLogger; -import io.enmasse.systemtest.platform.Kubernetes; -import io.enmasse.systemtest.time.TimeoutBudget; -import io.enmasse.systemtest.utils.AddressUtils; -import io.enmasse.systemtest.utils.TestUtils; -import org.apache.qpid.proton.amqp.Binary; -import org.apache.qpid.proton.amqp.messaging.AmqpValue; -import org.apache.qpid.proton.amqp.messaging.Data; -import org.apache.qpid.proton.amqp.transport.DeliveryState; -import org.apache.qpid.proton.message.Message; -import org.hamcrest.Description; -import org.hamcrest.Matcher; -import org.hamcrest.StringDescription; -import org.hamcrest.TypeSafeMatcher; -import org.slf4j.Logger; - -import java.time.Duration; -import java.util.*; -import java.util.concurrent.TimeUnit; -import java.util.concurrent.atomic.AtomicReference; -import java.util.stream.Collectors; -import java.util.stream.Stream; - -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertTrue; - -public class PlansTestBase extends TestBase { - private static Logger log = CustomLogger.getLogger(); - - - public static Matcher
assertAddressStatusNotReady(final String messageContains) { - return PlansTestBase.assertAddressStatus(false, Optional.empty(), Optional.of(messageContains)); - } - - public static Matcher
assertAddressStatusReady(String actualPlan) { - return PlansTestBase.assertAddressStatus(true, Optional.of(actualPlan), Optional.empty()); - } - - public static Matcher
assertAddressStatus(final boolean ready, final Optional actualPlan, final Optional messageContains) { - return new TypeSafeMatcher<>() { - @Override - public void describeTo(Description description) { - description.appendText("should match ready ").appendValue(ready); - actualPlan.ifPresent(s -> description.appendText("should match plan ").appendValue(s)); - messageContains.ifPresent(s -> description.appendText("should status should contain ").appendValue(s)); - } - - @Override - protected void describeMismatchSafely(Address a, Description description) { - if (a.getStatus() == null) { - description.appendText("address.status is absent"); - } - if (ready != a.getStatus().isReady()) { - description.appendText("ready was ").appendValue(a.getStatus().isReady()); - } - if (actualPlan.isPresent()) { - if (a.getStatus().getPlanStatus() == null) { - description.appendText("address.status.planStatus is absent"); - } else if (!actualPlan.get().equals(a.getStatus().getPlanStatus().getName())) { - description.appendText("actual plan was ").appendValue(a.getStatus().getPlanStatus().getName()); - } - } - if (messageContains.isPresent()) { - String cc = String.join(":", a.getStatus().getMessages()); - description.appendText("messages were: ").appendValue(cc); - } - - } - - @Override - public boolean matchesSafely(Address a) { - if (a.getStatus() == null) { - return false; - } - if (ready != a.getStatus().isReady()) { - return false; - } - if (actualPlan.isPresent() && !actualPlan.get().equals(a.getStatus().getPlanStatus().getName())) { - return false; - } - if (messageContains.isPresent()) { - Optional match = a.getStatus().getMessages().stream().filter(m -> m.contains(messageContains.get())).findFirst(); - return match.isPresent(); - } else { - return true; - } - } - }; - } - - public void doTestUnknownAddressPlan(AddressSpace addressSpace, List stageHolders) throws Exception { - - resourcesManager.createAddressSpace(addressSpace); - - do { - log.info("Starting stage"); - - List stages = stageHolders.stream().filter(StageHolder::hasStage).map(StageHolder::popStage).collect(Collectors.toList()); - - if (stages.isEmpty()) { - break; - } - - stages.stream().map(StageHolder.Stage::getAddress).forEach(address -> { - Kubernetes.getInstance().getAddressClient(address.getMetadata().getNamespace()).createOrReplace(address); - }); - - stages.forEach(s -> { - AtomicReference lastMatch = new AtomicReference<>(); - - boolean rv = TestUtils.waitUntilCondition(() -> { - Address current = resourcesManager.getAddress(s.getAddress().getMetadata().getNamespace(), s.getAddress()); - Matcher
matcher = s.getMatcher(); - boolean matches = matcher.matches(current); - StringDescription desc = new StringDescription(); - matcher.describeMismatch(current, desc); - lastMatch.set(desc.toString()); - if (matches) { - log.info("Address {} is now in expected state: {}", current.getMetadata().getName(), current.getStatus()); - } else { - log.info("Address {} is not in expected state: {} {}", current.getMetadata().getName(), lastMatch, current.getStatus()); - } - return matches; - }, Duration.ofMinutes(2), Duration.ofSeconds(10)); - assertTrue(rv, String.format("address %s did not reach desired state : %s", s.getAddress().getMetadata().getName(), lastMatch)); - }); - } while(true); - } - - public void doTestUpdatePlanBrokerCreditChangesPerAddressMaxSize(AddressSpace addressSpace, Address queueDest, AddressPlan phase1, AddressPlan phase2, AddressPlan redefinedPhase2, AmqpClientFactory amqpClientFactory) throws Exception { - //get destination - Address queue = kubernetes.getAddressClient(addressSpace.getMetadata().getNamespace()).withName(queueDest.getMetadata().getName()).get(); - - String assertMessage = "Queue plan wasn't set properly"; - assertEquals(queue.getSpec().getPlan(), - phase1.getMetadata().getName(), assertMessage); - - //Send messages to ensure queue fills up - UserCredentials user = new UserCredentials("test-newplan-name", "test_newplan_password"); - resourcesManager.createOrUpdateUser(addressSpace, user); - - AmqpClient client = amqpClientFactory.createQueueClient(addressSpace); - client.getConnectOptions().setCredentials(user); - byte[] bytes = new byte[1024 * 100]; - Random random = new Random(); - Message message = Message.Factory.create(); - random.nextBytes(bytes); - message.setBody(new AmqpValue(new Data(new Binary(bytes)))); - message.setAddress(queue.getSpec().getAddress()); - message.setDurable(true); - - Stream messageStream = Stream.generate(() -> message); - int messagesSent = client.sendMessagesCheckDelivery(queue.getSpec().getAddress(), messageStream::iterator, - protonDelivery -> protonDelivery.remotelySettled() && protonDelivery.getRemoteState().getType().equals(DeliveryState.DeliveryStateType.Rejected)) - .get(5, TimeUnit.MINUTES); - - assertTrue(messagesSent > 0, "Verify a few messages were sent before queue fills up"); - - //Verify maxSizeBytes are set - assertMaxSizeBytes(addressSpace, queue, 524288); - - //Redefine address to use next plan - Address largeQueue = new AddressBuilder(queue).editSpec().withPlan(phase2.getMetadata().getName()).endSpec().build(); - ITestBaseIsolated.isolatedResourcesManager.replaceAddress(largeQueue); - AddressUtils.waitForDestinationsReady(new TimeoutBudget(5, TimeUnit.MINUTES), largeQueue); - awaitPlanStatusResourceSync(addressSpace, largeQueue, phase2); - assertMaxSizeBytes(addressSpace, queue, 734003); - - //Redefine plan to to have more credit - ITestBaseIsolated.isolatedResourcesManager.replaceAddressPlan(redefinedPhase2); - AddressUtils.waitForDestinationsReady(new TimeoutBudget(5, TimeUnit.MINUTES), largeQueue); - awaitPlanStatusResourceSync(addressSpace, largeQueue, redefinedPhase2); - assertMaxSizeBytes(addressSpace, queue, 943718); - } - - public void awaitPlanStatusResourceSync(AddressSpace addressSpace, Address dest, AddressPlan plan) { - TestUtils.waitUntilCondition(() -> { - Address a = kubernetes.getAddressClient(addressSpace.getMetadata().getNamespace()).withName(dest.getMetadata().getName()).get(); - return a.getStatus().getPlanStatus() != null && a.getStatus().getPlanStatus().getResources().containsKey("broker") && - a.getStatus().getPlanStatus().getResources().get("broker").equals(plan.getResources().get("broker")); - }, Duration.ofMinutes(1), Duration.ofSeconds(1)); - } - - public void assertMaxSizeBytes(AddressSpace addressSpace, Address queue, Integer expected) throws Exception { - Map addressSettings = ArtemisUtils.getAddressSettings(kubernetes, addressSpace, queue.getSpec().getAddress()); - assertEquals(expected, (Integer) addressSettings.get("maxSizeBytes"), "maxSizeBytes should be set"); - } - - public class StageHolder { - private final AddressSpace addressSpace; - private final String addressName; - private final List stages = new ArrayList<>(); - - public class Stage { - private final String plan; - private final Matcher
matcher; - - Stage(Matcher
matcher, String plan) { - this.plan = plan; - this.matcher = matcher; - } - - public Address getAddress() { - return new AddressBuilder() - .withNewMetadata() - .withNamespace(addressSpace.getMetadata().getNamespace()) - .withName(AddressUtils.generateAddressMetadataName(addressSpace, addressName)) - .endMetadata() - .withNewSpec() - .withType("queue") - .withAddress(StageHolder.this.addressName) - .withPlan(this.plan) - .endSpec() - .build(); - } - - public Matcher
getMatcher() { - return matcher; - } - } - - public StageHolder(AddressSpace addressSpace, String addressName) { - this.addressSpace = addressSpace; - this.addressName = addressName; - } - - public StageHolder addStage(String plan, Matcher
addressMatcher) { - stages.add(new Stage(addressMatcher, plan)); - return this; - } - - public boolean hasStage() { - return !stages.isEmpty(); - } - - public Stage popStage() { - return stages.remove(0); - } - } -} diff --git a/systemtests/src/test/java/io/enmasse/systemtest/bases/soak/SoakTestBase.java b/systemtests/src/test/java/io/enmasse/systemtest/bases/soak/SoakTestBase.java deleted file mode 100644 index 511e6506712..00000000000 --- a/systemtests/src/test/java/io/enmasse/systemtest/bases/soak/SoakTestBase.java +++ /dev/null @@ -1,376 +0,0 @@ -/* - * Copyright 2019, EnMasse authors. - * License: Apache License 2.0 (see the file LICENSE or http://apache.org/licenses/LICENSE-2.0.html). - */ -package io.enmasse.systemtest.bases.soak; - -import io.enmasse.address.model.Address; -import io.enmasse.address.model.AddressBuilder; -import io.enmasse.address.model.AddressSpace; -import io.enmasse.address.model.AddressSpaceBuilder; -import io.enmasse.systemtest.SysytemTestsErrorCollector; -import io.enmasse.systemtest.UserCredentials; -import io.enmasse.systemtest.amqp.AmqpClient; -import io.enmasse.systemtest.bases.TestBase; -import io.enmasse.systemtest.bases.ThrowableRunner; -import io.enmasse.systemtest.logs.CustomLogger; -import io.enmasse.systemtest.model.address.AddressType; -import io.enmasse.systemtest.model.addressspace.AddressSpaceType; -import io.enmasse.systemtest.shared.standard.QueueTest; -import io.enmasse.systemtest.shared.standard.TopicTest; -import io.enmasse.systemtest.utils.AddressUtils; -import io.enmasse.systemtest.utils.TestUtils; -import org.apache.qpid.proton.message.Message; -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Tag; -import org.slf4j.Logger; - -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Date; -import java.util.HashMap; -import java.util.LinkedList; -import java.util.List; -import java.util.Map; -import java.util.concurrent.Future; -import java.util.concurrent.TimeUnit; -import java.util.stream.Collectors; -import java.util.stream.IntStream; - -import static io.enmasse.systemtest.TestTag.SOAK; -import static org.hamcrest.CoreMatchers.is; - -@Tag(SOAK) -public abstract class SoakTestBase extends TestBase { - private static Logger log = CustomLogger.getLogger(); - private ArrayList clients = new ArrayList<>(); - private SysytemTestsErrorCollector collector = new SysytemTestsErrorCollector(); - - @BeforeEach - void setupMarathonTests() { - collector.clear(); - } - - //======================================================================================================== - // Runner tests methods - //======================================================================================================== - - protected void runTestInLoop(int durationMinutes, ThrowableRunner test) throws Exception { - log.info(String.format("Starting test running for %d minutes at %s", - durationMinutes, new Date().toString())); - int fails = 0; - int limit = 10; - int i = 0; - for (long stop = System.nanoTime() + TimeUnit.MINUTES.toNanos(durationMinutes); stop > System.nanoTime(); ) { - try { - log.info("*********************************** Test run {} ***********************************", ++i); - test.run(); - fails = 0; - } catch (Exception ex) { - log.warn("Test run {} failed with: {}", i, ex.getMessage()); - collector.addError(ex); - if (++fails >= limit) { - throw new IllegalStateException(String.format("Test failed: %d times in a row: %s", fails, collector.toString())); - } - } finally { - closeClients(); - log.info("***********************************************************************************"); - Thread.sleep(60_000); - } - } - if (!collector.verify()) { - throw new IllegalStateException(String.format("Test failed with these exceptions: %s", collector.toString())); - } - } - - private void closeClients() { - for (AmqpClient client : clients) { - try { - client.close(); - log.info("Client is closed."); - } catch (Exception ex) { - collector.addError(ex); - } - } - clients.clear(); - } - - //======================================================================================================== - // Tests methods - //======================================================================================================== - protected void doTestQueueSendReceiveLong(AddressSpace addressSpace) throws Exception { - resourcesManager.createAddressSpace(addressSpace); - - int msgCount = 1000; - int queueCount = 10; - int senderCount = 10; - int recvCount = 20; - - List
queueList = new ArrayList<>(); - - //create queues - for (int i = 0; i < queueCount; i++) { - queueList.add(new AddressBuilder() - .withNewMetadata() - .withNamespace(addressSpace.getMetadata().getNamespace()) - .withName(AddressUtils.generateAddressMetadataName(addressSpace, "queue-sendreceive-" + i)) - .endMetadata() - .withNewSpec() - .withType("queue") - .withAddress("queue-sendreceive-" + i) - .withPlan(getDefaultPlan(AddressType.QUEUE)) - .endSpec() - .build()); - } - resourcesManager.setAddresses(queueList.toArray(new Address[0])); - - List msgBatch = TestUtils.generateMessages(msgCount); - - UserCredentials credentials = new UserCredentials("test", "test"); - resourcesManager.createOrUpdateUser(addressSpace, credentials); - - runTestInLoop(30, () -> { - //create client - AmqpClient client = resourcesManager.getAmqpClientFactory().createQueueClient(addressSpace); - client.getConnectOptions().setCredentials(credentials); - clients.add(client); - - //attach receivers - List>> recvResults = new ArrayList<>(); - for (int i = 0; i < recvCount / 2; i++) { - recvResults.add(client.recvMessages(queueList.get(i).getSpec().getAddress(), msgCount / 2)); - recvResults.add(client.recvMessages(queueList.get(i).getSpec().getAddress(), msgCount / 2)); - } - - //attach senders - for (int i = 0; i < senderCount; i++) { - collector.checkThat(client.sendMessages(queueList.get(i).getSpec().getAddress(), msgBatch).get(2, TimeUnit.MINUTES), is(msgBatch.size())); - } - - //check received messages - for (int i = 0; i < recvCount; i++) { - collector.checkThat(recvResults.get(i).get().size(), is(msgCount / 2)); - } - }); - } - - protected void doTestAuthSendReceiveLong(AddressSpace addressSpace) throws Exception { - log.info("testAuthSendReceiveLong start"); - resourcesManager.createAddressSpace(addressSpace); - log.info("Address space '{}'created", addressSpace); - - Address queue = new AddressBuilder() - .withNewMetadata() - .withNamespace(addressSpace.getMetadata().getNamespace()) - .withName(AddressUtils.generateAddressMetadataName(addressSpace, "test-auth-queue")) - .endMetadata() - .withNewSpec() - .withType("queue") - .withAddress("test-auth-queue") - .withPlan(getDefaultPlan(AddressType.QUEUE)) - .endSpec() - .build(); - Address topic = new AddressBuilder() - .withNewMetadata() - .withNamespace(addressSpace.getMetadata().getNamespace()) - .withName(AddressUtils.generateAddressMetadataName(addressSpace, "test-auth-topic")) - .endMetadata() - .withNewSpec() - .withType("topic") - .withAddress("test-auth-topic") - .withPlan(getDefaultPlan(AddressType.TOPIC)) - .endSpec() - .build(); - resourcesManager.setAddresses(queue, topic); - log.info("Addresses '{}', '{}' created", queue.getSpec().getAddress(), topic.getSpec().getAddress()); - - UserCredentials user = new UserCredentials("test-user", "test-user"); - resourcesManager.createOrUpdateUser(addressSpace, user); - - runTestInLoop(30, () -> { - log.info("Start test loop basic auth tests"); - getClientUtils().assertCanConnect(addressSpace, user, Arrays.asList(queue, topic), resourcesManager); - getClientUtils().assertCannotConnect(addressSpace, new UserCredentials("nobody", "nobody"), Arrays.asList(queue, topic), resourcesManager); - }); - log.info("testAuthSendReceiveLong finished"); - } - - protected void doTestTopicPubSubLong(AddressSpace addressSpace) throws Exception { - resourcesManager.createAddressSpace(addressSpace); - - int msgCount = 1000; - int topicCount = 10; - - List
topicList = new ArrayList<>(); - - //create queues - for (int i = 0; i < topicCount; i++) { - topicList.add(new AddressBuilder() - .withNewMetadata() - .withNamespace(addressSpace.getMetadata().getNamespace()) - .withName(AddressUtils.generateAddressMetadataName(addressSpace, "test-topic-pubsub-" + i)) - .endMetadata() - .withNewSpec() - .withType("topic") - .withAddress("test-topic-pubsub-" + i) - .withPlan(getDefaultPlan(AddressType.TOPIC)) - .endSpec() - .build()); - } - resourcesManager.setAddresses(topicList.toArray(new Address[0])); - - List msgBatch = TestUtils.generateMessages(msgCount); - - UserCredentials credentials = new UserCredentials("test", "test"); - resourcesManager.createOrUpdateUser(addressSpace, credentials); - runTestInLoop(30, () -> { - AmqpClient client = resourcesManager.getAmqpClientFactory().createTopicClient(addressSpace); - client.getConnectOptions().setCredentials(credentials); - clients.add(client); - - //attach subscibers - List>> recvResults = new ArrayList<>(); - for (int i = 0; i < topicCount; i++) { - recvResults.add(client.recvMessages(String.format("test-topic-pubsub-%d", i), msgCount)); - } - - //attach producers - for (int i = 0; i < topicCount; i++) { - collector.checkThat(client.sendMessages(topicList.get(i).getSpec().getAddress(), msgBatch).get(2, TimeUnit.MINUTES), is(msgBatch.size())); - } - - //check received messages - for (int i = 0; i < topicCount; i++) { - collector.checkThat(recvResults.get(i).get().size(), is(msgCount)); - } - }); - } - - //======================================================================================================== - // Help methods - //======================================================================================================== - - private void doAddressTest(AddressSpace addressSpace, String topicPattern, - String queuePattern, UserCredentials credentials) throws Exception { - List
queueList = new ArrayList<>(); - List
topicList = new ArrayList<>(); - - int destinationCount = 20; - - for (int i = 0; i < destinationCount; i++) { - queueList.add(new AddressBuilder() - .withNewMetadata() - .withNamespace(addressSpace.getMetadata().getNamespace()) - .withName(AddressUtils.generateAddressMetadataName(addressSpace, queuePattern + i)) - .endMetadata() - .withNewSpec() - .withType("queue") - .withAddress("queue-via-web") - .withPlan(getDefaultPlan(AddressType.QUEUE)) - .endSpec() - .build()); - topicList.add(new AddressBuilder() - .withNewMetadata() - .withNamespace(addressSpace.getMetadata().getNamespace()) - .withName(AddressUtils.generateAddressMetadataName(addressSpace, topicPattern + i)) - .endMetadata() - .withNewSpec() - .withType("topic") - .withAddress(topicPattern + i) - .withPlan(getDefaultPlan(AddressType.TOPIC)) - .endSpec() - .build()); - } - - AmqpClient queueClient; - AmqpClient topicClient; - - resourcesManager.setAddresses(queueList.toArray(new Address[0])); - resourcesManager.appendAddresses(topicList.toArray(new Address[0])); - - queueClient = resourcesManager.getAmqpClientFactory().createQueueClient(addressSpace); - queueClient.getConnectOptions().setCredentials(credentials); - clients.add(queueClient); - - topicClient = resourcesManager.getAmqpClientFactory().createTopicClient(addressSpace); - topicClient.getConnectOptions().setCredentials(credentials); - clients.add(topicClient); - - for (Address queue : queueList) { - QueueTest.runQueueTest(queueClient, queue, 1024); - } - - for (Address topic : topicList) { - TopicTest.runTopicTest(topicClient, topic, 1024); - } - - resourcesManager.deleteAddresses(queueList.toArray(new Address[0])); - resourcesManager.deleteAddresses(topicList.toArray(new Address[0])); - Thread.sleep(15000); - } - - protected void doTestLoad(AddressSpaceType type, String addressSpacePlans, String addressPlan) throws Exception { - - runTestInLoop(120, () -> { - int count = 5; - AmqpClient queueClient; - Map pairs = new HashMap(); - - - List addressSpaceList = IntStream.range(0, count).mapToObj(i -> - new AddressSpaceBuilder() - .withNewMetadata() - .withName("test-address-space-" + i) - .withNamespace(kubernetes.getInfraNamespace()) - .endMetadata() - .withNewSpec() - .withType(type.toString()) - .withPlan(addressSpacePlans) - .withNewAuthenticationService() - .withName("standard-authservice") - .endAuthenticationService() - .endSpec() - .build()).collect(Collectors.toList()); - - resourcesManager.createAddressSpace(addressSpaceList.toArray(new AddressSpace[0])); - List
addresses = new LinkedList<>(); - for (AddressSpace space : addressSpaceList) { - UserCredentials credentials = new UserCredentials("test", "test"); - resourcesManager.createOrUpdateUser(space, credentials); - queueClient = resourcesManager.getAmqpClientFactory().createQueueClient(space); - queueClient.getConnectOptions().setCredentials(credentials); - clients.add(queueClient); - - for (int i = 0; i < count; i++) { - addresses.add(new AddressBuilder() - .withNewMetadata() - .withNamespace(space.getMetadata().getNamespace()) - .withName(AddressUtils.generateAddressMetadataName(space, "test-address-" + i)) - .endMetadata() - .withNewSpec() - .withType("queue") - .withAddress("test-address-" + i) - .withPlan(addressPlan) - .endSpec() - .build()); - pairs.put(addresses.get(i), queueClient); - } - } - resourcesManager.setAddresses(addresses.toArray(new Address[0])); - for (Map.Entry pair : pairs.entrySet()) { - QueueTest.runQueueTest(pair.getValue(), pair.getKey(), 1024); - } - - for (AddressSpace space : addressSpaceList) { - resourcesManager.deleteAddressSpace(space); - } - }); - } -} - - - - - - - diff --git a/systemtests/src/test/java/io/enmasse/systemtest/bases/web/ConsoleTest.java b/systemtests/src/test/java/io/enmasse/systemtest/bases/web/ConsoleTest.java deleted file mode 100644 index 47cc690d419..00000000000 --- a/systemtests/src/test/java/io/enmasse/systemtest/bases/web/ConsoleTest.java +++ /dev/null @@ -1,2074 +0,0 @@ -/* - * Copyright 2018, EnMasse authors. - * License: Apache License 2.0 (see the file LICENSE or http://apache.org/licenses/LICENSE-2.0.html). - */ -package io.enmasse.systemtest.bases.web; - - -import io.enmasse.address.model.Address; -import io.enmasse.address.model.AddressBuilder; -import io.enmasse.address.model.AddressSpace; -import io.enmasse.address.model.AddressSpaceBuilder; -import io.enmasse.address.model.AuthenticationServiceType; -import io.enmasse.address.model.CertSpecBuilder; -import io.enmasse.admin.model.v1.AddressPlan; -import io.enmasse.admin.model.v1.AddressSpacePlan; -import io.enmasse.admin.model.v1.AuthenticationService; -import io.enmasse.admin.model.v1.ResourceAllowance; -import io.enmasse.admin.model.v1.ResourceRequest; -import io.enmasse.config.LabelKeys; -import io.enmasse.systemtest.UserCredentials; -import io.enmasse.systemtest.amqp.AmqpClient; -import io.enmasse.systemtest.bases.TestBase; -import io.enmasse.systemtest.certs.CertBundle; -import io.enmasse.systemtest.certs.CertProvider; -import io.enmasse.systemtest.certs.openssl.CertPair; -import io.enmasse.systemtest.certs.openssl.CertSigningRequest; -import io.enmasse.systemtest.certs.openssl.OpenSSLUtil; -import io.enmasse.systemtest.clients.ClientUtils; -import io.enmasse.systemtest.clients.ClientUtils.ClientAttacher; -import io.enmasse.systemtest.executor.Exec; -import io.enmasse.systemtest.executor.ExecutionResultData; -import io.enmasse.systemtest.info.TestInfo; -import io.enmasse.systemtest.isolated.Credentials; -import io.enmasse.systemtest.logs.CustomLogger; -import io.enmasse.systemtest.logs.GlobalLogCollector; -import io.enmasse.systemtest.messagingclients.ExternalMessagingClient; -import io.enmasse.systemtest.messagingclients.rhea.RheaClientReceiver; -import io.enmasse.systemtest.messagingclients.rhea.RheaClientSender; -import io.enmasse.systemtest.model.address.AddressStatus; -import io.enmasse.systemtest.model.address.AddressType; -import io.enmasse.systemtest.model.addressplan.DestinationPlan; -import io.enmasse.systemtest.model.addressspace.AddressSpacePlans; -import io.enmasse.systemtest.model.addressspace.AddressSpaceType; -import io.enmasse.systemtest.platform.KubeCMDClient; -import io.enmasse.systemtest.platform.Kubernetes; -import io.enmasse.systemtest.platform.apps.SystemtestsKubernetesApps; -import io.enmasse.systemtest.selenium.SeleniumProvider; -import io.enmasse.systemtest.selenium.page.ConsoleWebPage; -import io.enmasse.systemtest.selenium.resources.AddressWebItem; -import io.enmasse.systemtest.selenium.resources.ConnectionWebItem; -import io.enmasse.systemtest.selenium.resources.FilterType; -import io.enmasse.systemtest.selenium.resources.SortType; -import io.enmasse.systemtest.time.TimeoutBudget; -import io.enmasse.systemtest.utils.AddressSpaceUtils; -import io.enmasse.systemtest.utils.AddressUtils; -import io.enmasse.systemtest.utils.AuthServiceUtils; -import io.enmasse.systemtest.utils.Count; -import io.enmasse.systemtest.utils.PlanUtils; -import io.enmasse.systemtest.utils.TestUtils; -import io.fabric8.kubernetes.api.model.ConfigMap; -import io.fabric8.kubernetes.api.model.Pod; -import io.fabric8.kubernetes.api.model.apps.Deployment; -import io.fabric8.openshift.api.model.Route; -import org.apache.qpid.proton.message.Message; -import org.eclipse.hono.util.Strings; -import org.junit.jupiter.api.AfterEach; -import org.junit.jupiter.api.extension.ExtensionContext; -import org.openqa.selenium.By; -import org.openqa.selenium.WebElement; -import org.slf4j.Logger; - -import java.time.Duration; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Collections; -import java.util.Comparator; -import java.util.List; -import java.util.Map; -import java.util.Objects; -import java.util.Optional; -import java.util.concurrent.Future; -import java.util.concurrent.TimeUnit; -import java.util.concurrent.TimeoutException; -import java.util.function.Predicate; -import java.util.stream.Collectors; -import java.util.stream.IntStream; - -import static org.hamcrest.CoreMatchers.is; -import static org.hamcrest.MatcherAssert.assertThat; -import static org.hamcrest.Matchers.either; -import static org.hamcrest.Matchers.notNullValue; -import static org.junit.jupiter.api.Assertions.assertAll; -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertFalse; -import static org.junit.jupiter.api.Assertions.assertNotNull; -import static org.junit.jupiter.api.Assertions.assertNull; -import static org.junit.jupiter.api.Assertions.assertTrue; -import static org.junit.jupiter.api.Assertions.fail; - -public abstract class ConsoleTest extends TestBase { - private static Logger log = CustomLogger.getLogger(); - SeleniumProvider selenium = SeleniumProvider.getInstance(); - private List clientsList; - private ConsoleWebPage consolePage; - - @AfterEach - public void tearDownWebConsoleTests(ExtensionContext context) throws Exception { - if (clientsList != null) { - getClientUtils().stopClients(clientsList, context); - clientsList.clear(); - } - } - - //============================================================================================ - //============================ do test methods for addressspace part========================== - //============================================================================================ - - protected void doTestOpen() throws Exception { - consolePage = new ConsoleWebPage(selenium, TestUtils.getGlobalConsoleRoute(), clusterUser); - consolePage.openConsolePage(); - consolePage.getAddressSpaceItems(); - consolePage.logout(); - } - - protected void doTestCreateDeleteAddressSpace(AddressSpace addressSpace) throws Exception { - resourcesManager.addToAddressSpaces(addressSpace); - consolePage = new ConsoleWebPage(selenium, TestUtils.getGlobalConsoleRoute(), clusterUser); - consolePage.openConsolePage(); - consolePage.createAddressSpace(addressSpace); - waitUntilAddressSpaceActive(addressSpace); - consolePage.deleteAddressSpace(addressSpace); - } - - protected void doTestGoneAwayPageAfterAddressSpaceDeletion() throws Exception { - AddressSpace addressSpace = generateAddressSpaceObject(AddressSpaceType.STANDARD); - consolePage = new ConsoleWebPage(selenium, TestUtils.getGlobalConsoleRoute(), clusterUser); - consolePage.openConsolePage(); - consolePage.createAddressSpace(addressSpace); - waitUntilAddressSpaceActive(addressSpace); - consolePage.openAddressList(addressSpace); - resourcesManager.deleteAddressSpaceWithoutWait(addressSpace); - try { - consolePage.awaitGoneAwayPage(); - } finally { - resourcesManager.deleteAddressSpace(addressSpace); - } - } - - protected void doTestGoneAwayPageAfterAddressDeletion() throws Exception { - AddressSpace addressSpace = generateAddressSpaceObject(AddressSpaceType.STANDARD); - consolePage = new ConsoleWebPage(selenium, TestUtils.getGlobalConsoleRoute(), clusterUser); - consolePage.openConsolePage(); - consolePage.createAddressSpace(addressSpace); - consolePage.openAddressList(addressSpace); - Address address = generateAddressObject(addressSpace, DestinationPlan.STANDARD_SMALL_QUEUE); - consolePage.createAddress(address); - consolePage.openClientsList(address); - resourcesManager.deleteAddresses(address); - consolePage.awaitGoneAwayPage(); - resourcesManager.deleteAddressSpace(addressSpace); - } - - protected void doTestSnippetClient(AddressSpaceType addressSpaceType) throws Exception { - AddressSpace addressSpace = generateAddressSpaceObject(addressSpaceType); - consolePage = new ConsoleWebPage(selenium, TestUtils.getGlobalConsoleRoute(), clusterUser); - consolePage.openConsolePage(); - String firstLine = getSnippetFirstLine(addressSpace); - assertTrue(firstLine.startsWith(KubeCMDClient.getCMD()), "Snippet has right type of cmd client."); - } - - protected void doTestAddressSpaceSnippet(AddressSpaceType addressSpaceType) throws Exception { - AddressSpace addressSpace = generateAddressSpaceObject(addressSpaceType); - - getAndExecAddressSpaceDeploymentSnippet(addressSpace); - assertTrue(AddressSpaceUtils.addressSpaceExists(Kubernetes.getInstance().getInfraNamespace(), - addressSpace.getMetadata().getName())); - resourcesManager.waitForAddressSpaceReady(addressSpace); - resourcesManager.deleteAddressSpace(addressSpace); - } - - - protected void doTestCreateAddrSpaceWithCustomAuthService() throws Exception { - AuthenticationService standardAuth = AuthServiceUtils.createStandardAuthServiceObject("test-standard-authservice", true); - resourcesManager.createAuthService(standardAuth); - - AddressSpace addressSpace = new AddressSpaceBuilder() - .withNewMetadata() - .withName("test-addr-space-custom-auth") - .withNamespace(kubernetes.getInfraNamespace()) - .endMetadata() - .withNewSpec() - .withType(AddressSpaceType.BROKERED.toString()) - .withPlan(AddressSpacePlans.BROKERED) - .withNewAuthenticationService() - .withName(standardAuth.getMetadata().getName()) - .endAuthenticationService() - .endSpec() - .build(); - resourcesManager.addToAddressSpaces(addressSpace); - - consolePage = new ConsoleWebPage(selenium, TestUtils.getGlobalConsoleRoute(), clusterUser); - consolePage.openConsolePage(); - consolePage.createAddressSpace(addressSpace); - waitUntilAddressSpaceActive(addressSpace); - } - - protected void doTestViewAddressSpace() throws Exception { - AddressSpace addressSpace = new AddressSpaceBuilder() - .withNewMetadata() - .withName("test-addr-space-view-console") - .withNamespace(kubernetes.getInfraNamespace()) - .endMetadata() - .withNewSpec() - .withType(AddressSpaceType.BROKERED.toString()) - .withPlan(AddressSpacePlans.BROKERED) - .withNewAuthenticationService() - .withName("standard-authservice") - .endAuthenticationService() - .endSpec() - .build(); - - resourcesManager.createAddressSpace(addressSpace); - - consolePage = new ConsoleWebPage(selenium, TestUtils.getGlobalConsoleRoute(), clusterUser); - consolePage.openConsolePage(); - waitUntilAddressSpaceActive(addressSpace); - consolePage.deleteAddressSpace(addressSpace); - } - - protected void doTestCreateAddrSpaceNonClusterAdminMinimal() throws Exception { - int addressCount = 4; - String namespace = "test-namespace"; - UserCredentials user = Credentials.userCredentials(); - KubeCMDClient.createNamespace(namespace); - kubernetes.getClient().rbac().clusterRoles().createOrReplaceWithNew() - .editOrNewMetadata() - .withName(namespace) - .endMetadata() - .addNewRule() - .withApiGroups("") - .withResources("namespaces") - .withVerbs("get") - .withResourceNames(namespace) - .endRule() - .done(); - - kubernetes.getClient().rbac().clusterRoleBindings().createOrReplaceWithNew() - .editOrNewMetadata() - .withName(namespace) - .endMetadata() - .addNewSubject() - .withApiGroup("rbac.authorization.k8s.io") - .withKind("User") - .withName(user.getUsername()) - .endSubject() - .editOrNewRoleRef() - .withApiGroup("rbac.authorization.k8s.io") - .withKind("ClusterRole") - .withName(namespace) - .endRoleRef() - .done(); - - kubernetes.getClient().rbac().roleBindings().inNamespace(namespace).createOrReplaceWithNew() - .editOrNewMetadata() - .withName("pepa-admin") - .withNamespace(namespace) - .endMetadata() - .addNewSubject() - .withApiGroup("rbac.authorization.k8s.io") - .withKind("User") - .withName(user.getUsername()) - .endSubject() - .editOrNewRoleRef() - .withApiGroup("rbac.authorization.k8s.io") - .withKind("ClusterRole") - .withName("enmasse.io:tenant-edit") - .endRoleRef() - .done(); - - UserCredentials messagingUser = new UserCredentials("pepa", "zdepa"); - boolean success = false; - AddressSpace addressSpace = null; - try { - KubeCMDClient.loginUser(user.getUsername(), user.getPassword()); - - addressSpace = new AddressSpaceBuilder() - .withNewMetadata() - .withName("test-addr-space-api") - .withNamespace(namespace) - .endMetadata() - .withNewSpec() - .withType(AddressSpaceType.STANDARD.toString()) - .withPlan(AddressSpacePlans.STANDARD_MEDIUM) - .withNewAuthenticationService() - .withName("standard-authservice") - .endAuthenticationService() - .endSpec() - .build(); - - consolePage = new ConsoleWebPage(selenium, TestUtils.getGlobalConsoleRoute(), user); - consolePage.openConsolePage(); - consolePage.createAddressSpace(addressSpace); - waitUntilAddressSpaceActive(addressSpace); - - resourcesManager.createOrUpdateUser(addressSpace, messagingUser); - - consolePage.openAddressList(addressSpace); - - List
addresses = generateQueueTopicList(addressSpace, "test", IntStream.range(0, addressCount)); - consolePage.createAddresses(addresses.toArray(new Address[0])); - AddressUtils.waitForDestinationsReady(addresses.toArray(new Address[0])); - - clientsList = attachClients(addressSpace, addresses, messagingUser); - - consolePage.switchToConnectionTab(); - TestUtils.waitUntilConditionOrFail(() -> consolePage.getConnectionItems().stream() - .allMatch(c -> c.getReceivers() > 0), - Duration.ofSeconds(60), - Duration.ofSeconds(1), - () -> "Failed to wait for connections count"); - - consolePage.switchToAddressTab(); - consolePage.openClientsList(addresses.get(1)); - - TestUtils.waitUntilConditionOrFail(() -> consolePage.getClientItems().stream() - .noneMatch(c -> Strings.isNullOrEmpty(c.getContainerId())), - Duration.ofSeconds(60), - Duration.ofSeconds(1), - () -> "Failed to wait for clients count"); - - consolePage.openConsolePage(); - success = true; - } finally { - try { - if (!success) { - GlobalLogCollector testDirLogCollector = new GlobalLogCollector(kubernetes, TestUtils.getFailedTestLogsPath(TestInfo.getInstance().getActualTest()), environment.namespace(), false); - testDirLogCollector.collectLogsOfPodsByLabels(environment.namespace(), null, - Collections.singletonMap(LabelKeys.INFRA_UUID, AddressSpaceUtils.getAddressSpaceInfraUuid(addressSpace))); - } - consolePage.deleteAddressSpace(addressSpace); - } finally { - KubeCMDClient.loginUser(environment.getApiToken()); - KubeCMDClient.switchProject(environment.namespace()); - kubernetes.deleteNamespace(namespace); - kubernetes.getClient().rbac().clusterRoleBindings().withName(namespace).delete(); - kubernetes.getClient().rbac().clusterRoles().withName(namespace).delete(); - } - } - } - - protected void doTestCreateAddrSpaceNonClusterAdmin() throws Exception { - int addressCount = 4; - String namespace = "test-namespace"; - UserCredentials user = Credentials.userCredentials(); - UserCredentials messagingUser = new UserCredentials("pepa", "zdepa"); - KubeCMDClient.runOnCluster("create", "rolebinding", "clients-admin", "--clusterrole", "admin", "--user", user.getUsername(), "--namespace", SystemtestsKubernetesApps.MESSAGING_PROJECT); - boolean success = false; - AddressSpace addressSpace = null; - try { - KubeCMDClient.loginUser(user.getUsername(), user.getPassword()); - KubeCMDClient.createNamespace(namespace); - - addressSpace = new AddressSpaceBuilder() - .withNewMetadata() - .withName("test-addr-space-api") - .withNamespace(namespace) - .endMetadata() - .withNewSpec() - .withType(AddressSpaceType.STANDARD.toString()) - .withPlan(AddressSpacePlans.STANDARD_MEDIUM) - .withNewAuthenticationService() - .withName("standard-authservice") - .endAuthenticationService() - .endSpec() - .build(); - - consolePage = new ConsoleWebPage(selenium, TestUtils.getGlobalConsoleRoute(), user); - consolePage.openConsolePage(); - consolePage.createAddressSpace(addressSpace); - waitUntilAddressSpaceActive(addressSpace); - - resourcesManager.createOrUpdateUser(addressSpace, messagingUser); - - consolePage.openAddressList(addressSpace); - - List
addresses = generateQueueTopicList(addressSpace, "test", IntStream.range(0, addressCount)); - consolePage.createAddresses(addresses.toArray(new Address[0])); - AddressUtils.waitForDestinationsReady(addresses.toArray(new Address[0])); - - clientsList = attachClients(addressSpace, addresses, messagingUser); - - consolePage.switchToConnectionTab(); - TestUtils.waitUntilConditionOrFail(() -> consolePage.getConnectionItems().stream() - .allMatch(c -> c.getReceivers() > 0), - Duration.ofSeconds(60), - Duration.ofSeconds(1), - () -> "Failed to wait for connections count"); - - consolePage.switchToAddressTab(); - consolePage.openClientsList(addresses.get(1)); - - TestUtils.waitUntilConditionOrFail(() -> consolePage.getClientItems().stream() - .noneMatch(c -> Strings.isNullOrEmpty(c.getContainerId())), - Duration.ofSeconds(60), - Duration.ofSeconds(1), - () -> "Failed to wait for clients count"); - - consolePage.openConsolePage(); - success = true; - } finally { - try { - if (!success) { - GlobalLogCollector testDirLogCollector = new GlobalLogCollector(kubernetes, TestUtils.getFailedTestLogsPath(TestInfo.getInstance().getActualTest()), environment.namespace(), false); - testDirLogCollector.collectLogsOfPodsByLabels(environment.namespace(), null, - Collections.singletonMap(LabelKeys.INFRA_UUID, AddressSpaceUtils.getAddressSpaceInfraUuid(addressSpace))); - } - consolePage.deleteAddressSpace(addressSpace); - } finally { - KubeCMDClient.loginUser(environment.getApiToken()); - KubeCMDClient.switchProject(environment.namespace()); - kubernetes.deleteNamespace(namespace); - } - } - } - - protected void doTestRestrictAddressSpaceView() throws Exception { - String namespace = "test-namespace"; - UserCredentials user = Credentials.userCredentials(); - AddressSpace addressSpace = new AddressSpaceBuilder() - .withNewMetadata() - .withName("test-addr-space-api-2") - .withNamespace(kubernetes.getInfraNamespace()) - .endMetadata() - .withNewSpec() - .withType(AddressSpaceType.BROKERED.toString()) - .withPlan(AddressSpacePlans.BROKERED) - .withNewAuthenticationService() - .withName("standard-authservice") - .endAuthenticationService() - .endSpec() - .build(); - - resourcesManager.createAddressSpace(addressSpace); - try { - KubeCMDClient.loginUser(user.getUsername(), user.getPassword()); - KubeCMDClient.createNamespace(namespace); - - consolePage = new ConsoleWebPage(selenium, TestUtils.getGlobalConsoleRoute(), user); - consolePage.openConsolePage(); - assertNull(consolePage.getAddressSpaceItem(addressSpace)); - - } finally { - KubeCMDClient.loginUser(environment.getApiToken()); - KubeCMDClient.switchProject(environment.namespace()); - kubernetes.deleteNamespace(namespace); - } - } - - protected void doEditAddressSpace() throws Exception { - AddressSpace addressSpace = new AddressSpaceBuilder() - .withNewMetadata() - .withName("test-addr-space-api") - .withNamespace(kubernetes.getInfraNamespace()) - .endMetadata() - .withNewSpec() - .withType(AddressSpaceType.STANDARD.toString()) - .withPlan(AddressSpacePlans.STANDARD_MEDIUM) - .withNewAuthenticationService() - .withName("standard-authservice") - .endAuthenticationService() - .endSpec() - .build(); - resourcesManager.addToAddressSpaces(addressSpace); - consolePage = new ConsoleWebPage(selenium, TestUtils.getGlobalConsoleRoute(), clusterUser); - consolePage.openConsolePage(); - consolePage.createAddressSpace(addressSpace); - waitUntilAddressSpaceActive(addressSpace); - assertEquals(AddressSpacePlans.STANDARD_MEDIUM, - resourcesManager.getAddressSpace(addressSpace.getMetadata().getName()).getSpec().getPlan()); - String currentConfig = resourcesManager.getAddressSpace(addressSpace.getMetadata().getName()).getSpec().getPlan(); - consolePage.changeAddressSpacePlan(addressSpace, AddressSpacePlans.STANDARD_UNLIMITED); - AddressSpaceUtils.waitForAddressSpaceConfigurationApplied(addressSpace, currentConfig); - AddressSpaceUtils.waitForAddressSpaceReady(addressSpace); - assertEquals(AddressSpacePlans.STANDARD_UNLIMITED, - resourcesManager.getAddressSpace(addressSpace.getMetadata().getName()).getSpec().getPlan()); - - consolePage.changeAuthService(addressSpace, "none-authservice", AuthenticationServiceType.NONE); - AddressSpaceUtils.waitForAddressSpaceReady(addressSpace); - assertEquals("none-authservice", - resourcesManager.getAddressSpace(addressSpace.getMetadata().getName()).getSpec().getAuthenticationService().getName()); - } - - protected void doTestViewCustomPlans() throws Exception { - final String queuePlanName1 = "web-custom-plan-queue-1"; - final String queuePlanName2 = "web-custom-plan-queue-2"; - final String addressSpacePlanName = "web-addressspace-plan"; - //Custom address plans - AddressPlan testQueuePlan1 = PlanUtils.createAddressPlanObject(queuePlanName1, - AddressType.QUEUE, - Arrays.asList( - new ResourceRequest("broker", 0.001), - new ResourceRequest("router", 0.0002))); - AddressPlan testQueuePlan2 = PlanUtils.createAddressPlanObject(queuePlanName2, - AddressType.QUEUE, - Arrays.asList( - new ResourceRequest("broker", 0.01), - new ResourceRequest("router", 0.002))); - - getResourceManager().createAddressPlan(testQueuePlan1); - getResourceManager().createAddressPlan(testQueuePlan2); - - //Custom addressspace plan - List resources = Arrays.asList( - new ResourceAllowance("broker", 10_000), - new ResourceAllowance("router", 10_000), - new ResourceAllowance("aggregate", 10_000)); - List addressPlans = Arrays.asList(testQueuePlan1, testQueuePlan2); - - AddressSpacePlan addressSpacePlan = PlanUtils.createAddressSpacePlanObject(addressSpacePlanName, "default", AddressSpaceType.STANDARD, resources, addressPlans); - getResourceManager().createAddressSpacePlan(addressSpacePlan); - - AddressSpace addressSpace = new AddressSpaceBuilder() - .withNewMetadata() - .withName("test-addr-space-api") - .withNamespace(kubernetes.getInfraNamespace()) - .endMetadata() - .withNewSpec() - .withType(AddressSpaceType.STANDARD.toString()) - .withPlan(addressSpacePlanName) - .withNewAuthenticationService() - .withName("standard-authservice") - .endAuthenticationService() - .endSpec() - .build(); - - Address queue = new AddressBuilder() - .withNewMetadata() - .withNamespace(addressSpace.getMetadata().getNamespace()) - .withName(AddressUtils.generateAddressMetadataName(addressSpace, "test-queue")) - .endMetadata() - .withNewSpec() - .withType(AddressType.QUEUE.toString()) - .withAddress("test-queue") - .withPlan(queuePlanName1) - .endSpec() - .build(); - - getResourceManager().addToAddressSpaces(addressSpace); - consolePage = new ConsoleWebPage(selenium, TestUtils.getGlobalConsoleRoute(), clusterUser); - consolePage.openConsolePage(); - consolePage.createAddressSpace(addressSpace); - waitUntilAddressSpaceActive(addressSpace); - assertEquals(addressSpacePlanName, - getResourceManager().getAddressSpace(addressSpace.getMetadata().getName()).getSpec().getPlan()); - - consolePage.openAddressList(addressSpace); - consolePage.createAddress(queue); - - AddressWebItem addressWebItem = consolePage.getAddressItem(queue); - selenium.clickOnItem(addressWebItem.getActionDropDown()); - selenium.clickOnItem(addressWebItem.getEditMenuItem()); - selenium.clickOnItem(selenium.getWebElement(consolePage::getEditAddrPlan), "Edit address plan"); - assertTrue(selenium.getDriver().findElement(By.xpath("//option[@value='" + queuePlanName1 + "']")).getText().contains(queuePlanName1)); - } - - protected void doTestFilterAddrSpace() throws Exception { - int addressSpaceCount = 2; - AddressSpace brokered = new AddressSpaceBuilder() - .withNewMetadata() - .withName("brokered-test-addr-space") - .withNamespace(kubernetes.getInfraNamespace()) - .endMetadata() - .withNewSpec() - .withType(AddressSpaceType.BROKERED.toString()) - .withPlan(AddressSpacePlans.BROKERED) - .withNewAuthenticationService() - .withName("standard-authservice") - .endAuthenticationService() - .endSpec() - .build(); - - AddressSpace standard = new AddressSpaceBuilder() - .withNewMetadata() - .withName("standard-test-addr-space") - .withNamespace(kubernetes.getInfraNamespace()) - .endMetadata() - .withNewSpec() - .withType(AddressSpaceType.STANDARD.toString()) - .withPlan(AddressSpacePlans.STANDARD_SMALL) - .withNewAuthenticationService() - .withName("standard-authservice") - .endAuthenticationService() - .endSpec() - .build(); - resourcesManager.createAddressSpace(brokered, standard); - - consolePage = new ConsoleWebPage(selenium, TestUtils.getGlobalConsoleRoute(), clusterUser); - consolePage.openConsolePage(); - assertThat("Console does not show all addressspaces", consolePage.getAddressSpaceItems().size(), is(addressSpaceCount)); - - consolePage.addFilter(FilterType.NAMESPACE, "blah"); - assertThat("Console should show empty list", consolePage.getAddressSpaceItems().size(), is(0)); - - consolePage.removeAllFilters(); - - consolePage.addFilter(FilterType.TYPE, AddressSpaceType.BROKERED.toString()); - assertThat("Console should show not empty list", consolePage.getAddressSpaceItems().size(), is(addressSpaceCount / 2)); - - consolePage.addFilter(FilterType.TYPE, AddressSpaceType.STANDARD.toString()); - assertThat("Console should show not empty list", consolePage.getAddressSpaceItems().size(), is(addressSpaceCount / 2)); - - consolePage.removeAllFilters(); - - consolePage.addFilter(FilterType.NAME, "brokered"); - assertThat("Console should show not empty list", consolePage.getAddressSpaceItems().size(), is(addressSpaceCount / 2)); - - consolePage.addFilter(FilterType.NAME, "standard"); - assertThat("Console should show not empty list", consolePage.getAddressSpaceItems().size(), is(addressSpaceCount)); - - consolePage.deleteSelectedAddressSpaces(brokered, standard); - assertThat("Console should show empty list", consolePage.getAddressSpaceItems().size(), is(0)); - - WebElement emptyAddessSpace = selenium.getWebElement(() -> consolePage.getEmptyAddSpace()); - assertTrue(emptyAddessSpace.isDisplayed()); - } - - protected void doTestHelpLink() throws Exception { - consolePage = new ConsoleWebPage(selenium, TestUtils.getGlobalConsoleRoute(), clusterUser); - consolePage.openConsolePage(); - String expectedUrl = environment.enmasseDocs(); - String actualLink = consolePage.getHelpLink(); - - assertEquals(expectedUrl, actualLink); - - if (expectedUrl.contains("enmasse.io")) { - consolePage.openHelpLink(expectedUrl); - } - - } - - protected void doTestFilterAddressSpaceStatus() throws Exception { - AddressSpace standard1 = new AddressSpaceBuilder() - .withNewMetadata() - .withName("standard-1-test-addr-space") - .withNamespace(kubernetes.getInfraNamespace()) - .endMetadata() - .withNewSpec() - .withType(AddressSpaceType.BROKERED.toString()) - .withPlan(AddressSpacePlans.BROKERED) - .withNewAuthenticationService() - .withName("standard-authservice") - .endAuthenticationService() - .endSpec() - .build(); - - AddressSpace standard2 = new AddressSpaceBuilder() - .withNewMetadata() - .withName("standard-2-test-addr-space") - .withNamespace(kubernetes.getInfraNamespace()) - .endMetadata() - .withNewSpec() - .withType(AddressSpaceType.STANDARD.toString()) - .withPlan(AddressSpacePlans.STANDARD_SMALL) - .withNewAuthenticationService() - .withName("standard-authservice") - .endAuthenticationService() - .endSpec() - .build(); - - AddressSpace failed = new AddressSpaceBuilder() - .withNewMetadata() - .withName("failed-addr-space") - .withNamespace(kubernetes.getInfraNamespace()) - .endMetadata() - .withNewSpec() - .withType(AddressSpaceType.BROKERED.toString()) - .withPlan("unknown") - .withNewAuthenticationService() - .withName("standard-authservice") - .endAuthenticationService() - .endSpec() - .build(); - - consolePage = new ConsoleWebPage(selenium, TestUtils.getGlobalConsoleRoute(), clusterUser); - consolePage.openConsolePage(); - - resourcesManager.createAddressSpace(standard1, false); - resourcesManager.createAddressSpace(standard2, false); - - selenium.waitUntilItemPresent(30, () -> consolePage.getAddressSpaceItem(standard1)); - selenium.waitUntilItemPresent(30, () -> consolePage.getAddressSpaceItem(standard2)); - assertThat("Console does not show all addressspaces", consolePage.getAddressSpaceItems().size(), is(2)); - - consolePage.addFilter(FilterType.STATUS, "Active"); - assertThat("Console does not show all addressspaces", consolePage.getAddressSpaceItems().size(), is(0)); - - consolePage.addFilter(FilterType.STATUS, "Configuring"); - assertThat("Console does not show all addressspaces", consolePage.getAddressSpaceItems().size(), is(2)); - - resourcesManager.waitForAddressSpaceReady(standard1); - resourcesManager.waitForAddressSpaceReady(standard2); - - consolePage.removeAllFilters(); - - selenium.waitUntilItemPresent(30, () -> consolePage.getAddressSpaceItem(standard1)); - selenium.waitUntilItemPresent(30, () -> consolePage.getAddressSpaceItem(standard2)); - - consolePage.addFilter(FilterType.STATUS, "Active"); - assertThat("Console does not show all addressspaces", consolePage.getAddressSpaceItems().size(), is(2)); - - resourcesManager.createAddressSpace(failed, false); - - consolePage.removeAllFilters(); - - selenium.waitUntilItemPresent(30, () -> consolePage.getAddressSpaceItem(failed)); - - consolePage.addFilter(FilterType.STATUS, "Active"); - assertThat("Console does not show all addressspaces", consolePage.getAddressSpaceItems().size(), is(2)); - - consolePage.addFilter(FilterType.STATUS, "Pending"); - assertThat("Console does not show all addressspaces", consolePage.getAddressSpaceItems().size(), is(1)); - - consolePage.addFilter(FilterType.STATUS, "Terminating"); - assertThat("Console does not show all addressspaces", consolePage.getAddressSpaceItems().size(), is(0)); - consolePage.removeAllFilters(); - - consolePage.addFilter(FilterType.NAME, "standard"); - consolePage.addFilter(FilterType.NAMESPACE, kubernetes.getInfraNamespace()); - assertThat("Console does not show all addressspaces", consolePage.getAddressSpaceItems().size(), is(2)); - consolePage.addFilter(FilterType.STATUS, "Active"); - assertThat("Console does not show all addressspaces", consolePage.getAddressSpaceItems().size(), is(2)); - consolePage.addFilter(FilterType.TYPE, "Standard"); - assertThat("Console does not show all addressspaces", consolePage.getAddressSpaceItems().size(), is(1)); - consolePage.addFilter(FilterType.STATUS, "Pending"); - assertThat("Console does not show all addressspaces", consolePage.getAddressSpaceItems().size(), is(0)); - } - - protected void doTestListEndpoints() throws Exception { - AddressSpace addrSpace = new AddressSpaceBuilder() - .withNewMetadata() - .withName("standard-1-test-addr-space") - .withNamespace(kubernetes.getInfraNamespace()) - .endMetadata() - .withNewSpec() - .withType(AddressSpaceType.STANDARD.toString()) - .withPlan(AddressSpacePlans.STANDARD_SMALL) - .withNewAuthenticationService() - .withName("standard-authservice") - .endAuthenticationService() - .endSpec() - .build(); - - resourcesManager.addToAddressSpaces(addrSpace); - consolePage = new ConsoleWebPage(selenium, TestUtils.getGlobalConsoleRoute(), clusterUser); - consolePage.openConsolePage(); - consolePage.createAddressSpace(addrSpace); - consolePage.openEndpointList(addrSpace); - consolePage.getEndpointItems(); - - assertEquals(consolePage.getEndpointItem(addrSpace.getMetadata().getName() + "." + "messaging").getHost(), - Objects.requireNonNull(AddressSpaceUtils.getEndpointByName(addrSpace, "messaging")).getHost()); - assertEquals(consolePage.getEndpointItem(addrSpace.getMetadata().getName() + "." + "messaging-wss").getHost(), - Objects.requireNonNull(AddressSpaceUtils.getEndpointByName(addrSpace, "messaging-wss")).getHost()); - } - - protected void doTestEndpointSystemProvided() throws Exception { - AddressSpace addrSpace = new AddressSpaceBuilder() - .withNewMetadata() - .withName("standard-2-test-addr-space") - .withNamespace(kubernetes.getInfraNamespace()) - .endMetadata() - .withNewSpec() - .withType(AddressSpaceType.STANDARD.toString()) - .withPlan(AddressSpacePlans.STANDARD_SMALL) - .withNewAuthenticationService() - .withName("standard-authservice") - .endAuthenticationService() - .withEndpoints(AddressSpaceUtils.createEndpoint("messaging", new CertSpecBuilder() - .withProvider(CertProvider.selfsigned.name()) - .build(), null, "amqps")) - .endSpec() - .build(); - - resourcesManager.addToAddressSpaces(addrSpace); - consolePage = new ConsoleWebPage(selenium, TestUtils.getGlobalConsoleRoute(), clusterUser); - consolePage.openConsolePage(); - consolePage.createAddressSpace(addrSpace); - consolePage.openEndpointList(addrSpace); - consolePage.getEndpointItems(); - - assertEquals(consolePage.getEndpointItem(addrSpace.getMetadata().getName() + "." + "messaging").getHost(), - Objects.requireNonNull(AddressSpaceUtils.getEndpointByName(addrSpace, "messaging")).getHost()); - } - - protected void doTestEndpointOpenshiftProvided() throws Exception { - UserCredentials user = new UserCredentials("pepa", "kornys"); - AddressSpace addrSpace = new AddressSpaceBuilder() - .withNewMetadata() - .withName("standard-3-test-addr-space") - .withNamespace(kubernetes.getInfraNamespace()) - .endMetadata() - .withNewSpec() - .withType(AddressSpaceType.STANDARD.toString()) - .withPlan(AddressSpacePlans.STANDARD_SMALL) - .withNewAuthenticationService() - .withName("standard-authservice") - .endAuthenticationService() - .withEndpoints( - AddressSpaceUtils.createEndpoint("messaging", new CertSpecBuilder() - .withProvider(CertProvider.openshift.name()) - .build(), null, "amqps"), - AddressSpaceUtils.createEndpoint("messaging-wss", new CertSpecBuilder() - .withProvider(CertProvider.openshift.name()) - .build(), null, "https")) - .endSpec() - .build(); - - Address queue = new AddressBuilder() - .withNewMetadata() - .withNamespace(addrSpace.getMetadata().getNamespace()) - .withName(AddressUtils.generateAddressMetadataName(addrSpace, "test-queue")) - .endMetadata() - .withNewSpec() - .withType(AddressType.QUEUE.toString()) - .withAddress("test-queue") - .withPlan(DestinationPlan.STANDARD_SMALL_QUEUE) - .endSpec() - .build(); - - resourcesManager.addToAddressSpaces(addrSpace); - consolePage = new ConsoleWebPage(selenium, TestUtils.getGlobalConsoleRoute(), clusterUser); - consolePage.openConsolePage(); - consolePage.createAddressSpace(addrSpace); - - resourcesManager.createOrUpdateUser(addrSpace, user); - - consolePage.openAddressList(addrSpace); - consolePage.createAddress(queue); - - consolePage.switchToEndpointTab(); - consolePage.getEndpointItems(); - - getClientUtils().assertCanConnect(addrSpace, user, Collections.singletonList(queue), resourcesManager); - - assertEquals(consolePage.getEndpointItem(addrSpace.getMetadata().getName() + "." + "messaging-wss").getHost(), - Objects.requireNonNull(AddressSpaceUtils.getEndpointByName(addrSpace, "messaging-wss")).getHost()); - assertEquals(consolePage.getEndpointItem(addrSpace.getMetadata().getName() + "." + "messaging").getHost(), - Objects.requireNonNull(AddressSpaceUtils.getEndpointByName(addrSpace, "messaging")).getHost()); - } - - protected void doTestEndpointCustomCertsProvided() throws Exception { - CertBundle bundle = OpenSSLUtil.createCertBundle("kornys"); - - AddressSpace addrSpace = new AddressSpaceBuilder() - .withNewMetadata() - .withName("standard-4-test-addr-space") - .withNamespace(kubernetes.getInfraNamespace()) - .endMetadata() - .withNewSpec() - .withType(AddressSpaceType.STANDARD.toString()) - .withPlan(AddressSpacePlans.STANDARD_SMALL) - .withNewAuthenticationService() - .withName("standard-authservice") - .endAuthenticationService() - .withEndpoints( - AddressSpaceUtils.createEndpoint("messaging", new CertSpecBuilder() - .withProvider(CertProvider.certBundle.name()) - .withTlsCert(bundle.getCertB64()) - .withTlsKey(bundle.getKeyB64()) - .build(), null, "amqps")) - .endSpec() - .build(); - - resourcesManager.addToAddressSpaces(addrSpace); - consolePage = new ConsoleWebPage(selenium, TestUtils.getGlobalConsoleRoute(), clusterUser); - consolePage.openConsolePage(); - consolePage.createAddressSpace(addrSpace); - consolePage.openEndpointList(addrSpace); - consolePage.getEndpointItems(); - - assertEquals(consolePage.getEndpointItem(addrSpace.getMetadata().getName() + "." + "messaging").getHost(), - Objects.requireNonNull(AddressSpaceUtils.getEndpointByName(addrSpace, "messaging")).getHost()); - } - - //============================================================================================ - //============================ do test methods for address part ============================== - //============================================================================================ - - protected void doTestAddressSnippet(AddressSpaceType addressSpaceType, String destinationPlan) throws Exception { - AddressSpace addressSpace = generateAddressSpaceObject(addressSpaceType); - resourcesManager.createAddressSpace(addressSpace); - - Address address = generateAddressObject(addressSpace, destinationPlan); - getAndExecAddressDeploymentSnippet(addressSpace, address); - AddressUtils.waitForDestinationsReady(address); - AddressUtils.isAddressReady(addressSpace, address); - resourcesManager.deleteAddresses(address); - resourcesManager.deleteAddressSpace(addressSpace); - } - - protected void doTestCreateDeleteAddress(AddressSpace addressSpace, Address... destinations) throws Exception { - Kubernetes.getInstance().getAddressClient().inNamespace(addressSpace.getMetadata(). - getNamespace()).list().getItems().forEach(address -> log.info("Add from list: " + address)); - consolePage = new ConsoleWebPage(selenium, TestUtils.getGlobalConsoleRoute(), clusterUser); - consolePage.openConsolePage(); - consolePage.openAddressList(addressSpace); - for (Address dest : destinations) { - consolePage.createAddress(dest); - } - for (Address dest : destinations) { - consolePage.deleteAddress(dest); - } - } - - protected void doTestAddressStatus(AddressSpace addressSpace, Address destination) throws Exception { - consolePage = new ConsoleWebPage(selenium, TestUtils.getGlobalConsoleRoute(), clusterUser); - consolePage.openConsolePage(); - consolePage.openAddressList(addressSpace); - consolePage.createAddress(destination, false); - Thread.sleep(5000); - assertThat("Console failed, expected PENDING or READY state", - consolePage.getAddressItem(destination).getStatus(), - either(is(AddressStatus.PENDING)).or(is(AddressStatus.READY))); - - AddressUtils.waitForDestinationsReady(new TimeoutBudget(5, TimeUnit.MINUTES), destination); - Thread.sleep(5000); - assertEquals(AddressStatus.READY, consolePage.getAddressItem(destination).getStatus(), - "Console failed, expected READY state"); - } - - protected void doTestFilterAddressesByType(AddressSpace addressSpace) throws Exception { - int addressCount = 4; - List
addresses = generateQueueTopicList(addressSpace, "via-web", IntStream.range(0, addressCount)); - - consolePage = new ConsoleWebPage(selenium, TestUtils.getGlobalConsoleRoute(), clusterUser); - consolePage.openConsolePage(); - consolePage.openAddressList(addressSpace); - consolePage.createAddressesAndWait(addresses.toArray(new Address[0])); - assertThat(String.format("Console failed, does not contain %d addresses", addressCount), - consolePage.getAddressItems().size(), is(addressCount)); - - consolePage.addFilter(FilterType.TYPE, AddressType.QUEUE.toString()); - List items = consolePage.getAddressItems(); - assertThat(String.format("Console failed, does not contain %d addresses", addressCount / 2), - items.size(), is(addressCount / 2)); //assert correct count - assertAddressType("Console failed, does not contains only address type queue", - items, AddressType.QUEUE); //assert correct type - - consolePage.removeAddressFilter(FilterType.TYPE, AddressType.QUEUE.toString()); - assertThat(String.format("Console failed, does not contain %d addresses", addressCount), - consolePage.getAddressItems().size(), is(addressCount)); - - consolePage.addFilter(FilterType.TYPE, AddressType.TOPIC.toString()); - items = consolePage.getAddressItems(); - assertThat(String.format("Console failed, does not contain %d addresses", addressCount / 2), - items.size(), is(addressCount / 2)); //assert correct count - assertAddressType("Console failed, does not contains only address type topic", - items, AddressType.TOPIC); //assert correct type - - consolePage.removeAddressFilter(FilterType.TYPE, AddressType.TOPIC.toString()); - assertThat(String.format("Console failed, does not contain %d addresses", addressCount), - consolePage.getAddressItems().size(), is(addressCount)); - } - - protected void doTestFilterAddressesByName(AddressSpace addressSpace) throws Exception { - int addressCount = 4; - List
addresses = generateQueueTopicList(addressSpace, "via-web", IntStream.range(0, addressCount)); - - consolePage = new ConsoleWebPage(selenium, TestUtils.getGlobalConsoleRoute(), clusterUser); - consolePage.openConsolePage(); - consolePage.openAddressList(addressSpace); - consolePage.createAddressesAndWait(addresses.toArray(new Address[0])); - - String subText = "queue"; - consolePage.addFilter(FilterType.NAME, subText); - List items = consolePage.getAddressItems(); - assertEquals(addressCount / 2, items.size(), - String.format("Console failed, does not contain %d addresses", addressCount / 2)); - assertAddressName("Console failed, does not contain addresses contain " + subText, items, subText); - - subText = "topic"; - consolePage.addFilter(FilterType.NAME, subText); - items = consolePage.getAddressItems(); - assertEquals(addressCount, items.size(), - String.format("Console failed, does not contain %d addresses", addressCount)); - - - consolePage.removeAddressFilter(FilterType.NAME, "queue"); - items = consolePage.getAddressItems(); - assertEquals(addressCount / 2, items.size(), - String.format("Console failed, does not contain %d addresses", addressCount / 2)); - assertAddressName("Console failed, does not contain addresses contain " + subText, items, subText); - - consolePage.removeAllFilters(); - assertEquals(addressCount, consolePage.getAddressItems().size(), - String.format("Console failed, does not contain %d addresses", addressCount)); - - consolePage.deleteSelectedAddresses(addresses.toArray(new Address[0])); - assertEquals(0, consolePage.getAddressItems().size(), - String.format("Console failed, does not contain %d addresses", 0)); - } - - - protected void doTestFilterAddressesByStatus(AddressSpace addressSpace) throws Exception { - int addressCount = 4; - List
addresses = generateQueueTopicList(addressSpace, "via-web", IntStream.range(0, addressCount)); - - String nonexistentPlan = "foo-plan"; - IntStream.range(0, addressCount / 2) - .forEach(i -> { - addresses.get(i).getSpec().setPlan(nonexistentPlan); - ; - }); - List
goodAddresses = addresses.stream() - .filter(a -> !a.getSpec().getPlan().equals(nonexistentPlan)) - .collect(Collectors.toList()); - List
badAddresses = addresses.stream() - .filter(a -> a.getSpec().getPlan().equals(nonexistentPlan)) - .collect(Collectors.toList()); - - consolePage = new ConsoleWebPage(selenium, TestUtils.getGlobalConsoleRoute(), clusterUser); - consolePage.openConsolePage(); - consolePage.openAddressList(addressSpace); - getResourceManager().appendAddresses(true, goodAddresses.toArray(new Address[0])); - getResourceManager().appendAddresses(false, badAddresses.toArray(new Address[0])); - - TestUtils.waitUntilCondition(() -> consolePage.getAddressItems().size() == addressCount, Duration.ofSeconds(30), Duration.ofMillis(500)); - - consolePage.addFilter(FilterType.STATUS, "Pending"); - List items = consolePage.getAddressItems(); - assertEquals(badAddresses.size(), items.size(), - String.format("Console failed, does not contain %d addresses when %s filter", badAddresses.size(), "Pending")); - - consolePage.addFilter(FilterType.STATUS, "Active"); - items = consolePage.getAddressItems(); - assertEquals(goodAddresses.size(), items.size(), - String.format("Console failed, does not contain %d addresses when %s filter", goodAddresses.size(), "Active")); - } - - protected void doTestPurgeMessages(AddressSpace addressSpace) throws Exception { - Address queue1 = new AddressBuilder() - .withNewMetadata() - .withNamespace(addressSpace.getMetadata().getNamespace()) - .withName(AddressUtils.generateAddressMetadataName(addressSpace, "test-queue1")) - .endMetadata() - .withNewSpec() - .withType("queue") - .withAddress("test-queue1") - .withPlan(addressSpace.getSpec().getType().equals(AddressSpaceType.BROKERED.toString()) ? DestinationPlan.BROKERED_QUEUE : DestinationPlan.STANDARD_SMALL_QUEUE) - .endSpec() - .build(); - - Address queue2 = new AddressBuilder() - .withNewMetadata() - .withNamespace(addressSpace.getMetadata().getNamespace()) - .withName(AddressUtils.generateAddressMetadataName(addressSpace, "test-queue2")) - .endMetadata() - .withNewSpec() - .withType("queue") - .withAddress("test-queue2") - .withPlan(addressSpace.getSpec().getType().equals(AddressSpaceType.BROKERED.toString()) ? DestinationPlan.BROKERED_QUEUE : DestinationPlan.STANDARD_SMALL_QUEUE) - .endSpec() - .build(); - - Address queue3 = new AddressBuilder() - .withNewMetadata() - .withNamespace(addressSpace.getMetadata().getNamespace()) - .withName(AddressUtils.generateAddressMetadataName(addressSpace, "test-queue3")) - .endMetadata() - .withNewSpec() - .withType("queue") - .withAddress("test-queue3") - .withPlan(addressSpace.getSpec().getType().equals(AddressSpaceType.BROKERED.toString()) ? DestinationPlan.BROKERED_QUEUE : DestinationPlan.STANDARD_XLARGE_QUEUE) - .endSpec() - .build(); - - resourcesManager.setAddresses(queue1, queue2, queue3); - - ExternalMessagingClient client = new ExternalMessagingClient() - .withClientEngine(new RheaClientSender()) - .withMessagingRoute(AddressSpaceUtils.getMessagingRoute(addressSpace)) - .withCredentials(defaultCredentials) - .withCount(1000) - .withMessageBody("msg no. %d") - .withTimeout(30); - - assertTrue(client.withAddress(queue1).run(false)); - assertTrue(client.withAddress(queue2).run(false)); - assertTrue(client.withAddress(queue3).run(false)); - - consolePage = new ConsoleWebPage(selenium, TestUtils.getGlobalConsoleRoute(), clusterUser); - consolePage.openConsolePage(); - consolePage.openAddressList(addressSpace); - assertThat(String.format("Console failed, does not contain %d addresses", 3), - consolePage.getAddressItems().size(), is(3)); - - consolePage.purgeSelectedAddresses(queue1, queue3); - - selenium.waitUntilPropertyPresent(60, 0, () -> consolePage.getAddressItem(queue1).getMessagesStored()); - selenium.waitUntilPropertyPresent(60, 0, () -> consolePage.getAddressItem(queue3).getMessagesStored()); - - assertTrue(client.withAddress(queue2).withClientEngine(new RheaClientReceiver()).run(false)); - assertThat(client.getMessages().size(), is(1000)); - assertTrue(client.withAddress(queue3).withTimeout(10).withClientEngine(new RheaClientReceiver()).run(false)); - assertThat(client.getMessages().size(), is(0)); - } - - protected void doTestConnectionClose(AddressSpace addressSpace) throws Exception { - Address queue = new AddressBuilder() - .withNewMetadata() - .withNamespace(addressSpace.getMetadata().getNamespace()) - .withName(AddressUtils.generateAddressMetadataName(addressSpace, "test-queue")) - .endMetadata() - .withNewSpec() - .withType("queue") - .withAddress("test-queue1") - .withPlan(addressSpace.getSpec().getType().equals(AddressSpaceType.BROKERED.toString()) ? DestinationPlan.BROKERED_QUEUE : DestinationPlan.STANDARD_SMALL_QUEUE) - .endSpec() - .build(); - - resourcesManager.setAddresses(queue); - - ExternalMessagingClient client = new ExternalMessagingClient() - .withClientEngine(new RheaClientReceiver()) - .withMessagingRoute(AddressSpaceUtils.getMessagingRoute(addressSpace)) - .withCredentials(defaultCredentials) - .withCount(1) - .withReconnect(false) - .withTimeout(180); - - client.withAddress(queue).runAsync(false); - - consolePage = new ConsoleWebPage(selenium, TestUtils.getGlobalConsoleRoute(), clusterUser); - consolePage.openConsolePage(); - consolePage.openConnectionList(addressSpace); - - selenium.waitUntilPropertyPresent(60, 1, () -> consolePage.getConnectionItems().size()); - - Optional connRow = consolePage.getConnectionItems().stream().findFirst(); - assertThat("Connection item not found", connRow.isPresent(), is(true)); - - consolePage.closeSelectedConnection(connRow.get()); - - selenium.waitUntilPropertyPresent(60, 0, () -> consolePage.getConnectionItems().size()); - } - - protected void doTestEditAddress(AddressSpace addressSpace, Address address, String plan) throws Exception { - resourcesManager.setAddresses(address); - consolePage = new ConsoleWebPage(selenium, TestUtils.getGlobalConsoleRoute(), clusterUser); - consolePage.openConsolePage(); - consolePage.openAddressList(addressSpace); - consolePage.changeAddressPlan(address, plan); - Thread.sleep(10_000); - AddressUtils.waitForDestinationsReady(address); - assertThat(resourcesManager.getAddress(kubernetes.getInfraNamespace(), address).getSpec().getPlan(), is(plan)); - } - - protected void doTestDeleteFilteredAddress(AddressSpace addressSpace) throws Exception { - int addressTotal = 2; - - Address destQueue = new AddressBuilder() - .withNewMetadata() - .withNamespace(addressSpace.getMetadata().getNamespace()) - .withName(AddressUtils.generateAddressMetadataName(addressSpace, "test-queue")) - .endMetadata() - .withNewSpec() - .withType("queue") - .withAddress("test-queue") - .withPlan(getDefaultPlan(AddressType.QUEUE)) - .endSpec() - .build(); - - Address destTopic = new AddressBuilder() - .withNewMetadata() - .withNamespace(addressSpace.getMetadata().getNamespace()) - .withName(AddressUtils.generateAddressMetadataName(addressSpace, "test-topic")) - .endMetadata() - .withNewSpec() - .withType("topic") - .withAddress("test-topic") - .withPlan(getDefaultPlan(AddressType.TOPIC)) - .endSpec() - .build(); - - consolePage = new ConsoleWebPage(selenium, TestUtils.getGlobalConsoleRoute(), clusterUser); - consolePage.openConsolePage(); - consolePage.openAddressList(addressSpace); - consolePage.createAddresses(destQueue, destTopic); - - consolePage.addFilter(FilterType.TYPE, AddressType.QUEUE.toString()); - List items = consolePage.getAddressItems(); - - assertEquals(addressTotal / 2, items.size(), - String.format("Console failed, filter does not contain %d addresses", addressTotal / 2)); - - assertAddressName("Console failed, filter does not contain addresses", items, "queue"); - - consolePage.deleteAddress(destQueue); - items = consolePage.getAddressItems(); - assertEquals(0, items.size()); - log.info("filtered address has been deleted and no longer present in filter"); - - consolePage.removeAllFilters(); - items = consolePage.getAddressItems(); - assertEquals(addressTotal / 2, items.size()); - } - - protected void doTestSortAddressesByName(AddressSpace addressSpace) throws Exception { - int addressCount = 4; - List
addresses = generateQueueTopicList(addressSpace, "via-web", IntStream.range(0, addressCount)); - consolePage = new ConsoleWebPage(selenium, TestUtils.getGlobalConsoleRoute(), clusterUser); - consolePage.openConsolePage(); - consolePage.openAddressList(addressSpace); - consolePage.createAddresses(addresses.toArray(new Address[0])); - consolePage.sortAddresses(SortType.NAME, true); - assertSorted("Console failed, items are not sorted by name asc", consolePage.getAddressItems()); - consolePage.sortAddresses(SortType.NAME, false); - assertSorted("Console failed, items are not sorted by name desc", consolePage.getAddressItems(), true); - } - - protected void doTestSortAddressesBySenders(AddressSpace addressSpace) throws Exception { - doTestSortAddresses(addressSpace, - SortType.SENDERS, - this::attachClients, - a -> a.getSendersCount() > 0, - Comparator.comparingInt(AddressWebItem::getSendersCount)); - } - - protected void doTestSortAddressesByReceivers(AddressSpace addressSpace) throws Exception { - doTestSortAddresses(addressSpace, - SortType.RECEIVERS, - this::attachClients, - a -> a.getReceiversCount() > 0, - Comparator.comparingInt(AddressWebItem::getReceiversCount)); - } - - private void doTestSortAddresses(AddressSpace addressSpace, SortType sortType, ClientAttacher attacher, Predicate readyCondition, Comparator sortingComparator) throws Exception { - int addressCount = 4; - List
addresses = generateQueueTopicList(addressSpace, "via-web", IntStream.range(0, addressCount)); - - getResourceManager().setAddresses(addresses.toArray(new Address[0])); - - consolePage = new ConsoleWebPage(selenium, TestUtils.getGlobalConsoleRoute(), clusterUser); - consolePage.openConsolePage(); - consolePage.openAddressList(addressSpace); - - assertEquals(addressCount, consolePage.getAddressItems().size(), "Unexpected number of addresses present before attaching clients"); - - clientsList = attacher.attach(addressSpace, addresses, defaultCredentials); - - TestUtils.waitUntilConditionOrFail(() -> consolePage.getAddressItems().stream() - .allMatch(readyCondition), - Duration.ofSeconds(60), - Duration.ofSeconds(1), - () -> "Failed to wait for addresses count"); - - consolePage.sortAddresses(sortType, true); - assertSorted("Console failed, items are not sorted by count of senders asc", - consolePage.getAddressItems(), - sortingComparator); - - consolePage.sortAddresses(sortType, false); - assertSorted("Console failed, items are not sorted by count of senders desc", - consolePage.getAddressItems(), - true, - sortingComparator); - } - - protected void doTestSortConnectionsBySenders(AddressSpace addressSpace) throws Exception { - doTestSortConnections(addressSpace, - SortType.SENDERS, - this::attachClients, - c -> c.getSenders() > 0, - Comparator.comparingInt(ConnectionWebItem::getSenders)); - } - - protected void doTestSortConnectionsByReceivers(AddressSpace addressSpace) throws Exception { - doTestSortConnections(addressSpace, - SortType.RECEIVERS, - this::attachClients, - c -> c.getReceivers() > 0, - Comparator.comparingInt(ConnectionWebItem::getReceivers)); - } - - protected void doTestAddressLinks(AddressSpace addressSpace, String destinationPlan) throws Exception { - Address address = generateAddressObject(addressSpace, destinationPlan); - consolePage = new ConsoleWebPage(selenium, TestUtils.getGlobalConsoleRoute(), clusterUser); - consolePage.openConsolePage(); - consolePage.openAddressList(addressSpace); - consolePage.createAddress(address); - consolePage.openClientsList(address); - assertThat("Link table is not empty!", consolePage.isClientListEmpty()); - int link_count = attachClients(addressSpace, address, defaultCredentials); - selenium.waitUntilPropertyPresent(60, link_count, () -> consolePage.getClientItems().size()); - assertThat(consolePage.getClientItems().size(), is(link_count)); - } - - protected void doTestAddressLinksWithMismatchedAddressResourceNameAndSuffix(AddressSpace addressSpace, String destinationPlan) throws Exception { - consolePage = new ConsoleWebPage(selenium, TestUtils.getGlobalConsoleRoute(), clusterUser); - consolePage.openConsolePage(); - consolePage.openAddressList(addressSpace); - Address address = doCreateAddressWithMismatchedAddressResourceNameAndSuffix(addressSpace, destinationPlan); - consolePage.openClientsList(address); - assertThat("Link table is not empty!", consolePage.isClientListEmpty()); - int link_count = attachClients(addressSpace, address, defaultCredentials); - selenium.waitUntilPropertyPresent(60, link_count, () -> consolePage.getClientItems().size()); - assertThat(consolePage.getClientItems().size(), is(link_count)); - } - - private Address doCreateAddressWithMismatchedAddressResourceNameAndSuffix(AddressSpace addressSpace, String destinationPlan) throws Exception { - Address address = new AddressBuilder() - .withNewMetadata() - .withNamespace(addressSpace.getMetadata().getNamespace()) - .withName(AddressUtils.generateAddressMetadataName(addressSpace, "test-queue")) - .endMetadata() - .withNewSpec() - .withType("queue") - .withAddress("test-queues") - .withPlan(destinationPlan) - .endSpec() - .build(); - getResourceManager().setAddresses(address); - return address; - } - - private void doTestSortConnections(AddressSpace addressSpace, SortType sortType, ClientAttacher attacher, Predicate readyCondition, Comparator sortingComparator) throws Exception { - int addressCount = 2; - List
addresses = generateQueueTopicList(addressSpace, "via-web", IntStream.range(0, addressCount)); - - getResourceManager().setAddresses(addresses.toArray(new Address[0])); - - consolePage = new ConsoleWebPage(selenium, TestUtils.getGlobalConsoleRoute(), clusterUser); - consolePage.openConsolePage(); - consolePage.openConnectionList(addressSpace); - - assertEquals(0, consolePage.getConnectionItems().size(), "Unexpected number of connections present before attaching clients"); - - clientsList = attacher.attach(addressSpace, addresses, defaultCredentials); - - selenium.waitUntilPropertyPresent(60, clientsList.size(), () -> consolePage.getConnectionItems().size()); - - TestUtils.waitUntilConditionOrFail(() -> consolePage.getConnectionItems().stream() - .allMatch(readyCondition), - Duration.ofSeconds(60), - Duration.ofSeconds(1), - () -> "Failed to wait for connections count"); - - consolePage.sortConnections(sortType, true); - assertSorted("Console failed, items are not sorted by count of senders asc", - consolePage.getConnectionItems(), - sortingComparator); - - consolePage.sortConnections(sortType, false); - assertSorted("Console failed, items are not sorted by count of senders desc", - consolePage.getConnectionItems(), - true, - sortingComparator); - } - - protected void doTestFilterConnectionsByContainerId(AddressSpace addressSpace) throws Exception { - Address dest = new AddressBuilder() - .withNewMetadata() - .withNamespace(addressSpace.getMetadata().getNamespace()) - .withName(AddressUtils.generateAddressMetadataName(addressSpace, "queue-via-web")) - .endMetadata() - .withNewSpec() - .withType("queue") - .withAddress("queue-via-web") - .withPlan(getDefaultPlan(AddressType.QUEUE)) - .endSpec() - .build(); - - getResourceManager().setAddresses(dest); - - consolePage = new ConsoleWebPage(selenium, TestUtils.getGlobalConsoleRoute(), clusterUser); - consolePage.openConsolePage(); - consolePage.openConnectionList(addressSpace); - - int connectionCount = 5; - clientsList = new ArrayList<>(); - clientsList.add(getClientUtils().attachConnector(addressSpace, dest, connectionCount, 1, 1, defaultCredentials, 360)); - selenium.waitUntilPropertyPresent(60, connectionCount, () -> consolePage.getConnectionItems().size()); - - String containerID = consolePage.getConnectionItems().get(0).getContainerId(); - - consolePage.addConnectionsFilter(FilterType.CONTAINER, containerID); - assertThat(String.format("Console failed, does not contain %d connections", 1), - consolePage.getConnectionItems().size(), is(1)); - - consolePage.removeAllFilters(); - assertThat(String.format("Console failed, does not contain %d connections", connectionCount), - consolePage.getConnectionItems().size(), is(connectionCount)); - } - - protected void doTestFilterClientsByContainerId(AddressSpace addressSpace) throws Exception { - Address address = generateAddressObject(addressSpace, DestinationPlan.STANDARD_SMALL_QUEUE); - getResourceManager().setAddresses(address); - consolePage = new ConsoleWebPage(selenium, TestUtils.getGlobalConsoleRoute(), clusterUser); - consolePage.openConsolePage(); - consolePage.openAddressList(addressSpace); - consolePage.openClientsList(address); - - int connectionCount = 2; - clientsList = new ArrayList<>(); - clientsList.add(getClientUtils().attachConnector(addressSpace, address, connectionCount, 1, 1, defaultCredentials, 360)); - selenium.waitUntilPropertyPresent(60, connectionCount * 2, () -> consolePage.getClientItems().size()); - - String containerId = consolePage.getClientItems().get(0).getContainerId(); - - consolePage.addClientsFilter(FilterType.CONTAINER, containerId); - assertThat(String.format("Console failed, does not contain %d clients", 1), - consolePage.getClientItems().size(), is(2)); - - consolePage.removeAllFilters(); - - assertThat(String.format("Console failed, does not contain %d clients", connectionCount), - consolePage.getClientItems().size(), is(connectionCount * 2)); - } - - - protected void doTestFilterClientsByName(AddressSpace addressSpace) throws Exception { - Address address = generateAddressObject(addressSpace, DestinationPlan.STANDARD_SMALL_QUEUE); - getResourceManager().setAddresses(address); - consolePage = new ConsoleWebPage(selenium, TestUtils.getGlobalConsoleRoute(), clusterUser); - consolePage.openConsolePage(); - consolePage.openAddressList(addressSpace); - consolePage.openClientsList(address); - - int connectionCount = 2; - clientsList = new ArrayList<>(); - clientsList.add(getClientUtils().attachConnector(addressSpace, address, connectionCount, 1, 1, defaultCredentials, 360)); - selenium.waitUntilPropertyPresent(60, connectionCount * 2, () -> consolePage.getClientItems().size()); - - String containerId = consolePage.getClientItems().get(0).getName(); - - consolePage.addClientsFilter(FilterType.NAME, containerId); - assertThat(String.format("Console failed, does not contain %d clients", 1), - consolePage.getClientItems().size(), is(1)); - - consolePage.removeAllFilters(); - - assertThat(String.format("Console failed, does not contain %d clients", connectionCount), - consolePage.getClientItems().size(), is(connectionCount * 2)); - } - - protected void doTestGoneAwayPageAfterConnectionClose(AddressSpace addressSpace, ExtensionContext context) throws Exception { - consolePage = new ConsoleWebPage(selenium, TestUtils.getGlobalConsoleRoute(), clusterUser); - consolePage.openConsolePage(); - Address address = generateAddressObject(addressSpace, DestinationPlan.STANDARD_LARGE_QUEUE); - consolePage.openAddressList(addressSpace); - consolePage.createAddress(address); - clientsList = attachClients(addressSpace, - Collections.singletonList(address), defaultCredentials); - consolePage.switchToConnectionTab(); - selenium.waitUntilPropertyPresent(30, 3, () -> consolePage.getConnectionItems().size()); - consolePage.openConnection(consolePage.getConnectionItems().get(0).getHost()); - Pod pod = kubernetes.getClient().pods() - .inNamespace(SystemtestsKubernetesApps.MESSAGING_CLIENTS).list().getItems().get(0); - kubernetes.deletePod(SystemtestsKubernetesApps.MESSAGING_CLIENTS, pod.getMetadata().getName()); - waitForPodsToTerminate(Collections.singletonList(pod.getMetadata().getUid())); - - consolePage.awaitGoneAwayPage(); - } - - protected void doTestSortConnectionsByContainerId(AddressSpace addressSpace) throws Exception { - doTestSortConnections(addressSpace, SortType.CONTAINER_ID, - this::attachClients, - c -> c.getContainerId() != null, - Comparator.comparing(ConnectionWebItem::getContainerId)); - } - - protected void doTestClientsMetrics(AddressSpace addressSpace) throws Exception { - - Address dest = new AddressBuilder() - .withNewMetadata() - .withNamespace(addressSpace.getMetadata().getNamespace()) - .withName(AddressUtils.generateAddressMetadataName(addressSpace, "queue-in-and-out")) - .endMetadata() - .withNewSpec() - .withType("queue") - .withAddress("queue-in-and-out") - .withPlan(getDefaultPlan(AddressType.QUEUE)) - .endSpec() - .build(); - - - getResourceManager().setAddresses(dest); - - consolePage = new ConsoleWebPage(selenium, TestUtils.getGlobalConsoleRoute(), clusterUser); - consolePage.openConsolePage(); - consolePage.openAddressList(addressSpace); - - assertEquals(1, consolePage.getAddressItems().size(), "Unexpected number of addresses present before attaching clients"); - - //this creates 11 senders and 11 receivers in total - clientsList = this.attachClients(addressSpace, Arrays.asList(dest), defaultCredentials); - var senderCount = 11; - var receiverCount = 11; - - selenium.waitUntilPropertyPresent(60, senderCount, () -> consolePage.getAddressItem(dest).getSendersCount()); - selenium.waitUntilPropertyPresent(60, receiverCount, () -> consolePage.getAddressItem(dest).getReceiversCount()); - - assertAll( - () -> assertEquals(receiverCount, consolePage.getAddressItem(dest).getReceiversCount(), - String.format("Console failed, does not contain %d receivers", 10)), - () -> assertEquals(senderCount, consolePage.getAddressItem(dest).getSendersCount(), - String.format("Console failed, does not contain %d senders", 5))); - - TestUtils.waitUntilConditionOrFail(() -> consolePage.getAddressItem(dest).getMessagesIn() >= 5, - Duration.ofSeconds(180), - Duration.ofSeconds(3), - () -> "Failed to wait for messagesIn/sec to reach 1"); - - TestUtils.waitUntilConditionOrFail(() -> consolePage.getAddressItem(dest).getMessagesOut() >= 5, - Duration.ofSeconds(180), - Duration.ofSeconds(3), - () -> "Failed to wait for messagesOut/sec to reach 1"); - - } - - protected void doTestMessagesStoredMetrics(AddressSpace addressSpace) throws Exception { - - Address dest = new AddressBuilder() - .withNewMetadata() - .withNamespace(addressSpace.getMetadata().getNamespace()) - .withName(AddressUtils.generateAddressMetadataName(addressSpace, "queue-stored-msg")) - .endMetadata() - .withNewSpec() - .withType("queue") - .withAddress("queue-stored-msg") - .withPlan(getDefaultPlan(AddressType.QUEUE)) - .endSpec() - .build(); - - getResourceManager().setAddresses(dest); - - consolePage = new ConsoleWebPage(selenium, TestUtils.getGlobalConsoleRoute(), clusterUser); - consolePage.openConsolePage(); - consolePage.openAddressList(addressSpace); - - assertEquals(1, consolePage.getAddressItems().size(), "Unexpected number of addresses present before attaching clients"); - - AmqpClient amqpClient = getResourceManager().getAmqpClientFactory().createQueueClient(addressSpace); - var countMessages = 50; - List msgs = TestUtils.generateMessages(countMessages); - Count predicate = new Count<>(msgs.size()); - Future numSent = amqpClient.sendMessages(dest.getSpec().getAddress(), msgs, predicate); - long timeoutMs = countMessages * ClientUtils.ESTIMATE_MAX_MS_PER_MESSAGE; - assertNotNull(numSent, "Sending messages didn't start"); - int actual = 0; - try { - actual = numSent.get(timeoutMs, TimeUnit.MILLISECONDS); - } catch (TimeoutException t) { - logCollector.collectRouterState("runQueueTestSend"); - fail("Sending messages timed out after sending " + predicate.actual()); - } - assertThat("Wrong count of messages sent", actual, is(msgs.size())); - - selenium.waitUntilPropertyPresent(30, countMessages, () -> consolePage.getAddressItem(dest).getMessagesStored()); - - amqpClient.recvMessages(dest.getSpec().getAddress(), countMessages).get(timeoutMs, TimeUnit.MILLISECONDS); - - selenium.waitUntilPropertyPresent(30, 0, () -> consolePage.getAddressItem(dest).getMessagesStored()); - - } - - protected void doTestSortMessagesCount(AddressSpace addressSpace) throws Exception { - - Address dest = new AddressBuilder() - .withNewMetadata() - .withNamespace(addressSpace.getMetadata().getNamespace()) - .withName(AddressUtils.generateAddressMetadataName(addressSpace, "queue-stored-msg")) - .endMetadata() - .withNewSpec() - .withType("queue") - .withAddress("queue-stored-msg") - .withPlan(getDefaultPlan(AddressType.QUEUE)) - .endSpec() - .build(); - - Address dest2 = new AddressBuilder() - .withNewMetadata() - .withNamespace(addressSpace.getMetadata().getNamespace()) - .withName(AddressUtils.generateAddressMetadataName(addressSpace, "queue-stored-msg-2")) - .endMetadata() - .withNewSpec() - .withType("queue") - .withAddress("queue-stored-msg-2") - .withPlan(getDefaultPlan(AddressType.QUEUE)) - .endSpec() - .build(); - - Address dest3 = new AddressBuilder() - .withNewMetadata() - .withNamespace(addressSpace.getMetadata().getNamespace()) - .withName(AddressUtils.generateAddressMetadataName(addressSpace, "queue-stored-msg-3")) - .endMetadata() - .withNewSpec() - .withType("queue") - .withAddress("queue-stored-msg-3") - .withPlan(getDefaultPlan(AddressType.QUEUE)) - .endSpec() - .build(); - - getResourceManager().setAddresses(dest, dest2, dest3); - - var countMessages = 50; - AmqpClient amqpClient = getResourceManager().getAmqpClientFactory().createQueueClient(addressSpace); - - Future numSent1 = amqpClient.sendMessages(dest.getSpec().getAddress(), countMessages); - Future numSent2 = amqpClient.sendMessages(dest2.getSpec().getAddress(), countMessages - 10); - Future numSent3 = amqpClient.sendMessages(dest3.getSpec().getAddress(), countMessages - 30); - - assertThat(numSent1.get(1, TimeUnit.MINUTES), is(countMessages)); - assertThat(numSent2.get(1, TimeUnit.MINUTES), is(countMessages - 10)); - assertThat(numSent3.get(1, TimeUnit.MINUTES), is(countMessages - 30)); - - consolePage = new ConsoleWebPage(selenium, TestUtils.getGlobalConsoleRoute(), clusterUser); - consolePage.openConsolePage(); - consolePage.openAddressList(addressSpace); - - selenium.waitUntilPropertyPresent(30, countMessages, () -> consolePage.getAddressItem(dest).getMessagesStored()); - selenium.waitUntilPropertyPresent(30, countMessages - 10, () -> consolePage.getAddressItem(dest2).getMessagesStored()); - selenium.waitUntilPropertyPresent(30, countMessages - 30, () -> consolePage.getAddressItem(dest3).getMessagesStored()); - - consolePage.sortAddresses(SortType.STORED_MESSAGES, true); - assertSorted("Addresses are not sorted by stored messages", consolePage.getAddressItems(), Comparator.comparingInt(AddressWebItem::getMessagesStored)); - consolePage.sortAddresses(SortType.STORED_MESSAGES, false); - assertSorted("Addresses are not sorted by stored messages", consolePage.getAddressItems(), true, Comparator.comparingInt(AddressWebItem::getMessagesStored)); - } - - protected void doTestCanOpenConsolePage(AddressSpace addressSpace, UserCredentials credentials, boolean userAllowed) throws Exception { - consolePage = new ConsoleWebPage(selenium, TestUtils.getGlobalConsoleRoute(), credentials); - consolePage.openConsolePage(); - log.info("User {} successfully authenticated", credentials); - - if (userAllowed) { - consolePage.openAddressList(addressSpace); - } else { - try { - consolePage.openAddressList(addressSpace); - fail("Exception not thrown"); - } catch (NullPointerException ex) { - // PASS - } - - throw new IllegalAccessException(); - } - } - - protected void doTestWithStrangeAddressNames(AddressSpace addressSpace, boolean hyphen, boolean longName, AddressType... types) throws Exception { - String testString = null; - if (hyphen) { - testString = String.join("-", Collections.nCopies(2, "10charhere")); - } - if (longName) { - testString = String.join("", Collections.nCopies(5, "10charhere")); - } - - consolePage = new ConsoleWebPage(selenium, TestUtils.getGlobalConsoleRoute(), clusterUser); - - consolePage.openConsolePage(); - consolePage.openAddressList(addressSpace); - - for (AddressType type : types) { - int assert_value = 1; - Address dest; - Address dest_topic = null; - if (type == AddressType.SUBSCRIPTION) { - dest_topic = new AddressBuilder() - .withNewMetadata() - .withNamespace(addressSpace.getMetadata().getNamespace()) - .withName(AddressUtils.generateAddressMetadataName(addressSpace, "topic-sub" + testString)) - .endMetadata() - .withNewSpec() - .withType("topic") - .withAddress("topic-sub" + testString) - .withPlan(getDefaultPlan(AddressType.TOPIC)) - .endSpec() - .build(); - log.info("Creating topic for subscription"); - consolePage.createAddress(dest_topic); - dest = new AddressBuilder() - .withNewMetadata() - .withNamespace(addressSpace.getMetadata().getNamespace()) - .withName(AddressUtils.generateAddressMetadataName(addressSpace, testString)) - .endMetadata() - .withNewSpec() - .withType("subscription") - .withAddress(testString) - .withTopic(dest_topic.getSpec().getAddress()) - .withPlan(DestinationPlan.STANDARD_SMALL_SUBSCRIPTION) - .endSpec() - .build(); - assert_value = 2; - } else { - dest = new AddressBuilder() - .withNewMetadata() - .withNamespace(addressSpace.getMetadata().getNamespace()) - .withName(AddressUtils.generateAddressMetadataName(addressSpace, type.toString() + "-" + testString)) - .endMetadata() - .withNewSpec() - .withType(type.toString()) - .withAddress(type.toString() + "-" + testString) - .withPlan(getDefaultPlan(type)) - .endSpec() - .build(); - assert_value = 1; - } - - consolePage.createAddress(dest); - assertWaitForValue(assert_value, () -> consolePage.getAddressItems().size(), new TimeoutBudget(120, TimeUnit.SECONDS)); - - if (type.equals(AddressType.SUBSCRIPTION)) { - consolePage.deleteAddress(dest_topic); - } - consolePage.deleteAddress(dest); - assertWaitForValue(0, () -> consolePage.getAddressItems().size(), new TimeoutBudget(20, TimeUnit.SECONDS)); - } - } - - protected void doTestValidAddressNames(AddressSpace addressSpace) throws Exception { - - consolePage = new ConsoleWebPage(selenium, TestUtils.getGlobalConsoleRoute(), clusterUser); - consolePage.openConsolePage(); - consolePage.openAddressList(addressSpace); - consolePage.openAddressCreationDialog(); - - String validName = "dummy"; - String[] invalidNames = { - "#dummy", - "du#mmy", - "dummy#", - - "*dummy", - "du*mmy", - "dummy*", - - " dummy", - "dum my", - "dummy ", - }; - - for (var name : invalidNames) { - consolePage.fillAddressName(validName); - assertFalse(consolePage.isAddressNameInvalid()); - - consolePage.fillAddressName(name); - assertTrue(consolePage.isAddressNameInvalid(), String.format("Address name %s is not marked as invalid", name)); - } - - String[] validNames = { - "du$mmy", - "du-mmy", - "dummy/foo", - "dum)my", - "dum2my", - - ":dummy", - "du:mmy", - "dummy:", - - ".dummy", - "dum.my", - "dummy.", - }; - - for (String name : validNames) { - consolePage.fillAddressName(validName); - assertFalse(consolePage.isAddressNameInvalid()); - - consolePage.fillAddressName(name); - assertFalse(consolePage.isAddressNameInvalid(), String.format("Address name %s is not marked as valid", name)); - } - } - - protected String getSnippetFirstLine(AddressSpace addressSpace) throws Exception { - consolePage = new ConsoleWebPage(selenium, TestUtils.getGlobalConsoleRoute(), clusterUser); - consolePage.openConsolePage(); - consolePage.prepareAddressSpaceInstall(addressSpace); - return consolePage.getFirstLineOfDeploymentSnippet().getText(); - } - - protected void getAndExecAddressSpaceDeploymentSnippet(AddressSpace addressSpace) throws Exception { - consolePage = new ConsoleWebPage(selenium, TestUtils.getGlobalConsoleRoute(), clusterUser); - consolePage.openConsolePage(); - consolePage.prepareAddressSpaceInstall(addressSpace); - String snippet = consolePage.getDeploymentSnippet(); - KubeCMDClient.createCR(Kubernetes.getInstance().getInfraNamespace(), snippet); - } - - protected void getAndExecAddressDeploymentSnippet(AddressSpace addressSpace, Address address) throws Exception { - consolePage = new ConsoleWebPage(selenium, TestUtils.getGlobalConsoleRoute(), clusterUser); - consolePage.openConsolePage(); - consolePage.openAddressList(addressSpace); - consolePage.prepareAddressCreation(address); - String snippet = consolePage.getDeploymentSnippet(); - KubeCMDClient.createCR(Kubernetes.getInstance().getInfraNamespace(), snippet); - } - - protected void doTestErrorDialog(AddressSpace addressSpace) throws Exception { - consolePage = new ConsoleWebPage(selenium, TestUtils.getGlobalConsoleRoute(), clusterUser); - consolePage.openConsolePage(); - consolePage.openAddressList(addressSpace); - Address address = generateAddressObject(addressSpace, DestinationPlan.STANDARD_SMALL_QUEUE); - consolePage.createAddress(address); - consolePage.createAddress(address); - consolePage.waitForErrorDialogToBePresent(); - assertThat("Error dialog is not present after error situation!", - selenium.getWebElement(consolePage::getDangerAlertElement), notNullValue()); - - } - - protected void doTestOpenShiftWithCustomCert() throws Exception { - String openshiftConfigNamespace = "openshift-config"; - String openshiftIngressNamespace = "openshift-ingress"; - String openshiftIngressOperatorNamespace = "openshift-ingress-operator"; - String openshiftAuthenticationNamespace = "openshift-authentication"; - - Map oauthDeploymentLabels = Map.of(LabelKeys.APP, "oauth-openshift"); - Map oauthRouteLabels = Map.of(LabelKeys.APP, "oauth-openshift"); - Map consoleDeploymentLabels = Map.of(LabelKeys.APP, "enmasse", LabelKeys.NAME, "console"); - - Optional oauth = kubernetes.listDeployments(openshiftAuthenticationNamespace, oauthDeploymentLabels).stream().findFirst(); - assertThat(oauth.isPresent(), is(true)); - - Optional oauthRoute = kubernetes.listRoutes(openshiftAuthenticationNamespace, oauthRouteLabels).stream().findFirst(); - assertThat(oauthRoute.isPresent(), is(true)); - - CertPair originalOauthCert = OpenSSLUtil.downloadCert(oauthRoute.get().getSpec().getHost(), 443); - - String customCaCnName = "custom-ca"; - String customIngressSecretName = "custom-ingress"; - String wildcardSan = String.format("*.%s", environment.kubernetesDomain()); - log.info("Wildcard SAN for custom cert: {}", wildcardSan); - try (CertPair ca = OpenSSLUtil.createSelfSignedCert("/O=io.enmasse/CN=MyCA"); - CertPair unsignedCluster = OpenSSLUtil.createSelfSignedCert("/O=io.enmasse//CN=MyCluster"); - CertSigningRequest csr = OpenSSLUtil.createCsr(unsignedCluster); - CertPair cluster = OpenSSLUtil.signCsr(csr, Collections.singletonList(wildcardSan), ca) - ) { - // Steps from https://docs.openshift.com/container-platform/4.3/authentication/certificates/replacing-default-ingress-certificate.html#replacing-default-ingress_replacing-default-ingress - - assertThat(cluster.getCert().canRead(), is(true)); - assertThat(cluster.getKey().canRead(), is(true)); - - ExecutionResultData ingressSecret = Exec.execute(Arrays.asList(kubernetes.getCluster().getKubeCmd(), "create", "secret", "tls", customIngressSecretName, - "--namespace", openshiftIngressNamespace, - String.format("--cert=%s", cluster.getCert()), - String.format("--key=%s", cluster.getKey())), true); - assertThat("failed to create ingress secret", ingressSecret.getRetCode(), is(true)); - - ExecutionResultData cmCustomCa = Exec.execute(Arrays.asList(kubernetes.getCluster().getKubeCmd(), "create", "configmap", customCaCnName, - "--namespace", openshiftConfigNamespace, - String.format("--from-file=ca-bundle.crt=%s", ca.getCert().getAbsolutePath())), true); - assertThat("failed to create custom-ca configmap", cmCustomCa.getRetCode(), is(true)); - - ExecutionResultData patchProxy = Exec.execute(Arrays.asList(kubernetes.getCluster().getKubeCmd(), "patch", "proxy/cluster", - "--type=merge", - String.format("--patch={\"spec\":{\"trustedCA\":{\"name\":\"%s\"}}}", customCaCnName)), true); - assertThat("failed to patch proxy/cluster", patchProxy.getRetCode(), is(true)); - - ExecutionResultData patchIngress = Exec.execute(Arrays.asList(kubernetes.getCluster().getKubeCmd(), "patch", "ingresscontroller.operator/default", - "--type=merge", - "--namespace", openshiftIngressOperatorNamespace, - String.format("--patch={\"spec\":{\"defaultCertificate\": {\"name\": \"%s\"}}}", customIngressSecretName)), true); - assertThat("failed to patch ingress", patchIngress.getRetCode(), is(true)); - - awaitCertChange(ca, oauth.get(), oauthRoute.get()); - - awaitEnMasseConsoleAvailable("Ensuring console functional after certificate change"); - SeleniumProvider.getInstance().tearDownDrivers(); - SeleniumProvider.getInstance().setupDriver(this.getClass().getSimpleName().toLowerCase().contains("chrome") ? TestUtils.getChromeDriver() : TestUtils.getFirefoxDriver()); - } finally { - Optional bundle = kubernetes.listConfigMaps(Map.of("app", "enmasse")).stream().filter(cm -> "console-trusted-ca-bundle".equals(cm.getMetadata().getName())).findFirst(); - bundle.ifPresent(cm -> log.info("console-trusted-ca-bundle resource version before rollback : {}", cm.getMetadata().getResourceVersion())); - - Exec.execute(Arrays.asList(kubernetes.getCluster().getKubeCmd(), "patch", "proxy/cluster", - "--type=merge", - "--patch={\"spec\":{\"trustedCA\": null}}"), false); - Exec.execute(Arrays.asList(kubernetes.getCluster().getKubeCmd(), "patch", "ingresscontroller.operator/default", - "--type=merge", - "--namespace", openshiftIngressOperatorNamespace, - "--patch={\"spec\":{\"defaultCertificate\": null}}"), false); - Exec.execute(Arrays.asList(kubernetes.getCluster().getKubeCmd(), "delete", "configmap", customCaCnName, - "--namespace", openshiftConfigNamespace), false); - Exec.execute(Arrays.asList(kubernetes.getCluster().getKubeCmd(), "delete", "secret", customIngressSecretName, - "--namespace", openshiftIngressNamespace), false); - - - awaitCertChange(originalOauthCert, oauth.get(), oauthRoute.get()); - awaitEnMasseConsoleAvailable("Ensuring console functional after certificate rollback"); - } - } - - private void awaitEnMasseConsoleAvailable(String forWhat) { - TestUtils.waitUntilCondition(forWhat, waitPhase -> { - try { - doTestCanOpenConsolePage(resourcesManager.getSharedAddressSpace(), clusterUser, true); - return true; - } catch (Exception e) { - return false; - } - }, new TimeoutBudget(5, TimeUnit.MINUTES)); - } - - private void awaitCertChange(CertPair expectedCa, Deployment oauthDeployment, Route oauthRoute) { - TestUtils.waitUntilCondition("Awaiting cert change", waitPhase -> { - try { - KubeCMDClient.loginUser(clusterUser.getUsername(), clusterUser.getUsername()); - verifyCertChange(expectedCa, oauthDeployment, oauthRoute); - return true; - } catch (Exception e) { - return false; - } - }, new TimeoutBudget(5, TimeUnit.MINUTES)); - } - - private void verifyCertChange(CertPair ca, Deployment oauthDeployment, Route oauthRoute) throws Exception { - Optional bundle = kubernetes.listConfigMaps(Map.of("app", "enmasse")).stream().filter(cm -> "console-trusted-ca-bundle".equals(cm.getMetadata().getName())).findFirst(); - bundle.ifPresent(cm -> log.info("console-trusted-ca-bundle resource version : {}", cm.getMetadata().getResourceVersion())); - - log.info("Awaiting openshift oauth to be ready again"); - TestUtils.waitForChangedResourceVersion(new TimeoutBudget(1, TimeUnit.MINUTES), oauthDeployment.getMetadata().getResourceVersion(), - () -> { - Optional upd = kubernetes.listDeployments(oauthDeployment.getMetadata().getNamespace(), oauthDeployment.getMetadata().getLabels()).stream().findFirst(); - assertThat(upd.isPresent(), is(true)); - return upd.get().getMetadata().getResourceVersion(); - }); - - KubeCMDClient.awaitRollout(new TimeoutBudget(3, TimeUnit.MINUTES), oauthDeployment.getMetadata().getNamespace(), oauthDeployment.getMetadata().getName()); - - // await for oauth and verify it uses the correct certificate - if (ca != null) { - String oauthHost = oauthRoute.getSpec().getHost(); - TestUtils.waitUntilCondition(String.format("Await for oauth http endpoint to become ready: %s", oauthHost), waitPhase -> { - List curl = Arrays.asList("curl", - "--cacert", ca.getCert().getAbsolutePath(), - "--verbose", String.format("https://%s", oauthHost)); - ExecutionResultData consolePing = Exec.execute(curl, true); - return consolePing.getRetCode(); - }, new TimeoutBudget(3, TimeUnit.MINUTES)); - } - } - - //============================================================================================ - //============================ Help methods ================================================== - //============================================================================================ - - private AddressSpace generateAddressSpaceObject(AddressSpaceType addressSpaceType) { - return new AddressSpaceBuilder() - .withNewMetadata() - .withName("test-address-space-" + addressSpaceType.toString()) - .withNamespace(Kubernetes.getInstance().getInfraNamespace()) - .endMetadata() - .withNewSpec() - .withType(addressSpaceType.toString()) - .withPlan(addressSpaceType.toString().equals(AddressSpaceType.BROKERED.toString()) ? - AddressSpacePlans.BROKERED : AddressSpacePlans.STANDARD_MEDIUM) - .withNewAuthenticationService() - .withName("standard-authservice") - .endAuthenticationService() - .endSpec() - .build(); - } - - private Address generateAddressObject(AddressSpace addressSpace, String destinationPlan) { - return new AddressBuilder() - .withNewMetadata() - .withNamespace(Kubernetes.getInstance().getInfraNamespace()) - .withName(AddressUtils.generateAddressMetadataName(addressSpace, "test-queue")) - .endMetadata() - .withNewSpec() - .withAddress("test-queue") - .withType("queue") - .withPlan(destinationPlan) - .endSpec() - .build(); - } - - private List
generateQueueTopicList(AddressSpace addressspace, String infix, IntStream range) { - List
addresses = new ArrayList<>(); - range.forEach(i -> { - if (i % 2 == 0) { - addresses.add(new AddressBuilder() - .withNewMetadata() - .withNamespace(addressspace.getMetadata().getNamespace()) - .withName(AddressUtils.generateAddressMetadataName(addressspace, String.format("topic-%s-%d", infix, i))) - .endMetadata() - .withNewSpec() - .withType("topic") - .withAddress(String.format("topic-%s-%d", infix, i)) - .withPlan(getDefaultPlan(AddressType.TOPIC)) - .endSpec() - .build()); - } else { - addresses.add(new AddressBuilder() - .withNewMetadata() - .withNamespace(addressspace.getMetadata().getNamespace()) - .withName(AddressUtils.generateAddressMetadataName(addressspace, String.format("queue-%s-%d", infix, i))) - .endMetadata() - .withNewSpec() - .withType("queue") - .withAddress(String.format("queue-%s-%d", infix, i)) - .withPlan(getDefaultPlan(AddressType.QUEUE)) - .endSpec() - .build()); - } - }); - return addresses; - } - - /** - * @param addressSpace dest addressspace - * @param destination dest address - * @param userCredentials messaging user credentials - * @return senders + receivers count - * @throws Exception - */ - private int attachClients(AddressSpace addressSpace, Address destination, UserCredentials userCredentials) throws Exception { - final int SENDER_COUNT = 6; - final int RECEIVER_COUNT = 4; - if (addressSpace.getSpec().getPlan().equals(AddressSpacePlans.BROKERED)) { - for (int i = 0; i < SENDER_COUNT; i++) { - getClientUtils().attachSender(addressSpace, destination, userCredentials, 1000, 60000); - if (i < RECEIVER_COUNT) { - getClientUtils().attachReceiver(addressSpace, destination, userCredentials, 1100); - } - } - } else { - getClientUtils().attachConnector(addressSpace, destination, 1, SENDER_COUNT, RECEIVER_COUNT, userCredentials, 60000); - } - return (SENDER_COUNT + RECEIVER_COUNT); - } - - private List attachClients(AddressSpace addressSpace, List
destinations, UserCredentials userCredentials) throws Exception { - List clients = new ArrayList<>(); - for (Address destination : destinations) { - clients.add(getClientUtils().attachConnector(addressSpace, destination, 1, 6, 1, userCredentials, 60000)); - clients.add(getClientUtils().attachConnector(addressSpace, destination, 1, 4, 4, userCredentials, 60000)); - clients.add(getClientUtils().attachConnector(addressSpace, destination, 1, 1, 6, userCredentials, 60000)); - } - Thread.sleep(5000); - return clients; - } - - private void assertAddressType(String message, List allItems, AddressType type) { - assertThat(message, getAddressProperty(allItems, (item -> item.getType().contains(type.toString()))).size(), is(allItems.size())); - } - - private void assertAddressName(String message, List allItems, String subString) { - assertThat(message, getAddressProperty(allItems, (item -> item.getAddress().contains(subString))).size(), is(allItems.size())); - } - - private List getConnectionProperty(List allItems, Predicate f) { - return allItems.stream().filter(f).collect(Collectors.toList()); - } - - private List getAddressProperty(List allItems, Predicate f) { - return allItems.stream().filter(f).collect(Collectors.toList()); - } - - private void waitUntilAddressSpaceActive(AddressSpace addressSpace) throws Exception { - String name = addressSpace.getMetadata().getName(); - resourcesManager.waitForAddressSpaceReady(addressSpace); - Boolean active = Optional.ofNullable(selenium.waitUntilItemPresent(60, () -> consolePage.getAddressSpaceItem(addressSpace))) - .map(webItem -> webItem.getStatus().contains("Active")) - .orElseGet(() -> { - log.error("AddressSpaceWebItem {} not present", name); - return false; - }); - assertTrue(active, String.format("Address space %s not marked active in UI within timeout", name)); - } - -} diff --git a/systemtests/src/test/java/io/enmasse/systemtest/bases/web/WebSocketBrowserTest.java b/systemtests/src/test/java/io/enmasse/systemtest/bases/web/WebSocketBrowserTest.java deleted file mode 100644 index 88c88ba97e0..00000000000 --- a/systemtests/src/test/java/io/enmasse/systemtest/bases/web/WebSocketBrowserTest.java +++ /dev/null @@ -1,41 +0,0 @@ -/* - * Copyright 2018, EnMasse authors. - * License: Apache License 2.0 (see the file LICENSE or http://apache.org/licenses/LICENSE-2.0.html). - */ -package io.enmasse.systemtest.bases.web; - - -import io.enmasse.address.model.Address; -import io.enmasse.systemtest.bases.TestBase; -import io.enmasse.systemtest.bases.shared.ITestBaseShared; -import io.enmasse.systemtest.model.addressspace.AddressSpaceType; -import io.enmasse.systemtest.selenium.SeleniumProvider; -import io.enmasse.systemtest.selenium.page.RheaWebPage; -import io.enmasse.systemtest.utils.AddressSpaceUtils; -import org.junit.jupiter.api.BeforeEach; - -import static org.junit.jupiter.api.Assertions.assertTrue; - -public abstract class WebSocketBrowserTest extends TestBase implements ITestBaseShared { - - private RheaWebPage rheaWebPage; - - @BeforeEach - public void setUpWebConsoleTests() throws Exception { - rheaWebPage = new RheaWebPage(SeleniumProvider.getInstance()); - resourcesManager.deleteAddresses(getSharedAddressSpace()); - } - - //============================================================================================ - //============================ do test methods =============================================== - //============================================================================================ - - protected void doWebSocketSendReceive(Address destination) throws Exception { - resourcesManager.setAddresses(destination); - int count = 10; - - rheaWebPage.sendReceiveMessages(AddressSpaceUtils.getMessagingWssRoute(getSharedAddressSpace()).toString(), destination.getSpec().getAddress(), - count, defaultCredentials, AddressSpaceType.valueOf(getSharedAddressSpace().getSpec().getType().toUpperCase())); - assertTrue(rheaWebPage.checkCountMessage(count * 2), "Browser client didn't sent and received all messages"); - } -} diff --git a/systemtests/src/test/java/io/enmasse/systemtest/resources/CliOutputDataTest.java b/systemtests/src/test/java/io/enmasse/systemtest/framework/CliOutputDataTest.java similarity index 98% rename from systemtests/src/test/java/io/enmasse/systemtest/resources/CliOutputDataTest.java rename to systemtests/src/test/java/io/enmasse/systemtest/framework/CliOutputDataTest.java index 8503b3c9191..0e031eb2df8 100644 --- a/systemtests/src/test/java/io/enmasse/systemtest/resources/CliOutputDataTest.java +++ b/systemtests/src/test/java/io/enmasse/systemtest/framework/CliOutputDataTest.java @@ -2,9 +2,10 @@ * Copyright 2020, EnMasse authors. * License: Apache License 2.0 (see the file LICENSE or http://apache.org/licenses/LICENSE-2.0.html). */ -package io.enmasse.systemtest.resources; +package io.enmasse.systemtest.framework; import io.enmasse.systemtest.TestTag; +import io.enmasse.systemtest.resources.CliOutputData; import io.enmasse.systemtest.resources.CliOutputData.CliOutputDataType; import org.junit.jupiter.api.Tag; diff --git a/systemtests/src/test/java/io/enmasse/systemtest/iot/CredentialsTest.java b/systemtests/src/test/java/io/enmasse/systemtest/iot/CredentialsTest.java deleted file mode 100644 index c2303c03139..00000000000 --- a/systemtests/src/test/java/io/enmasse/systemtest/iot/CredentialsTest.java +++ /dev/null @@ -1,32 +0,0 @@ -/* - * Copyright 2019, EnMasse authors. - * License: Apache License 2.0 (see the file LICENSE or http://apache.org/licenses/LICENSE-2.0.html). - */ - -package io.enmasse.systemtest.iot; - -import io.enmasse.systemtest.TestTag; -import io.vertx.core.json.JsonObject; - -import org.junit.jupiter.api.Tag; -import org.junit.jupiter.api.Test; - -import static org.junit.Assert.assertNotNull; - -@Tag(TestTag.FRAMEWORK) -public class CredentialsTest { - - @Test - void testCreateSecret() { - var creds = JsonObject.mapFrom(CredentialsRegistryClient.createCredentialsObject("auth-id", "foo", null)); - - var secrets = creds.getJsonArray("secrets"); - assertNotNull(secrets); - var secret = secrets.getJsonObject(0); - assertNotNull(secret); - - assertNotNull(secret.getBinary("pwd-hash")); - assertNotNull(secret.getBinary("salt")); - } - -} diff --git a/systemtests/src/test/java/io/enmasse/systemtest/iot/DeviceCertificateManagerTest.java b/systemtests/src/test/java/io/enmasse/systemtest/iot/DeviceCertificateManagerTest.java deleted file mode 100644 index f0376781fef..00000000000 --- a/systemtests/src/test/java/io/enmasse/systemtest/iot/DeviceCertificateManagerTest.java +++ /dev/null @@ -1,52 +0,0 @@ -/* - * Copyright 2020, EnMasse authors. - * License: Apache License 2.0 (see the file LICENSE or http://apache.org/licenses/LICENSE-2.0.html). - */ - -package io.enmasse.systemtest.iot; - -import static io.enmasse.systemtest.TestTag.FRAMEWORK; -import static org.junit.Assert.assertNotNull; - -import java.security.cert.X509Certificate; -import java.time.Duration; -import java.time.Instant; - -import javax.security.auth.x500.X500Principal; - -import org.junit.jupiter.api.Tag; -import org.junit.jupiter.api.Test; - -import io.enmasse.systemtest.iot.DeviceCertificateManager.Device; -import io.enmasse.systemtest.iot.DeviceCertificateManager.Mode; - -@Tag(FRAMEWORK) -public class DeviceCertificateManagerTest { - - @Test - public void testRsa() throws Exception { - final DeviceCertificateManager mgr = new DeviceCertificateManager(Mode.RSA, new X500Principal("CN=foo,OU=bar,O=baz")); - - final X509Certificate cert = mgr.getCertificate(); - assertNotNull(cert); - - final Device device = mgr.createDevice("dev1", Instant.now(), Duration.ofDays(90)); - assertNotNull(device); - assertNotNull(device.getKey()); - assertNotNull(device.getCertificate()); - } - - @Test - public void testEc() throws Exception { - final DeviceCertificateManager mgr = new DeviceCertificateManager(Mode.EC, new X500Principal("CN=foo,OU=bar,O=baz")); - - final X509Certificate cert = mgr.getCertificate(); - assertNotNull(cert); - - final Device device = mgr.createDevice("dev1", Instant.now(), Duration.ofDays(90)); - assertNotNull(device); - assertNotNull(device.getKey()); - assertNotNull(device.getCertificate()); - } - -} diff --git a/systemtests/src/test/java/io/enmasse/systemtest/iot/DeviceSupplier.java b/systemtests/src/test/java/io/enmasse/systemtest/iot/DeviceSupplier.java deleted file mode 100644 index 0b024785756..00000000000 --- a/systemtests/src/test/java/io/enmasse/systemtest/iot/DeviceSupplier.java +++ /dev/null @@ -1,65 +0,0 @@ -/* - * Copyright 2020, EnMasse authors. - * License: Apache License 2.0 (see the file LICENSE or http://apache.org/licenses/LICENSE-2.0.html). - */ - -package io.enmasse.systemtest.iot; - -import java.util.Objects; - -import org.junit.jupiter.params.provider.MethodSource; - -import io.enmasse.systemtest.iot.IoTTestSession.Device; - -/** - * A supplier for devices. Required due to an issue in Surefire. - *

- * This is a workaround for SUREFIRE-1799, which silently drops unit tests in case - * there is an exception being thrown in a method of a {@link MethodSource}. As we - * create {@link Device}s for parametrized tests and creation of devices might fail, - * that will be an issue. - *

- * The idea is to delay the actual execution of the device creation until it - * is running in the actual test case. So instead of creating devices, we create - * code that creates devices. The call to the {@link #get()} method should perform - * the actual device creation, and it may fail, which is reported as a test failure. - *

- * @see SUREFIRE-1799 - */ -@FunctionalInterface -public interface DeviceSupplier { - - public Device get() throws Exception; - - /** - * Allows to override the output of the {@link #toString()} method. - *

- * This may be used to provide a stable name for parameterized test. - * - * @implNote This was originally part of the {@link Device} class. Due to - * SUREFIRE-1799 this code currently lives here. - * - * @param name The value to report from {@link #toString()}. - * @return The new instance, reporting the provided name. - */ - static DeviceSupplier named(final String name, final DeviceSupplier supplier) { - - Objects.requireNonNull(name); - Objects.requireNonNull(supplier); - - return new DeviceSupplier() { - - @Override - public Device get() throws Exception { - return supplier.get(); - } - - @Override - public String toString() { - return name; - } - }; - - } - -} diff --git a/systemtests/src/test/java/io/enmasse/systemtest/iot/IoTTestSessionTest.java b/systemtests/src/test/java/io/enmasse/systemtest/iot/IoTTestSessionTest.java deleted file mode 100644 index a3346e7db16..00000000000 --- a/systemtests/src/test/java/io/enmasse/systemtest/iot/IoTTestSessionTest.java +++ /dev/null @@ -1,82 +0,0 @@ -/* - * Copyright 2020, EnMasse authors. - * License: Apache License 2.0 (see the file LICENSE or http://apache.org/licenses/LICENSE-2.0.html). - */ - -package io.enmasse.systemtest.iot; - -import static io.enmasse.systemtest.TestTag.FRAMEWORK; -import static io.enmasse.systemtest.iot.IoTTestSession.Adapter.HTTP; -import static org.junit.Assert.assertTrue; -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertNotNull; - -import java.util.concurrent.atomic.AtomicBoolean; - -import org.junit.jupiter.api.Tag; -import org.junit.jupiter.api.Test; - -import io.enmasse.iot.model.v1.IoTConfig; -import io.enmasse.systemtest.iot.IoTTestSession.Adapter; - -@Tag(FRAMEWORK) -public class IoTTestSessionTest { - - @Test - public void testNameInDefaultConfig() throws Exception { - var config = IoTTestSession.createDefaultConfig("default-ns", true).build(); - - assertDefaultConfig(config); - } - - @Test - public void testNameInDefaultConfigAfterChange() throws Exception { - var configBuilder = IoTTestSession.createDefaultConfig("default-ns", true); - - assertDefaultConfig(configBuilder.build()); - - for (Adapter adapter : Adapter.values()) { - configBuilder = adapter.disable(configBuilder); - } - configBuilder = HTTP.enable(configBuilder); - - assertDefaultConfig(configBuilder.build()); - - } - - private void assertDefaultConfig(IoTConfig config) { - assertNotNull(config); - - assertNotNull(config.getMetadata()); - assertEquals("default", config.getMetadata().getName()); - assertEquals("default-ns", config.getMetadata().getNamespace()); - } - - @Test - public void testEnableAdapter() throws Exception { - AtomicBoolean called = new AtomicBoolean(); - - IoTTestSession - .create("default-ns", true) - .adapters(Adapter.HTTP) - .config(configBuilder -> { - - var config = configBuilder.build(); - - assertNotNull(config.getSpec()); - assertNotNull(config.getSpec().getAdapters()); - - assertNotNull(config.getSpec().getAdapters().getHttp()); - assertEquals(Boolean.TRUE, config.getSpec().getAdapters().getHttp().getEnabled()); - - assertNotNull(config.getSpec().getAdapters().getMqtt()); - assertEquals(Boolean.FALSE, config.getSpec().getAdapters().getMqtt().getEnabled()); - - called.set(true); - - }); - - assertTrue(called.get()); - } - -} diff --git a/systemtests/src/test/java/io/enmasse/systemtest/iot/KeyStoreCreatorTest.java b/systemtests/src/test/java/io/enmasse/systemtest/iot/KeyStoreCreatorTest.java deleted file mode 100644 index 7cefd2e5f7c..00000000000 --- a/systemtests/src/test/java/io/enmasse/systemtest/iot/KeyStoreCreatorTest.java +++ /dev/null @@ -1,51 +0,0 @@ -/* - * Copyright 2020, EnMasse authors. - * License: Apache License 2.0 (see the file LICENSE or http://apache.org/licenses/LICENSE-2.0.html). - */ - -package io.enmasse.systemtest.iot; - -import static org.junit.Assert.assertNotNull; - -import java.time.Duration; -import java.time.Instant; - -import javax.security.auth.x500.X500Principal; - -import org.junit.jupiter.api.Tag; -import org.junit.jupiter.api.Test; -import org.junit.jupiter.params.ParameterizedTest; -import org.junit.jupiter.params.provider.EnumSource; - -import io.enmasse.systemtest.TestTag; -import io.enmasse.systemtest.iot.DeviceCertificateManager.Mode; - -@Tag(TestTag.FRAMEWORK) -public class KeyStoreCreatorTest { - - @Test - public void testBasicRsa() throws Exception { - var mgr = new DeviceCertificateManager(Mode.RSA, new X500Principal("O=EnMasse,C=IO")); - var device = mgr.createDevice("foo", Instant.now(), Duration.ofDays(90)); - KeyStoreCreator.from(device.getKey().getPrivate(), device.getCertificate()); - } - - @Test - public void testBasicEc() throws Exception { - var mgr = new DeviceCertificateManager(Mode.EC, new X500Principal("O=EnMasse,C=IO")); - var device = mgr.createDevice("foo", Instant.now(), Duration.ofDays(90)); - KeyStoreCreator.from(device.getKey().getPrivate(), device.getCertificate()); - } - - @ParameterizedTest(name = "testToByteArray-{0}") - @EnumSource(Mode.class) - public void testToByteArray(final Mode mode) throws Exception { - var mgr = new DeviceCertificateManager(mode, new X500Principal("O=EnMasse,C=IO")); - var device = mgr.createDevice("foo", Instant.now(), Duration.ofDays(90)); - - byte[] bytes = KeyStoreCreator.toByteArray(device.getKey().getPrivate(), device.getCertificate()); - - assertNotNull(bytes); - } - -} diff --git a/systemtests/src/test/java/io/enmasse/systemtest/iot/MessageSendTesterTest.java b/systemtests/src/test/java/io/enmasse/systemtest/iot/MessageSendTesterTest.java deleted file mode 100644 index a74c23fa442..00000000000 --- a/systemtests/src/test/java/io/enmasse/systemtest/iot/MessageSendTesterTest.java +++ /dev/null @@ -1,62 +0,0 @@ -/* - * Copyright 2020, EnMasse authors. - * License: Apache License 2.0 (see the file LICENSE or http://apache.org/licenses/LICENSE-2.0.html). - */ - -package io.enmasse.systemtest.iot; - -import static org.junit.jupiter.api.Assertions.assertEquals; - -import java.nio.charset.StandardCharsets; -import java.util.Arrays; - -import org.junit.jupiter.api.Tag; -import org.junit.jupiter.api.Test; - -import io.enmasse.systemtest.TestTag; -import io.vertx.core.json.JsonObject; - -@Tag(TestTag.FRAMEWORK) -public class MessageSendTesterTest { - - @Test - public void testFillingEmpty() { - final JsonObject json = new JsonObject(); - var result = MessageSendTester.fillWithPayload(json, 1024); - assertEquals(1024, result.length()); - } - - @Test - public void testFillUtf8() { - final JsonObject json = new JsonObject(); - json.put("some-utf-8", "\u1F926"); - var result = MessageSendTester.fillWithPayload(json, 1024); - assertEquals(1024, result.length()); - } - - @Test - public void testFillingNonEmpty() { - - final int fixedLength = "{\"init\":\"\"}".getBytes(StandardCharsets.UTF_8).length; - - for (int initialLength = fixedLength; initialLength < 1024; initialLength++) { - - final char init[] = new char[initialLength - fixedLength]; - Arrays.fill(init, 'b'); - final String initValue = String.valueOf(init); - - for (int expectedLength = initialLength + MessageSendTester.FIXED_JSON_EXTRA_SIZE + 1; expectedLength < 1024; expectedLength++) { - - final JsonObject json = new JsonObject(); - json.put("init", initValue); - - var result = MessageSendTester.fillWithPayload(json, expectedLength); - assertEquals(expectedLength, result.length()); - - } - - } - - } - -} diff --git a/systemtests/src/test/java/io/enmasse/systemtest/iot/StandardIoTTests.java b/systemtests/src/test/java/io/enmasse/systemtest/iot/StandardIoTTests.java deleted file mode 100644 index 277a841e8e9..00000000000 --- a/systemtests/src/test/java/io/enmasse/systemtest/iot/StandardIoTTests.java +++ /dev/null @@ -1,27 +0,0 @@ -/* - * Copyright 2020, EnMasse authors. - * License: Apache License 2.0 (see the file LICENSE or http://apache.org/licenses/LICENSE-2.0.html). - */ - -package io.enmasse.systemtest.iot; - -import java.util.List; - -public interface StandardIoTTests extends IoTTests { - - /** - * Test the test session. - */ - public IoTTestSession getSession(); - - /** - * Get a list of devices which must succeed. - */ - public List getDevices(); - - /** - * Get a list of devices which must fail. - */ - public List getInvalidDevices(); - -} diff --git a/systemtests/src/test/java/io/enmasse/systemtest/iot/http/StandardIoTHttpTests.java b/systemtests/src/test/java/io/enmasse/systemtest/iot/http/StandardIoTHttpTests.java deleted file mode 100644 index 6796c8ad454..00000000000 --- a/systemtests/src/test/java/io/enmasse/systemtest/iot/http/StandardIoTHttpTests.java +++ /dev/null @@ -1,199 +0,0 @@ -/* - * Copyright 2020, EnMasse authors. - * License: Apache License 2.0 (see the file LICENSE or http://apache.org/licenses/LICENSE-2.0.html). - */ - -package io.enmasse.systemtest.iot.http; - -import static io.enmasse.systemtest.TestTag.ACCEPTANCE; -import static io.enmasse.systemtest.iot.HttpAdapterClient.causedBy; -import static io.enmasse.systemtest.iot.HttpAdapterClient.ResponseException.statusCode; -import static org.junit.jupiter.api.Assertions.assertThrows; - -import java.time.Duration; -import java.util.concurrent.TimeoutException; - -import javax.net.ssl.SSLHandshakeException; - -import org.junit.jupiter.api.Tag; -import org.junit.jupiter.params.ParameterizedTest; -import org.junit.jupiter.params.provider.MethodSource; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import io.enmasse.systemtest.iot.DeviceSupplier; -import io.enmasse.systemtest.iot.HttpAdapterClient; -import io.enmasse.systemtest.iot.MessageSendTester; -import io.enmasse.systemtest.iot.MessageSendTester.ConsumerFactory; -import io.enmasse.systemtest.iot.StandardIoTTests; - -public interface StandardIoTHttpTests extends StandardIoTTests { - - static final Logger log = LoggerFactory.getLogger(StandardIoTHttpTests.class); - - /** - * Single telemetry message with attached consumer. - */ - @Tag(ACCEPTANCE) - @ParameterizedTest(name = "testHttpTelemetrySingle-{0}") - @MethodSource("getDevices") - default void testHttpTelemetrySingle(final DeviceSupplier device) throws Exception { - - try (HttpAdapterClient client = device.get().createHttpAdapterClient()) { - new MessageSendTester() - .type(MessageSendTester.Type.TELEMETRY) - .delay(Duration.ofSeconds(1)) - .consumerFactory(ConsumerFactory.of(getSession().getConsumerClient(), getSession().getTenantId())) - .sender(client::send) - .amount(1) - .consume(MessageSendTester.Consume.BEFORE) - .execute(); - } - - } - - /** - * Test a single event message. - *
- * Send a single message, no consumer attached. The message gets delivered - * when the consumer attaches. - */ - @ParameterizedTest(name = "testHttpEventSingle-{0}") - @MethodSource("getDevices") - default void testHttpEventSingle(final DeviceSupplier device) throws Exception { - - try (HttpAdapterClient client = device.get().createHttpAdapterClient()) { - new MessageSendTester() - .type(MessageSendTester.Type.EVENT) - .delay(Duration.ofSeconds(1)) - .consumerFactory(ConsumerFactory.of(getSession().getConsumerClient(), getSession().getTenantId())) - .sender(client::send) - .amount(1) - .consume(MessageSendTester.Consume.AFTER) - .execute(); - } - - } - - /** - * Test a batch of telemetry messages, consumer is started before sending. - *
- * This is the normal telemetry case. - */ - @ParameterizedTest(name = "testHttpTelemetryBatch50-{0}") - @MethodSource("getDevices") - default void testHttpTelemetryBatch50(final DeviceSupplier device) throws Exception { - - try (HttpAdapterClient client = device.get().createHttpAdapterClient()) { - new MessageSendTester() - .type(MessageSendTester.Type.TELEMETRY) - .delay(Duration.ofSeconds(1)) - .consumerFactory(ConsumerFactory.of(getSession().getConsumerClient(), getSession().getTenantId())) - .sender(client::send) - .amount(50) - .consume(MessageSendTester.Consume.BEFORE) - .execute(); - } - - } - - /** - * Test a batch of events, having no consumer attached. - *
- * As events get buffered by the broker, there is no requirement to start - * a consumer before sending the messages. However when the consumer is - * attached, it should receive those messages. - */ - @ParameterizedTest(name = "testHttpEventBatch5After-{0}") - @MethodSource("getDevices") - default void testHttpEventBatch5After(final DeviceSupplier device) throws Exception { - - try (HttpAdapterClient client = device.get().createHttpAdapterClient()) { - new MessageSendTester() - .type(MessageSendTester.Type.EVENT) - .delay(Duration.ofMillis(100)) - .additionalSendTimeout(Duration.ofSeconds(10)) - .consumerFactory(ConsumerFactory.of(getSession().getConsumerClient(), getSession().getTenantId())) - .sender(client::send) - .amount(5) - .consume(MessageSendTester.Consume.AFTER) - .execute(); - } - - } - - /** - * Test a batch of events, starting the consumer before sending. - *
- * This is the default use case with events, and should simply work - * as with telemetry. - */ - @ParameterizedTest(name = "testHttpEventBatch5Before-{0}") - @MethodSource("getDevices") - default void testHttpEventBatch5Before(final DeviceSupplier device) throws Exception { - - try (HttpAdapterClient client = device.get().createHttpAdapterClient()) { - new MessageSendTester() - .type(MessageSendTester.Type.EVENT) - .delay(Duration.ZERO) - .additionalSendTimeout(Duration.ofSeconds(10)) - .consumerFactory(ConsumerFactory.of(getSession().getConsumerClient(), getSession().getTenantId())) - .sender(client::send) - .amount(5) - .consume(MessageSendTester.Consume.BEFORE) - .execute(); - } - - } - - /** - * Test for an invalid device. - *
- * With an invalid device, no messages must pass. - */ - @ParameterizedTest(name = "testHttpDeviceFails-{0}") - @MethodSource("getInvalidDevices") - default void testHttpDeviceFails(final DeviceSupplier deviceSupplier) throws Exception { - - log.info("Testing invalid devices, the following exception may be expected"); - var device = deviceSupplier.get(); - - /* - * We test an invalid device by trying to send either telemetry or event messages. - * Two separate connections, and more than one message. - */ - - try (HttpAdapterClient client = device.createHttpAdapterClient()) { - client.suppressExceptions( - statusCode(401) - .or(causedBy(SSLHandshakeException.class))); - assertThrows(TimeoutException.class, () -> { - new MessageSendTester() - .type(MessageSendTester.Type.TELEMETRY) - .delay(Duration.ofSeconds(1)) - .consumerFactory(ConsumerFactory.of(getSession().getConsumerClient(), getSession().getTenantId())) - .sender(client::send) - .amount(5) - .consume(MessageSendTester.Consume.BEFORE) - .execute(); - }); - } - - try (HttpAdapterClient client = device.createHttpAdapterClient()) { - client.suppressExceptions( - statusCode(401) - .or(causedBy(SSLHandshakeException.class))); - assertThrows(TimeoutException.class, () -> { - new MessageSendTester() - .type(MessageSendTester.Type.EVENT) - .delay(Duration.ofSeconds(1)) - .consumerFactory(ConsumerFactory.of(getSession().getConsumerClient(), getSession().getTenantId())) - .sender(client::send) - .amount(5) - .consume(MessageSendTester.Consume.BEFORE) - .execute(); - }); - } - - } -} diff --git a/systemtests/src/test/java/io/enmasse/systemtest/iot/isolated/AbstractMaxPayloadSizeTest.java b/systemtests/src/test/java/io/enmasse/systemtest/iot/isolated/AbstractMaxPayloadSizeTest.java deleted file mode 100644 index f54a937838f..00000000000 --- a/systemtests/src/test/java/io/enmasse/systemtest/iot/isolated/AbstractMaxPayloadSizeTest.java +++ /dev/null @@ -1,144 +0,0 @@ -/* - * Copyright 2020, EnMasse authors. - * License: Apache License 2.0 (see the file LICENSE or http://apache.org/licenses/LICENSE-2.0.html). - */ - -package io.enmasse.systemtest.iot.isolated; - -import static org.junit.jupiter.api.Assertions.assertThrows; - -import java.time.Duration; -import java.util.concurrent.TimeoutException; - -import org.junit.jupiter.api.Test; - -import io.enmasse.systemtest.bases.TestBase; -import io.enmasse.systemtest.bases.iot.ITestIoTIsolated; -import io.enmasse.systemtest.iot.IoTTestSession; -import io.enmasse.systemtest.iot.IoTTestSession.Adapter; -import io.enmasse.systemtest.iot.IoTTestSession.Device; -import io.enmasse.systemtest.iot.MessageSendTester; -import io.enmasse.systemtest.iot.MessageSendTester.ConsumerFactory; -import io.enmasse.systemtest.iot.MessageSendTester.Sender; -import io.enmasse.systemtest.iot.MessageSendTester.Type; - -public abstract class AbstractMaxPayloadSizeTest extends TestBase implements ITestIoTIsolated { - - protected abstract Adapter adapter(); - - protected abstract Sender sender(Device device) throws Exception; - - /** - * Reduce the payload by this amount of bytes. - *

- * This is required by e.g. the MQTT protocol, as the topic name counts against the total length of - * the MQTT payload. - * - * @param type the type to send. - * - * @return The number of bytes to remove from the payload. - */ - protected int reducePayloadBy(final Type type) { - return 0; - } - - @Test - public void testDecreasedMaxPayloadDefault() throws Exception { - - IoTTestSession - .createDefault() - .adapters(adapter()) - .config(config -> config - .editOrNewSpec() - .editOrNewAdapters() - .editOrNewDefaults() - .withMaxPayloadSize(256) - .endDefaults() - .endAdapters() - .endSpec()) - .run(session -> { - - var device = session - .newDevice("4711") - .register() - .setPassword("auth-1", "123456"); - - // we expect the sending to fail, due to timeouts, as the payload exceeds the maximum - - assertThrows(TimeoutException.class, () -> { - new MessageSendTester() - .type(MessageSendTester.Type.TELEMETRY) - .payloadSize(1024 - reducePayloadBy(MessageSendTester.Type.TELEMETRY)) - .delay(Duration.ofSeconds(1)) - .consumerFactory(ConsumerFactory.of(session.getConsumerClient(), session.getTenantId())) - .sender(sender(device)) - .amount(50) - .consume(MessageSendTester.Consume.BEFORE) - .execute(); - }); - - }); - - } - - @Test - public void testIncreasedMaxPayloadDefault() throws Exception { - - IoTTestSession - .createDefault() - .adapters(adapter()) - .config(config -> config - .editOrNewSpec() - .editOrNewAdapters() - .editOrNewDefaults() - .withMaxPayloadSize(16 * 1024) - .endDefaults() - .endAdapters() - .endSpec()) - .run(session -> { - - var device = session - .newDevice("4711") - .register() - .setPassword("auth-1", "123456"); - - new MessageSendTester() - .type(MessageSendTester.Type.TELEMETRY) - .payloadSize((16 * 1024) - reducePayloadBy(MessageSendTester.Type.TELEMETRY)) - .delay(Duration.ofSeconds(1)) - .consumerFactory(ConsumerFactory.of(session.getConsumerClient(), session.getTenantId())) - .sender(sender(device)) - .amount(50) - .consume(MessageSendTester.Consume.BEFORE) - .execute(); - }); - - } - - @Test - public void testStandardMaxPayloadDefault() throws Exception { - - IoTTestSession - .createDefault() - .adapters(adapter()) - .run(session -> { - - var device = session - .newDevice("4711") - .register() - .setPassword("auth-1", "123456"); - - new MessageSendTester() - .type(MessageSendTester.Type.TELEMETRY) - .payloadSize(1024 - reducePayloadBy(MessageSendTester.Type.TELEMETRY)) - .delay(Duration.ofSeconds(1)) - .consumerFactory(ConsumerFactory.of(session.getConsumerClient(), session.getTenantId())) - .sender(sender(device)) - .amount(50) - .consume(MessageSendTester.Consume.BEFORE) - .execute(); - }); - - } - -} diff --git a/systemtests/src/test/java/io/enmasse/systemtest/iot/isolated/IoTProjectManagedTest.java b/systemtests/src/test/java/io/enmasse/systemtest/iot/isolated/IoTProjectManagedTest.java deleted file mode 100644 index 8cdc076141a..00000000000 --- a/systemtests/src/test/java/io/enmasse/systemtest/iot/isolated/IoTProjectManagedTest.java +++ /dev/null @@ -1,421 +0,0 @@ -/* - * Copyright 2018-2020, EnMasse authors. - * License: Apache License 2.0 (see the file LICENSE or http://apache.org/licenses/LICENSE-2.0.html). - */ -package io.enmasse.systemtest.iot.isolated; - -import static io.enmasse.systemtest.TestTag.ACCEPTANCE; -import static io.enmasse.systemtest.TestTag.SMOKE; -import static io.enmasse.systemtest.iot.DefaultDeviceRegistry.newDefaultInstance; -import static io.enmasse.systemtest.time.TimeoutBudget.ofDuration; -import static io.enmasse.user.model.v1.Operation.recv; -import static io.enmasse.user.model.v1.Operation.send; -import static java.time.Duration.ofMinutes; -import static java.util.Arrays.asList; -import static java.util.EnumSet.of; -import static java.util.Optional.ofNullable; -import static org.hamcrest.MatcherAssert.assertThat; -import static org.hamcrest.beans.HasPropertyWithValue.hasProperty; -import static org.hamcrest.collection.IsCollectionWithSize.hasSize; -import static org.hamcrest.collection.IsIterableContainingInAnyOrder.containsInAnyOrder; -import static org.hamcrest.core.AllOf.allOf; -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertNotNull; -import static org.junit.jupiter.api.Assertions.assertTrue; - -import java.time.Duration; -import java.util.Arrays; -import java.util.List; -import java.util.Optional; -import java.util.Set; -import java.util.function.Function; -import java.util.stream.Collectors; -import java.util.stream.Stream; - -import org.hamcrest.Matcher; -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Tag; -import org.junit.jupiter.api.Test; -import org.slf4j.Logger; - -import io.enmasse.address.model.Address; -import io.enmasse.address.model.AddressSpace; -import io.enmasse.address.model.AddressSpaceStatus; -import io.enmasse.address.model.AddressStatus; -import io.enmasse.address.model.KubeUtil; -import io.enmasse.address.model.Phase; -import io.enmasse.iot.model.v1.DoneableIoTProject; -import io.enmasse.iot.model.v1.IoTProject; -import io.enmasse.iot.model.v1.IoTProjectList; -import io.enmasse.systemtest.bases.TestBase; -import io.enmasse.systemtest.bases.iot.ITestIoTIsolated; -import io.enmasse.systemtest.iot.IoTTestSession; -import io.enmasse.systemtest.logs.CustomLogger; -import io.enmasse.systemtest.model.address.AddressType; -import io.enmasse.systemtest.model.addressplan.DestinationPlan; -import io.enmasse.systemtest.model.addressspace.AddressSpacePlans; -import io.enmasse.systemtest.model.addressspace.AddressSpaceType; -import io.enmasse.systemtest.platform.Kubernetes; -import io.enmasse.systemtest.time.TimeoutBudget; -import io.enmasse.systemtest.utils.AddressUtils; -import io.enmasse.systemtest.utils.IoTUtils; -import io.enmasse.systemtest.utils.TestUtils; -import io.enmasse.user.model.v1.Operation; -import io.enmasse.user.model.v1.User; -import io.enmasse.user.model.v1.UserAuthorization; -import io.enmasse.user.model.v1.UserStatus; -import io.fabric8.kubernetes.api.model.Doneable; -import io.fabric8.kubernetes.api.model.HasMetadata; -import io.fabric8.kubernetes.api.model.ObjectMeta; -import io.fabric8.kubernetes.api.model.OwnerReference; -import io.fabric8.kubernetes.client.dsl.MixedOperation; -import io.fabric8.kubernetes.client.dsl.Resource; - -class IoTProjectManagedTest extends TestBase implements ITestIoTIsolated { - private final static Logger log = CustomLogger.getLogger(); - - @FunctionalInterface - interface ProjectModificator { - /** - * Modify a project - * - * @param timeout The timeout budget the operation has available. - * @param project The project to work with. It is an provisioned project, which already passed a - * call to {@link IoTProjectManagedTest#assertManaged(IoTProject)}. - * @return {@code true} if the state has been change and a final call to - * {@link IoTProjectManagedTest#assertManaged(IoTProject)} to be performed. - */ - boolean modify(TimeoutBudget timeout, IoTProject project) throws Exception; - } - - private Kubernetes kubernetes = Kubernetes.getInstance(); - - private MixedOperation> projectClient; - - @BeforeEach - void createIoTClient() { - this.projectClient = this.kubernetes.getIoTProjectClient(IOT_PROJECT_NAMESPACE); - } - - /** - * Simply create a project and directly test it. - */ - @Test - @Tag(SMOKE) - @Tag(ACCEPTANCE) - void testCreate() throws Exception { - // run a default test, with no modifications - doTestAddressSpaceWithModifications(ofDuration(ofMinutes(5)), (timeout, project) -> false); - } - - /** - * Delete a resource and wait for it to be re-created properly. - * - * @param The resource type. - * @param The list type of the resource. - * @param The {@link Doneable} type. - * @param project The IoT project to process. - * @param clazz The class instance of the resource type. - * @param name The kubernetes name of the resource. - * @param basicClient The basic resource client. Does not need to be namespaced. - * @param phaseExtractor The function which extracts the phase information. - */ - ProjectModificator deleteAndWaitResource(final Class clazz, final Function nameExtractor, - final MixedOperation> basicClient, final Function> phaseExtractor) { - - return (timeout, project) -> { - - final var name = nameExtractor.apply(project); - final var className = clazz.getSimpleName(); - final var client = basicClient.inNamespace(IOT_PROJECT_NAMESPACE); - - // get the current address space - - final var namedClient = client.withName(name); - final var currentResource = namedClient.get(); - assertNotNull(currentResource, () -> String.format("Unable to find resource - type: %s, name: '%s'", className, name)); - - // remember its ID - - final String originalId = currentResource.getMetadata().getUid(); - assertNotNull(originalId); - - // delete it - - client.withName(name).delete(); - - // now wait for it to be re-created - - TestUtils.waitUntilConditionOrFail(() -> { - - var current = namedClient.get(); - if (current == null) { - log.info("{} is still missing", className); - return false; - } - if (originalId.equals(current.getMetadata().getUid())) { - log.info("{} has still the same ID", className); - // still the same object - return false; - } - - // get current phase - final var phase = phaseExtractor.apply(current); - if (!phase.isPresent()) { - log.info("{} no phase information", className); - return false; - } - - if (phase.get() != Phase.Active) { - log.info("{} is not ready yet: {}", className, phase.get()); - return false; - } - - return true; - }, timeout.remaining(), Duration.ofSeconds(10), () -> className + " did not get re-created"); - - // and wait for the IoT project to become ready again - - var projectAccess = kubernetes.getIoTProjectClient(project.getMetadata().getNamespace()).withName(project.getMetadata().getName()); - TestUtils.waitUntilConditionOrFail(() -> { - var current = projectAccess.get(); - - if (current == null) { - log.info("IoTProject missing"); - return false; - } - - if (current.getStatus() == null || current.getStatus().getPhase() == null) { - log.info("IoTProject is missing status information"); - return false; - } - - if (!current.getStatus().getPhase().equals("Active")) { - return false; - } - - return true; - }, timeout.remaining(), Duration.ofSeconds(10), () -> "IoTProject failed to switch back to 'Active'"); - - return true; - }; - } - - /** - * Test deleting the whole address space. - */ - @Test - void testDeleteAddressSpace() throws Exception { - doTestAddressSpaceWithModifications( - ofDuration(ofMinutes(15)), - deleteAndWaitResource( - AddressSpace.class, - project -> project.getSpec().getDownstreamStrategy().getManagedStrategy().getAddressSpace().getName(), - kubernetes.getAddressSpaceClient(), - addressSpace -> ofNullable(addressSpace).map(AddressSpace::getStatus).map(AddressSpaceStatus::getPhase))); - } - - /** - * Test deleting the telememtry address. - */ - @Test - void testDeleteTelemetryAddress() throws Exception { - doTestAddressSpaceWithModifications( - ofDuration(ofMinutes(10)), - deleteAndWaitResource( - Address.class, - project -> KubeUtil.sanitizeForGo( - project.getSpec().getDownstreamStrategy().getManagedStrategy().getAddressSpace().getName(), - "telemetry/" + project.getStatus().getTenantName()), - kubernetes.getAddressClient(), - address -> ofNullable(address).map(Address::getStatus).map(AddressStatus::getPhase))); - } - - /** - * Test deleting the event address. - *
- * The event address is expected to be backed by a brokered address. Thus is might behave - * differently than the others. - */ - @Test - void testDeleteEventAddress() throws Exception { - doTestAddressSpaceWithModifications( - ofDuration(ofMinutes(15)), - deleteAndWaitResource( - Address.class, - project -> KubeUtil.sanitizeForGo( - project.getSpec().getDownstreamStrategy().getManagedStrategy().getAddressSpace().getName(), - "event/" + project.getStatus().getTenantName()), - kubernetes.getAddressClient(), - address -> ofNullable(address).map(Address::getStatus).map(AddressStatus::getPhase))); - } - - /** - * Test deleting the adapter user. - */ - @Test - void testDeleteAdapterUser() throws Exception { - doTestAddressSpaceWithModifications( - ofDuration(ofMinutes(10)), - deleteAndWaitResource( - User.class, - project -> project.getSpec().getDownstreamStrategy().getManagedStrategy().getAddressSpace().getName() - + "." - + project.getStatus().getDownstreamEndpoint().getUsername(), - kubernetes.getUserClient(), - user -> ofNullable(user).map(User::getStatus).map(UserStatus::getPhase))); - } - - void doTestAddressSpaceWithModifications(final TimeoutBudget timeout, final ProjectModificator modificator) throws Exception { - - isolatedIoTManager.createIoTConfig(IoTTestSession.createDefaultConfig() - .editOrNewSpec().withServices(newDefaultInstance()).endSpec() - .build()); - - final String addressSpaceName = "managed-address-space"; - final String iotProjectName = "iot-project-managed"; - - IoTProject project = IoTUtils.getBasicIoTProjectObject(iotProjectName, addressSpaceName, - IOT_PROJECT_NAMESPACE, getDefaultAddressSpacePlan()); - LOGGER.warn("NAMESPACE EXISTS? {}, {}", project.getMetadata().getNamespace(), kubernetes.namespaceExists(project.getMetadata().getNamespace())); - isolatedIoTManager.createIoTProject(project); // waiting until ready - IoTProject created = this.projectClient.withName(iotProjectName).get(); - - assertNotNull(created); - assertEquals(IOT_PROJECT_NAMESPACE, created.getMetadata().getNamespace()); - assertEquals(project.getMetadata().getName(), created.getMetadata().getName()); - assertEquals( - project.getSpec().getDownstreamStrategy().getManagedStrategy().getAddressSpace().getName(), - created.getSpec().getDownstreamStrategy().getManagedStrategy().getAddressSpace().getName()); - - assertManaged(created); - - if (modificator.modify(timeout, created)) { - created = this.projectClient.withName(iotProjectName).get(); - assertNotNull(created); - assertManaged(created); - } - } - - private static void assertAddressType(final Address address, final AddressType type, final String plan) { - assertEquals(type.toString(), address.getSpec().getType()); - assertEquals(plan, address.getSpec().getPlan()); - } - - private void assertManaged(IoTProject project) throws Exception { - // address spaces - AddressSpace addressSpace = - isolatedIoTManager.getAddressSpace(IOT_PROJECT_NAMESPACE, project.getSpec().getDownstreamStrategy().getManagedStrategy().getAddressSpace().getName()); - assertEquals(project.getSpec().getDownstreamStrategy().getManagedStrategy().getAddressSpace().getName(), addressSpace.getMetadata().getName()); - assertEquals(AddressSpaceType.STANDARD.toString(), addressSpace.getSpec().getType()); - assertEquals(AddressSpacePlans.STANDARD_SMALL, addressSpace.getSpec().getPlan()); - assertEquals(Phase.Active, addressSpace.getStatus().getPhase()); - - // addresses - // {event/control/command/command_response/telemetry}/"project-namespace"."project-name" - final String addressSuffix = "/" + project.getMetadata().getNamespace() + "." + project.getMetadata().getName(); - final List

addresses = AddressUtils.getAddresses(addressSpace); - // assert that we have the right number of addresses - assertEquals(5, addresses.size()); - // assert that all addresses have the project set as owner - assertEquals(5, addresses.stream() - .map(Address::getMetadata) - .map(ObjectMeta::getOwnerReferences) - .flatMap(List::stream) - .filter(reference -> isOwner(project, reference)) - .count()); - - int correctAddressesCounter = 0; - for (Address address : addresses) { - - final String addressName = address.getSpec().getAddress(); - - if (addressName.equals(IOT_ADDRESS_EVENT + addressSuffix)) { - - assertAddressType(address, AddressType.QUEUE, DestinationPlan.STANDARD_SMALL_QUEUE); - correctAddressesCounter++; - - } else if (addressName.equals(IOT_ADDRESS_CONTROL + addressSuffix) - || addressName.equals(IOT_ADDRESS_TELEMETRY + addressSuffix) - || addressName.equals(IOT_ADDRESS_COMMAND + addressSuffix) - || addressName.equals(IOT_ADDRESS_COMMAND_RESPONSE + addressSuffix)) { - - assertAddressType(address, AddressType.ANYCAST, DestinationPlan.STANDARD_SMALL_ANYCAST); - correctAddressesCounter++; - - } - - assertEquals(Phase.Active, address.getStatus().getPhase()); - - } - assertEquals(5, correctAddressesCounter, "There are incorrect IoT addresses " + addresses); - - // username "adapter" - // name "project-address-space"+".adapter" - User user = isolatedIoTManager.getUser(addressSpace, "adapter-" + project.getMetadata().getUid()); - assertNotNull(user); - assertEquals(1, user.getMetadata().getOwnerReferences().size()); - assertTrue(isOwner(project, user.getMetadata().getOwnerReferences().get(0))); - - final List authorizations = user.getSpec().getAuthorization(); - - assertThat(authorizations, hasSize(3)); - - assertThat(authorizations, containsInAnyOrder( - asList( - assertAdapterAuthorization(of(send), expandAddresses(addressSuffix, IOT_ADDRESS_TELEMETRY, IOT_ADDRESS_EVENT, IOT_ADDRESS_COMMAND_RESPONSE)), - assertAdapterAuthorization(of(recv), expandAddresses(addressSuffix, IOT_ADDRESS_COMMAND)), - assertAdapterAuthorization(of(recv, send), expandAddresses(addressSuffix, IOT_ADDRESS_CONTROL))))); - } - - /** - * Assert an authorization entry. - * - * @param operations The expected operations. - * @param addresses The expected addresses. - * @return A matcher, asserting the entry. - */ - private static Matcher assertAdapterAuthorization(final Set operations, final Set addresses) { - - return allOf(asList( - - hasProperty("operations", containsInAnyOrder(operations.toArray(Operation[]::new))), - hasProperty("addresses", containsInAnyOrder(addresses.toArray(String[]::new))) - - )); - - } - - /** - * Expand addresses to match ACLs. - * - * @param addressSuffix The "suffix" (tenant) of the address. - * @return A set of all addresses. - */ - private static Set expandAddresses(final String addressSuffix, final String... baseAddresses) { - - return Arrays - .stream(baseAddresses) - .flatMap(address -> { - return Stream.of( - address + addressSuffix, - address + addressSuffix + "/*"); - }) - - .collect(Collectors.toSet()); - - } - - /** - * Test if the project is the owner the reference points to. - * - * @param project The project to check for. - * @param ownerReference The reference to check. - * @return {@code true} if the reference points to the project. - */ - private boolean isOwner(final IoTProject project, final OwnerReference ownerReference) { - return ownerReference.getKind().equals(IoTProject.KIND) && - project.getMetadata().getName().equals(ownerReference.getName()); - } - -} diff --git a/systemtests/src/test/java/io/enmasse/systemtest/iot/isolated/MultipleProjectsTest.java b/systemtests/src/test/java/io/enmasse/systemtest/iot/isolated/MultipleProjectsTest.java deleted file mode 100644 index bf470525fc2..00000000000 --- a/systemtests/src/test/java/io/enmasse/systemtest/iot/isolated/MultipleProjectsTest.java +++ /dev/null @@ -1,227 +0,0 @@ -/* - * Copyright 2019-2020, EnMasse authors. - * License: Apache License 2.0 (see the file LICENSE or http://apache.org/licenses/LICENSE-2.0.html). - */ -package io.enmasse.systemtest.iot.isolated; - -import static io.enmasse.systemtest.TestTag.ACCEPTANCE; -import static io.enmasse.systemtest.iot.DefaultDeviceRegistry.newDefaultInstance; - -import java.net.HttpURLConnection; -import java.nio.charset.StandardCharsets; -import java.time.Duration; -import java.util.ArrayList; -import java.util.Base64; -import java.util.Collections; -import java.util.List; -import java.util.UUID; -import java.util.concurrent.TimeUnit; - -import org.eclipse.paho.client.mqttv3.IMqttClient; -import org.eclipse.paho.client.mqttv3.MqttException; -import org.junit.jupiter.api.AfterEach; -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Tag; -import org.junit.jupiter.api.Test; -import org.junit.jupiter.api.extension.ExtensionContext; -import org.slf4j.Logger; - -import io.enmasse.address.model.AddressSpace; -import io.enmasse.iot.model.v1.IoTConfig; -import io.enmasse.iot.model.v1.IoTProject; -import io.enmasse.systemtest.Endpoint; -import io.enmasse.systemtest.TestTag; -import io.enmasse.systemtest.amqp.AmqpClient; -import io.enmasse.systemtest.bases.TestBase; -import io.enmasse.systemtest.bases.iot.ITestIoTIsolated; -import io.enmasse.systemtest.iot.CredentialsRegistryClient; -import io.enmasse.systemtest.iot.DeviceRegistryClient; -import io.enmasse.systemtest.iot.HttpAdapterClient; -import io.enmasse.systemtest.iot.IoTProjectTestContext; -import io.enmasse.systemtest.iot.IoTTestSession; -import io.enmasse.systemtest.iot.MessageSendTester; -import io.enmasse.systemtest.iot.MessageSendTester.ConsumerFactory; -import io.enmasse.systemtest.logs.CustomLogger; -import io.enmasse.systemtest.mqtt.MqttClientFactory; -import io.enmasse.systemtest.time.TimeoutBudget; -import io.enmasse.systemtest.time.WaitPhase; -import io.enmasse.systemtest.utils.IoTUtils; -import io.enmasse.systemtest.utils.TestUtils; -import io.enmasse.user.model.v1.Operation; -import io.enmasse.user.model.v1.User; -import io.enmasse.user.model.v1.UserAuthenticationType; -import io.enmasse.user.model.v1.UserAuthorizationBuilder; -import io.enmasse.user.model.v1.UserBuilder; - -@Tag(TestTag.SMOKE) -class MultipleProjectsTest extends TestBase implements ITestIoTIsolated { - private static Logger log = CustomLogger.getLogger(); - private DeviceRegistryClient registryClient; - private CredentialsRegistryClient credentialsClient; - - private int numberOfProjects = 2; - private List projects = new ArrayList<>(); - - @BeforeEach - void initEnv() throws Exception { - IoTConfig iotConfig = IoTTestSession.createDefaultConfig() - .editOrNewSpec().withServices(newDefaultInstance()).endSpec() - .build(); - isolatedIoTManager.createIoTConfig(iotConfig); - - Endpoint deviceRegistryEndpoint = IoTUtils.getDeviceRegistryManagementEndpoint(); - registryClient = new DeviceRegistryClient(deviceRegistryEndpoint); - credentialsClient = new CredentialsRegistryClient(deviceRegistryEndpoint); - - for (int i = 1; i <= numberOfProjects; i++) { - String projectName = String.format("project-%s", i); - - kubernetes.createNamespace(projectName); - - IoTProject project = IoTUtils.getBasicIoTProjectObject(projectName, projectName, - projectName, getDefaultAddressSpacePlan()); - isolatedIoTManager.createIoTProject(project); - IoTProjectTestContext ctx = new IoTProjectTestContext(projectName, project); - - configureDeviceSide(ctx); - - configureAmqpSide(ctx); - - projects.add(ctx); - } - } - - @AfterEach - void cleanEnv(ExtensionContext context) throws Exception { - for (IoTProjectTestContext ctx : projects) { - cleanDeviceSide(ctx); - cleanAmqpSide(ctx); - } - } - - @Test - @Tag(ACCEPTANCE) - void testMultipleProjects() throws Exception { - - for (final IoTProjectTestContext ctx : projects) { - try (var http = ctx.getHttpAdapterClient()) { - new MessageSendTester() - .type(MessageSendTester.Type.TELEMETRY) - .delay(Duration.ofSeconds(1)) - .consumerFactory(ConsumerFactory.of(ctx.getAmqpClient(), IoTUtils.getTenantId(ctx.getProject()))) - .sender(http::send) - .amount(50) - .consume(MessageSendTester.Consume.BEFORE) - .execute(); - - new MessageSendTester() - .type(MessageSendTester.Type.EVENT) - .delay(Duration.ofMillis(100)) - .consumerFactory(ConsumerFactory.of(ctx.getAmqpClient(), IoTUtils.getTenantId(ctx.getProject()))) - .sender(http::send) - .amount(5) - .consume(MessageSendTester.Consume.AFTER) - .execute(); - } - } - - } - - private void configureAmqpSide(IoTProjectTestContext ctx) throws Exception { - AddressSpace addressSpace = isolatedIoTManager.getAddressSpace(ctx.getNamespace(), - ctx.getProject().getSpec().getDownstreamStrategy().getManagedStrategy().getAddressSpace().getName()); - User amqpUser = configureAmqpUser(ctx.getProject(), addressSpace); - ctx.setAmqpUser(amqpUser); - AmqpClient amqpClient = configureAmqpClient(addressSpace, amqpUser); - ctx.setAmqpClient(amqpClient); - } - - private User configureAmqpUser(IoTProject project, AddressSpace addressSpace) { - String tenant = IoTUtils.getTenantId(project); - - User amqpUser = new UserBuilder() - - .withNewMetadata() - .withName(String.format("%s.%s", addressSpace.getMetadata().getName(), project.getMetadata().getName())) - .endMetadata() - - .withNewSpec() - .withUsername(UUID.randomUUID().toString()) - .withNewAuthentication() - .withPassword(Base64.getEncoder().encodeToString(UUID.randomUUID().toString().getBytes(StandardCharsets.UTF_8))) - .withType(UserAuthenticationType.password) - .endAuthentication() - .withAuthorization(Collections.singletonList(new UserAuthorizationBuilder() - .withAddresses(IOT_ADDRESS_TELEMETRY + "/" + tenant, - IOT_ADDRESS_TELEMETRY + "/" + tenant + "/*", - IOT_ADDRESS_EVENT + "/" + tenant, - IOT_ADDRESS_EVENT + "/" + tenant + "/*") - .withOperations(Operation.recv) - .build())) - .endSpec() - .build(); - kubernetes.getUserClient(project.getMetadata().getNamespace()).create(amqpUser); - - return amqpUser; - } - - private AmqpClient configureAmqpClient(AddressSpace addressSpace, User user) throws Exception { - LOGGER.warn("Amqp factory: " + getAmqpClientFactory()); - AmqpClient amqpClient = getAmqpClientFactory().createQueueClient(addressSpace); - amqpClient.getConnectOptions() - .setUsername(user.getSpec().getUsername()) - .setPassword(new String(Base64.getDecoder().decode(user.getSpec().getAuthentication().getPassword()))); - return amqpClient; - } - - private void cleanAmqpSide(IoTProjectTestContext ctx) throws Exception { - ctx.getAmqpClient().close(); - var userClient = kubernetes.getUserClient(ctx.getNamespace()); - userClient.withName(ctx.getAmqpUser().getMetadata().getName()).cascading(true).delete(); - } - - private void configureDeviceSide(IoTProjectTestContext ctx) throws Exception { - String tenant = IoTUtils.getTenantId(ctx.getProject()); - ctx.setDeviceId(UUID.randomUUID().toString()); - ctx.setDeviceAuthId(UUID.randomUUID().toString()); - ctx.setDevicePassword(UUID.randomUUID().toString()); - registryClient.registerDevice(tenant, ctx.getDeviceId()); - credentialsClient.addCredentials(tenant, ctx.getDeviceId(), ctx.getDeviceAuthId(), ctx.getDevicePassword(), null, HttpURLConnection.HTTP_NO_CONTENT); - Endpoint httpAdapterEndpoint = kubernetes.getExternalEndpoint("iot-http-adapter"); - ctx.setHttpAdapterClient(new HttpAdapterClient(httpAdapterEndpoint, ctx.getDeviceAuthId(), tenant, ctx.getDevicePassword())); - IMqttClient mqttAdapterClient = new MqttClientFactory.Builder() - .clientId(ctx.getDeviceId()) - .endpoint(kubernetes.getExternalEndpoint("iot-mqtt-adapter")) - .usernameAndPassword(ctx.getDeviceAuthId() + "@" + tenant, ctx.getDevicePassword()) - .mqttConnectionOptions(options -> { - options.setAutomaticReconnect(true); - options.setConnectionTimeout(60); - options.setHttpsHostnameVerificationEnabled(false); - }) - .create(); - TestUtils.waitUntilCondition("Successfully connect to mqtt adapter", phase -> { - try { - mqttAdapterClient.connect(); - return true; - } catch (MqttException mqttException) { - if (phase == WaitPhase.LAST_TRY) { - log.error("Error waiting to connect mqtt adapter", mqttException); - } - return false; - } - }, new TimeoutBudget(1, TimeUnit.MINUTES)); - log.info("Connection to mqtt adapter succeeded"); - ctx.setMqttAdapterClient(mqttAdapterClient); - } - - private void cleanDeviceSide(IoTProjectTestContext ctx) throws Exception { - String tenant = IoTUtils.getTenantId(ctx.getProject()); - String deviceId = ctx.getDeviceId(); - credentialsClient.deleteAllCredentials(tenant, deviceId); - registryClient.deleteDeviceRegistration(tenant, deviceId); - registryClient.getDeviceRegistration(tenant, deviceId, HttpURLConnection.HTTP_NOT_FOUND); - ctx.getHttpAdapterClient().close(); - ctx.getMqttAdapterClient().close(); - } - -} diff --git a/systemtests/src/test/java/io/enmasse/systemtest/iot/isolated/SimpleK8sDeployTest.java b/systemtests/src/test/java/io/enmasse/systemtest/iot/isolated/SimpleK8sDeployTest.java deleted file mode 100644 index 0dcbe0bab1b..00000000000 --- a/systemtests/src/test/java/io/enmasse/systemtest/iot/isolated/SimpleK8sDeployTest.java +++ /dev/null @@ -1,162 +0,0 @@ -/* - * Copyright 2019-2020, EnMasse authors. - * License: Apache License 2.0 (see the file LICENSE or http://apache.org/licenses/LICENSE-2.0.html). - */ - -package io.enmasse.systemtest.iot.isolated; - -import static io.enmasse.systemtest.TestTag.SMOKE; -import static io.enmasse.systemtest.time.TimeoutBudget.ofDuration; -import static java.time.Duration.ofMinutes; -import static java.util.Collections.singletonMap; - -import java.nio.file.Files; -import java.nio.file.Path; -import java.util.HashMap; -import java.util.Map; - -import org.junit.jupiter.api.AfterAll; -import org.junit.jupiter.api.BeforeAll; -import org.junit.jupiter.api.Tag; -import org.junit.jupiter.api.Test; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import com.fasterxml.jackson.databind.ObjectMapper; - -import io.enmasse.iot.model.v1.CommonAdapterContainersBuilder; -import io.enmasse.iot.model.v1.ContainerConfigBuilder; -import io.enmasse.iot.model.v1.IoTConfig; -import io.enmasse.iot.model.v1.JavaContainerConfigBuilder; -import io.enmasse.systemtest.Environment; -import io.enmasse.systemtest.bases.TestBase; -import io.enmasse.systemtest.bases.iot.ITestIoTIsolated; -import io.enmasse.systemtest.condition.Kubernetes; -import io.enmasse.systemtest.iot.DefaultDeviceRegistry; -import io.enmasse.systemtest.iot.IoTTestSession; -import io.enmasse.systemtest.platform.KubeCMDClient; -import io.enmasse.systemtest.platform.apps.SystemtestsKubernetesApps; -import io.enmasse.systemtest.utils.IoTUtils; -import io.enmasse.systemtest.utils.TestUtils; -import io.fabric8.kubernetes.api.model.Quantity; - -@Tag(SMOKE) -@Kubernetes -class SimpleK8sDeployTest extends TestBase implements ITestIoTIsolated { - - private static final Logger log = LoggerFactory.getLogger(SimpleK8sDeployTest.class); - private static final String NAMESPACE = Environment.getInstance().namespace(); - private static IoTConfig config; - private io.enmasse.systemtest.platform.Kubernetes client = io.enmasse.systemtest.platform.Kubernetes.getInstance(); - - @BeforeAll - static void setup() throws Exception { - Map secrets = new HashMap<>(); - secrets.put("iot-auth-service", "systemtests-iot-auth-service-tls"); - secrets.put("iot-tenant-service", "systemtests-iot-tenant-service-tls"); - secrets.put("iot-device-connection", "systemtests-iot-device-connection-tls"); - secrets.put("iot-device-registry", "systemtests-iot-device-registry-tls"); - - var r1 = new ContainerConfigBuilder() - .withNewResources().addToLimits("memory", new Quantity("64Mi")).endResources() - .build(); - var j2 = new JavaContainerConfigBuilder() - .withNewContainerConfig() - .withNewResources().addToLimits("memory", new Quantity("256Mi")).endResources() - .endContainerConfig() - .build(); - - var commonContainers = new CommonAdapterContainersBuilder() - .withNewAdapterLike(j2).endAdapter() - .withNewProxyLike(r1).endProxy() - .withNewProxyConfiguratorLike(r1).endProxyConfigurator() - .build(); - - var jdbcEndpoint = SystemtestsKubernetesApps.deployPostgresqlServer(); - - config = IoTTestSession.createDefaultConfig() - - .editOrNewSpec() - - .editOrNewAdapters() - - .editOrNewHttp() - .withNewContainersLike(commonContainers).endContainers() - .endHttp() - - .editOrNewMqtt() - .withNewContainersLike(commonContainers).endContainers() - .endMqtt() - - .editOrNewSigfox() - .withNewContainersLike(commonContainers).endContainers() - .endSigfox() - - .editOrNewLoraWan() - .withNewContainersLike(commonContainers).endContainers() - .endLoraWan() - - .endAdapters() - - .withNewServices() - - .withNewAuthentication() - .withNewContainerLike(j2).endContainer() - .endAuthentication() - - .withNewTenant() - .withNewContainerLike(j2).endContainer() - .endTenant() - - .withDeviceConnection(DefaultDeviceRegistry.newPostgresBasedConnection(jdbcEndpoint)) - .withDeviceRegistry(DefaultDeviceRegistry.newPostgresBasedRegistry(jdbcEndpoint, false)) - - .editDeviceConnection() - .editJdbc() - .editOrNewCommonServiceConfig() - .withNewContainerLike(j2).endContainer() - .endCommonServiceConfig() - .endJdbc() - .endDeviceConnection() - - .editDeviceRegistry() - .editJdbc() - .editServer() - .editExternal() - .editManagement() - .editOrNewCommonConfig() - .withNewContainerLike(j2).endContainer() - .endCommonConfig() - .endManagement() - .endExternal() - .endServer() - .endJdbc() - .endDeviceRegistry() - - .endServices() - - .endSpec() - .build(); - - final Path configTempFile = Files.createTempFile("iot-config", "json"); - try { - Files.write(configTempFile, new ObjectMapper().writeValueAsBytes(config)); - KubeCMDClient.createFromFile(NAMESPACE, configTempFile); - } finally { - Files.deleteIfExists(configTempFile); - } - } - - @AfterAll - static void cleanup() throws Exception { - KubeCMDClient.deleteIoTConfig(NAMESPACE, "default"); - log.info("Waiting for IoT components to be removed"); - TestUtils.waitForNReplicas(0, NAMESPACE, singletonMap("component", "iot"), ofDuration(ofMinutes(5))); - } - - @Test - void testDeploy() throws Exception { - IoTUtils.waitForIoTConfigReady(client, config); - } - -} diff --git a/systemtests/src/test/java/io/enmasse/systemtest/iot/isolated/http/HttpAdapterTest.java b/systemtests/src/test/java/io/enmasse/systemtest/iot/isolated/http/HttpAdapterTest.java deleted file mode 100644 index ee4098122a3..00000000000 --- a/systemtests/src/test/java/io/enmasse/systemtest/iot/isolated/http/HttpAdapterTest.java +++ /dev/null @@ -1,64 +0,0 @@ -/* - * Copyright 2019-2020, EnMasse authors. - * License: Apache License 2.0 (see the file LICENSE or http://apache.org/licenses/LICENSE-2.0.html). - */ - -package io.enmasse.systemtest.iot.isolated.http; - -import static io.enmasse.systemtest.TestTag.ISOLATED_IOT; -import static io.enmasse.systemtest.iot.DeviceSupplier.named; -import static io.enmasse.systemtest.iot.IoTTestSession.Adapter.HTTP; - -import java.util.Arrays; -import java.util.List; - -import org.junit.jupiter.api.AfterAll; -import org.junit.jupiter.api.BeforeAll; -import org.junit.jupiter.api.Tag; - -import io.enmasse.systemtest.iot.DeviceSupplier; -import io.enmasse.systemtest.iot.IoTTestSession; -import io.enmasse.systemtest.iot.http.StandardIoTHttpTests; - -@Tag(ISOLATED_IOT) -class HttpAdapterTest implements StandardIoTHttpTests { - - private static IoTTestSession session; - - @BeforeAll - public static void setup() throws Exception { - session = IoTTestSession.createDefault() - .adapters(HTTP) - .deploy(); - } - - @AfterAll - public static void cleanup() throws Exception { - if (session != null) { - session.close(); - session = null; - } - } - - @Override - public List getDevices() { - return Arrays.asList( - named("default", () -> session.newDevice() - .register() - .setPassword())); - } - - @Override - public List getInvalidDevices() { - return Arrays.asList( - named("invalidPassword", () -> session.newDevice() - .register() - .setPassword() - .overridePassword())); - } - - @Override - public IoTTestSession getSession() { - return session; - } -} diff --git a/systemtests/src/test/java/io/enmasse/systemtest/iot/isolated/http/MaxPayloadSizeTest.java b/systemtests/src/test/java/io/enmasse/systemtest/iot/isolated/http/MaxPayloadSizeTest.java deleted file mode 100644 index 7b57b6375fd..00000000000 --- a/systemtests/src/test/java/io/enmasse/systemtest/iot/isolated/http/MaxPayloadSizeTest.java +++ /dev/null @@ -1,30 +0,0 @@ -/* - * Copyright 2020, EnMasse authors. - * License: Apache License 2.0 (see the file LICENSE or http://apache.org/licenses/LICENSE-2.0.html). - */ - -package io.enmasse.systemtest.iot.isolated.http; - -import static io.enmasse.systemtest.iot.HttpAdapterClient.ResponseException.statusCode; - -import io.enmasse.systemtest.iot.IoTTestSession.Adapter; -import io.enmasse.systemtest.iot.IoTTestSession.Device; -import io.enmasse.systemtest.iot.MessageSendTester.Sender; -import io.enmasse.systemtest.iot.isolated.AbstractMaxPayloadSizeTest; - -public class MaxPayloadSizeTest extends AbstractMaxPayloadSizeTest { - - @Override - protected Adapter adapter() { - return Adapter.HTTP; - } - - @SuppressWarnings("resource") - @Override - protected Sender sender(Device device) throws Exception { - return cleanup(device - .createHttpAdapterClient() - .suppressExceptions(statusCode(413)))::send; - } - -} diff --git a/systemtests/src/test/java/io/enmasse/systemtest/iot/isolated/mqtt/MaxPayloadSizeTest.java b/systemtests/src/test/java/io/enmasse/systemtest/iot/isolated/mqtt/MaxPayloadSizeTest.java deleted file mode 100644 index 5acfc5fb908..00000000000 --- a/systemtests/src/test/java/io/enmasse/systemtest/iot/isolated/mqtt/MaxPayloadSizeTest.java +++ /dev/null @@ -1,35 +0,0 @@ -/* - * Copyright 2020, EnMasse authors. - * License: Apache License 2.0 (see the file LICENSE or http://apache.org/licenses/LICENSE-2.0.html). - */ - -package io.enmasse.systemtest.iot.isolated.mqtt; - -import java.nio.charset.StandardCharsets; - -import io.enmasse.systemtest.iot.IoTTestSession.Adapter; -import io.enmasse.systemtest.iot.IoTTestSession.Device; -import io.enmasse.systemtest.iot.MessageSendTester.Sender; -import io.enmasse.systemtest.iot.MessageSendTester.Type; -import io.enmasse.systemtest.iot.isolated.AbstractMaxPayloadSizeTest; - -public class MaxPayloadSizeTest extends AbstractMaxPayloadSizeTest { - - @Override - protected int reducePayloadBy(final Type type) { - // reduce by: 2 (length) + x (topic name) bytes + 2 (QoS 1 msg id) - return 2 + type.type().address().getBytes(StandardCharsets.UTF_8).length + 2; - } - - @Override - protected Adapter adapter() { - return Adapter.MQTT; - } - - @SuppressWarnings("resource") - @Override - protected Sender sender(Device device) throws Exception { - return cleanup(device.createMqttAdapterClient())::sendQos1; - } - -} diff --git a/systemtests/src/test/java/io/enmasse/systemtest/iot/isolated/mqtt/MqttAdapterTest.java b/systemtests/src/test/java/io/enmasse/systemtest/iot/isolated/mqtt/MqttAdapterTest.java deleted file mode 100644 index 9cc2b8e715f..00000000000 --- a/systemtests/src/test/java/io/enmasse/systemtest/iot/isolated/mqtt/MqttAdapterTest.java +++ /dev/null @@ -1,68 +0,0 @@ -/* - * Copyright 2019-2020, EnMasse authors. - * License: Apache License 2.0 (see the file LICENSE or http://apache.org/licenses/LICENSE-2.0.html). - */ - -package io.enmasse.systemtest.iot.isolated.mqtt; - -import static io.enmasse.systemtest.TestTag.ISOLATED_IOT; -import static io.enmasse.systemtest.iot.DeviceSupplier.named; -import static io.enmasse.systemtest.iot.IoTTestSession.Adapter.MQTT; - -import java.util.Arrays; -import java.util.List; - -import org.junit.jupiter.api.AfterAll; -import org.junit.jupiter.api.BeforeAll; -import org.junit.jupiter.api.Tag; - -import io.enmasse.systemtest.iot.DeviceSupplier; -import io.enmasse.systemtest.iot.IoTTestSession; -import io.enmasse.systemtest.iot.mqtt.StandardIoTMqttTests; - -/** - * Testing MQTT message transmission. - */ -@Tag(ISOLATED_IOT) -class MqttAdapterTest implements StandardIoTMqttTests { - - private static IoTTestSession session; - - @BeforeAll - public static void setup() throws Exception { - session = IoTTestSession.createDefault() - .adapters(MQTT) - .deploy(); - } - - @AfterAll - public static void cleanup() throws Exception { - if (session != null) { - session.close(); - session = null; - } - } - - @Override - public List getDevices() { - return Arrays.asList( - named("default", () -> session.newDevice() - .register() - .setPassword())); - } - - @Override - public List getInvalidDevices() { - return Arrays.asList( - named("invalidPassword", () -> session.newDevice() - .register() - .setPassword() - .overridePassword())); - } - - @Override - public IoTTestSession getSession() { - return session; - } - -} diff --git a/systemtests/src/test/java/io/enmasse/systemtest/iot/isolated/project/ManagedTest.java b/systemtests/src/test/java/io/enmasse/systemtest/iot/isolated/project/ManagedTest.java deleted file mode 100644 index 5893410b771..00000000000 --- a/systemtests/src/test/java/io/enmasse/systemtest/iot/isolated/project/ManagedTest.java +++ /dev/null @@ -1,210 +0,0 @@ -/* - * Copyright 2019-2020, EnMasse authors. - * License: Apache License 2.0 (see the file LICENSE or http://apache.org/licenses/LICENSE-2.0.html). - */ -package io.enmasse.systemtest.iot.isolated.project; - -import static io.enmasse.systemtest.iot.DefaultDeviceRegistry.newDefaultInstance; -import static io.enmasse.systemtest.utils.AddressSpaceUtils.addressSpaceExists; -import static io.enmasse.systemtest.utils.TestUtils.waitUntilConditionOrFail; -import static java.time.Duration.ofMinutes; -import static java.time.Duration.ofSeconds; -import static org.hamcrest.collection.IsEmptyIterable.emptyIterable; -import static org.junit.Assert.assertThat; - -import java.net.HttpURLConnection; -import java.util.LinkedList; -import java.util.List; -import java.util.UUID; -import java.util.function.BiConsumer; - -import org.apache.qpid.proton.message.Message; -import org.junit.jupiter.api.Assertions; -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Test; -import org.slf4j.Logger; - -import io.enmasse.address.model.Address; -import io.enmasse.address.model.AddressList; -import io.enmasse.address.model.AddressSpace; -import io.enmasse.address.model.AddressSpaceList; -import io.enmasse.address.model.DoneableAddress; -import io.enmasse.address.model.DoneableAddressSpace; -import io.enmasse.address.model.KubeUtil; -import io.enmasse.iot.model.v1.DoneableIoTProject; -import io.enmasse.iot.model.v1.IoTConfig; -import io.enmasse.iot.model.v1.IoTProject; -import io.enmasse.iot.model.v1.IoTProjectBuilder; -import io.enmasse.iot.model.v1.IoTProjectList; -import io.enmasse.systemtest.Endpoint; -import io.enmasse.systemtest.UserCredentials; -import io.enmasse.systemtest.amqp.AmqpClientFactory; -import io.enmasse.systemtest.bases.TestBase; -import io.enmasse.systemtest.bases.iot.ITestIoTIsolated; -import io.enmasse.systemtest.iot.CredentialsRegistryClient; -import io.enmasse.systemtest.iot.DeviceRegistryClient; -import io.enmasse.systemtest.iot.HttpAdapterClient; -import io.enmasse.systemtest.iot.IoTTestSession; -import io.enmasse.systemtest.iot.MessageSendTester; -import io.enmasse.systemtest.iot.MessageSendTester.Type; -import io.enmasse.systemtest.logs.CustomLogger; -import io.enmasse.systemtest.utils.IoTUtils; -import io.enmasse.user.model.v1.DoneableUser; -import io.enmasse.user.model.v1.User; -import io.enmasse.user.model.v1.UserList; -import io.fabric8.kubernetes.client.dsl.MixedOperation; -import io.fabric8.kubernetes.client.dsl.Resource; - -public class ManagedTest extends TestBase implements ITestIoTIsolated { - - private static final String MANAGED_TEST_ADDRESSSPACE = "managed-test-addrspace"; - - private static final Logger log = CustomLogger.getLogger(); - - private MixedOperation> client; - private MixedOperation> addressClient; - private MixedOperation> addressSpaceClient; - private MixedOperation> userClient; - protected DeviceRegistryClient registryClient; - protected CredentialsRegistryClient credentialsClient; - - private Endpoint httpAdapterEndpoint; - private UserCredentials credentials; - - @BeforeEach - public void initClients () throws Exception { - IoTConfig iotConfig = IoTTestSession.createDefaultConfig() - .editOrNewSpec().withServices(newDefaultInstance()).endSpec() - .build(); - - isolatedIoTManager.createIoTConfig(iotConfig); - - final Endpoint deviceRegistryEndpoint = IoTUtils.getDeviceRegistryManagementEndpoint(); - this.registryClient = new DeviceRegistryClient(deviceRegistryEndpoint); - this.credentialsClient = new CredentialsRegistryClient(deviceRegistryEndpoint); - this.client = kubernetes.getIoTProjectClient(IOT_PROJECT_NAMESPACE); - this.addressClient = kubernetes.getAddressClient(IOT_PROJECT_NAMESPACE); - this.addressSpaceClient = kubernetes.getAddressSpaceClient(IOT_PROJECT_NAMESPACE); - this.userClient = kubernetes.getUserClient(IOT_PROJECT_NAMESPACE); - this.httpAdapterEndpoint = kubernetes.getExternalEndpoint("iot-http-adapter"); - - this.credentials = new UserCredentials(UUID.randomUUID().toString(), UUID.randomUUID().toString()); - } - - @Test - public void testChangeAddressSpace() throws Exception { - - var project = IoTUtils.getBasicIoTProjectObject("iot1", "as1", - IOT_PROJECT_NAMESPACE, getDefaultAddressSpacePlan()); - isolatedIoTManager.createIoTProject(project); - - assertManagedResources(Assertions::assertNotNull, project, "as1"); - - project = new IoTProjectBuilder(project) - .editSpec() - .editDownstreamStrategy() - .editManagedStrategy() - .editAddressSpace() - - .withName("as1a") - - .endAddressSpace() - .endManagedStrategy() - .endDownstreamStrategy() - .endSpec() - .build(); - - // update the project - - log.info("Update project namespace"); - client.createOrReplace(project); - - // immediately after the change, the project is still ready but the new - // address space is still missing, so we need to wait for it to be created - // otherwise io.enmasse.systemtest.utils.IoTUtils.waitForIoTProjectReady(Kubernetes, IoTProject) will fail - - waitUntilConditionOrFail( - addressSpaceExists(project.getMetadata().getNamespace(), project.getSpec().getDownstreamStrategy().getManagedStrategy().getAddressSpace().getName()), - ofMinutes(5), ofSeconds(10), - () -> String.format("Expected address space to be created")); - - // wait until the project and address space become ready - - log.info("For for project to become ready again"); - IoTUtils.waitForIoTProjectReady(kubernetes, project); - - // assert existence - - assertManagedResources(Assertions::assertNotNull, project, "as1a"); - assertManagedResources(Assertions::assertNull, project, "as1"); - } - - @Test - public void testTwoManagedToTheSameAddressSpace() throws Exception { - - // first create two projects for a single address space - - var project1 = IoTUtils.getBasicIoTProjectObject("iot1", MANAGED_TEST_ADDRESSSPACE, - IOT_PROJECT_NAMESPACE, getDefaultAddressSpacePlan()); - var project2 = IoTUtils.getBasicIoTProjectObject("iot2", MANAGED_TEST_ADDRESSSPACE, - IOT_PROJECT_NAMESPACE, getDefaultAddressSpacePlan()); - - var tenant1 = IoTUtils.getTenantId(project1); - var tenant2 = IoTUtils.getTenantId(project2); - - // wait for the projects to be ready - - isolatedIoTManager.createIoTProject(project1); - isolatedIoTManager.createIoTProject(project2); - - assertManagedResources(Assertions::assertNotNull, project1, MANAGED_TEST_ADDRESSSPACE); - assertManagedResources(Assertions::assertNotNull, project2, MANAGED_TEST_ADDRESSSPACE); - - // register two devices with the same ids, but for different tenants - - this.registryClient.registerDevice(tenant1, "device1"); - this.registryClient.registerDevice(tenant2, "device1"); - this.credentialsClient.addPlainPasswordCredentials(tenant1, "device1", "auth1", "password1", null, HttpURLConnection.HTTP_NO_CONTENT); - this.credentialsClient.addPlainPasswordCredentials(tenant2, "device1", "auth1", "password1", null, HttpURLConnection.HTTP_NO_CONTENT); - - // set up client - - isolatedIoTManager.createOrUpdateUser(isolatedIoTManager.getAddressSpace(IOT_PROJECT_NAMESPACE, MANAGED_TEST_ADDRESSSPACE), this.credentials); - var iotAmqpClientFactory = new AmqpClientFactory(this.resourcesManager.getAddressSpace(IOT_PROJECT_NAMESPACE, MANAGED_TEST_ADDRESSSPACE), this.credentials); - var amqpClient = iotAmqpClientFactory.createQueueClient(); - - // now try to send some messages - - final List otherMessages = new LinkedList<>(); - try ( - var httpAdapterClient = new HttpAdapterClient(this.httpAdapterEndpoint, "auth1", tenant1, "password1"); - var otherReceiver = MessageSendTester.ConsumerFactory.of(amqpClient, tenant2).start(Type.TELEMETRY, msg -> otherMessages.add(msg)) ) { - - new MessageSendTester() - .type(MessageSendTester.Type.TELEMETRY) - .amount(1) - .consumerFactory(MessageSendTester.ConsumerFactory.of(amqpClient, tenant1)) - .sender(httpAdapterClient::send) - .execute(); - - } - - assertThat(otherMessages, emptyIterable()); - } - - private void assertManagedResources(final BiConsumer assertor, final IoTProject project, final String addressSpaceName) { - assertObject(assertor, "Address space", this.addressSpaceClient, addressSpaceName); - assertObject(assertor, "Adapter user", this.userClient, addressSpaceName + ".adapter-" + project.getMetadata().getUid()); - for ( final String address : IOT_ADDRESSES) { - var addressName = address + "/" + IOT_PROJECT_NAMESPACE + "." + project.getMetadata().getName(); - var metaName = KubeUtil.sanitizeForGo(addressSpaceName, addressName); - assertObject(assertor, "Address: " + addressName + " / " + metaName, this.addressClient, metaName); - } - } - - private static void assertObject(final BiConsumer assertor, final String message, final MixedOperation client, final String name) { - var object = client.withName(name).get(); - assertor.accept(object, message); - } - -} diff --git a/systemtests/src/test/java/io/enmasse/systemtest/iot/isolated/registry/DeviceRegistryTest.java b/systemtests/src/test/java/io/enmasse/systemtest/iot/isolated/registry/DeviceRegistryTest.java deleted file mode 100644 index 832a121b4fc..00000000000 --- a/systemtests/src/test/java/io/enmasse/systemtest/iot/isolated/registry/DeviceRegistryTest.java +++ /dev/null @@ -1,391 +0,0 @@ -/* - * Copyright 2019, EnMasse authors. - * License: Apache License 2.0 (see the file LICENSE or http://apache.org/licenses/LICENSE-2.0.html). - */ -package io.enmasse.systemtest.iot.isolated.registry; - -import static io.enmasse.systemtest.TestTag.SMOKE; -import static io.enmasse.systemtest.iot.IoTTestSession.Adapter.HTTP; -import static java.net.HttpURLConnection.HTTP_CONFLICT; -import static java.net.HttpURLConnection.HTTP_CREATED; -import static java.net.HttpURLConnection.HTTP_NOT_FOUND; -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertNotNull; -import static org.junit.jupiter.api.Assertions.assertNull; - -import java.net.HttpURLConnection; -import java.nio.charset.StandardCharsets; -import java.time.Duration; -import java.time.Instant; -import java.util.HashSet; -import java.util.List; -import java.util.Set; -import java.util.UUID; - -import org.eclipse.hono.service.management.credentials.CommonCredential; -import org.eclipse.hono.service.management.credentials.PasswordCredential; -import org.eclipse.hono.service.management.device.Device; -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Tag; -import org.junit.jupiter.api.Test; - -import io.enmasse.iot.model.v1.IoTConfigBuilder; -import io.enmasse.iot.model.v1.IoTProject; -import io.enmasse.systemtest.Endpoint; -import io.enmasse.systemtest.UserCredentials; -import io.enmasse.systemtest.amqp.AmqpClient; -import io.enmasse.systemtest.amqp.AmqpClientFactory; -import io.enmasse.systemtest.bases.TestBase; -import io.enmasse.systemtest.bases.iot.ITestIoTIsolated; -import io.enmasse.systemtest.iot.CredentialsRegistryClient; -import io.enmasse.systemtest.iot.DeviceRegistryClient; -import io.enmasse.systemtest.iot.IoTTestSession.Adapter; -import io.enmasse.systemtest.utils.IoTUtils; - -@Tag(SMOKE) -abstract class DeviceRegistryTest extends TestBase implements ITestIoTIsolated { - - private static final String DEVICE_REGISTRY_TEST_ADDRESSSPACE = "device-registry-test-addrspace"; - - private static final String DEVICE_REGISTRY_TEST_PROJECT = "device-registry-test-project"; - - private String randomDeviceId; - private IoTProject iotProject; - private Endpoint deviceRegistryEndpoint; - private Endpoint httpAdapterEndpoint; - private DeviceRegistryClient client; - - private UserCredentials credentials; - - private AmqpClientFactory iotAmqpClientFactory; - - private AmqpClient amqpClient; - - protected abstract IoTConfigBuilder provideIoTConfig() throws Exception; - - protected int tenantDoesNotExistCode() { - return HttpURLConnection.HTTP_UNAUTHORIZED; - } - - @BeforeEach - public void setAttributes() throws Exception { - var iotConfigBuilder = provideIoTConfig(); - - // disable all but HTTP - - for (Adapter adapter : Adapter.values()) { - iotConfigBuilder = adapter.disable(iotConfigBuilder); - } - iotConfigBuilder = HTTP.enable(iotConfigBuilder); - - // build config - - var iotConfig = iotConfigBuilder.build(); - - isolatedIoTManager.createIoTConfig(iotConfig); - - iotProject = IoTUtils.getBasicIoTProjectObject(DEVICE_REGISTRY_TEST_PROJECT, - DEVICE_REGISTRY_TEST_ADDRESSSPACE, IOT_PROJECT_NAMESPACE, getDefaultAddressSpacePlan()); - isolatedIoTManager.createIoTProject(iotProject); - - deviceRegistryEndpoint = IoTUtils.getDeviceRegistryManagementEndpoint(); - - httpAdapterEndpoint = kubernetes.getExternalEndpoint("iot-http-adapter"); - - client = new DeviceRegistryClient(deviceRegistryEndpoint); - - this.randomDeviceId = UUID.randomUUID().toString(); - - this.credentials = new UserCredentials(UUID.randomUUID().toString(), UUID.randomUUID().toString()); - isolatedIoTManager.createOrUpdateUser(isolatedIoTManager.getAddressSpace(IOT_PROJECT_NAMESPACE, DEVICE_REGISTRY_TEST_ADDRESSSPACE), this.credentials); - this.iotAmqpClientFactory = new AmqpClientFactory(resourcesManager.getAddressSpace(IOT_PROJECT_NAMESPACE, DEVICE_REGISTRY_TEST_ADDRESSSPACE), this.credentials); - this.amqpClient = iotAmqpClientFactory.createQueueClient(); - - } - - protected void doTestRegisterDevice() throws Exception { - client.registerDevice(isolatedIoTManager.getTenantId(), randomDeviceId); - final Device result = client.getDeviceRegistration(isolatedIoTManager.getTenantId(), randomDeviceId); - assertNotNull(result); - assertDefaultEnabled(result.getEnabled()); - - client.deleteDeviceRegistration(isolatedIoTManager.getTenantId(), randomDeviceId); - client.getDeviceRegistration(isolatedIoTManager.getTenantId(), randomDeviceId, HTTP_NOT_FOUND); - } - - protected void doTestDisableDevice() throws Exception { - client.registerDevice(isolatedIoTManager.getTenantId(), randomDeviceId); - - final Device payload = new Device(); - payload.setEnabled(false); - - client.updateDeviceRegistration(isolatedIoTManager.getTenantId(), randomDeviceId, payload); - - final Device result = client.getDeviceRegistration(isolatedIoTManager.getTenantId(), randomDeviceId); - - // as we set it explicitly, we expect the explicit value of "false" - assertEquals(Boolean.FALSE, result.getEnabled()); - - client.deleteDeviceRegistration(isolatedIoTManager.getTenantId(), randomDeviceId); - client.getDeviceRegistration(isolatedIoTManager.getTenantId(), randomDeviceId, HTTP_NOT_FOUND); - } - - protected void doTestDeviceCredentials() throws Exception { - try (var credentialsClient = new CredentialsRegistryClient(deviceRegistryEndpoint)) { - - client.registerDevice(isolatedIoTManager.getTenantId(), randomDeviceId); - - String authId = "sensor-" + UUID.randomUUID().toString(); - String password = "password1234"; - credentialsClient.addCredentials(isolatedIoTManager.getTenantId(), randomDeviceId, authId, password, null, HttpURLConnection.HTTP_NO_CONTENT); - - IoTUtils.checkCredentials(authId, password, false, httpAdapterEndpoint, amqpClient, iotProject); - - credentialsClient.deleteAllCredentials(isolatedIoTManager.getTenantId(), randomDeviceId); - - client.deleteDeviceRegistration(isolatedIoTManager.getTenantId(), randomDeviceId); - client.getDeviceRegistration(isolatedIoTManager.getTenantId(), randomDeviceId, HTTP_NOT_FOUND); - - } - } - - protected void doTestDeviceCredentialsPlainPassword() throws Exception { - try (var credentialsClient = new CredentialsRegistryClient(deviceRegistryEndpoint)) { - - client.registerDevice(isolatedIoTManager.getTenantId(), randomDeviceId); - - String authId = "sensor-" + UUID.randomUUID().toString(); - String password = "password1234"; - credentialsClient.addPlainPasswordCredentials(isolatedIoTManager.getTenantId(), randomDeviceId, authId, password, null, HttpURLConnection.HTTP_NO_CONTENT); - - IoTUtils.checkCredentials(authId, password, false, httpAdapterEndpoint, amqpClient, iotProject); - - credentialsClient.deleteAllCredentials(isolatedIoTManager.getTenantId(), randomDeviceId); - - client.deleteDeviceRegistration(isolatedIoTManager.getTenantId(), randomDeviceId); - client.getDeviceRegistration(isolatedIoTManager.getTenantId(), randomDeviceId, HTTP_NOT_FOUND); - - } - } - - protected void doTestDeviceCredentialsDoesNotContainsPasswordDetails() throws Exception { - try (var credentialsClient = new CredentialsRegistryClient(deviceRegistryEndpoint)) { - - client.registerDevice(isolatedIoTManager.getTenantId(), randomDeviceId); - - String authId = "sensor-" + UUID.randomUUID().toString(); - String password = "password1234"; - credentialsClient.addPlainPasswordCredentials(isolatedIoTManager.getTenantId(), randomDeviceId, authId, password, null, HttpURLConnection.HTTP_NO_CONTENT); - - List credentials = credentialsClient.getCredentials(isolatedIoTManager.getTenantId(), randomDeviceId); - - assertEquals(1, credentials.size()); - PasswordCredential passwordCredential = ((PasswordCredential) credentials.get(0)); - assertEquals(1, passwordCredential.getSecrets().size()); - assertNull(passwordCredential.getSecrets().get(0).getHashFunction()); - assertNull(passwordCredential.getSecrets().get(0).getPasswordHash()); - assertNull(passwordCredential.getSecrets().get(0).getPasswordPlain()); - assertNull(passwordCredential.getSecrets().get(0).getSalt()); - - credentialsClient.deleteAllCredentials(isolatedIoTManager.getTenantId(), randomDeviceId); - - client.deleteDeviceRegistration(isolatedIoTManager.getTenantId(), randomDeviceId); - client.getDeviceRegistration(isolatedIoTManager.getTenantId(), randomDeviceId, HTTP_NOT_FOUND); - - } - } - - - protected void doTestCacheExpiryForCredentials() throws Exception { - try (var credentialsClient = new CredentialsRegistryClient(deviceRegistryEndpoint)) { - - final Duration cacheExpiration = Duration.ofMinutes(3); - - // register device - - client.registerDevice(isolatedIoTManager.getTenantId(), randomDeviceId); - - final String authId = UUID.randomUUID().toString(); - final String password = "password1234"; - credentialsClient.addCredentials(isolatedIoTManager.getTenantId(), randomDeviceId, authId, password, null, HttpURLConnection.HTTP_NO_CONTENT); - - // first test, cache filled - - IoTUtils.checkCredentials(authId, password, false, httpAdapterEndpoint, amqpClient, iotProject); - - // set new password - - final String newPassword = "new-password1234"; - credentialsClient.updateCredentials(isolatedIoTManager.getTenantId(), randomDeviceId, authId, newPassword, null); - - // expect failure due to cached info - - IoTUtils.checkCredentials(authId, newPassword, true, httpAdapterEndpoint, amqpClient, iotProject); - LOGGER.info("Waiting {} seconds for credentials to expire", cacheExpiration); - Thread.sleep(cacheExpiration.toMillis()); - - // cache must be expired, new password can be used - - IoTUtils.checkCredentials(authId, newPassword, false, httpAdapterEndpoint, amqpClient, iotProject); - - credentialsClient.deleteAllCredentials(isolatedIoTManager.getTenantId(), randomDeviceId); - - client.deleteDeviceRegistration(isolatedIoTManager.getTenantId(), randomDeviceId); - client.getDeviceRegistration(isolatedIoTManager.getTenantId(), randomDeviceId, HTTP_NOT_FOUND); - } - } - - - protected void doTestSetExpiryForCredentials() throws Exception { - try (var credentialsClient = new CredentialsRegistryClient(deviceRegistryEndpoint)) { - client.registerDevice(isolatedIoTManager.getTenantId(), randomDeviceId); - - final String authId = UUID.randomUUID().toString(); - final Duration expiry = Duration.ofSeconds(30); - final Instant notAfter = Instant.now().plus(expiry); - final String newPassword = "password1234"; - - credentialsClient.addCredentials(isolatedIoTManager.getTenantId(), randomDeviceId, authId, newPassword, notAfter, HttpURLConnection.HTTP_NO_CONTENT); - - // first check, must succeed - Thread.sleep(20_000); - IoTUtils.checkCredentials(authId, newPassword, false, httpAdapterEndpoint, amqpClient, iotProject); - - LOGGER.info("Waiting {} for credentials to expire", expiry); - Thread.sleep(expiry.toMillis()); - - // second check, after expiration, must fail - - IoTUtils.checkCredentials(authId, newPassword, true, httpAdapterEndpoint, amqpClient, iotProject); - - credentialsClient.deleteAllCredentials(isolatedIoTManager.getTenantId(), randomDeviceId); - - client.deleteDeviceRegistration(isolatedIoTManager.getTenantId(), randomDeviceId); - client.getDeviceRegistration(isolatedIoTManager.getTenantId(), randomDeviceId, HTTP_NOT_FOUND); - } - } - - - protected void doTestCreateForNonExistingTenantFails() throws Exception { - var response = client.registerDeviceWithResponse("invalid-" + isolatedIoTManager.getTenantId(), randomDeviceId); - assertEquals(tenantDoesNotExistCode(), response.statusCode()); - } - - protected void doTestTenantDeletionTriggersDevicesDeletion() throws Exception { - var tenantId = isolatedIoTManager.getTenantId(); - try (var credentialsClient = new CredentialsRegistryClient(deviceRegistryEndpoint)) { - client.registerDevice(tenantId, randomDeviceId); - - final String authId = UUID.randomUUID().toString(); - final Duration expiry = Duration.ofSeconds(30); - final Instant notAfter = Instant.now().plus(expiry); - final String newPassword = "password1234"; - - credentialsClient.addCredentials(tenantId, randomDeviceId, authId, newPassword, notAfter, HttpURLConnection.HTTP_NO_CONTENT); - - // first check, must succeed - - IoTUtils.checkCredentials(authId, newPassword, false, httpAdapterEndpoint, amqpClient, iotProject); - - // Now delete the tenant - isolatedIoTManager.deleteIoTProject(iotProject); - - // second check, the credentials and device should be deleted - - client.getDeviceRegistration(tenantId, randomDeviceId, tenantDoesNotExistCode()); - IoTUtils.checkCredentials(authId, newPassword, true, httpAdapterEndpoint, amqpClient, iotProject); - } - } - - protected void doCreateDuplicateDeviceFails() throws Exception { - var tenantId = isolatedIoTManager.getTenantId(); - var deviceId = UUID.randomUUID().toString(); - - // create device - - var response = client.registerDeviceWithResponse(tenantId, deviceId); - assertEquals(HTTP_CREATED, response.statusCode()); - - // create device a second time - - var response2 = client.registerDeviceWithResponse(tenantId, deviceId); - assertEquals(HTTP_CONFLICT, response2.statusCode()); - } - - @Test - public void testDeviceWithSameAuthIdOfDifferentTypesSucceeds() throws Exception { - - var tenantId = isolatedIoTManager.getTenantId(); - var deviceId = UUID.randomUUID().toString(); - - // create device - - client.registerDevice(tenantId, deviceId); - - var authId = UUID.randomUUID().toString(); - - try (var credentialsClient = new CredentialsRegistryClient(deviceRegistryEndpoint)) { - credentialsClient.addPlainPasswordCredentials(tenantId, deviceId, authId, "foo-bar", null, HttpURLConnection.HTTP_NO_CONTENT); - credentialsClient.addPskCredentials(tenantId, deviceId, authId, "foo-bar".getBytes(StandardCharsets.UTF_8), null, HttpURLConnection.HTTP_NO_CONTENT); - } - - } - - @Test - public void testDeviceWithSameAuthIdOfSameTypesFails() throws Exception { - - var tenantId = isolatedIoTManager.getTenantId(); - var deviceId1 = UUID.randomUUID().toString(); - var deviceId2 = UUID.randomUUID().toString(); - - // create device - - client.registerDevice(tenantId, deviceId1); - client.registerDevice(tenantId, deviceId2); - - var authId = UUID.randomUUID().toString(); - - try (var credentialsClient = new CredentialsRegistryClient(deviceRegistryEndpoint)) { - // must succeed - credentialsClient.addPlainPasswordCredentials(tenantId, deviceId1, authId, "foo-bar", null, HttpURLConnection.HTTP_NO_CONTENT); - // must fail - credentialsClient.addPlainPasswordCredentials(tenantId, deviceId2, authId, "foo-bar", null, HttpURLConnection.HTTP_BAD_REQUEST); - } - - } - - protected void doRegisterMultipleDevices() throws Exception { - - final String tenantId = isolatedIoTManager.getTenantId(); - final String prefix = UUID.randomUUID().toString(); - final Set devices = new HashSet<>(); - - for (int i = 0; i < 1_000; i++) { - var deviceId = prefix + "-" + UUID.randomUUID().toString(); - client.registerDevice(tenantId, deviceId); - devices.add(deviceId); - } - - try (var credentialsClient = new CredentialsRegistryClient(deviceRegistryEndpoint)) { - for (final String deviceId : devices) { - credentialsClient.addPlainPasswordCredentials(tenantId, deviceId, UUID.randomUUID().toString(), UUID.randomUUID().toString(), null, HttpURLConnection.HTTP_NO_CONTENT); - } - } - - for (final String deviceId : devices) { - final Device result = client.getDeviceRegistration(tenantId, deviceId); - assertNotNull(result); - assertDefaultEnabled(result.getEnabled()); - } - - for (final String deviceId : devices) { - client.deleteDeviceRegistration(tenantId, deviceId); - } - - for (final String deviceId : devices) { - client.getDeviceRegistration(tenantId, deviceId, HTTP_NOT_FOUND); - } - - } - -} \ No newline at end of file diff --git a/systemtests/src/test/java/io/enmasse/systemtest/iot/isolated/registry/H2DeviceRegistryTest.java b/systemtests/src/test/java/io/enmasse/systemtest/iot/isolated/registry/H2DeviceRegistryTest.java deleted file mode 100644 index e1552f0288e..00000000000 --- a/systemtests/src/test/java/io/enmasse/systemtest/iot/isolated/registry/H2DeviceRegistryTest.java +++ /dev/null @@ -1,148 +0,0 @@ -/* - * Copyright 2019-2020, EnMasse authors. - * License: Apache License 2.0 (see the file LICENSE or http://apache.org/licenses/LICENSE-2.0.html). - */ - -package io.enmasse.systemtest.iot.isolated.registry; - -import static io.enmasse.systemtest.TestTag.ACCEPTANCE; -import static io.enmasse.systemtest.iot.DefaultDeviceRegistry.newH2Based; -import static io.enmasse.systemtest.utils.IoTUtils.assertCorrectDeviceConnectionType; -import static io.enmasse.systemtest.utils.IoTUtils.assertCorrectRegistryType; -import static org.junit.jupiter.api.Assertions.assertEquals; - -import java.io.IOException; -import java.time.Duration; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.List; -import java.util.concurrent.TimeoutException; - -import org.junit.jupiter.api.Disabled; -import org.junit.jupiter.api.Tag; -import org.junit.jupiter.api.Test; -import org.slf4j.Logger; - -import io.enmasse.iot.model.v1.IoTConfigBuilder; -import io.enmasse.systemtest.iot.IoTTestSession; -import io.enmasse.systemtest.logs.CustomLogger; -import io.enmasse.systemtest.platform.Kubernetes; -import io.enmasse.systemtest.platform.apps.SystemtestsKubernetesApps; - -class H2DeviceRegistryTest extends DeviceRegistryTest { - - private final Logger log = CustomLogger.getLogger(); - - @Override - protected IoTConfigBuilder provideIoTConfig() throws Exception { - return IoTTestSession - .createDefaultConfig() - .editOrNewSpec() - .withServices(newH2Based()) - .endSpec(); - } - - @Test - void testCorrectTypeDeployed () { - assertCorrectDeviceConnectionType("jdbc"); - assertCorrectRegistryType("jdbc"); - } - - @Test - @Tag(ACCEPTANCE) - void testRegisterDevice() throws Exception { - super.doTestRegisterDevice(); - } - - @Test - @Tag(ACCEPTANCE) - void testDisableDevice() throws Exception { - super.doTestDisableDevice(); - } - - @Test - void testDeviceCredentials() throws Exception { - super.doTestDeviceCredentials(); - } - - @Test - void testDeviceCredentialsPlainPassword() throws Exception { - super.doTestDeviceCredentialsPlainPassword(); - } - - @Test - @Disabled("Fixed in hono/pull/1565") - void testDeviceCredentialsDoesNotContainsPasswordDetails() throws Exception { - super.doTestDeviceCredentialsDoesNotContainsPasswordDetails(); - } - - @Test - @Disabled("Caches expire a bit unpredictably") - void testCacheExpiryForCredentials() throws Exception { - super.doTestCacheExpiryForCredentials(); - } - - @Test - void testSetExpiryForCredentials() throws Exception { - super.doTestSetExpiryForCredentials(); - } - - @Test - void testCreateForNonExistingTenantFails() throws Exception { - super.doTestCreateForNonExistingTenantFails(); - } - - @Test - void testCreateDuplicateDeviceFails() throws Exception { - super.doCreateDuplicateDeviceFails(); - } - - @Test - void testRegisterMultipleDevices() throws Exception { - super.doRegisterMultipleDevices(); - } - - @Test - void testTenantDeletionTriggersDevicesDeletion() throws Exception { - super.doTestTenantDeletionTriggersDevicesDeletion(); - - // dump content, just in case - - dumpH2Database(); - - // ensure that the database is still present, but has zero entries - - final List command = new ArrayList<>(Arrays.asList(SystemtestsKubernetesApps.H2_SHELL_COMMAND)); - command.addAll(Arrays.asList("-sql", "select count(*) as NUM from device_registrations")); - - var pod = SystemtestsKubernetesApps - .getH2ServerPod() - .orElseThrow(() -> new IllegalStateException("Unable to find H2 server pod")); - final String[] result = Kubernetes.executeWithInput(pod, null, null, Duration.ofSeconds(10), - command.toArray(String[]::new) - ) - .split("[\r\n]+"); - - // must have three lines - assertEquals(3, result.length); - // first: the column name - assertEquals("NUM", result[0]); - // second: the number of entries, which must be zero - assertEquals("0", result[1]); - // third: the execution statistics, which we ignore - - } - - private void dumpH2Database() throws IOException, InterruptedException, TimeoutException { - final List command = new ArrayList<>(Arrays.asList(SystemtestsKubernetesApps.H2_SHELL_COMMAND)); - command.addAll(Arrays.asList("-sql", "select * from device_registrations")); - - var pod = SystemtestsKubernetesApps - .getH2ServerPod() - .orElseThrow(() -> new IllegalStateException("Unable to find H2 server pod")); - final String result = Kubernetes.executeWithInput(pod, null, null, Duration.ofSeconds(10), - command.toArray(String[]::new)); - - log.info("Current H2 database:\n{}", result); - } -} diff --git a/systemtests/src/test/java/io/enmasse/systemtest/iot/isolated/registry/InfinispanDeviceRegistryTest.java b/systemtests/src/test/java/io/enmasse/systemtest/iot/isolated/registry/InfinispanDeviceRegistryTest.java deleted file mode 100644 index c510e7ed00c..00000000000 --- a/systemtests/src/test/java/io/enmasse/systemtest/iot/isolated/registry/InfinispanDeviceRegistryTest.java +++ /dev/null @@ -1,108 +0,0 @@ -/* - * Copyright 2019-2020, EnMasse authors. - * License: Apache License 2.0 (see the file LICENSE or http://apache.org/licenses/LICENSE-2.0.html). - */ - -package io.enmasse.systemtest.iot.isolated.registry; - -import static io.enmasse.systemtest.TestTag.ACCEPTANCE; -import static io.enmasse.systemtest.iot.DefaultDeviceRegistry.newInfinispanBased; -import static io.enmasse.systemtest.utils.IoTUtils.assertCorrectDeviceConnectionType; -import static io.enmasse.systemtest.utils.IoTUtils.assertCorrectRegistryType; - -import java.net.HttpURLConnection; - -import org.junit.jupiter.api.Disabled; -import org.junit.jupiter.api.Tag; -import org.junit.jupiter.api.Test; - -import io.enmasse.iot.model.v1.IoTConfigBuilder; -import io.enmasse.systemtest.iot.IoTTestSession; - -class InfinispanDeviceRegistryTest extends DeviceRegistryTest { - - @Override - protected int tenantDoesNotExistCode() { - return HttpURLConnection.HTTP_UNAUTHORIZED; - } - - @Override - protected IoTConfigBuilder provideIoTConfig() throws Exception { - return IoTTestSession - .createDefaultConfig() - .editOrNewSpec() - .withServices(newInfinispanBased()) - .endSpec(); - } - - @Test - void testCorrectTypeDeployed () { - assertCorrectDeviceConnectionType("infinispan"); - assertCorrectRegistryType("infinispan"); - } - - @Test - @Tag(ACCEPTANCE) - void testRegisterDevice() throws Exception { - super.doTestRegisterDevice(); - } - - @Test - @Tag(ACCEPTANCE) - void testDisableDevice() throws Exception { - super.doTestDisableDevice(); - } - - @Test - void testDeviceCredentials() throws Exception { - super.doTestDeviceCredentials(); - } - - @Test - void testDeviceCredentialsPlainPassword() throws Exception { - super.doTestDeviceCredentialsPlainPassword(); - } - - @Test - @Disabled("Fixed in hono/pull/1565") - void testDeviceCredentialsDoesNotContainsPasswordDetails() throws Exception { - super.doTestDeviceCredentialsDoesNotContainsPasswordDetails(); - } - - @Test - @Disabled("Caches expire a bit unpredictably") - void testCacheExpiryForCredentials() throws Exception { - super.doTestCacheExpiryForCredentials(); - } - - @Test - void testSetExpiryForCredentials() throws Exception { - super.doTestSetExpiryForCredentials(); - } - - @Test - void testCreateForNonExistingTenantFails() throws Exception { - super.doTestCreateForNonExistingTenantFails(); - } - - @Test - void testCreateDuplicateDeviceFails() throws Exception { - super.doCreateDuplicateDeviceFails(); - } - - @Test - void testRegisterMultipleDevices() throws Exception { - super.doRegisterMultipleDevices(); - } - - @Test - void testTenantDeletionTriggersDevicesDeletion() throws Exception { - super.doTestTenantDeletionTriggersDevicesDeletion(); - } - - @Override - @Disabled("Not supported by Infinispan") - public void testDeviceWithSameAuthIdOfSameTypesFails() throws Exception { - super.testDeviceWithSameAuthIdOfSameTypesFails(); - } -} diff --git a/systemtests/src/test/java/io/enmasse/systemtest/iot/isolated/registry/MixedDeviceRegistryTest.java b/systemtests/src/test/java/io/enmasse/systemtest/iot/isolated/registry/MixedDeviceRegistryTest.java deleted file mode 100644 index a69acad66cf..00000000000 --- a/systemtests/src/test/java/io/enmasse/systemtest/iot/isolated/registry/MixedDeviceRegistryTest.java +++ /dev/null @@ -1,105 +0,0 @@ -/* - * Copyright 2019-2020, EnMasse authors. - * License: Apache License 2.0 (see the file LICENSE or http://apache.org/licenses/LICENSE-2.0.html). - */ - -package io.enmasse.systemtest.iot.isolated.registry; - -import static io.enmasse.systemtest.TestTag.ACCEPTANCE; -import static io.enmasse.systemtest.iot.DefaultDeviceRegistry.newPostgresBasedRegistry; -import static io.enmasse.systemtest.utils.IoTUtils.assertCorrectDeviceConnectionType; -import static io.enmasse.systemtest.utils.IoTUtils.assertCorrectRegistryType; - -import org.junit.jupiter.api.Disabled; -import org.junit.jupiter.api.Tag; -import org.junit.jupiter.api.Test; - -import io.enmasse.iot.model.v1.IoTConfigBuilder; -import io.enmasse.systemtest.iot.DefaultDeviceRegistry; -import io.enmasse.systemtest.iot.IoTTestSession; -import io.enmasse.systemtest.platform.apps.SystemtestsKubernetesApps; - -class MixedDeviceRegistryTest extends DeviceRegistryTest { - - @Override - protected IoTConfigBuilder provideIoTConfig() throws Exception { - - var jdbcEndpoint = SystemtestsKubernetesApps.deployPostgresqlServer(); - var infinispanEndpoint = SystemtestsKubernetesApps.deployInfinispanServer(); - - return IoTTestSession - .createDefaultConfig() - .editOrNewSpec() - .withNewServices() - .withDeviceConnection(DefaultDeviceRegistry.newInfinispanDeviceConnectionService(infinispanEndpoint)) - .withDeviceRegistry(newPostgresBasedRegistry(jdbcEndpoint, false)) - .endServices() - .endSpec(); - - } - - @Test - void testCorrectTypeDeployed () { - assertCorrectDeviceConnectionType("infinispan"); - assertCorrectRegistryType("jdbc"); - } - - @Test - @Tag(ACCEPTANCE) - void testRegisterDevice() throws Exception { - super.doTestRegisterDevice(); - } - - @Test - @Tag(ACCEPTANCE) - void testDisableDevice() throws Exception { - super.doTestDisableDevice(); - } - - @Test - void testDeviceCredentials() throws Exception { - super.doTestDeviceCredentials(); - } - - @Test - void testDeviceCredentialsPlainPassword() throws Exception { - super.doTestDeviceCredentialsPlainPassword(); - } - - @Test - @Disabled("Fixed in hono/pull/1565") - void testDeviceCredentialsDoesNotContainsPasswordDetails() throws Exception { - super.doTestDeviceCredentialsDoesNotContainsPasswordDetails(); - } - - @Test - @Disabled("Caches expire a bit unpredictably") - void testCacheExpiryForCredentials() throws Exception { - super.doTestCacheExpiryForCredentials(); - } - - @Test - void testSetExpiryForCredentials() throws Exception { - super.doTestSetExpiryForCredentials(); - } - - @Test - void testCreateForNonExistingTenantFails() throws Exception { - super.doTestCreateForNonExistingTenantFails(); - } - - @Test - void testCreateDuplicateDeviceFails() throws Exception { - super.doCreateDuplicateDeviceFails(); - } - - @Test - void testRegisterMultipleDevices() throws Exception { - super.doRegisterMultipleDevices(); - } - - @Test - void testTenantDeletionTriggersDevicesDeletion() throws Exception { - super.doTestTenantDeletionTriggersDevicesDeletion(); - } -} diff --git a/systemtests/src/test/java/io/enmasse/systemtest/iot/isolated/registry/PostgresTableDeviceRegistryTest.java b/systemtests/src/test/java/io/enmasse/systemtest/iot/isolated/registry/PostgresTableDeviceRegistryTest.java deleted file mode 100644 index f0491ad3aef..00000000000 --- a/systemtests/src/test/java/io/enmasse/systemtest/iot/isolated/registry/PostgresTableDeviceRegistryTest.java +++ /dev/null @@ -1,95 +0,0 @@ -/* - * Copyright 2019-2020, EnMasse authors. - * License: Apache License 2.0 (see the file LICENSE or http://apache.org/licenses/LICENSE-2.0.html). - */ - -package io.enmasse.systemtest.iot.isolated.registry; - -import static io.enmasse.systemtest.TestTag.ACCEPTANCE; -import static io.enmasse.systemtest.iot.DefaultDeviceRegistry.newPostgresBased; -import static io.enmasse.systemtest.utils.IoTUtils.assertCorrectDeviceConnectionType; -import static io.enmasse.systemtest.utils.IoTUtils.assertCorrectRegistryType; - -import org.junit.jupiter.api.Disabled; -import org.junit.jupiter.api.Tag; -import org.junit.jupiter.api.Test; - -import io.enmasse.iot.model.v1.IoTConfigBuilder; -import io.enmasse.systemtest.iot.IoTTestSession; - -class PostgresTableDeviceRegistryTest extends DeviceRegistryTest { - - @Override - protected IoTConfigBuilder provideIoTConfig() throws Exception { - return IoTTestSession - .createDefaultConfig() - .editOrNewSpec() - .withServices(newPostgresBased(false)) - .endSpec(); - } - - @Test - void testCorrectTypeDeployed () { - assertCorrectDeviceConnectionType("jdbc"); - assertCorrectRegistryType("jdbc"); - } - - @Test - @Tag(ACCEPTANCE) - void testRegisterDevice() throws Exception { - super.doTestRegisterDevice(); - } - - @Test - @Tag(ACCEPTANCE) - void testDisableDevice() throws Exception { - super.doTestDisableDevice(); - } - - @Test - void testDeviceCredentials() throws Exception { - super.doTestDeviceCredentials(); - } - - @Test - void testDeviceCredentialsPlainPassword() throws Exception { - super.doTestDeviceCredentialsPlainPassword(); - } - - @Test - @Disabled("Fixed in hono/pull/1565") - void testDeviceCredentialsDoesNotContainsPasswordDetails() throws Exception { - super.doTestDeviceCredentialsDoesNotContainsPasswordDetails(); - } - - @Test - @Disabled("Caches expire a bit unpredictably") - void testCacheExpiryForCredentials() throws Exception { - super.doTestCacheExpiryForCredentials(); - } - - @Test - void testSetExpiryForCredentials() throws Exception { - super.doTestSetExpiryForCredentials(); - } - - @Test - void testCreateForNonExistingTenantFails() throws Exception { - super.doTestCreateForNonExistingTenantFails(); - } - - @Test - void testCreateDuplicateDeviceFails() throws Exception { - super.doCreateDuplicateDeviceFails(); - } - - @Test - void testRegisterMultipleDevices() throws Exception { - super.doRegisterMultipleDevices(); - } - - @Test - void testTenantDeletionTriggersDevicesDeletion() throws Exception { - super.doTestTenantDeletionTriggersDevicesDeletion(); - } -} diff --git a/systemtests/src/test/java/io/enmasse/systemtest/iot/isolated/registry/PostgresTableSplitDeviceRegistryTest.java b/systemtests/src/test/java/io/enmasse/systemtest/iot/isolated/registry/PostgresTableSplitDeviceRegistryTest.java deleted file mode 100644 index 307a9a085e9..00000000000 --- a/systemtests/src/test/java/io/enmasse/systemtest/iot/isolated/registry/PostgresTableSplitDeviceRegistryTest.java +++ /dev/null @@ -1,96 +0,0 @@ -/* - * Copyright 2019-2020, EnMasse authors. - * License: Apache License 2.0 (see the file LICENSE or http://apache.org/licenses/LICENSE-2.0.html). - */ - -package io.enmasse.systemtest.iot.isolated.registry; - -import static io.enmasse.systemtest.TestTag.ACCEPTANCE; -import static io.enmasse.systemtest.iot.DefaultDeviceRegistry.newPostgresBased; -import static io.enmasse.systemtest.utils.IoTUtils.assertCorrectRegistryType; - -import org.junit.jupiter.api.Disabled; -import org.junit.jupiter.api.Tag; -import org.junit.jupiter.api.Test; - -import io.enmasse.iot.model.v1.IoTConfigBuilder; -import io.enmasse.systemtest.iot.IoTTestSession; - -/** - * Postgres table model, split adapter and management pods. - */ -class PostgresTableSplitDeviceRegistryTest extends DeviceRegistryTest { - - @Override - protected IoTConfigBuilder provideIoTConfig() throws Exception { - return IoTTestSession - .createDefaultConfig() - .editOrNewSpec() - .withServices(newPostgresBased(true)) - .endSpec(); - } - - @Test - void testCorrectTypeDeployed () { - assertCorrectRegistryType("jdbc"); - } - - @Test - @Tag(ACCEPTANCE) - void testRegisterDevice() throws Exception { - super.doTestRegisterDevice(); - } - - @Test - @Tag(ACCEPTANCE) - void testDisableDevice() throws Exception { - super.doTestDisableDevice(); - } - - @Test - void testDeviceCredentials() throws Exception { - super.doTestDeviceCredentials(); - } - - @Test - void testDeviceCredentialsPlainPassword() throws Exception { - super.doTestDeviceCredentialsPlainPassword(); - } - - @Test - @Disabled("Fixed in hono/pull/1565") - void testDeviceCredentialsDoesNotContainsPasswordDetails() throws Exception { - super.doTestDeviceCredentialsDoesNotContainsPasswordDetails(); - } - - @Test - @Disabled("Caches expire a bit unpredictably") - void testCacheExpiryForCredentials() throws Exception { - super.doTestCacheExpiryForCredentials(); - } - - @Test - void testSetExpiryForCredentials() throws Exception { - super.doTestSetExpiryForCredentials(); - } - - @Test - void testCreateForNonExistingTenantFails() throws Exception { - super.doTestCreateForNonExistingTenantFails(); - } - - @Test - void testCreateDuplicateDeviceFails() throws Exception { - super.doCreateDuplicateDeviceFails(); - } - - @Test - void testRegisterMultipleDevices() throws Exception { - super.doRegisterMultipleDevices(); - } - - @Test - void testTenantDeletionTriggersDevicesDeletion() throws Exception { - super.doTestTenantDeletionTriggersDevicesDeletion(); - } -} diff --git a/systemtests/src/test/java/io/enmasse/systemtest/iot/isolated/tls/ReloadCertificatesTest.java b/systemtests/src/test/java/io/enmasse/systemtest/iot/isolated/tls/ReloadCertificatesTest.java deleted file mode 100644 index 0e06318f3f9..00000000000 --- a/systemtests/src/test/java/io/enmasse/systemtest/iot/isolated/tls/ReloadCertificatesTest.java +++ /dev/null @@ -1,145 +0,0 @@ -/* - * Copyright 2020, EnMasse authors. - * License: Apache License 2.0 (see the file LICENSE or http://apache.org/licenses/LICENSE-2.0.html). - */ - -package io.enmasse.systemtest.iot.isolated.tls; - -import static io.enmasse.systemtest.TestTag.ISOLATED; -import static io.enmasse.systemtest.condition.OpenShiftVersion.OCP4; -import static io.enmasse.systemtest.iot.IoTTestSession.Adapter.HTTP; -import static io.enmasse.systemtest.time.TimeoutBudget.ofDuration; -import static java.time.Duration.ofMinutes; -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertNotNull; - -import java.time.Duration; -import java.util.Map; -import java.util.Optional; -import java.util.UUID; - -import org.junit.jupiter.api.Tag; -import org.junit.jupiter.api.Test; - -import io.enmasse.systemtest.bases.TestBase; -import io.enmasse.systemtest.bases.iot.ITestIoTIsolated; -import io.enmasse.systemtest.condition.OpenShift; -import io.enmasse.systemtest.iot.IoTTestSession; -import io.enmasse.systemtest.iot.IoTTestSession.Device; -import io.enmasse.systemtest.iot.MessageSendTester; -import io.enmasse.systemtest.iot.MessageSendTester.ConsumerFactory; -import io.enmasse.systemtest.platform.Kubernetes; -import io.enmasse.systemtest.time.TimeoutBudget; -import io.enmasse.systemtest.utils.IoTUtils; -import io.enmasse.systemtest.utils.TestUtils; -import io.fabric8.kubernetes.client.KubernetesClient; - -@Tag(ISOLATED) -public class ReloadCertificatesTest extends TestBase implements ITestIoTIsolated { - - private static final String NAMESPACE = Kubernetes.getInstance().getInfraNamespace(); - - private KubernetesClient client = Kubernetes.getInstance().getClient(); - - @Test - @OpenShift(version = OCP4) - public void testRecreateCertificate() throws Exception { - - IoTTestSession - .createDefault() - .adapters(HTTP) - .config(config -> { - // ensure we are using the service-ca - config.editOrNewSpec() - .withNewInterServiceCertificates() - .withNewServiceCAStrategy() - .endServiceCAStrategy() - .endInterServiceCertificates() - - .editOrNewAdapters() - .editOrNewHttp() - // reset to use service CA endpoint secret - .withNewEndpoint().endEndpoint() - .endHttp() - .endAdapters() - - .endSpec(); - }) - .run((session) -> { - - var deviceId = UUID.randomUUID().toString(); - var authId = UUID.randomUUID().toString(); - var password = UUID.randomUUID().toString(); - var device = session.newDevice(deviceId) - .register() - .setPassword(authId, password); - - // ensure everything works before starting - - assertTelemetryWorks(session, device); - - // get current pod - - var deploymentAccess = this.client - .apps().deployments() - .inNamespace(NAMESPACE) - .withName("iot-http-adapter"); - - var pod = this.client - .pods() - .inNamespace(NAMESPACE).withLabels(Map.of( - "app", "enmasse", - "name", "iot-http-adapter")) - .list().getItems().stream() - .map(p -> p.getMetadata().getName()) - .findFirst() - .orElse(null); - - assertNotNull(pod); - - // then: reset http adapter key/cert - - var deleteResult = this.client.secrets() - .inNamespace(NAMESPACE) - .withName("iot-http-adapter-tls") - .delete(); - - assertEquals(Boolean.TRUE, deleteResult); - - final TimeoutBudget budget = ofDuration(ofMinutes(10)); - - // wait until the deployment has changed - - var initialVersion = deploymentAccess.get().getMetadata().getResourceVersion(); - TestUtils.waitForChangedResourceVersion(budget, initialVersion, () -> { - return Optional.ofNullable(deploymentAccess.get()) - .map(d -> d.getMetadata().getResourceVersion()) - .orElse(initialVersion); - }); - - // and wait until the IoTConfig is ready again - - IoTUtils.waitForIoTConfigReady(Kubernetes.getInstance(), session.getConfig()); - - // now try to send messages again - - assertTelemetryWorks(session, device); - - }); - - } - - protected void assertTelemetryWorks(final IoTTestSession session, final Device device) throws Exception { - try (var adapterClient = device.createHttpAdapterClient()) { - new MessageSendTester() - .type(MessageSendTester.Type.TELEMETRY) - .delay(Duration.ofMillis(500)) - .consumerFactory(ConsumerFactory.of(session.getConsumerClient(), session.getTenantId())) - .sender(adapterClient::send) - .amount(30) - .consume(MessageSendTester.Consume.BEFORE) - .execute(); - } - - } -} diff --git a/systemtests/src/test/java/io/enmasse/systemtest/iot/isolated/tls/TlsVersionTest.java b/systemtests/src/test/java/io/enmasse/systemtest/iot/isolated/tls/TlsVersionTest.java deleted file mode 100644 index e75bf753189..00000000000 --- a/systemtests/src/test/java/io/enmasse/systemtest/iot/isolated/tls/TlsVersionTest.java +++ /dev/null @@ -1,77 +0,0 @@ -/* - * Copyright 2020, EnMasse authors. - * License: Apache License 2.0 (see the file LICENSE or http://apache.org/licenses/LICENSE-2.0.html). - */ - -package io.enmasse.systemtest.iot.isolated.tls; - -import static io.enmasse.systemtest.TestTag.ISOLATED; -import static java.util.Collections.singleton; -import static org.junit.jupiter.api.Assertions.assertThrows; - -import java.time.Duration; -import java.util.concurrent.TimeoutException; - -import org.junit.jupiter.api.Tag; -import org.junit.jupiter.api.Test; - -import io.enmasse.systemtest.condition.OpenShift; -import io.enmasse.systemtest.condition.OpenShiftVersion; -import io.enmasse.systemtest.iot.HttpAdapterClient; -import io.enmasse.systemtest.iot.IoTTestSession; -import io.enmasse.systemtest.iot.IoTTestSession.Adapter; -import io.enmasse.systemtest.iot.MessageSendTester; -import io.enmasse.systemtest.iot.MessageSendTester.ConsumerFactory; - -@Tag(ISOLATED) -public class TlsVersionTest { - - @OpenShift(version = OpenShiftVersion.OCP4) - @Test - public void testWithExplicitVersion() throws Exception { - IoTTestSession - .createDefault() - .adapters(Adapter.HTTP) - .config(config -> config - .editOrNewSpec() - .editOrNewTls() - .withVersions("TLSv1.3") - .endTls() - .endSpec()) - .run(session -> { - - var device = session.registerNewRandomDeviceWithPassword(); - - try (HttpAdapterClient client = device.createHttpAdapterClient(singleton("TLSv1.3"))) { - - new MessageSendTester() - .type(MessageSendTester.Type.TELEMETRY) - .delay(Duration.ofSeconds(1)) - .consumerFactory(ConsumerFactory.of(session.getConsumerClient(), session.getTenantId())) - .sender(client::send) - .amount(50) - .consume(MessageSendTester.Consume.BEFORE) - .execute(); - - } - - try (HttpAdapterClient client = device.createHttpAdapterClient(singleton("TLSv1.2"))) { - - // this must fail, as we offer TLSv1.2, but have only 1.3 configured - assertThrows(TimeoutException.class, () -> { - new MessageSendTester() - .type(MessageSendTester.Type.TELEMETRY) - .delay(Duration.ofSeconds(1)) - .consumerFactory(ConsumerFactory.of(session.getConsumerClient(), session.getTenantId())) - .sender(client::send) - .amount(50) - .consume(MessageSendTester.Consume.BEFORE) - .execute(); - }); - - } - - }); - } - -} diff --git a/systemtests/src/test/java/io/enmasse/systemtest/iot/isolated/x509/StandardX509Cases.java b/systemtests/src/test/java/io/enmasse/systemtest/iot/isolated/x509/StandardX509Cases.java deleted file mode 100644 index 0afb7cdc95e..00000000000 --- a/systemtests/src/test/java/io/enmasse/systemtest/iot/isolated/x509/StandardX509Cases.java +++ /dev/null @@ -1,97 +0,0 @@ -/* - * Copyright 2020, EnMasse authors. - * License: Apache License 2.0 (see the file LICENSE or http://apache.org/licenses/LICENSE-2.0.html). - */ - -package io.enmasse.systemtest.iot.isolated.x509; - -import static io.enmasse.systemtest.iot.DeviceSupplier.named; - -import java.time.Duration; -import java.time.Instant; -import java.util.Arrays; -import java.util.List; -import java.util.UUID; - -import javax.security.auth.x500.X500Principal; - -import io.enmasse.systemtest.iot.DeviceCertificateManager; -import io.enmasse.systemtest.iot.DeviceCertificateManager.Mode; -import io.enmasse.systemtest.iot.DeviceSupplier; -import io.enmasse.systemtest.iot.StandardIoTTests; - -public interface StandardX509Cases extends StandardIoTTests { - - DeviceCertificateManager getCertificateManager(); - - @Override - default List getDevices() { - - return Arrays.asList( - named("default", () -> getSession().newDevice() - .register() - .enableX509(getCertificateManager().createDevice()))); - - } - - @Override - default List getInvalidDevices() { - - return Arrays.asList( - notOurCaDevice(), - expiredCertificate(), - futureCertificate()); - - } - - /** - * Create a device which has a certificate signed by a CA with the same name, but which actually is - * not the same CA. - */ - default DeviceSupplier notOurCaDevice() { - - return named("notOurCa", () -> { - var otherMgr = new DeviceCertificateManager(Mode.RSA, new X500Principal("OU=Tenant 1,OU=IoT,O=EnMasse,C=IO")); - - return getSession().newDevice() - .register() - .enableX509(otherMgr.createDevice()); - }); - - } - - /** - * Create a device which has an expired certificate. - */ - default DeviceSupplier expiredCertificate() { - - final Instant now = Instant.now(); - - return named("expiredCertificate", () -> getSession().newDevice() - .register() - .enableX509(getCertificateManager() - .createDevice( - UUID.randomUUID().toString(), - now.minus(Duration.ofDays(30)), - now))); - - } - - /** - * Create a device which has an valid date in the future. - */ - default DeviceSupplier futureCertificate() { - - final Instant now = Instant.now(); - - return named("futureCertificate", () -> getSession().newDevice() - .register() - .enableX509(getCertificateManager() - .createDevice( - UUID.randomUUID().toString(), - now.plus(Duration.ofDays(100)), - now.plus(Duration.ofDays(200))))); - - } - -} diff --git a/systemtests/src/test/java/io/enmasse/systemtest/iot/isolated/x509/TrustAnchorTests.java b/systemtests/src/test/java/io/enmasse/systemtest/iot/isolated/x509/TrustAnchorTests.java deleted file mode 100644 index 4d287b288b3..00000000000 --- a/systemtests/src/test/java/io/enmasse/systemtest/iot/isolated/x509/TrustAnchorTests.java +++ /dev/null @@ -1,114 +0,0 @@ -/* - * Copyright 2020, EnMasse authors. - * License: Apache License 2.0 (see the file LICENSE or http://apache.org/licenses/LICENSE-2.0.html). - */ - -package io.enmasse.systemtest.iot.isolated.x509; - -import static io.enmasse.systemtest.TestTag.ACCEPTANCE; -import static io.enmasse.systemtest.TestTag.ISOLATED_IOT; -import static io.enmasse.systemtest.iot.IoTTestSession.Adapter.HTTP; -import static io.enmasse.systemtest.utils.TestUtils.toPem; - -import java.time.Duration; -import java.util.Optional; -import java.util.UUID; - -import javax.security.auth.x500.X500Principal; - -import org.junit.jupiter.api.Tag; -import org.junit.jupiter.api.Test; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import io.enmasse.iot.model.v1.IoTProject; -import io.enmasse.iot.model.v1.IoTProjectBuilder; -import io.enmasse.iot.model.v1.IoTProjectStatus; -import io.enmasse.iot.model.v1.ProjectConditionType; -import io.enmasse.systemtest.iot.DeviceCertificateManager; -import io.enmasse.systemtest.iot.DeviceCertificateManager.Mode; -import io.enmasse.systemtest.iot.IoTTestSession; -import io.enmasse.systemtest.iot.IoTTests; -import io.enmasse.systemtest.model.addressspace.AddressSpacePlans; -import io.enmasse.systemtest.platform.Kubernetes; -import io.enmasse.systemtest.utils.IoTUtils; -import io.enmasse.systemtest.utils.TestUtils; - -@Tag(ISOLATED_IOT) -public class TrustAnchorTests implements IoTTests { - - private static final Logger log = LoggerFactory.getLogger(TrustAnchorTests.class); - - /** - * Test creating duplicate "subject dn". - */ - @Test - @Tag(ACCEPTANCE) - public void testDuplicateSubjectDn() throws Exception { - var mgr1 = new DeviceCertificateManager(Mode.RSA, new X500Principal("OU=Tenant 1,OU=IoT,O=EnMasse,C=IO")); - var mgr2 = new DeviceCertificateManager(Mode.RSA, new X500Principal("OU=Tenant 1,OU=IoT,O=EnMasse,C=IO")); - - final String name2 = UUID.randomUUID().toString(); - final String ns2 = UUID.randomUUID().toString(); - Kubernetes.getInstance().createNamespace(ns2); - - try (IoTTestSession session = IoTTestSession.createDefault() - .project(project -> project.editOrNewSpec() - .editOrNewConfiguration() - .addNewTrustAnchor() - .withCertificate(toPem(mgr1.getCertificate())) - .endTrustAnchor() - .endConfiguration() - .endSpec()) - .adapters(HTTP) - .deploy()) { - - // now create a second project, in second namespace, with a new CA - // having the same name. This must fail. - - var project2 = new IoTProjectBuilder(IoTUtils.getBasicIoTProjectObject( - name2, name2, ns2, - AddressSpacePlans.STANDARD_SMALL)) - - .editOrNewSpec() - .editOrNewConfiguration() - .addNewTrustAnchor() - .withCertificate(toPem(mgr2.getCertificate())) - .endTrustAnchor() - .endConfiguration() - .endSpec() - - .build(); - - // create - - var projectClient = Kubernetes.getInstance().getIoTProjectClient(ns2); - session.addCleanup(() -> IoTUtils.deleteIoTProjectAndWait(project2)); - projectClient.create(project2); - var projectAccess = projectClient.withName(name2); - - // and wait for the condition - - TestUtils.waitUntilConditionOrFail(() -> { - - // wait until the condition 'TrustAnchorsUnique' becomes false - return Optional - .ofNullable(projectAccess.get()) - .map(IoTProject::getStatus) - .map(IoTProjectStatus::getConditions) - .flatMap(c -> c.stream() - .filter(condition -> condition.getType() == ProjectConditionType.TRUST_ANCHORS_UNIQUE) - .findAny()) - .map(c -> "false".equalsIgnoreCase(c.getStatus()) && - "DuplicateTrustAnchors".equals(c.getReason())) - .orElse(false); - - }, Duration.ofMinutes(5), Duration.ofSeconds(10), () -> "Conditions 'TrustAnchorsUnique' should become 'false'"); - - log.info("Successfully detected 'DuplicateTrustAnchors' problem on second project"); - - } - - } - -} diff --git a/systemtests/src/test/java/io/enmasse/systemtest/iot/isolated/x509/X509EcAuthenticationTests.java b/systemtests/src/test/java/io/enmasse/systemtest/iot/isolated/x509/X509EcAuthenticationTests.java deleted file mode 100644 index bdfa0237ceb..00000000000 --- a/systemtests/src/test/java/io/enmasse/systemtest/iot/isolated/x509/X509EcAuthenticationTests.java +++ /dev/null @@ -1,67 +0,0 @@ -/* - * Copyright 2020, EnMasse authors. - * License: Apache License 2.0 (see the file LICENSE or http://apache.org/licenses/LICENSE-2.0.html). - */ - -package io.enmasse.systemtest.iot.isolated.x509; - -import static io.enmasse.systemtest.TestTag.ISOLATED_IOT; -import static io.enmasse.systemtest.iot.IoTTestSession.Adapter.HTTP; -import static io.enmasse.systemtest.iot.IoTTestSession.Adapter.MQTT; -import static io.enmasse.systemtest.utils.TestUtils.toPem; - -import javax.security.auth.x500.X500Principal; - -import org.junit.jupiter.api.AfterAll; -import org.junit.jupiter.api.BeforeAll; -import org.junit.jupiter.api.Tag; - -import io.enmasse.systemtest.iot.DeviceCertificateManager; -import io.enmasse.systemtest.iot.DeviceCertificateManager.Mode; -import io.enmasse.systemtest.iot.IoTTestSession; -import io.enmasse.systemtest.iot.http.StandardIoTHttpTests; -import io.enmasse.systemtest.iot.mqtt.StandardIoTMqttTests; - -@Tag(ISOLATED_IOT) -public class X509EcAuthenticationTests implements StandardX509Cases, StandardIoTHttpTests, StandardIoTMqttTests { - - private static DeviceCertificateManager certificateManager; - private static IoTTestSession session; - - @BeforeAll - public static void setup() throws Exception { - - certificateManager = new DeviceCertificateManager(Mode.EC, new X500Principal("OU=Tenant 1,OU=IoT,O=EnMasse,C=IO")); - - session = IoTTestSession.createDefault() - .project(project -> project.editOrNewSpec() - .editOrNewConfiguration() - .addNewTrustAnchor() - .withCertificate(toPem(certificateManager.getCertificate())) - .endTrustAnchor() - .endConfiguration() - .endSpec()) - .adapters(MQTT, HTTP) - .deploy(); - - } - - @AfterAll - public static void cleanup() throws Exception { - if (session != null) { - session.close(); - session = null; - } - } - - @Override - public DeviceCertificateManager getCertificateManager() { - return certificateManager; - } - - @Override - public IoTTestSession getSession() { - return session; - } - -} diff --git a/systemtests/src/test/java/io/enmasse/systemtest/iot/isolated/x509/X509RsaAuthenticationTests.java b/systemtests/src/test/java/io/enmasse/systemtest/iot/isolated/x509/X509RsaAuthenticationTests.java deleted file mode 100644 index 7b3540767c1..00000000000 --- a/systemtests/src/test/java/io/enmasse/systemtest/iot/isolated/x509/X509RsaAuthenticationTests.java +++ /dev/null @@ -1,67 +0,0 @@ -/* - * Copyright 2020, EnMasse authors. - * License: Apache License 2.0 (see the file LICENSE or http://apache.org/licenses/LICENSE-2.0.html). - */ - -package io.enmasse.systemtest.iot.isolated.x509; - -import static io.enmasse.systemtest.TestTag.ISOLATED_IOT; -import static io.enmasse.systemtest.iot.IoTTestSession.Adapter.HTTP; -import static io.enmasse.systemtest.iot.IoTTestSession.Adapter.MQTT; -import static io.enmasse.systemtest.utils.TestUtils.toPem; - -import javax.security.auth.x500.X500Principal; - -import org.junit.jupiter.api.AfterAll; -import org.junit.jupiter.api.BeforeAll; -import org.junit.jupiter.api.Tag; - -import io.enmasse.systemtest.iot.DeviceCertificateManager; -import io.enmasse.systemtest.iot.DeviceCertificateManager.Mode; -import io.enmasse.systemtest.iot.IoTTestSession; -import io.enmasse.systemtest.iot.http.StandardIoTHttpTests; -import io.enmasse.systemtest.iot.mqtt.StandardIoTMqttTests; - -@Tag(ISOLATED_IOT) -public class X509RsaAuthenticationTests implements StandardX509Cases, StandardIoTHttpTests, StandardIoTMqttTests { - - private static DeviceCertificateManager certificateManager; - private static IoTTestSession session; - - @BeforeAll - public static void setup() throws Exception { - - certificateManager = new DeviceCertificateManager(Mode.RSA, new X500Principal("OU=Tenant 1,OU=IoT,O=EnMasse,C=IO")); - - session = IoTTestSession.createDefault() - .project(project -> project.editOrNewSpec() - .editOrNewConfiguration() - .addNewTrustAnchor() - .withCertificate(toPem(certificateManager.getCertificate())) - .endTrustAnchor() - .endConfiguration() - .endSpec()) - .adapters(MQTT, HTTP) - .deploy(); - - } - - @AfterAll - public static void cleanup() throws Exception { - if (session != null) { - session.close(); - session = null; - } - } - - @Override - public DeviceCertificateManager getCertificateManager() { - return certificateManager; - } - - @Override - public IoTTestSession getSession() { - return session; - } - -} diff --git a/systemtests/src/test/java/io/enmasse/systemtest/iot/mqtt/StandardIoTMqttTests.java b/systemtests/src/test/java/io/enmasse/systemtest/iot/mqtt/StandardIoTMqttTests.java deleted file mode 100644 index dcf43f5bba6..00000000000 --- a/systemtests/src/test/java/io/enmasse/systemtest/iot/mqtt/StandardIoTMqttTests.java +++ /dev/null @@ -1,255 +0,0 @@ -/* - * Copyright 2020, EnMasse authors. - * License: Apache License 2.0 (see the file LICENSE or http://apache.org/licenses/LICENSE-2.0.html). - */ - -package io.enmasse.systemtest.iot.mqtt; - -import static io.enmasse.systemtest.TestTag.ACCEPTANCE; -import static org.junit.jupiter.api.Assertions.assertThrows; -import static org.junit.jupiter.api.Assertions.fail; - -import java.time.Duration; -import java.util.concurrent.TimeoutException; - -import javax.net.ssl.SSLHandshakeException; - -import org.eclipse.paho.client.mqttv3.MqttSecurityException; -import org.junit.jupiter.api.Tag; -import org.junit.jupiter.params.ParameterizedTest; -import org.junit.jupiter.params.provider.MethodSource; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import com.google.common.base.Throwables; - -import io.enmasse.systemtest.iot.DeviceSupplier; -import io.enmasse.systemtest.iot.MessageSendTester; -import io.enmasse.systemtest.iot.MessageSendTester.ConsumerFactory; -import io.enmasse.systemtest.iot.MqttAdapterClient; -import io.enmasse.systemtest.iot.StandardIoTTests; - -/** - * Standard MQTT IoT tests - *

- * Note: we do not test single telemetry with QoS 0 here, as we don't receive any - * feedback if the message was accepted or not. So we couldn't re-try and could only assume that a - * message loss of 100% would be acceptable, which doesn't test much. For bigger batch sizes we can - * test with an acceptable message loss rate of e.g. 10%. - */ -public interface StandardIoTMqttTests extends StandardIoTTests { - - final static Logger log = LoggerFactory.getLogger(StandardIoTMqttTests.class); - - /** - * Single telemetry message with attached consumer. QoS 1. - *
- * Sending with QoS 1 is ok. - */ - @Tag(ACCEPTANCE) - @ParameterizedTest(name = "testMqttTelemetrySingleQos1-{0}") - @MethodSource("getDevices") - default void testMqttTelemetrySingleQos1(final DeviceSupplier device) throws Exception { - - try (MqttAdapterClient client = device.get().createMqttAdapterClient()) { - new MessageSendTester() - .type(MessageSendTester.Type.TELEMETRY) - .delay(Duration.ofSeconds(1)) - .consumerFactory(ConsumerFactory.of(getSession().getConsumerClient(), getSession().getTenantId())) - .sender(client::sendQos1) - .amount(1) - .consume(MessageSendTester.Consume.BEFORE) - .execute(); - } - - } - - /** - * Single event message with non-attached consumer. - *
- * This is the normal use case. - */ - @ParameterizedTest(name = "testMqttEventSingle-{0}") - @MethodSource("getDevices") - default void testMqttEventSingle(final DeviceSupplier device) throws Exception { - - try (MqttAdapterClient client = device.get().createMqttAdapterClient()) { - new MessageSendTester() - .type(MessageSendTester.Type.EVENT) - .delay(Duration.ofSeconds(1)) - .consumerFactory(ConsumerFactory.of(getSession().getConsumerClient(), getSession().getTenantId())) - .sender(client::sendQos1) - .amount(1) - .consume(MessageSendTester.Consume.AFTER) - .execute(); - } - - } - - /** - * Batch of telemetry messages with attached consumer. QoS 0. - *
- * Batched version of the normal use case. We do accept message loss of 10% here. - */ - @ParameterizedTest(name = "testMqttTelemetryBatch50Qos0-{0}") - @MethodSource("getDevices") - default void testMqttTelemetryBatch50Qos0(final DeviceSupplier device) throws Exception { - - try (MqttAdapterClient client = device.get().createMqttAdapterClient()) { - new MessageSendTester() - .type(MessageSendTester.Type.TELEMETRY) - .delay(Duration.ofSeconds(1)) - .consumerFactory(ConsumerFactory.of(getSession().getConsumerClient(), getSession().getTenantId())) - .sender(client::sendQos0) - .amount(50) - .consume(MessageSendTester.Consume.BEFORE) - .acceptableMessageLoss(5) // allow for 10% - .execute(); - } - - } - - /** - * Batch of telemetry messages with attached consumer. QoS 1. - *
- * Compared to QoS 0, we do not accept message loss here. - */ - @ParameterizedTest(name = "testMqttTelemetryBatch50Qos1-{0}") - @MethodSource("getDevices") - default void testMqttTelemetryBatch50Qos1(final DeviceSupplier device) throws Exception { - - try (MqttAdapterClient client = device.get().createMqttAdapterClient()) { - new MessageSendTester() - .type(MessageSendTester.Type.TELEMETRY) - .delay(Duration.ofSeconds(1)) - .consumerFactory(ConsumerFactory.of(getSession().getConsumerClient(), getSession().getTenantId())) - .sender(client::sendQos1) - .amount(50) - .consume(MessageSendTester.Consume.BEFORE) - .execute(); - } - - } - - /** - * Batch of event messages with non-attached consumer. - *
- * This sends messages without an attached consumer. The broker is expected - * to take care of that. Still we expect to receive the messages later. - */ - @ParameterizedTest(name = "testMqttEventBatch5After-{0}") - @MethodSource("getDevices") - default void testMqttEventBatch5After(final DeviceSupplier device) throws Exception { - - try (MqttAdapterClient client = device.get().createMqttAdapterClient()) { - new MessageSendTester() - .type(MessageSendTester.Type.EVENT) - .delay(Duration.ofMillis(100)) - .additionalSendTimeout(Duration.ofSeconds(2)) - .consumerFactory(ConsumerFactory.of(getSession().getConsumerClient(), getSession().getTenantId())) - .sender(client::sendQos1) - .amount(5) - .consume(MessageSendTester.Consume.AFTER) - .execute(); - } - - } - - /** - * Batch of event messages with attached consumer. - *
- * This is the normal use case. - */ - @ParameterizedTest(name = "testMqttEventBatch5Before-{0}") - @MethodSource("getDevices") - default void testMqttEventBatch5Before(final DeviceSupplier device) throws Exception { - - try (MqttAdapterClient client = device.get().createMqttAdapterClient()) { - new MessageSendTester() - .type(MessageSendTester.Type.EVENT) - .delay(Duration.ofSeconds(1)) - .consumerFactory(ConsumerFactory.of(getSession().getConsumerClient(), getSession().getTenantId())) - .sender(client::sendQos1) - .amount(5) - .consume(MessageSendTester.Consume.BEFORE) - .execute(); - } - - } - - /** - * Test for an invalid device. - *
- * With an invalid device, no messages must pass. - */ - @ParameterizedTest(name = "testMqttInvalidDevice-{0}") - @MethodSource("getInvalidDevices") - default void testMqttInvalidDevice(final DeviceSupplier deviceSupplier) throws Exception { - - log.info("Testing invalid devices, the following exception may be expected"); - - // get the device now, once, throwing out of the test method - - var device = deviceSupplier.get(); - - /* - * We test an invalid device by trying to send either telemetry or event messages. - * Two separate connections, and more than one message. - * - * We do expect a failure, but it must be a specific failure. We do accept - * an MqttSecurityException when opening the connection, or a TimeoutException - * when we could open the connection, but not send/receive. - */ - - try (MqttAdapterClient client = device.createMqttAdapterClient()) { - assertThrows(TimeoutException.class, () -> { - new MessageSendTester() - .type(MessageSendTester.Type.TELEMETRY) - .delay(Duration.ofSeconds(1)) - .consumerFactory(ConsumerFactory.of(getSession().getConsumerClient(), getSession().getTenantId())) - .sender(client::sendQos1) - .amount(5) - .consume(MessageSendTester.Consume.BEFORE) - .execute(); - }); - } catch (Exception e) { - assertConnectionException(e); - log.debug("Accepting MQTT exception", e); - } - - try (MqttAdapterClient client = device.createMqttAdapterClient()) { - assertThrows(TimeoutException.class, () -> { - new MessageSendTester() - .type(MessageSendTester.Type.EVENT) - .delay(Duration.ofSeconds(1)) - .consumerFactory(ConsumerFactory.of(getSession().getConsumerClient(), getSession().getTenantId())) - .sender(client::sendQos1) - .amount(5) - .consume(MessageSendTester.Consume.BEFORE) - .execute(); - }); - } catch (Exception e) { - assertConnectionException(e); - log.debug("Accepting MQTT exception", e); - } - - } - - public static void assertConnectionException(final Throwable e) { - - // if we get an exception, it must be an MqttSecurityException or SSLHandshakeException - - if (e instanceof MqttSecurityException) { - return; - } - - final Throwable cause = Throwables.getRootCause(e); - if (cause instanceof SSLHandshakeException) { - return; - } - - fail("Failed to connect with non-permitted exception", e); - - } - -} diff --git a/systemtests/src/test/java/io/enmasse/systemtest/iot/shared/control/CommandAndControlTest.java b/systemtests/src/test/java/io/enmasse/systemtest/iot/shared/control/CommandAndControlTest.java deleted file mode 100644 index a727845e731..00000000000 --- a/systemtests/src/test/java/io/enmasse/systemtest/iot/shared/control/CommandAndControlTest.java +++ /dev/null @@ -1,306 +0,0 @@ -/* - * Copyright 2019, EnMasse authors. - * License: Apache License 2.0 (see the file LICENSE or http://apache.org/licenses/LICENSE-2.0.html). - */ -package io.enmasse.systemtest.iot.shared.control; - -import static io.enmasse.systemtest.TestTag.ACCEPTANCE; -import static io.enmasse.systemtest.iot.MessageType.COMMAND_RESPONSE; -import static io.enmasse.systemtest.iot.MessageType.TELEMETRY; -import static io.enmasse.systemtest.utils.Predicates.is; -import static java.net.HttpURLConnection.HTTP_ACCEPTED; -import static java.net.HttpURLConnection.HTTP_OK; -import static org.hamcrest.CoreMatchers.anyOf; -import static org.hamcrest.collection.IsCollectionWithSize.hasSize; -import static org.hamcrest.collection.IsIterableContainingInOrder.contains; -import static org.hamcrest.core.IsInstanceOf.instanceOf; -import static org.hamcrest.core.IsNull.notNullValue; -import static org.junit.Assert.assertThat; - -import java.net.HttpURLConnection; -import java.nio.ByteBuffer; -import java.time.Duration; -import java.util.List; -import java.util.Optional; -import java.util.UUID; -import java.util.concurrent.ExecutionException; -import java.util.concurrent.Future; -import java.util.concurrent.TimeUnit; -import java.util.concurrent.TimeoutException; -import java.util.concurrent.atomic.AtomicReference; -import java.util.function.Consumer; -import java.util.stream.Collectors; - -import org.apache.qpid.proton.amqp.Binary; -import org.apache.qpid.proton.amqp.messaging.Accepted; -import org.apache.qpid.proton.amqp.messaging.Data; -import org.apache.qpid.proton.amqp.messaging.Released; -import org.apache.qpid.proton.message.Message; -import org.hamcrest.core.Is; -import org.junit.jupiter.api.AfterEach; -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Tag; -import org.junit.jupiter.api.Test; -import org.slf4j.Logger; - -import io.enmasse.systemtest.Endpoint; -import io.enmasse.systemtest.amqp.QueueTerminusFactory; -import io.enmasse.systemtest.bases.TestBase; -import io.enmasse.systemtest.bases.iot.ITestIoTShared; -import io.enmasse.systemtest.iot.CredentialsRegistryClient; -import io.enmasse.systemtest.iot.DeviceRegistryClient; -import io.enmasse.systemtest.iot.HttpAdapterClient; -import io.enmasse.systemtest.iot.MessageType; -import io.enmasse.systemtest.logs.CustomLogger; -import io.enmasse.systemtest.utils.IoTUtils; -import io.enmasse.systemtest.utils.TestUtils; -import io.vertx.core.buffer.Buffer; -import io.vertx.core.json.JsonObject; -import io.vertx.ext.web.client.HttpResponse; -import io.vertx.proton.ProtonDelivery; - -class CommandAndControlTest extends TestBase implements ITestIoTShared { - - private static Logger log = CustomLogger.getLogger(); - - private Endpoint deviceRegistryEndpoint; - private Endpoint httpAdapterEndpoint; - private DeviceRegistryClient registryClient; - private CredentialsRegistryClient credentialsClient; - - private String deviceId; - - private String authId; - - private String password; - - private HttpAdapterClient httpClient; - private String commandPayload; - private int ttd; - - @BeforeEach - void init() throws Exception { - - this.deviceRegistryEndpoint = IoTUtils.getDeviceRegistryManagementEndpoint(); - this.httpAdapterEndpoint = kubernetes.getExternalEndpoint("iot-http-adapter"); - this.registryClient = new DeviceRegistryClient(this.deviceRegistryEndpoint); - this.credentialsClient = new CredentialsRegistryClient(this.deviceRegistryEndpoint); - - // setup device information - this.deviceId = UUID.randomUUID().toString(); - this.authId = UUID.randomUUID().toString(); - this.password = UUID.randomUUID().toString(); - log.info("Registering device [deviceId]: {}, [authId]: {}, [password]: {}", deviceId, authId, password); - this.httpClient = new HttpAdapterClient(this.httpAdapterEndpoint, this.authId, sharedIoTResourceManager.getTenantId(), this.password); - - // set up new random device - this.registryClient.registerDevice(sharedIoTResourceManager.getTenantId(), this.deviceId); - this.credentialsClient.addCredentials(sharedIoTResourceManager.getTenantId(), this.deviceId, this.authId, this.password, null, HttpURLConnection.HTTP_NO_CONTENT); - - // setup payload - this.commandPayload = UUID.randomUUID().toString(); - this.ttd = 30; - - } - - @AfterEach - void closeHttpClient() { - if (this.httpClient != null) { - this.httpClient.close(); - this.httpClient = null; - } - } - - @Test - void testOneShotCommand() throws Exception { - - final AtomicReference>> sentFuture = new AtomicReference<>(); - - var f1 = setupMessagingReceiver(sentFuture, null); - - IoTUtils.waitForFirstSuccess(httpClient, MessageType.TELEMETRY); - - var response = sendTelemetryWithTtd(); - - assertTelemetryResponse(response); - - assertCloudTelemetryMessage(f1); - assertCommandMessageDeliveries(sentFuture.get()); - - } - - @Test - @Tag(ACCEPTANCE) - void testRequestResponseCommand() throws Exception { - - final var reqId = UUID.randomUUID().toString(); - final var replyToAddress = "command_response/" + sharedIoTResourceManager.getTenantId() + "/" + UUID.randomUUID().toString(); - - final AtomicReference>> sentFuture = new AtomicReference<>(); - - // set up command response consumer (before responding to telemetry) - var f3 = sharedIoTResourceManager.getAmqpClient().recvMessages(replyToAddress, 1); - - var f1 = setupMessagingReceiver(sentFuture, commandMessage -> { - commandMessage.setCorrelationId(reqId); - commandMessage.setReplyTo(replyToAddress); - }); - - IoTUtils.waitForFirstSuccess(httpClient, MessageType.TELEMETRY); - - var response = sendTelemetryWithTtd(); - - assertTelemetryResponse(response); - - // also assert response id - - var responseId = response.getHeader("hono-cmd-req-id"); - assertThat(responseId, notNullValue()); - - // send the reply to the command - - TestUtils.runUntilPass(5, () -> { - this.httpClient.send(COMMAND_RESPONSE, "/" + responseId, - new JsonObject().put("data", "command-response").toBuffer(), - is(HTTP_ACCEPTED), - request -> request.putHeader("hono-cmd-status", "202" /* accepted */), Duration.ofSeconds(5)); - }); - - assertCloudTelemetryMessage(f1); - assertCommandMessageDeliveries(sentFuture.get()); - - // assert command response message - cloud side - - var responses = f3.get(10, TimeUnit.SECONDS); - assertThat(responses, hasSize(1)); - var responseMsg = responses.get(0); - assertThat(responseMsg.getCorrelationId(), Is.is(reqId)); - assertThat(responseMsg.getBody(), instanceOf(Data.class)); - assertThat(new JsonObject(Buffer.buffer(((Data) responseMsg.getBody()).getValue().getArray())), Is.is(new JsonObject().put("data", "command-response"))); - assertThat(responseMsg.getApplicationProperties().getValue().get("status"), Is.is(202) /* accepted */); - - } - - private HttpResponse sendTelemetryWithTtd() throws Exception { - - // consumer link should be ready now ... send telemetry with "ttd" - - log.info("Send telemetry with TTD - ttd: {}", this.ttd); - - var response = TestUtils.runUntilPass(5, () -> { - return this.httpClient.send(TELEMETRY, null, is(HTTP_OK /* OK for command responses */), request -> { - // set "time to disconnect" - request.putHeader("hono-ttd", Integer.toString(this.ttd)); - }, Duration.ofSeconds(this.ttd + 5)); - }); - - log.info("Telemetry response: {}: {}", response.statusCode(), response.bodyAsString()); - - return response; - - } - - private Future> setupMessagingReceiver(final AtomicReference>> sentFuture, final Consumer messageCustomizer) { - - // setup telemetry consumer - - var f1 = sharedIoTResourceManager.getAmqpClient().recvMessages(new QueueTerminusFactory().getSource("telemetry/" + sharedIoTResourceManager.getTenantId()), msg -> { - - log.info("Received message: {}", msg); - - var ttdValue = msg.getApplicationProperties().getValue().get("ttd"); - - if (ttdValue == null) { - // this was the initial message, without waiting for commands - return false; - } - - var deviceId = msg.getApplicationProperties().getValue().get("device_id").toString(); - - // prepare message - - var commandMessage = Message.Factory.create(); - commandMessage.setSubject("CMD1"); - commandMessage.setMessageId(UUID.randomUUID().toString()); - - commandMessage.setContentType("application/octet-stream"); - commandMessage.setBody(new Data(Binary.create(ByteBuffer.wrap(this.commandPayload.getBytes())))); - commandMessage.setAddress("command/" + sharedIoTResourceManager.getTenantId() + "/" + deviceId); - - if (messageCustomizer != null) { - messageCustomizer.accept(commandMessage); - } - - // send request command - - log.info("Sending out command message"); - var f2 = sharedIoTResourceManager.getAmqpClient().sendMessage("command/" + sharedIoTResourceManager.getTenantId(), commandMessage) - .whenComplete((res, err) -> { - String strres = null; - if (res != null) { - strres = res.stream().map(ProtonDelivery::getRemoteState).map(Object::toString).collect(Collectors.joining(", ")); - } - log.info("Message result - res: {}, err:", // no need for final {}, as this is an exception - strres, err); - }); - sentFuture.set(f2); - log.info("Message underway"); - - // stop listening for more messages - - return true; - - }, Optional.empty()).getResult(); - return f1; - - } - - private void assertTelemetryResponse(final HttpResponse response) { - - // assert message - device side - - final var actualCommand = response.bodyAsString(); - assertThat(response.getHeader("hono-command"), Is.is("CMD1")); - assertThat(actualCommand, Is.is(this.commandPayload)); - - } - - private void assertCloudTelemetryMessage(Future> f1) throws InterruptedException, ExecutionException, TimeoutException { - - // assert message - cloud side - - // wait for the future of the sent message - var m1 = f1.get(10, TimeUnit.SECONDS); - - // dump messages - m1.forEach(m -> log.info("Message: {}", m)); - - // we expect two messages, the "test" message and the actual one - assertThat(m1, hasSize(2)); - // get the second message, the real one - var msg = m1.get(1); - - // message must have "ttd" set - var ttdValue = msg.getApplicationProperties().getValue().get("ttd"); - assertThat(ttdValue, instanceOf(Number.class)); - assertThat(ttdValue, Is.is(30)); - - } - - private void assertCommandMessageDeliveries(Future> messageFuture) throws InterruptedException, ExecutionException, TimeoutException { - - assertThat(messageFuture, notNullValue()); - - // assert command message deliveries - cloud side - - final List deliveries = messageFuture.get(10, TimeUnit.SECONDS); - assertThat(deliveries, hasSize(1)); - assertThat(deliveries.stream().map(ProtonDelivery::getRemoteState).collect(Collectors.toList()), - contains( - anyOf( - instanceOf(Released.class), // remove once issue eclipse/hono#1149 is fixed - instanceOf(Accepted.class)))); - - } - -} diff --git a/systemtests/src/test/java/io/enmasse/systemtest/isolated/CommonTest.java b/systemtests/src/test/java/io/enmasse/systemtest/isolated/CommonTest.java deleted file mode 100644 index 3fc69c6e27f..00000000000 --- a/systemtests/src/test/java/io/enmasse/systemtest/isolated/CommonTest.java +++ /dev/null @@ -1,841 +0,0 @@ -/* - * Copyright 2018-2020, EnMasse authors. - * License: Apache License 2.0 (see the file LICENSE or http://apache.org/licenses/LICENSE-2.0.html). - */ -package io.enmasse.systemtest.isolated; - -import com.google.common.collect.HashMultimap; -import com.google.common.collect.Multimap; -import io.enmasse.address.model.Address; -import io.enmasse.address.model.AddressBuilder; -import io.enmasse.address.model.AddressSpace; -import io.enmasse.address.model.AddressSpaceBuilder; -import io.enmasse.admin.model.v1.AddressPlan; -import io.enmasse.admin.model.v1.AddressSpacePlan; -import io.enmasse.admin.model.v1.AddressSpacePlanBuilder; -import io.enmasse.admin.model.v1.BrokeredInfraConfig; -import io.enmasse.admin.model.v1.BrokeredInfraConfigBuilder; -import io.enmasse.admin.model.v1.BrokeredInfraConfigSpecAdminBuilder; -import io.enmasse.admin.model.v1.BrokeredInfraConfigSpecBrokerBuilder; -import io.enmasse.admin.model.v1.ResourceAllowance; -import io.enmasse.admin.model.v1.ResourceRequest; -import io.enmasse.admin.model.v1.StandardInfraConfig; -import io.enmasse.admin.model.v1.StandardInfraConfigBuilder; -import io.enmasse.admin.model.v1.StandardInfraConfigSpecAdminBuilder; -import io.enmasse.admin.model.v1.StandardInfraConfigSpecBrokerBuilder; -import io.enmasse.admin.model.v1.StandardInfraConfigSpecRouterBuilder; -import io.enmasse.config.LabelKeys; -import io.enmasse.systemtest.UserCredentials; -import io.enmasse.systemtest.amqp.AmqpClient; -import io.enmasse.systemtest.bases.TestBase; -import io.enmasse.systemtest.bases.isolated.ITestBaseIsolated; -import io.enmasse.systemtest.executor.ExecutionResultData; -import io.enmasse.systemtest.logs.CustomLogger; -import io.enmasse.systemtest.model.address.AddressType; -import io.enmasse.systemtest.model.addressplan.DestinationPlan; -import io.enmasse.systemtest.model.addressspace.AddressSpacePlans; -import io.enmasse.systemtest.model.addressspace.AddressSpaceType; -import io.enmasse.systemtest.platform.KubeCMDClient; -import io.enmasse.systemtest.platform.Kubernetes; -import io.enmasse.systemtest.platform.apps.SystemtestsKubernetesApps; -import io.enmasse.systemtest.shared.standard.QueueTest; -import io.enmasse.systemtest.time.TimeoutBudget; -import io.enmasse.systemtest.utils.AddressSpaceUtils; -import io.enmasse.systemtest.utils.AddressUtils; -import io.enmasse.systemtest.utils.PlanUtils; -import io.enmasse.systemtest.utils.TestUtils; -import io.fabric8.kubernetes.api.model.Pod; -import io.github.artsok.RepeatedIfExceptionsTest; -import org.apache.qpid.proton.amqp.Binary; -import org.apache.qpid.proton.amqp.messaging.AmqpValue; -import org.apache.qpid.proton.amqp.messaging.Data; -import org.apache.qpid.proton.amqp.transport.DeliveryState.DeliveryStateType; -import org.apache.qpid.proton.message.Message; -import org.junit.jupiter.api.DisplayName; -import org.junit.jupiter.api.Tag; -import org.junit.jupiter.api.Test; -import org.junit.jupiter.params.ParameterizedTest; -import org.junit.jupiter.params.provider.ValueSource; -import org.slf4j.Logger; - -import java.time.Duration; -import java.util.Arrays; -import java.util.Collections; -import java.util.HashMap; -import java.util.Iterator; -import java.util.LinkedList; -import java.util.List; -import java.util.Map; -import java.util.Optional; -import java.util.Random; -import java.util.UUID; -import java.util.concurrent.CompletableFuture; -import java.util.concurrent.TimeUnit; -import java.util.stream.Collectors; -import java.util.stream.Stream; - -import static io.enmasse.systemtest.TestTag.ISOLATED; -import static io.enmasse.systemtest.TestTag.NON_PR; -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertTrue; -import static org.junit.jupiter.api.Assertions.fail; - -@Tag(ISOLATED) -class CommonTest extends TestBase implements ITestBaseIsolated { - private static Logger log = CustomLogger.getLogger(); - - @Test - void testAccessLogs() throws Exception { - AddressSpace standard = new AddressSpaceBuilder() - .withNewMetadata() - .withName("mystandard") - .withNamespace(kubernetes.getInfraNamespace()) - .endMetadata() - .withNewSpec() - .withType(AddressSpaceType.STANDARD.toString()) - .withPlan(AddressSpacePlans.STANDARD_UNLIMITED) - .withNewAuthenticationService() - .withName("standard-authservice") - .endAuthenticationService() - .endSpec() - .build(); - resourcesManager.createAddressSpace(standard); - - Address dest = new AddressBuilder() - .withNewMetadata() - .withNamespace(standard.getMetadata().getNamespace()) - .withName(AddressUtils.generateAddressMetadataName(standard, "test-queue")) - .endMetadata() - .withNewSpec() - .withType("queue") - .withAddress("test-queue") - .withPlan(DestinationPlan.STANDARD_SMALL_QUEUE) - .endSpec() - .build(); - resourcesManager.setAddresses(dest); - - kubernetes.awaitPodsReady(standard.getMetadata().getNamespace(), new TimeoutBudget(5, TimeUnit.MINUTES)); - - Multimap podsContainersWithNoLog = HashMultimap.create(); - - kubernetes.listPods().stream().filter(pod -> !pod.getMetadata().getName().contains("none-authservice")).forEach(pod -> kubernetes.getContainersFromPod(pod.getMetadata().getNamespace(), pod.getMetadata().getName()).forEach(container -> { - String podNamespace = pod.getMetadata().getNamespace(); - String podName = pod.getMetadata().getName(); - String containerName = container.getName(); - log.info("Getting log from pod: {}, for container: {}", podName, containerName); - String podlog = kubernetes.getLog(podNamespace, podName, containerName); - - // Retry - diagnostic code to help understand a sporadic Ci failure. - if (podlog.isEmpty()) { - try { - Thread.sleep(5000); - } catch (InterruptedException e) { - Thread.currentThread().interrupt(); - } - log.info("(Retry) Getting log from pod: {}, for container: {}", podName, containerName); - podlog = kubernetes.getLog(podNamespace, podName, containerName); - } - - if (podlog.isEmpty()) { - podsContainersWithNoLog.put(podName, containerName); - } - - })); - - if (!podsContainersWithNoLog.isEmpty()) { - String podContainerNames = podsContainersWithNoLog.entries().stream().map(e -> String.format("%s-%s", e.getKey(), e.getValue())).collect(Collectors.joining(",")); - fail(String.format("%d pod container(s) had unexpectedly empty logs : %s ", podsContainersWithNoLog.size(), podContainerNames)); - } - } - - @Test - void testRestartComponents() throws Exception { - List

brokeredAddresses = AddressUtils.getAllBrokeredAddresses(brokered); - List
standardAddresses = AddressUtils.getAllStandardAddresses(standard); - - resourcesManager.setAddresses(brokeredAddresses.toArray(new Address[0])); - resourcesManager.setAddresses(standardAddresses.toArray(new Address[0])); - - getClientUtils().assertCanConnect(brokered, user, brokeredAddresses, resourcesManager); - getClientUtils().assertCanConnect(standard, user, standardAddresses, resourcesManager); - - log.info("------------------------------------------------------------"); - log.info("------------------- Start with restarting -------------------"); - log.info("------------------------------------------------------------"); - - List pods = kubernetes.listPods(); - int runningPodsBefore = pods.size(); - log.info("Number of running pods before restarting any: {}", runningPodsBefore); - for (Label label : labels) { - log.info("Restarting {}", label.labelValue); - KubeCMDClient.deletePodByLabel(label.getLabelName(), label.getLabelValue()); - Thread.sleep(30_000); - TestUtils.waitForExpectedReadyPods(kubernetes, kubernetes.getInfraNamespace(), runningPodsBefore, new TimeoutBudget(10, TimeUnit.MINUTES)); - assertSystemWorks(brokered, standard, user, brokeredAddresses, standardAddresses); - } - - log.info("Restarting whole enmasse"); - KubeCMDClient.deletePodByLabel("app", kubernetes.getEnmasseAppLabel()); - Thread.sleep(180_000); - TestUtils.waitForExpectedReadyPods(kubernetes, kubernetes.getInfraNamespace(), runningPodsBefore, new TimeoutBudget(10, TimeUnit.MINUTES)); - AddressUtils.waitForDestinationsReady(new TimeoutBudget(10, TimeUnit.MINUTES), - standardAddresses.toArray(new Address[0])); - assertSystemWorks(brokered, standard, user, brokeredAddresses, standardAddresses); - - //TODO: Uncomment when #2127 will be fixed - -// Pod qdrouter = pods.stream().filter(pod -> pod.getMetadata().getName().contains("qdrouter")).collect(Collectors.toList()).get(0); -// kubernetes.deletePod(environment.namespace(), qdrouter.getMetadata().getName()); -// assertSystemWorks(brokered, standard, user, brokeredAddresses, standardAddresses); - } - //https://github.com/EnMasseProject/enmasse/issues/3098 - - @Test - void testRestartAdminComponent() throws Exception { - List