From dd34ce42d7b8bcb781cf9ad0f47c75157481e28f Mon Sep 17 00:00:00 2001 From: Ulf Lilleengen Date: Tue, 10 Dec 2019 11:34:14 +0100 Subject: [PATCH] Remove api-server module Remove all code related to api-server as well as build system references to it. Moves swagger definition to documentation module for the time being, and add configuration in documentation to replace properties in swagger.json. Eventually this should be generated from the CRDs. --- .gitignore | 1 - HACKING.md | 7 - Makefile | 1 - Makefile.env.mk | 3 - ansible/roles/api_server/tasks/main.yml | 10 - ansible/roles/api_service/tasks/main.yml | 55 --- api-server/Dockerfile | 12 - api-server/Makefile | 4 - api-server/pom.xml | 158 ------ .../java/io/enmasse/api/server/ApiServer.java | 184 ------- .../enmasse/api/server/ApiServerOptions.java | 203 -------- .../enmasse/api/server/HTTPHealthServer.java | 68 --- .../io/enmasse/api/server/HTTPServer.java | 182 ------- .../io/enmasse/api/server/RequestLogger.java | 183 ------- .../api/server/ResteasyDeploymentFactory.java | 77 --- .../io/enmasse/api/v1/AddressApiHelper.java | 181 ------- .../api/v1/http/HttpApiRootService.java | 77 --- .../api/v1/http/HttpClusterUserService.java | 92 ---- .../api/v1/http/HttpOpenApiService.java | 21 - .../enmasse/api/v1/http/HttpRootService.java | 32 -- .../enmasse/api/v1/http/HttpUserService.java | 459 ------------------ .../api/v1/http/SwaggerSpecEndpoint.java | 21 - .../io/enmasse/api/v1/types/APIGroup.java | 79 --- .../io/enmasse/api/v1/types/APIGroupList.java | 50 -- .../enmasse/api/v1/types/APIGroupVersion.java | 32 -- .../io/enmasse/api/v1/types/APIResource.java | 62 --- .../enmasse/api/v1/types/APIResourceList.java | 59 --- api-server/src/main/resources/logback.xml | 15 - .../io/enmasse/api/server/HTTPServerTest.java | 300 ------------ .../api/server/TestSchemaProvider.java | 18 - .../api/v1/http/HttpUserServiceTest.java | 454 ----------------- .../io/enmasse/api/v1/http/TestUserApi.java | 134 ----- .../src/test/resources/logback-test.xml | 17 - documentation/Makefile | 3 +- .../assembly-installing-manual-steps.adoc | 22 - documentation/pom.xml | 19 + .../src/main/resources/swagger.json | 0 pom.xml | 2 +- .../systemtest/logs/GlobalLogCollector.java | 5 - .../systemtest/operator/OperatorManager.java | 12 - .../systemtest/platform/OpenShift.java | 3 +- .../systemtest/isolated/CommonTest.java | 6 - .../010-PrometheusRules-enmasse.yaml | 2 - 43 files changed, 23 insertions(+), 3302 deletions(-) delete mode 100644 ansible/roles/api_server/tasks/main.yml delete mode 100644 ansible/roles/api_service/tasks/main.yml delete mode 100644 api-server/Dockerfile delete mode 100644 api-server/Makefile delete mode 100644 api-server/pom.xml delete mode 100644 api-server/src/main/java/io/enmasse/api/server/ApiServer.java delete mode 100644 api-server/src/main/java/io/enmasse/api/server/ApiServerOptions.java delete mode 100644 api-server/src/main/java/io/enmasse/api/server/HTTPHealthServer.java delete mode 100644 api-server/src/main/java/io/enmasse/api/server/HTTPServer.java delete mode 100644 api-server/src/main/java/io/enmasse/api/server/RequestLogger.java delete mode 100644 api-server/src/main/java/io/enmasse/api/server/ResteasyDeploymentFactory.java delete mode 100644 api-server/src/main/java/io/enmasse/api/v1/AddressApiHelper.java delete mode 100644 api-server/src/main/java/io/enmasse/api/v1/http/HttpApiRootService.java delete mode 100644 api-server/src/main/java/io/enmasse/api/v1/http/HttpClusterUserService.java delete mode 100644 api-server/src/main/java/io/enmasse/api/v1/http/HttpOpenApiService.java delete mode 100644 api-server/src/main/java/io/enmasse/api/v1/http/HttpRootService.java delete mode 100644 api-server/src/main/java/io/enmasse/api/v1/http/HttpUserService.java delete mode 100644 api-server/src/main/java/io/enmasse/api/v1/http/SwaggerSpecEndpoint.java delete mode 100644 api-server/src/main/java/io/enmasse/api/v1/types/APIGroup.java delete mode 100644 api-server/src/main/java/io/enmasse/api/v1/types/APIGroupList.java delete mode 100644 api-server/src/main/java/io/enmasse/api/v1/types/APIGroupVersion.java delete mode 100644 api-server/src/main/java/io/enmasse/api/v1/types/APIResource.java delete mode 100644 api-server/src/main/java/io/enmasse/api/v1/types/APIResourceList.java delete mode 100644 api-server/src/main/resources/logback.xml delete mode 100644 api-server/src/test/java/io/enmasse/api/server/HTTPServerTest.java delete mode 100644 api-server/src/test/java/io/enmasse/api/server/TestSchemaProvider.java delete mode 100644 api-server/src/test/java/io/enmasse/api/v1/http/HttpUserServiceTest.java delete mode 100644 api-server/src/test/java/io/enmasse/api/v1/http/TestUserApi.java delete mode 100644 api-server/src/test/resources/logback-test.xml delete mode 100644 documentation/assemblies/assembly-installing-manual-steps.adoc create mode 100644 documentation/pom.xml rename {api-server => documentation}/src/main/resources/swagger.json (100%) diff --git a/.gitignore b/.gitignore index d62bac9c9ab..9c94346b565 100644 --- a/.gitignore +++ b/.gitignore @@ -36,6 +36,5 @@ scripts/swagger2markup.jar # Testing output artifacts/ -api-server-cert/ imageenv.txt diff --git a/HACKING.md b/HACKING.md index 999c33c33e5..a6f20ca6e8b 100644 --- a/HACKING.md +++ b/HACKING.md @@ -68,15 +68,9 @@ by setting `DOCKER_ORG=myproject` and `DOCKER_REGISTRY=172.30.1.1:5000` instead. #### Deploying to a Kubernetes instance assuming already logged in with cluster-admin permissions -*Note*: This assumes you have [OpenSSL](https://www.openssl.org) installed. - kubectl create namespace enmasse-infra kubectl config set-context $(kubectl config current-context) --namespace=enmasse-infra - mkdir -p api-server-cert - openssl req -new -x509 -batch -nodes -days 11000 -subj "/O=io.enmasse/CN=api-server.enmasse-infra.svc.cluster.local" -out api-server-cert/tls.crt -keyout api-server-cert/tls.key - kubectl create secret tls api-server-cert --cert=api-server-cert/tls.crt --key=api-server-cert/tls.key - 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 @@ -169,7 +163,6 @@ The following deployment names are available depending on their types and EnMass * `address-space-controller` * `admin` - * `api-server` * `keycloak-controller` * `standard-controller` * `service-broker` diff --git a/Makefile b/Makefile index e33b862ed04..e22208605c1 100644 --- a/Makefile +++ b/Makefile @@ -9,7 +9,6 @@ DOCKER_DIRS = \ agent \ topic-forwarder \ broker-plugin \ - api-server \ address-space-controller \ none-authservice \ standard-controller \ diff --git a/Makefile.env.mk b/Makefile.env.mk index cd350bc98e5..c080e526319 100644 --- a/Makefile.env.mk +++ b/Makefile.env.mk @@ -19,7 +19,6 @@ export GOPATH DOCKER_REGISTRY_PREFIX ?= $(DOCKER_REGISTRY)/ IMAGE_VERSION ?= $(TAG) ADDRESS_SPACE_CONTROLLER_IMAGE ?= $(DOCKER_REGISTRY_PREFIX)$(DOCKER_ORG)/address-space-controller:$(IMAGE_VERSION) -API_SERVER_IMAGE ?= $(DOCKER_REGISTRY_PREFIX)$(DOCKER_ORG)/api-server:$(IMAGE_VERSION) STANDARD_CONTROLLER_IMAGE ?= $(DOCKER_REGISTRY_PREFIX)$(DOCKER_ORG)/standard-controller:$(IMAGE_VERSION) ROUTER_IMAGE ?= quay.io/interconnectedcloud/qdrouterd:1.9.0 BROKER_PLUGIN_IMAGE ?= $(DOCKER_REGISTRY_PREFIX)$(DOCKER_ORG)/broker-plugin:$(IMAGE_VERSION) @@ -64,7 +63,6 @@ IMAGE_PULL_POLICY ?= IfNotPresent endif IMAGE_ENV=ADDRESS_SPACE_CONTROLLER_IMAGE=$(ADDRESS_SPACE_CONTROLLER_IMAGE) \ - API_SERVER_IMAGE=$(API_SERVER_IMAGE) \ STANDARD_CONTROLLER_IMAGE=$(STANDARD_CONTROLLER_IMAGE) \ ROUTER_IMAGE=$(ROUTER_IMAGE) \ BROKER_IMAGE=$(BROKER_IMAGE) \ @@ -106,7 +104,6 @@ IMAGE_ENV=ADDRESS_SPACE_CONTROLLER_IMAGE=$(ADDRESS_SPACE_CONTROLLER_IMAGE) \ IMAGE_LIST=\ $(ADDRESS_SPACE_CONTROLLER_IMAGE) \ - $(API_SERVER_IMAGE) \ $(STANDARD_CONTROLLER_IMAGE) \ $(ROUTER_IMAGE) \ $(BROKER_IMAGE) \ diff --git a/ansible/roles/api_server/tasks/main.yml b/ansible/roles/api_server/tasks/main.yml deleted file mode 100644 index d419e2eabe4..00000000000 --- a/ansible/roles/api_server/tasks/main.yml +++ /dev/null @@ -1,10 +0,0 @@ ---- -- name: Create API Server Config - shell: oc create -n {{ namespace }} configmap api-server-config --from-literal=enableRbac={{ enable_rbac }} - register: config_exists - failed_when: config_exists.stderr != '' and 'already exists' not in config_exists.stderr -- name: Label spi server configmap - shell: oc label configmap api-server-config -n {{ namespace }} app=enmasse - when: config_exists.rc == 0 -- name: Apply the API Server configuration - shell: oc apply -f "{{ playbook_dir }}/install/api-server" diff --git a/ansible/roles/api_service/tasks/main.yml b/ansible/roles/api_service/tasks/main.yml deleted file mode 100644 index f7a2f1ac9f0..00000000000 --- a/ansible/roles/api_service/tasks/main.yml +++ /dev/null @@ -1,55 +0,0 @@ ---- -- name: Extract API Server CA - shell: oc extract secret/api-server-cert -n {{ namespace }} --keys=tls.crt --to=- - until: secret_result.rc == 0 - retries: 10 - delay: 5 - register: secret_result - -- set_fact: - ca_bundle: "{{ secret_result.stdout }}" - -- name: Register Legacy User API Server with API Aggregator - when: '"standard" in authentication_services' - shell: - cmd: | - cat < - - - io.enmasse - enmasse - 0.31-SNAPSHOT - - 4.0.0 - api-server - - - io.enmasse - metrics-api - compile - - - io.enmasse - api-model - - - io.enmasse - keycloak-user-api - compile - - - io.enmasse - api-common - compile - - - io.enmasse - k8s-api - compile - - - io.fabric8 - openshift-client - 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 - - - org.jboss.resteasy - resteasy-jackson2-provider - compile - - - org.jboss.resteasy - resteasy-vertx - compile - - - com.github.fge - json-patch - - - org.junit.jupiter - junit-jupiter-api - test - - - org.junit.jupiter - junit-jupiter-engine - test - - - org.junit.jupiter - junit-jupiter-params - test - - - org.junit.platform - junit-platform-launcher - test - - - io.vertx - vertx-junit5 - 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.api.server.ApiServer - - - - - *:* - - META-INF/*.SF - META-INF/*.DSA - META-INF/*.RSA - - - - - - - - - - diff --git a/api-server/src/main/java/io/enmasse/api/server/ApiServer.java b/api-server/src/main/java/io/enmasse/api/server/ApiServer.java deleted file mode 100644 index d80066a120e..00000000000 --- a/api-server/src/main/java/io/enmasse/api/server/ApiServer.java +++ /dev/null @@ -1,184 +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.api.server; - -import com.fasterxml.jackson.core.type.TypeReference; -import com.fasterxml.jackson.databind.ObjectMapper; -import io.enmasse.admin.model.v1.AuthenticationServiceType; -import io.enmasse.api.auth.ApiHeaderConfig; -import io.enmasse.api.auth.AuthApi; -import io.enmasse.api.auth.KubeAuthApi; -import io.enmasse.api.common.OpenShift; -import io.enmasse.k8s.api.*; -import io.enmasse.metrics.api.Metrics; -import io.enmasse.model.CustomResourceDefinitions; -import io.enmasse.user.api.DelegateUserApi; -import io.enmasse.user.api.NullUserApi; -import io.enmasse.user.api.UserApi; -import io.enmasse.user.keycloak.KeycloakFactory; -import io.enmasse.user.keycloak.KeycloakUserApi; -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.KubernetesClientException; -import io.fabric8.kubernetes.client.NamespacedKubernetesClient; -import io.fabric8.kubernetes.client.utils.HttpClientUtils; -import io.vertx.core.*; -import okhttp3.OkHttpClient; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import java.io.ByteArrayInputStream; -import java.io.IOException; -import java.nio.charset.StandardCharsets; -import java.security.cert.CertificateException; -import java.security.cert.CertificateFactory; -import java.time.Clock; -import java.util.List; -import java.util.Map; - -public class ApiServer extends AbstractVerticle { - private static final Logger log = LoggerFactory.getLogger(ApiServer.class.getName()); - private final NamespacedKubernetesClient client; - private final ApiServerOptions options; - private static final ObjectMapper mapper = new ObjectMapper(); - - static { - try { - CustomResourceDefinitions.registerAll(); - } catch (RuntimeException t) { - t.printStackTrace(); - throw new ExceptionInInitializerError(t); - } - } - - private ApiServer(ApiServerOptions options) throws IOException { - Config config = new ConfigBuilder().build(); - OkHttpClient httpClient = HttpClientUtils.createHttpClient(config); - httpClient = httpClient.newBuilder() - .connectTimeout(options.getKubernetesApiConnectTimeout()) - .writeTimeout(options.getKubernetesApiWriteTimeout()) - .readTimeout(options.getKubernetesApiReadTimeout()) - .build(); - this.client = new DefaultKubernetesClient(httpClient, config); - this.options = options; - } - - @Override - public void start(Future startPromise) throws Exception { - boolean isOpenShift = OpenShift.isOpenShift(client); - SchemaApi schemaApi = KubeSchemaApi.create(client, client.getNamespace(), options.getVersion(), isOpenShift); - CachingSchemaProvider schemaProvider = new CachingSchemaProvider(); - schemaApi.watchSchema(schemaProvider, options.getResyncInterval()); - - AddressSpaceApi addressSpaceApi = KubeAddressSpaceApi.create(client, null, options.getVersion()); - - AuthApi authApi = new KubeAuthApi(client, client.getConfiguration().getOauthToken()); - - AuthenticationServiceRegistry authenticationServiceRegistry = new SchemaAuthenticationServiceRegistry(schemaProvider); - - Clock clock = Clock.systemUTC(); - KeycloakFactory keycloakFactory = new KubeKeycloakFactory(client); - KeycloakUserApi keycloakUserApi = new KeycloakUserApi(keycloakFactory, clock, options.getUserApiTimeout()); - schemaProvider.registerListener(newSchema -> keycloakUserApi.retainAuthenticationServices(newSchema.findAuthenticationServiceType(AuthenticationServiceType.standard))); - UserApi userApi = new DelegateUserApi(Map.of(AuthenticationServiceType.none, new NullUserApi(), - AuthenticationServiceType.external, new NullUserApi(), - AuthenticationServiceType.standard, keycloakUserApi)); - - String clientCa; - String requestHeaderClientCa; - ApiHeaderConfig apiHeaderConfig = ApiHeaderConfig.DEFAULT_HEADERS_CONFIG; - try { - ConfigMap extensionApiserverAuthentication = client.configMaps().inNamespace(options.getApiserverClientCaConfigNamespace()).withName(options.getApiserverClientCaConfigName()).get(); - clientCa = validateCert("client-ca", extensionApiserverAuthentication.getData().get("client-ca-file")); - apiHeaderConfig = parseApiHeaderConfig(extensionApiserverAuthentication, apiHeaderConfig); - requestHeaderClientCa = validateCert("request-header-client-ca", extensionApiserverAuthentication.getData().get("requestheader-client-ca-file")); - } catch (KubernetesClientException e) { - log.info("Unable to retrieve config for client CA. Skipping", e); - clientCa = null; - requestHeaderClientCa = null; - } - - Metrics metrics = new Metrics(); - - HTTPHealthServer httpHealthServer = new HTTPHealthServer(options.getVersion(), metrics); - - ResteasyDeploymentFactory resteasyDeploymentFactory = new ResteasyDeploymentFactory(addressSpaceApi, schemaProvider, authApi, userApi, clock, authenticationServiceRegistry, apiHeaderConfig, metrics, options.isEnableRbac()); - String finalRequestHeaderClientCa = requestHeaderClientCa; - String finalClientCa = clientCa; - vertx.deployVerticle(() -> new HTTPServer(options, resteasyDeploymentFactory, finalClientCa, finalRequestHeaderClientCa), - new DeploymentOptions().setWorker(true).setInstances(options.getNumWorkerThreads()), result -> { - if (result.succeeded()) { - vertx.deployVerticle(httpHealthServer, ar -> { - if (ar.succeeded()) { - log.info("API Server started successfully"); - startPromise.complete(); - } else { - log.error("API Server failed to start", result.cause()); - startPromise.fail(result.cause()); - } - }); - } else { - log.error("API Server failed to start", result.cause()); - startPromise.fail(result.cause()); - } - }); - } - - private ApiHeaderConfig parseApiHeaderConfig(ConfigMap extensionApiserverAuthentication, ApiHeaderConfig defaultConfig) throws IOException { - String userJson = extensionApiserverAuthentication.getData().get("requestheader-username-headers"); - String groupJson = extensionApiserverAuthentication.getData().get("requestheader-group-headers"); - String extraJson = extensionApiserverAuthentication.getData().get("requestheader-extra-headers-prefix"); - - List userHeader = defaultConfig.getUserHeaders(); - if (userJson != null) { - userHeader = mapper.readValue(userJson, new TypeReference>() {}); - } - - List groupHeader = defaultConfig.getGroupHeaders(); - if (groupJson != null) { - groupHeader = mapper.readValue(groupJson, new TypeReference>() {}); - } - - List extraHeader = defaultConfig.getExtraHeadersPrefix(); - if (extraJson != null) { - extraHeader = mapper.readValue(extraJson, new TypeReference>() {}); - } - - return new ApiHeaderConfig(userHeader, groupHeader, extraHeader); - } - - private static String validateCert(String id, String ca) { - if (ca == null) { - return null; - } - try { - CertificateFactory cf = CertificateFactory.getInstance("X.509"); - cf.generateCertificates(new ByteArrayInputStream(ca.getBytes(StandardCharsets.UTF_8))); - return ca; - } catch (CertificateException e) { - log.info("Error validating certificate {}. Skipping", id); - return null; - } - } - - public static void main(String args[]) { - try { - final ApiServerOptions options = ApiServerOptions.fromEnv(System.getenv()); - Vertx vertx = Vertx.vertx(new VertxOptions().setWorkerPoolSize(options.getNumWorkerThreads())); - log.info("ApiServer starting with options: {}", options); - vertx.deployVerticle(new ApiServer(options)); - } 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 API server: " + e.getMessage()); - System.exit(1); - } - } -} diff --git a/api-server/src/main/java/io/enmasse/api/server/ApiServerOptions.java b/api-server/src/main/java/io/enmasse/api/server/ApiServerOptions.java deleted file mode 100644 index 603d523be7b..00000000000 --- a/api-server/src/main/java/io/enmasse/api/server/ApiServerOptions.java +++ /dev/null @@ -1,203 +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.api.server; - -import java.io.File; -import java.io.IOException; -import java.io.UncheckedIOException; -import java.nio.file.Files; -import java.time.Duration; -import java.util.Map; -import java.util.Optional; - -public class ApiServerOptions { - private static final String SERVICEACCOUNT_PATH = "/var/run/secrets/kubernetes.io/serviceaccount"; - private String namespace; - private String certDir; - private Duration resyncInterval; - private boolean enableRbac; - private String apiserverClientCaConfigName; - private String apiserverClientCaConfigNamespace; - private Duration userApiTimeout; - private Duration kubernetesApiConnectTimeout; - private Duration kubernetesApiReadTimeout; - private Duration kubernetesApiWriteTimeout; - private int numWorkerThreads; - private String version; - - public static ApiServerOptions fromEnv(Map env) { - - ApiServerOptions options = new ApiServerOptions(); - - options.setNamespace(getEnv(env, "NAMESPACE") - .orElseGet(() -> readFile(new File(SERVICEACCOUNT_PATH, "namespace")))); - - options.setCertDir(getEnv(env, "CERT_DIR").orElse("/api-server-cert")); - - options.setResyncInterval(getEnv(env, "RESYNC_INTERVAL") - .map(i -> Duration.ofSeconds(Long.parseLong(i))) - .orElse(Duration.ofMinutes(5))); - - options.setUserApiTimeout(getEnv(env, "USER_API_TIMEOUT") - .map(i -> Duration.ofSeconds(Long.parseLong(i))) - .orElse(Duration.ofSeconds(10))); - - 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.setNumWorkerThreads(getEnv(env, "NUM_WORKER_THREADS") - .map(Integer::parseInt) - .orElse(Runtime.getRuntime().availableProcessors() * 4)); - options.setEnableRbac(Boolean.parseBoolean(getEnv(env, "ENABLE_RBAC").orElse("false"))); - - options.setApiserverClientCaConfigName(getEnv(env, "APISERVER_CLIENT_CA_CONFIG_NAME").orElse(null)); - options.setApiserverClientCaConfigNamespace(getEnv(env, "APISERVER_CLIENT_CA_CONFIG_NAMESPACE").orElse(null)); - - 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))); - } - - private static String readFile(File file) { - try { - return new String(Files.readAllBytes(file.toPath())); - } catch (IOException e) { - throw new UncheckedIOException(e); - } - } - - public String getNamespace() { - return namespace; - } - - public void setNamespace(String namespace) { - this.namespace = namespace; - } - - public String getCertDir() { - return certDir; - } - - public void setCertDir(String certDir) { - this.certDir = certDir; - } - - public Duration getResyncInterval() { - return resyncInterval; - } - - public void setResyncInterval(Duration resyncInterval) { - this.resyncInterval = resyncInterval; - } - - public boolean isEnableRbac() { - return enableRbac; - } - - public void setEnableRbac(boolean enableRbac) { - this.enableRbac = enableRbac; - } - - public String getApiserverClientCaConfigName() { - return apiserverClientCaConfigName; - } - - public void setApiserverClientCaConfigName(String apiserverClientCaConfigName) { - this.apiserverClientCaConfigName = apiserverClientCaConfigName; - } - - public String getApiserverClientCaConfigNamespace() { - return apiserverClientCaConfigNamespace; - } - - public void setApiserverClientCaConfigNamespace(String apiserverClientCaConfigNamespace) { - this.apiserverClientCaConfigNamespace = apiserverClientCaConfigNamespace; - } - - public Duration getUserApiTimeout() { - return userApiTimeout; - } - - public void setUserApiTimeout(Duration userApiTimeout) { - this.userApiTimeout = userApiTimeout; - } - - public String getVersion() { - return version; - } - - public void setVersion(String version) { - this.version = version; - } - - 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; - } - - @Override - public String toString() { - return "ApiServerOptions{" + - "namespace='" + namespace + '\'' + - ", certDir='" + certDir + '\'' + - ", resyncInterval=" + resyncInterval + - ", enableRbac=" + enableRbac + - ", numWorkerThreads=" + numWorkerThreads + - ", apiserverClientCaConfigName='" + apiserverClientCaConfigName + '\'' + - ", apiserverClientCaConfigNamespace='" + apiserverClientCaConfigNamespace + '\'' + - ", userApiTimeout=" + userApiTimeout + - ", kubernetesApiConnectTimeout=" + kubernetesApiConnectTimeout + - ", kubernetesApiReadTimeout=" + kubernetesApiReadTimeout + - ", kubernetesApiWriteTimeout=" + kubernetesApiWriteTimeout + - ", version='" + version + '\'' + - '}'; - } - - public int getNumWorkerThreads() { - return numWorkerThreads; - } - - public void setNumWorkerThreads(int numWorkerThreads) { - this.numWorkerThreads = numWorkerThreads; - } -} diff --git a/api-server/src/main/java/io/enmasse/api/server/HTTPHealthServer.java b/api-server/src/main/java/io/enmasse/api/server/HTTPHealthServer.java deleted file mode 100644 index 2ac03658e77..00000000000 --- a/api-server/src/main/java/io/enmasse/api/server/HTTPHealthServer.java +++ /dev/null @@ -1,68 +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.api.server; - -import io.enmasse.metrics.api.*; -import io.vertx.core.AbstractVerticle; -import io.vertx.core.Future; -import io.vertx.core.http.HttpServer; -import io.vertx.core.http.HttpServerRequest; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import javax.ws.rs.core.MediaType; -import java.util.Collections; - -public class HTTPHealthServer extends AbstractVerticle { - public static final int PORT = 8080; - private static final Logger log = LoggerFactory.getLogger(HTTPHealthServer.class); - private final Metrics metrics; - private final int port; - private HttpServer httpServer; - private final MetricsFormatter formatter = new PrometheusMetricsFormatter(); - - public HTTPHealthServer(String version, Metrics metrics) { - this.metrics = metrics; - this.port = PORT; - metrics.registerMetric(new ScalarMetric( - "version", - "The version of the api-server", - MetricType.gauge, - () -> Collections.singletonList(new MetricValue(0, new MetricLabel("name", "api-server"), new MetricLabel("version", version))))); - } - - @Override - public void start(Future startPromise) { - httpServer = vertx.createHttpServer() - .requestHandler(request -> { - if (request.path().startsWith("/metrics")) { - handleMetrics(request); - } else { - request.response().setStatusCode(200).putHeader("Content-Type", MediaType.TEXT_PLAIN).end("OK"); - } - }).listen(this.port, ar -> { - if (ar.succeeded()) { - int actualPort = ar.result().actualPort(); - log.info("Started HTTP server. Listening on port {}", actualPort); - startPromise.complete(); - } else { - log.info("Error starting HTTP server"); - startPromise.fail(ar.cause()); - } - }); - } - - @Override - public void stop() { - if (httpServer != null) { - httpServer.close(); - } - } - - private void handleMetrics(HttpServerRequest request) { - String data = formatter.format(metrics.getMetrics(), System.currentTimeMillis()); - request.response().setStatusCode(200).putHeader("Content-Type", MediaType.TEXT_HTML).end(data); - } -} diff --git a/api-server/src/main/java/io/enmasse/api/server/HTTPServer.java b/api-server/src/main/java/io/enmasse/api/server/HTTPServer.java deleted file mode 100644 index 967aedff7ca..00000000000 --- a/api-server/src/main/java/io/enmasse/api/server/HTTPServer.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.api.server; - -import io.vertx.core.AbstractVerticle; -import io.vertx.core.Future; -import io.vertx.core.Handler; -import io.vertx.core.buffer.Buffer; -import io.vertx.core.http.ClientAuth; -import io.vertx.core.http.HttpServer; -import io.vertx.core.http.HttpServerOptions; -import io.vertx.core.http.HttpServerRequest; -import io.vertx.core.net.JksOptions; -import io.vertx.core.net.PemTrustOptions; -import org.jboss.resteasy.plugins.server.vertx.VertxRequestHandler; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import java.io.BufferedReader; -import java.io.File; -import java.io.IOException; -import java.io.InputStreamReader; -import java.io.UncheckedIOException; -import java.util.Deque; -import java.util.concurrent.LinkedBlockingDeque; -import java.util.concurrent.TimeUnit; - -/** - * HTTP server for deploying address config - */ -public class HTTPServer extends AbstractVerticle { - public static final int SECURE_PORT = 8443; - private static final int PROCESS_LINE_BUFFER_SIZE = 10; - private static final Logger log = LoggerFactory.getLogger(HTTPServer.class.getName()); - private final String certDir; - private final String clientCa; - private final String requestHeaderClientCa; - private final ResteasyDeploymentFactory resteasyDeploymentFactory; - - private final int port; - - private HttpServer httpServer; - - public HTTPServer(ApiServerOptions options, - ResteasyDeploymentFactory resteasyDeploymentFactory, - String clientCa, - String requestHeaderClientCa) { - this(options, resteasyDeploymentFactory, clientCa, requestHeaderClientCa, SECURE_PORT); - } - - public HTTPServer(ApiServerOptions options, - ResteasyDeploymentFactory resteasyDeploymentFactory, - String clientCa, - String requestHeaderClientCa, - int port) { - this.certDir = options.getCertDir(); - this.clientCa = clientCa; - this.requestHeaderClientCa = requestHeaderClientCa; - this.port = port; - this.resteasyDeploymentFactory = resteasyDeploymentFactory; - } - - @Override - public void start(Future startPromise) { - - VertxRequestHandler vertxRequestHandler = new VertxRequestHandler(vertx, resteasyDeploymentFactory.getInstance()); - - createSecureServer(vertxRequestHandler, startPromise); - } - - @Override - public void stop() { - if (httpServer != null) { - httpServer.close(); - } - } - - private void createSecureServer(Handler requestHandler, Future startPromise) { - HttpServerOptions options = new HttpServerOptions(); - if (new File(certDir).exists()) { - File keyFile = new File(certDir, "tls.key"); - if (!keyFile.exists()) { - keyFile = new File(certDir, "apiserver.key"); - } - File certFile = new File(certDir, "tls.crt"); - if (!certFile.exists()) { - certFile = new File(certDir, "apiserver.crt"); - } - - log.info("Loading key from " + keyFile.getAbsolutePath() + ", cert from " + certFile.getAbsolutePath()); - runCommand("openssl", "pkcs12", "-export", "-passout", "pass:enmasse", "-in", certFile.getAbsolutePath(), "-inkey", keyFile.getAbsolutePath(), "-name", "server", "-out", "/tmp/cert.p12"); - runCommand("keytool", "-importkeystore", "-srcstorepass", "enmasse", "-deststorepass", "enmasse", "-destkeystore", "/tmp/keystore.jks", "-srckeystore", "/tmp/cert.p12", "-srcstoretype", "PKCS12"); - options.setKeyCertOptions(new JksOptions() - .setPassword("enmasse") - .setPath("/tmp/keystore.jks")); - options.setSsl(true); - - if (clientCa != null || requestHeaderClientCa != null) { - log.info("Enabling client authentication"); - PemTrustOptions trustOptions = new PemTrustOptions(); - if (clientCa != null) { - log.info("Adding client CA"); - trustOptions.addCertValue(Buffer.buffer(clientCa)); - } - - if (requestHeaderClientCa != null) { - log.info("Adding request header client CA"); - trustOptions.addCertValue(Buffer.buffer(requestHeaderClientCa)); - } - - options.setTrustOptions(trustOptions); - options.setClientAuth(ClientAuth.REQUEST); - } - } - - httpServer = vertx.createHttpServer(options) - .requestHandler(requestHandler) - .listen(this.port, ar -> { - if (ar.succeeded()) { - int actualPort = ar.result().actualPort(); - log.info("Started HTTPS server. Listening on port {}", actualPort); - startPromise.complete(); - } else { - log.info("Error starting HTTPS server"); - startPromise.fail(ar.cause()); - } - }); - } - - public int getActualPort() { - return httpServer.actualPort(); - } - - 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)); - } - } - } -} diff --git a/api-server/src/main/java/io/enmasse/api/server/RequestLogger.java b/api-server/src/main/java/io/enmasse/api/server/RequestLogger.java deleted file mode 100644 index b2f421fa2be..00000000000 --- a/api-server/src/main/java/io/enmasse/api/server/RequestLogger.java +++ /dev/null @@ -1,183 +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.api.server; - -import io.enmasse.metrics.api.HistogramMetric; -import io.enmasse.metrics.api.MetricLabel; -import io.enmasse.metrics.api.MetricType; -import io.enmasse.metrics.api.MetricValue; -import io.enmasse.metrics.api.MetricValueSupplier; -import io.enmasse.metrics.api.Metrics; -import io.enmasse.metrics.api.ScalarMetric; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import javax.ws.rs.container.ContainerRequestContext; -import javax.ws.rs.container.ContainerRequestFilter; -import javax.ws.rs.container.ContainerResponseContext; -import javax.ws.rs.container.ContainerResponseFilter; -import java.util.ArrayList; -import java.util.HashMap; -import java.util.List; -import java.util.Map; -import java.util.concurrent.atomic.AtomicLong; -import java.util.concurrent.atomic.AtomicLongArray; -import java.util.stream.Collectors; -import java.util.stream.IntStream; - -public class RequestLogger implements ContainerRequestFilter, ContainerResponseFilter { - private static final Logger log = LoggerFactory.getLogger(RequestLogger.class); - private static final String PROP_NAME = "request.start"; - - private static final List methods = List.of("POST", "PUT", "GET", "DELETE", "PATCH"); - private static final List services = List.of("addressspaces", "addresses", "messagingusers", "other"); - - // Assume status code above 599 is not used - private static final int MAX_CODE = 600; - - // Buckets: - // 0-9 ms - // 10-99 ms - // 100-999 ms - // 1000-9999 ms - // 10000-+Inf ms - private static final int NUM_BUCKETS = 5; - private static final Long latencyBucketLimits[] = new Long[] { - 10L, - 100L, - 1000L, - 10000L, - null}; - - private final Map> serviceMetrics = new HashMap<>(); - - private static class ServiceMetrics { - private final String service; - private final String method; - private final AtomicLongArray statusCodeCounter = new AtomicLongArray(MAX_CODE); - private final AtomicLong latencySum = new AtomicLong(0); - private final AtomicLongArray latencyBucketsCount = new AtomicLongArray(NUM_BUCKETS); - - private ServiceMetrics(String service, String method) { - this.service = service; - this.method = method; - } - - public void update(int status, long latencyMs) { - if (status < 0 || status >= MAX_CODE) { - return; - } - - statusCodeCounter.incrementAndGet(status); - latencySum.addAndGet(latencyMs); - - for (int i = 0; i < NUM_BUCKETS; i++) { - if (latencyBucketLimits[i] == null || latencyMs < latencyBucketLimits[i]) { - latencyBucketsCount.incrementAndGet(i); - } - } - } - } - - public RequestLogger(Metrics metrics) { - for (String service : services) { - serviceMetrics.put(service, new HashMap<>()); - for (String method : methods) { - serviceMetrics.get(service).put(method, new ServiceMetrics(service, method)); - } - } - List list = serviceMetrics.values().stream() - .flatMap(m -> m.values().stream()) - .collect(Collectors.toList()); - metrics.registerMetric(new ScalarMetric( - "http_requests_total", - "The number of HTTP requests", - MetricType.counter, - () -> { - List values = new ArrayList<>(); - for (ServiceMetrics service : list) { - for (int i = 0; i < MAX_CODE; i++) { - long count = service.statusCodeCounter.get(i); - if (count > 0) { - values.add(new MetricValue(count, - new MetricLabel("status", String.format("%3d", i)), - new MetricLabel("service", service.service), - new MetricLabel("method", service.method))); - } - } - } - return values; - })); - - metrics.registerMetric(new HistogramMetric( - "api_request_duration_milliseconds", - "The request duration of HTTP requests in milliseconds", - MetricType.histogram, - () -> { - List values = new ArrayList<>(); - for (ServiceMetrics service : list) { - values.add(new MetricValue(service.latencySum.get(), - new MetricLabel("service", service.service), - new MetricLabel("method", service.method))); - } - return values; - }, - () -> { - List values = new ArrayList<>(); - for (ServiceMetrics service : list) { - values.add(new MetricValue(service.latencyBucketsCount.get(NUM_BUCKETS - 1), - new MetricLabel("service", service.service), - new MetricLabel("method", service.method))); - } - return values; - }, - IntStream.range(0, NUM_BUCKETS) - .mapToObj(bucketIdx -> (MetricValueSupplier) () -> { - List values = new ArrayList<>(); - for (ServiceMetrics service : list) { - Number limit = latencyBucketLimits[bucketIdx]; - values.add(new MetricValue(service.latencyBucketsCount.get(bucketIdx), - new MetricLabel("le", limit == null ? "+Inf" : String.valueOf(limit)), - new MetricLabel("service", service.service), - new MetricLabel("method", service.method))); - } - return values; - }).collect(Collectors.toList()))); - } - - @Override - public void filter(ContainerRequestContext containerRequestContext) { - containerRequestContext.setProperty(PROP_NAME, System.currentTimeMillis()); - } - - @Override - public void filter(ContainerRequestContext containerRequestContext, ContainerResponseContext containerResponseContext) { - long start = (long) containerRequestContext.getProperty(PROP_NAME); - long end = System.currentTimeMillis(); - int status = containerResponseContext.getStatus(); - long latencyMs = end - start; - - log.info("{} {} {} ({} ms)", containerRequestContext.getMethod(), containerRequestContext.getUriInfo().getPath(), status, latencyMs); - updateMetrics(containerRequestContext.getMethod(), containerRequestContext.getUriInfo().getPath(), status, latencyMs); - } - - private void updateMetrics(String method, String path, int status, long latencyMs) { - String service = detectServiceFromPath(path); - if (!methods.contains(method) || !services.contains(service)) { - return; - } - - serviceMetrics.get(service).get(method).update(status, latencyMs); - } - - private String detectServiceFromPath(String path) { - for (String service : services) { - if (path.contains(service)) { - return service; - } - } - return "other"; - } -} diff --git a/api-server/src/main/java/io/enmasse/api/server/ResteasyDeploymentFactory.java b/api-server/src/main/java/io/enmasse/api/server/ResteasyDeploymentFactory.java deleted file mode 100644 index 5a7496f17c9..00000000000 --- a/api-server/src/main/java/io/enmasse/api/server/ResteasyDeploymentFactory.java +++ /dev/null @@ -1,77 +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.api.server; - -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.api.v1.http.HttpApiRootService; -import io.enmasse.api.v1.http.HttpClusterUserService; -import io.enmasse.api.v1.http.HttpOpenApiService; -import io.enmasse.api.v1.http.HttpRootService; -import io.enmasse.api.v1.http.HttpUserService; -import io.enmasse.api.v1.http.SwaggerSpecEndpoint; -import io.enmasse.k8s.api.AddressSpaceApi; -import io.enmasse.k8s.api.AuthenticationServiceRegistry; -import io.enmasse.k8s.api.SchemaProvider; -import io.enmasse.metrics.api.Metrics; -import io.enmasse.user.api.UserApi; -import org.jboss.resteasy.plugins.server.vertx.VertxResteasyDeployment; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import javax.ws.rs.container.ContainerRequestFilter; -import java.time.Clock; - -public class ResteasyDeploymentFactory { - private static final Logger log = LoggerFactory.getLogger(ResteasyDeploymentFactory.class); - - private final RequestLogger requestLogger; - private final ContainerRequestFilter authInterceptor; - private final SwaggerSpecEndpoint swaggerSpecEndpoint; - private final HttpOpenApiService openApiService; - private final HttpUserService userService; - private final HttpClusterUserService clusterUserService; - private final HttpRootService rootService; - private final HttpApiRootService apiRootService; - - - public ResteasyDeploymentFactory(AddressSpaceApi addressSpaceApi, SchemaProvider schemaProvider, AuthApi authApi, UserApi userApi, Clock clock, AuthenticationServiceRegistry authenticationServiceRegistry, ApiHeaderConfig apiHeaderConfig, Metrics metrics, boolean isRbacEnabled) { - requestLogger = new RequestLogger(metrics); - - if (isRbacEnabled) { - log.info("Enabling RBAC for REST API"); - authInterceptor = new AuthInterceptor(authApi, apiHeaderConfig, path -> path.equals("/swagger.json")); - } else { - log.info("Disabling authentication and authorization for REST API"); - authInterceptor = new AllowAllAuthInterceptor(); - } - - swaggerSpecEndpoint = new SwaggerSpecEndpoint(); - openApiService = new HttpOpenApiService(); - userService = new HttpUserService(addressSpaceApi, userApi, authenticationServiceRegistry, clock); - clusterUserService = new HttpClusterUserService(userApi, authenticationServiceRegistry, clock); - rootService = new HttpRootService(); - apiRootService = new HttpApiRootService(); - } - - public org.jboss.resteasy.spi.ResteasyDeployment getInstance() { - VertxResteasyDeployment deployment = new VertxResteasyDeployment(); - deployment.start(); - - deployment.getProviderFactory().registerProvider(DefaultExceptionMapper.class); - deployment.getProviderFactory().registerProviderInstance(requestLogger); - deployment.getProviderFactory().registerProviderInstance(authInterceptor); - deployment.getRegistry().addSingletonResource(swaggerSpecEndpoint); - deployment.getRegistry().addSingletonResource(openApiService); - deployment.getRegistry().addSingletonResource(userService); - deployment.getRegistry().addSingletonResource(clusterUserService); - deployment.getRegistry().addSingletonResource(rootService); - deployment.getRegistry().addSingletonResource(apiRootService); - return deployment; - } -} diff --git a/api-server/src/main/java/io/enmasse/api/v1/AddressApiHelper.java b/api-server/src/main/java/io/enmasse/api/v1/AddressApiHelper.java deleted file mode 100644 index 03184f1c11c..00000000000 --- a/api-server/src/main/java/io/enmasse/api/v1/AddressApiHelper.java +++ /dev/null @@ -1,181 +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.api.v1; - -import java.util.*; -import java.util.function.BiFunction; -import java.util.stream.Collectors; - -import javax.ws.rs.BadRequestException; -import javax.ws.rs.NotFoundException; - -import io.enmasse.address.model.*; -import io.enmasse.k8s.api.SchemaProvider; -import io.enmasse.k8s.api.AddressApi; -import io.enmasse.k8s.api.AddressSpaceApi; - -/** - * This is a handler for doing operations on the addressing manager that works independent of AMQP and HTTP. - */ -public class AddressApiHelper { - private final AddressSpaceApi addressSpaceApi; - private final SchemaProvider schemaProvider; - - public AddressApiHelper(AddressSpaceApi addressSpaceApi, SchemaProvider schemaProvider) { - this.addressSpaceApi = addressSpaceApi; - this.schemaProvider = schemaProvider; - } - - protected AddressList queryAddresses(Collection addressSpaces, BiFunction> lister) throws Exception { - final List
list = new ArrayList<>(); - - for (final AddressSpace addressSpace : addressSpaces) { - list.addAll(lister.apply(addressSpace.getMetadata().getNamespace(), addressSpaceApi.withAddressSpace(addressSpace))); - } - - return new AddressList(list); - } - - protected Collection getAddressSpaces(String namespace, String addressSpaceId) throws Exception { - if (addressSpaceId == null) { - if ( namespace == null || namespace.isEmpty() ) { - return addressSpaceApi.listAllAddressSpaces(); - } else { - return addressSpaceApi.listAddressSpaces(namespace); - } - } else { - return Collections.singleton(getAddressSpace(namespace, addressSpaceId)); - } - } - - public AddressList getAddresses(String namespace, String addressSpaceId) throws Exception { - return queryAddresses(getAddressSpaces(namespace, addressSpaceId), (ns, api) -> api.listAddresses(ns)); - } - - public AddressList getAddressesWithLabels(String namespace, String addressSpaceId, Map labels) throws Exception { - return queryAddresses(getAddressSpaces(namespace, addressSpaceId), (ns, api) -> api.listAddressesWithLabels(ns, labels)); - } - - public AddressList getAllAddresses() throws Exception { - return queryAddresses(addressSpaceApi.listAllAddressSpaces(), (ns, api) -> api.listAddresses(ns)); - } - - public AddressList getAllAddressesWithLabels(final Map labels) throws Exception { - return queryAddresses(addressSpaceApi.listAllAddressSpaces(), (ns, api) -> api.listAddressesWithLabels(ns, labels)); - } - - private void validateAddress(AddressSpace addressSpace, Address address) { - AddressResolver addressResolver = getAddressResolver(addressSpace); - - /* - Brokered address space has no operator that manipulates address phase and readiness, so we need to perform validation at API server. - For the standard address space, the validation is done in AddressController#onUpdate in order to avoid slowing down the request. - */ - if (addressSpace.getSpec().getType().equals("brokered")) { - Collection
existingAddresses = addressSpaceApi.withAddressSpace(addressSpace).listAddresses(address.getMetadata().getNamespace()); - addressResolver.validate(address); - for (Address existing : existingAddresses) { - if (address.getSpec().getAddress().equals(existing.getSpec().getAddress()) && !address.getMetadata().getName().equals(existing.getMetadata().getName())) { - throw new BadRequestException("Address '" + address.getSpec().getAddress() + "' already exists with resource name '" + existing.getMetadata().getName() + "'"); - } - } - } - } - - private AddressResolver getAddressResolver(AddressSpace addressSpace) { - Schema schema = schemaProvider.getSchema(); - AddressSpaceType type = schema.findAddressSpaceType(addressSpace.getSpec().getType()).orElseThrow(() -> new UnresolvedAddressSpaceException("Unable to resolve address space type " + addressSpace.getSpec().getType())); - - return new AddressResolver(type); - } - - public AddressSpace getAddressSpace(String namespace, String addressSpaceId) throws Exception { - return addressSpaceApi.getAddressSpaceWithName(namespace, addressSpaceId) - .orElseThrow(() -> new NotFoundException("Address space " + addressSpaceId + " not found")); - } - - public Optional
getAddress(String namespace, AddressSpace addressSpace, String address) throws Exception { - return addressSpaceApi.withAddressSpace(addressSpace).getAddressWithName(namespace, address); - } - - public Optional
getAddress(String namespace, String addressSpaceId, String address) throws Exception { - AddressSpace addressSpace = getAddressSpace(namespace, addressSpaceId); - return getAddress(namespace, addressSpace, address); - } - - public Address deleteAddress(String namespace, String addressSpaceId, String name) throws Exception { - AddressSpace addressSpace = getAddressSpace(namespace, addressSpaceId); - AddressApi addressApi = addressSpaceApi.withAddressSpace(addressSpace); - - Optional
addressOptional = addressApi.getAddressWithName(namespace, name); - return addressOptional.filter(addressApi::deleteAddress).orElse(null); - } - - public Address createAddress(AddressSpace addressSpace, Address address) throws Exception { - validateAddress(addressSpace, address); - AddressApi addressApi = addressSpaceApi.withAddressSpace(addressSpace); - addressApi.createAddress(address); - return address; - } - - public void createAddresses(String addressSpaceId, Set
address) throws Exception { - - List allResourceNames = address.stream() - .map(a -> a.getMetadata().getName()) - .collect(Collectors.toList()); - Set resourceNameSet = new HashSet<>(allResourceNames); - if (resourceNameSet.size() != allResourceNames.size()) { - List duplicates = new ArrayList<>(allResourceNames); - resourceNameSet.forEach(duplicates::remove); // removes first - throw new BadRequestException("Address resource names must be unique. Duplicate resource names: " + duplicates); - } - - List
sorted = address.stream() - .sorted(Comparator.comparing(a -> a.getMetadata().getNamespace())) - .collect(Collectors.toList()); - - Map apiMap = new HashMap<>(); - AddressSpace addressSpace = null; - AddressApi addressApi = null; - AddressResolver addressResolver = null; - Collection
existingAddresses = null; - for(Address a : sorted) { - if (addressSpace == null || !Objects.equals(addressSpace.getMetadata().getNamespace(), a.getMetadata().getNamespace())) { - addressSpace = getAddressSpace(a.getMetadata().getNamespace(), addressSpaceId); - addressApi = addressSpaceApi.withAddressSpace(addressSpace); - addressResolver = getAddressResolver(addressSpace); - existingAddresses = addressApi.listAddresses(a.getMetadata().getNamespace()); - } - - addressResolver.validate(a); - for (Address existing : existingAddresses) { - if (a.getSpec().getAddress().equals(existing.getSpec().getAddress()) && !a.getMetadata().getName().equals(existing.getMetadata().getName())) { - throw new BadRequestException("Address '" + a.getSpec().getAddress() + "' already exists with resource name '" + existing.getMetadata().getName() + "'"); - } - } - apiMap.put(a, addressApi); - } - - apiMap.forEach((addr, api) -> api.createAddress(addr)); - } - - public Address replaceAddress(AddressSpace addressSpace, Address address) throws Exception { - validateAddress(addressSpace, address); - AddressApi addressApi = addressSpaceApi.withAddressSpace(addressSpace); - if (!addressApi.replaceAddress(address)) { - throw new NotFoundException("Address " + address.getMetadata().getName() + " not found"); - } - return address; - } - - - - public void deleteAddresses(String namespace) { - for (AddressSpace addressSpace : addressSpaceApi.listAddressSpaces(namespace)) { - AddressApi addressApi = addressSpaceApi.withAddressSpace(addressSpace); - addressApi.deleteAddresses(namespace); - } - } -} diff --git a/api-server/src/main/java/io/enmasse/api/v1/http/HttpApiRootService.java b/api-server/src/main/java/io/enmasse/api/v1/http/HttpApiRootService.java deleted file mode 100644 index ff6b3fe4f53..00000000000 --- a/api-server/src/main/java/io/enmasse/api/v1/http/HttpApiRootService.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). - */ -package io.enmasse.api.v1.http; - -import io.enmasse.api.auth.RbacSecurityContext; -import io.enmasse.api.common.Exceptions; -import io.enmasse.api.v1.types.*; - -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.SecurityContext; -import javax.ws.rs.core.UriInfo; -import java.util.Arrays; -import java.util.List; - -@Path("/apis") -public class HttpApiRootService { - private static final APIGroup userApiGroup = - new APIGroup("user.enmasse.io", Arrays.asList( - new APIGroupVersion("user.enmasse.io/v1alpha1", "v1alpha1"), - new APIGroupVersion("user.enmasse.io/v1beta1", "v1beta1")), - new APIGroupVersion("user.enmasse.io/v1beta1", "v1beta1"), - null); - - private static final APIGroupList apiGroupList = new APIGroupList(Arrays.asList( userApiGroup)); - - private static void verifyAuthorized(SecurityContext securityContext, String method, String path) { - if (!securityContext.isUserInRole(RbacSecurityContext.rbacToRole(path, method))) { - throw Exceptions.notAuthorizedException(); - } - } - - @GET - @Produces({MediaType.APPLICATION_JSON}) - public APIGroupList getApiGroupList(@Context SecurityContext securityContext, @Context UriInfo uriInfo) { - verifyAuthorized(securityContext, "get", uriInfo.getPath()); - return apiGroupList; - } - - @GET - @Path("user.enmasse.io") - @Produces({MediaType.APPLICATION_JSON}) - public APIGroup getUserApiGroup(@Context SecurityContext securityContext, @Context UriInfo uriInfo) { - verifyAuthorized(securityContext, "get", uriInfo.getPath()); - return userApiGroup; - } - - - private static final List enmasseUserResources = Arrays.asList( - new APIResource("messagingusers", "", true, "MessagingUser", - Arrays.asList("create", "delete", "get", "list", "update"))); - - private static final APIResourceList enmasseV1Alpha1UserResourceList = new APIResourceList("user.enmasse.io/v1alpha1", enmasseUserResources); - - @GET - @Path("user.enmasse.io/v1alpha1") - @Produces({MediaType.APPLICATION_JSON}) - public APIResourceList getUserApiGroupV1Alpha1(@Context SecurityContext securityContext, @Context UriInfo uriInfo) { - // verifyAuthorized(securityContext, "get", uriInfo.getPath()); - return enmasseV1Alpha1UserResourceList; - } - - private static final APIResourceList enmasseV1Beta1UserResourceList = new APIResourceList("user.enmasse.io/v1beta1", enmasseUserResources); - - @GET - @Path("user.enmasse.io/v1beta1") - @Produces({MediaType.APPLICATION_JSON}) - public APIResourceList getUserApiGroupV1Beta1(@Context SecurityContext securityContext, @Context UriInfo uriInfo) { - // verifyAuthorized(securityContext, "get", uriInfo.getPath()); - return enmasseV1Beta1UserResourceList; - } -} diff --git a/api-server/src/main/java/io/enmasse/api/v1/http/HttpClusterUserService.java b/api-server/src/main/java/io/enmasse/api/v1/http/HttpClusterUserService.java deleted file mode 100644 index e82b7692148..00000000000 --- a/api-server/src/main/java/io/enmasse/api/v1/http/HttpClusterUserService.java +++ /dev/null @@ -1,92 +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.api.v1.http; - -import java.time.Clock; -import java.time.Instant; -import java.util.Map; -import java.util.concurrent.Callable; - -import javax.ws.rs.GET; -import javax.ws.rs.HeaderParam; -import javax.ws.rs.Path; -import javax.ws.rs.Produces; -import javax.ws.rs.QueryParam; -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.admin.model.v1.AuthenticationService; -import io.enmasse.k8s.api.AuthenticationServiceRegistry; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import io.enmasse.api.auth.RbacSecurityContext; -import io.enmasse.api.auth.ResourceVerb; -import io.enmasse.api.common.Exceptions; -import io.enmasse.api.v1.AddressApiHelper; -import io.enmasse.user.api.UserApi; -import io.enmasse.user.model.v1.UserCrd; -import io.enmasse.user.model.v1.UserList; - -import static io.enmasse.api.v1.http.HttpUserService.parseLabelSelector; - -@Path(HttpClusterUserService.BASE_URI) -public class HttpClusterUserService { - - private static final String RESOURCE_NAME = "messagingusers"; - - static final String BASE_URI = "/apis/" + UserCrd.GROUP + "/{version:v1alpha1|v1beta1}/" + RESOURCE_NAME; - - private static final Logger log = LoggerFactory.getLogger(HttpClusterUserService.class.getName()); - - private final UserApi userApi; - private final AuthenticationServiceRegistry authenticationServiceRegistry; - private final Clock clock; - - public HttpClusterUserService(UserApi userApi, AuthenticationServiceRegistry authenticationServiceRegistry, Clock clock) { - this.userApi = userApi; - this.authenticationServiceRegistry = authenticationServiceRegistry; - this.clock = clock; - } - - private Response doRequest(String errorMessage, Callable request) throws Exception { - try { - return request.call(); - } catch (Exception e) { - log.error(errorMessage, e); - throw e; - } - } - - private static void verifyAuthorized(SecurityContext securityContext, ResourceVerb verb) { - if (!securityContext.isUserInRole(RbacSecurityContext.rbacToRole("", verb, RESOURCE_NAME, UserCrd.GROUP))) { - throw Exceptions.notAuthorizedException(); - } - } - - @GET - @Produces({MediaType.APPLICATION_JSON}) - public Response getUserList(@Context SecurityContext securityContext, @HeaderParam("Accept") String acceptHeader, @QueryParam("labelSelector") String labelSelector) throws Exception { - return doRequest("Error getting user list", () -> { - verifyAuthorized(securityContext, ResourceVerb.list); - - Instant now = clock.instant(); - UserList userList = new UserList(); - - for (AuthenticationService authenticationService : authenticationServiceRegistry.listAuthenticationServices()) { - if (labelSelector != null) { - Map labels = parseLabelSelector(labelSelector); - userList.getItems().addAll(userApi.listAllUsersWithLabels(authenticationService, labels).getItems()); - } else { - userList.getItems().addAll(userApi.listAllUsers(authenticationService).getItems()); - } - } - - return Response.ok(HttpUserService.formatResponse(acceptHeader, now, userList)).build(); - }); - } -} diff --git a/api-server/src/main/java/io/enmasse/api/v1/http/HttpOpenApiService.java b/api-server/src/main/java/io/enmasse/api/v1/http/HttpOpenApiService.java deleted file mode 100644 index b0d5a0d38d1..00000000000 --- a/api-server/src/main/java/io/enmasse/api/v1/http/HttpOpenApiService.java +++ /dev/null @@ -1,21 +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.api.v1.http; - -import javax.ws.rs.GET; -import javax.ws.rs.Path; -import javax.ws.rs.Produces; -import javax.ws.rs.core.MediaType; -import java.io.InputStream; - -@Path("/openapi/v2") -public class HttpOpenApiService { - - @GET - @Produces({MediaType.APPLICATION_JSON}) - public InputStream getOpenApiSpec() { - return getClass().getResourceAsStream("/swagger.json"); - } -} diff --git a/api-server/src/main/java/io/enmasse/api/v1/http/HttpRootService.java b/api-server/src/main/java/io/enmasse/api/v1/http/HttpRootService.java deleted file mode 100644 index a77ea59862d..00000000000 --- a/api-server/src/main/java/io/enmasse/api/v1/http/HttpRootService.java +++ /dev/null @@ -1,32 +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.api.v1.http; - -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.UriInfo; -import java.net.URI; -import java.util.ArrayList; -import java.util.List; - -@Path("/") -public class HttpRootService { - @GET - @Produces({MediaType.APPLICATION_JSON}) - public Response getAll(@Context UriInfo uriInfo) { - List uriList = new ArrayList<>(); - URI baseUri = uriInfo.getBaseUri(); - uriList.add(baseUri.resolve("/apis")); - uriList.add(baseUri.resolve("/apis/enmasse.io")); - uriList.add(baseUri.resolve("/apis/enmasse.io/v1beta1")); - uriList.add(baseUri.resolve("/healthz")); - uriList.add(baseUri.resolve("/swagger.json")); - return Response.status(200).entity(uriList).build(); - } -} diff --git a/api-server/src/main/java/io/enmasse/api/v1/http/HttpUserService.java b/api-server/src/main/java/io/enmasse/api/v1/http/HttpUserService.java deleted file mode 100644 index c1ebc29052f..00000000000 --- a/api-server/src/main/java/io/enmasse/api/v1/http/HttpUserService.java +++ /dev/null @@ -1,459 +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.api.v1.http; - -import java.time.Clock; -import java.time.Duration; -import java.time.Instant; -import java.util.*; -import java.util.concurrent.Callable; -import java.util.stream.Collectors; - -import javax.validation.constraints.NotNull; -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 javax.ws.rs.core.UriBuilder; -import javax.ws.rs.core.UriInfo; - -import com.fasterxml.jackson.databind.JsonNode; -import com.fasterxml.jackson.databind.ObjectMapper; -import com.github.fge.jsonpatch.JsonPatch; -import com.github.fge.jsonpatch.JsonPatchException; -import com.github.fge.jsonpatch.mergepatch.JsonMergePatch; -import io.enmasse.admin.model.v1.AuthenticationService; -import io.enmasse.api.common.CheckedFunction; -import io.enmasse.k8s.api.AuthenticationServiceRegistry; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import io.enmasse.address.model.AddressSpace; -import io.enmasse.api.auth.RbacSecurityContext; -import io.enmasse.api.auth.ResourceVerb; -import io.enmasse.api.common.Exceptions; -import io.enmasse.api.common.Status; -import io.enmasse.api.v1.AddressApiHelper; -import io.enmasse.config.AnnotationKeys; -import io.enmasse.k8s.api.AddressSpaceApi; -import io.enmasse.k8s.model.v1beta1.PartialObjectMetadata; -import io.enmasse.k8s.model.v1beta1.Table; -import io.enmasse.k8s.model.v1beta1.TableColumnDefinition; -import io.enmasse.k8s.model.v1beta1.TableRow; -import io.enmasse.k8s.util.TimeUtil; -import io.enmasse.user.api.UserApi; -import io.enmasse.user.model.v1.User; -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.api.model.ListMeta; -import io.fabric8.kubernetes.api.model.ObjectMetaBuilder; - -@Path(HttpUserService.BASE_URI) -public class HttpUserService { - - private static final String RESOURCE_NAME = "messagingusers"; - private static final ObjectMapper MAPPER = new ObjectMapper(); - static final String BASE_URI = "/apis/" + UserCrd.GROUP + "/{version:v1alpha1|v1beta1}/namespaces/{namespace}/" + RESOURCE_NAME; - - private static final Logger log = LoggerFactory.getLogger(HttpUserService.class.getName()); - - private final AddressSpaceApi addressSpaceApi; - private final UserApi userApi; - private final AuthenticationServiceRegistry authenticationServiceRegistry; - private final Clock clock; - - public HttpUserService(AddressSpaceApi addressSpaceApi, UserApi userApi, AuthenticationServiceRegistry authenticationServiceRegistry, Clock clock) { - this.addressSpaceApi = addressSpaceApi; - this.userApi = userApi; - this.authenticationServiceRegistry = authenticationServiceRegistry; - this.clock = clock; - } - - private Response doRequest(String errorMessage, Callable request) throws Exception { - try { - return request.call(); - } catch (Exception e) { - log.error(errorMessage, e); - throw e; - } - } - - private static void verifyAuthorized(SecurityContext securityContext, String namespace, ResourceVerb verb) { - if (!securityContext.isUserInRole(RbacSecurityContext.rbacToRole(namespace, verb, RESOURCE_NAME, UserCrd.GROUP))) { - throw Exceptions.notAuthorizedException(); - } - } - - @GET - @Produces({MediaType.APPLICATION_JSON}) - public Response getUserList(@Context SecurityContext securityContext, @HeaderParam("Accept") String acceptHeader, @PathParam("namespace") String namespace, @QueryParam("labelSelector") String labelSelector) throws Exception { - return doRequest("Error getting user list", () -> { - verifyAuthorized(securityContext, namespace, ResourceVerb.list); - - Instant now = clock.instant(); - UserList userList = new UserList(); - Set addressSpaces = addressSpaceApi.listAddressSpaces(namespace); - Map authenticationServiceMap = new HashMap<>(); - for (AddressSpace addressSpace : addressSpaces) { - AuthenticationService authenticationService = authenticationServiceRegistry.findAuthenticationService(addressSpace.getSpec().getAuthenticationService()).orElse(null); - if (authenticationService == null) { - return Response.status(404).entity(Status.notFound("AuthenticationService", addressSpace.getSpec().getAuthenticationService().getName())).build(); - } - if (authenticationServiceMap.get(authenticationService.getMetadata().getName()) == null) { - authenticationServiceMap.put(authenticationService.getMetadata().getName(), authenticationService); - if (labelSelector != null) { - Map labels = parseLabelSelector(labelSelector); - userList.getItems().addAll(userApi.listUsersWithLabels(authenticationService, namespace, labels).getItems()); - } else { - userList.getItems().addAll(userApi.listUsers(authenticationService, namespace).getItems()); - } - } - } - return Response.ok(formatResponse(acceptHeader, now, userList)).build(); - }); - } - - @GET - @Produces({MediaType.APPLICATION_JSON}) - @Path("{userName}") - public Response getUser(@Context SecurityContext securityContext, @HeaderParam("Accept") String acceptHeader, @PathParam("namespace") String namespace, @PathParam("userName") String userNameWithAddressSpace) throws Exception { - return doRequest("Error getting user " + userNameWithAddressSpace, () -> { - verifyAuthorized(securityContext, namespace, ResourceVerb.get); - - Instant now = clock.instant(); - String addressSpaceName = parseAddressSpace(userNameWithAddressSpace); - checkAddressSpaceName(userNameWithAddressSpace, addressSpaceName); - AddressSpace addressSpace = addressSpaceApi.getAddressSpaceWithName(namespace, addressSpaceName).orElse(null); - if (addressSpace == null) { - return Response.status(404).entity(Status.notFound("AddressSpace", addressSpaceName)).build(); - } - AuthenticationService authenticationService = authenticationServiceRegistry.findAuthenticationService(addressSpace.getSpec().getAuthenticationService()).orElse(null); - if (authenticationService == null) { - return Response.status(404).entity(Status.notFound("AuthenticationService", addressSpace.getSpec().getAuthenticationService().getName())).build(); - } - - String realm = getRealm(authenticationService, addressSpace); - - log.debug("Retrieving user {} in realm {} namespace {}", userNameWithAddressSpace, realm, namespace); - - return userApi.getUserWithName(authenticationService, realm, userNameWithAddressSpace) - .map(user -> Response.ok(formatResponse(acceptHeader, now, user)).build()) - .orElseGet(() -> Response.status(404).entity(Status.notFound("MessagingUser", userNameWithAddressSpace)).build()); - }); - } - - private static String getRealm(AuthenticationService authenticationService, AddressSpace addressSpace) { - String realm = authenticationService.getSpec().getRealm(); - if (realm == null) { - realm = addressSpace.getAnnotation(AnnotationKeys.REALM_NAME); - } - return realm; - } - - private static String parseAddressSpace(String userNameWithAddressSpace) { - String [] parts = userNameWithAddressSpace.split("\\."); - if (parts.length < 2) { - throw new BadRequestException("User name '" + userNameWithAddressSpace + "' does not contain valid separator (.) to identify address space"); - } - return parts[0]; - } - - @POST - @Consumes({MediaType.APPLICATION_JSON}) - @Produces({MediaType.APPLICATION_JSON}) - public Response createUser(@Context SecurityContext securityContext, @Context UriInfo uriInfo, @PathParam("namespace") String namespace, @NotNull User input) throws Exception { - return doRequest("Error creating user " + input.getMetadata().getName(), () -> { - verifyAuthorized(securityContext, namespace, ResourceVerb.create); - User user = setUserDefaults(input, namespace); - String addressSpaceName = parseAddressSpace(user.getMetadata().getName()); - checkAddressSpaceName(user.getMetadata().getName(), addressSpaceName); - - AddressSpace addressSpace = addressSpaceApi.getAddressSpaceWithName(namespace, addressSpaceName).orElse(null); - if (addressSpace == null) { - return Response.status(404).entity(Status.notFound("AddressSpace", addressSpaceName)).build(); - } - AuthenticationService authenticationService = authenticationServiceRegistry.findAuthenticationService(addressSpace.getSpec().getAuthenticationService()).orElse(null); - if (authenticationService == null) { - return Response.status(404).entity(Status.notFound("AuthenticationService", addressSpace.getSpec().getAuthenticationService().getName())).build(); - } - - String realm = getRealm(authenticationService, addressSpace); - - userApi.createUser(authenticationService, realm, user); - User created = userApi.getUserWithName(authenticationService, realm, user.getMetadata().getName()).orElse(user); - UriBuilder builder = uriInfo.getAbsolutePathBuilder(); - builder.path(created.getMetadata().getName()); - return Response.created(builder.build()).entity(created).build(); - }); - } - - private User setUserDefaults(User user, String namespace) { - if (user.getMetadata().getNamespace() == null) { - user = new UserBuilder(user) - .editOrNewMetadata() - .withNamespace(namespace) - .endMetadata() - .build(); - } - return user; - } - - @PUT - @Consumes({MediaType.APPLICATION_JSON}) - @Produces({MediaType.APPLICATION_JSON}) - @Path("{userName}") - public Response replaceUser(@Context SecurityContext securityContext, @PathParam("namespace") String namespace, @PathParam("userName") String userNameWithAddressSpace, @NotNull User input) throws Exception { - return doRequest("Error replacing user " + input.getMetadata().getName(), () -> { - verifyAuthorized(securityContext, namespace, ResourceVerb.update); - User user = setUserDefaults(input, namespace); - - String addressSpaceName = parseAddressSpace(user.getMetadata().getName()); - checkAddressSpaceName(user.getMetadata().getName(), addressSpaceName); - checkMatchingUserName(userNameWithAddressSpace, user); - AddressSpace addressSpace = addressSpaceApi.getAddressSpaceWithName(namespace, addressSpaceName).orElse(null); - if (addressSpace == null) { - return Response.status(404).entity(Status.notFound("AddressSpace", addressSpaceName)).build(); - } - - AuthenticationService authenticationService = authenticationServiceRegistry.findAuthenticationService(addressSpace.getSpec().getAuthenticationService()).orElse(null); - if (authenticationService == null) { - return Response.status(404).entity(Status.notFound("AuthenticationService", addressSpace.getSpec().getAuthenticationService().getName())).build(); - } - - String realm = getRealm(authenticationService, addressSpace); - - if (!userApi.replaceUser(authenticationService, realm, user)) { - return Response.status(404).entity(Status.notFound("MessagingUser", user.getMetadata().getName())).build(); - } - User replaced = userApi.getUserWithName(authenticationService, realm, user.getMetadata().getName()).orElse(user); - return Response.ok().entity(replaced).build(); - }); - } - - @PATCH - @Consumes({MediaType.APPLICATION_JSON_PATCH_JSON}) - @Produces({MediaType.APPLICATION_JSON}) - @Path("{userName}") - public Response patchUser(@Context SecurityContext securityContext, - @PathParam("namespace") String namespace, - @PathParam("userName") String userNameWithAddressSpace, - @NotNull JsonPatch patch) throws Exception { - String addressSpace = parseAddressSpace(userNameWithAddressSpace); - return doPatch(securityContext, namespace, addressSpace, userNameWithAddressSpace, patch::apply); - } - - @PATCH - @Consumes({"application/merge-patch+json", "application/strategic-merge-patch+json"}) - @Produces({MediaType.APPLICATION_JSON}) - @Path("{userName}") - public Response patchUser(@Context SecurityContext securityContext, - @PathParam("namespace") String namespace, - @PathParam("userName") String userNameWithAddressSpace, - @NotNull JsonMergePatch patch) throws Exception { - - String addressSpace = parseAddressSpace(userNameWithAddressSpace); - return doPatch(securityContext, namespace, addressSpace, userNameWithAddressSpace, patch::apply); - } - - protected Response doPatch(@Context SecurityContext securityContext, String namespace, String addressSpaceName, String userNameWithAddressSpace, - CheckedFunction patcher) throws Exception { - return doRequest("Error patching user " + userNameWithAddressSpace, () -> { - verifyAuthorized(securityContext, namespace, ResourceVerb.patch); - - checkAddressSpaceName(userNameWithAddressSpace, addressSpaceName); - AddressSpace addressSpace = addressSpaceApi.getAddressSpaceWithName(namespace, addressSpaceName).orElse(null); - if (addressSpace == null) { - return Response.status(404).entity(Status.notFound("AddressSpace", addressSpaceName)).build(); - } - - AuthenticationService authenticationService = authenticationServiceRegistry.findAuthenticationService(addressSpace.getSpec().getAuthenticationService()).orElse(null); - if (authenticationService == null) { - return Response.status(404).entity(Status.notFound("AuthenticationService", addressSpace.getSpec().getAuthenticationService().getName())).build(); - } - - String realm = getRealm(authenticationService, addressSpace); - log.debug("Retrieving user {} in realm {} namespace {}", userNameWithAddressSpace, realm, namespace); - - Optional existing = userApi.getUserWithName(authenticationService, realm, userNameWithAddressSpace); - - if (!existing.isPresent()) { - return Response.status(404).entity(Status.notFound("MessagingUser", userNameWithAddressSpace)).build(); - } - User existingUser = existing.get(); - - JsonNode source = MAPPER.valueToTree(existingUser); - - JsonNode patched = patcher.apply(source); - - User replacement = MAPPER.treeToValue(patched, User.class); - replacement = overrideNameAndNamespace(existingUser, replacement); - - if (!userApi.replaceUser(authenticationService, realm, replacement)) { - return Response.status(404).entity(Status.notFound("MessagingUser", replacement.getMetadata().getName())).build(); - } - return Response.ok().entity(replacement).build(); - }); - } - - private User overrideNameAndNamespace(User existingUser, User replacement) { - - return new UserBuilder(replacement) - - .editOrNewSpec() - .withUsername(existingUser.getSpec().getUsername()) - .endSpec() - - .editOrNewMetadata() - .withName(existingUser.getMetadata().getName()) - .withNamespace(existingUser.getMetadata().getNamespace()) - .endMetadata() - - .build(); - - } - - @DELETE - @Path("{userName}") - @Produces({MediaType.APPLICATION_JSON}) - public Response deleteUser(@Context SecurityContext securityContext, @PathParam("namespace") String namespace, @PathParam("userName") String userNameWithAddressSpace) throws Exception { - return doRequest("Error deleting user " + userNameWithAddressSpace, () -> { - verifyAuthorized(securityContext, namespace, ResourceVerb.delete); - - String addressSpaceName = parseAddressSpace(userNameWithAddressSpace); - checkAddressSpaceName(userNameWithAddressSpace, addressSpaceName); - AddressSpace addressSpace = addressSpaceApi.getAddressSpaceWithName(namespace, addressSpaceName).orElse(null); - if (addressSpace == null) { - return Response.status(404).entity(Status.notFound("AddressSpace", addressSpaceName)).build(); - } - - AuthenticationService authenticationService = authenticationServiceRegistry.findAuthenticationService(addressSpace.getSpec().getAuthenticationService()).orElse(null); - if (authenticationService == null) { - return Response.status(404).entity(Status.notFound("AuthenticationService", addressSpace.getSpec().getAuthenticationService().getName())).build(); - } - - String realm = getRealm(authenticationService, addressSpace); - - log.debug("Deleting user {} in realm {} namespace {}", userNameWithAddressSpace, realm, namespace); - User user = userApi.getUserWithName(authenticationService, realm, userNameWithAddressSpace).orElse(null); - if (user == null) { - return Response.status(404).entity(Status.notFound("MessagingUser", userNameWithAddressSpace)).build(); - } - userApi.deleteUser(authenticationService, realm, user); - return Response.ok(Status.successStatus(200)).build(); - }); - } - - @DELETE - @Produces({MediaType.APPLICATION_JSON}) - public Response deleteUsers(@Context SecurityContext securityContext, @PathParam("namespace") String namespace) throws Exception { - return doRequest("Error deleting address space s", () -> { - verifyAuthorized(securityContext, namespace, ResourceVerb.delete); - - Map authenticationServiceMap = new HashMap<>(); - Set addressSpaces = addressSpaceApi.listAddressSpaces(namespace); - for (AddressSpace addressSpace : addressSpaces) { - AuthenticationService authenticationService = authenticationServiceRegistry.findAuthenticationService(addressSpace.getSpec().getAuthenticationService()).orElse(null); - if (authenticationService == null) { - return Response.status(404).entity(Status.notFound("AuthenticationService", addressSpace.getSpec().getAuthenticationService().getName())).build(); - } - if (authenticationServiceMap.get(authenticationService.getMetadata().getName()) == null) { - authenticationServiceMap.put(authenticationService.getMetadata().getName(), authenticationService); - userApi.deleteUsers(authenticationService, namespace); - } - } - return Response.ok(Status.successStatus(200)).build(); - }); - } - - private static void checkAddressSpaceName(String pathName, String addressSpaceName) { - if (addressSpaceName.isEmpty()) { - throw new BadRequestException("The name of the object (" + pathName + ") is not valid"); - } - } - - private static void checkMatchingUserName(String userNameFromUrl, User input) { - if (input.getMetadata().getName() != null && !input.getMetadata().getName().equals(userNameFromUrl)) { - throw new BadRequestException("The name of the object (" + input.getMetadata().getName() + ") does not match the name on the URL (" + userNameFromUrl + ")"); - } - } - - private static final List tableColumnDefinitions = Arrays.asList( - new TableColumnDefinition("Name must be unique within a namespace.", - "name", - "Name", - 0, - "string"), - new TableColumnDefinition("User name used by clients.", - "", - "Username", - 0, - "string"), - new TableColumnDefinition("Authentication type.", - "", - "Type", - 1, - "string"), - new TableColumnDefinition("The timestamp representing server time when this user was created.", - "", - "Age", - 0, - "string")); - - static Object formatResponse(String headerParam, Instant now, UserList userList) { - if (isTableFormat(headerParam)) { - return new Table(new ListMeta(), tableColumnDefinitions, createRows(userList, now)); - } else { - return userList; - } - } - - static Object formatResponse(String headerParam, Instant now, User user) { - if (isTableFormat(headerParam)) { - UserList list = new UserList(); - list.getItems().add(user); - return new Table(new ListMeta(), tableColumnDefinitions, createRows(list, now)); - } else { - return user; - } - } - - static boolean isTableFormat(String acceptHeader) { - return acceptHeader != null && acceptHeader.contains("as=Table") && acceptHeader.contains("g=meta.k8s.io") && acceptHeader.contains("v=v1beta1"); - } - - static List createRows(UserList userList, Instant now) { - return userList.getItems().stream() - .map(user -> new TableRow( - Arrays.asList( - user.getMetadata().getName(), - user.getSpec().getUsername(), - user.getSpec().getAuthentication().getType().name(), - Optional.ofNullable(user.getMetadata().getCreationTimestamp()) - .map(s -> TimeUtil.formatHumanReadable(Duration.between(TimeUtil.parseRfc3339(s), now))) - .orElse("")), - new PartialObjectMetadata(new ObjectMetaBuilder() - .withNamespace(user.getMetadata().getNamespace()) - .withName(user.getMetadata().getName()) - .withCreationTimestamp(user.getMetadata().getCreationTimestamp()) - .withSelfLink(user.getMetadata().getSelfLink()) - .withResourceVersion(user.getMetadata().getResourceVersion()) - .build()))) - .collect(Collectors.toList()); - } - - public static Map parseLabelSelector(String labelSelector) { - Map labels = new HashMap<>(); - String [] pairs = labelSelector.split(","); - for (String pair : pairs) { - String elements[] = pair.split("="); - if (elements.length > 1) { - labels.put(elements[0], elements[1]); - } - } - return labels; - } -} diff --git a/api-server/src/main/java/io/enmasse/api/v1/http/SwaggerSpecEndpoint.java b/api-server/src/main/java/io/enmasse/api/v1/http/SwaggerSpecEndpoint.java deleted file mode 100644 index 477f1e33c2c..00000000000 --- a/api-server/src/main/java/io/enmasse/api/v1/http/SwaggerSpecEndpoint.java +++ /dev/null @@ -1,21 +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.api.v1.http; - -import java.io.InputStream; -import javax.ws.rs.GET; -import javax.ws.rs.Path; -import javax.ws.rs.Produces; -import javax.ws.rs.core.MediaType; - -@Path("swagger.json") -public class SwaggerSpecEndpoint { - - @GET - @Produces({MediaType.APPLICATION_JSON}) - public InputStream getOpenApiSpec() { - return getClass().getResourceAsStream("/swagger.json"); - } -} diff --git a/api-server/src/main/java/io/enmasse/api/v1/types/APIGroup.java b/api-server/src/main/java/io/enmasse/api/v1/types/APIGroup.java deleted file mode 100644 index 472cc0d45ed..00000000000 --- a/api-server/src/main/java/io/enmasse/api/v1/types/APIGroup.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.api.v1.types; - -import com.fasterxml.jackson.annotation.JsonInclude; -import com.fasterxml.jackson.annotation.JsonProperty; -import com.fasterxml.jackson.annotation.JsonPropertyOrder; - -import javax.validation.Valid; -import javax.validation.constraints.NotNull; -import java.util.List; - -@JsonInclude(JsonInclude.Include.NON_NULL) -@JsonPropertyOrder({ - "apiVersion", - "kind" -}) -public class APIGroup -{ - @NotNull - @JsonProperty("apiVersion") - private String apiVersion = "v1"; - - @NotNull - @JsonProperty("kind") - private String kind = "APIGroup"; - - @JsonProperty("name") - private String name; - - @JsonProperty("versions") - private List<@Valid APIGroupVersion> versions; - - @JsonProperty("preferredVersion") - @Valid - private APIGroupVersion preferredVersion; - - @JsonProperty("serverAddressByClientCIDRs") - private String serverAddressByClientCIDRs; - - public APIGroup(String name, List versions, APIGroupVersion preferredVersion, String serverAddressByClientCIDRs) { - this.name = name; - this.versions = versions; - this.preferredVersion = preferredVersion; - this.serverAddressByClientCIDRs = serverAddressByClientCIDRs; - } - - @JsonProperty("apiVersion") - public String getApiVersion() { - return apiVersion; - } - - @JsonProperty("kind") - public String getKind() { - return kind; - } - - @JsonProperty("name") - public String getName() { - return name; - } - - @JsonProperty("versions") - private List getVersions() { - return versions; - } - - @JsonProperty("preferredVersion") - private APIGroupVersion getPreferredVersion() { - return preferredVersion; - } - - @JsonProperty("serverAddressByClientCIDRs") - private String getServerAddressByClientCIDRs() { - return serverAddressByClientCIDRs; - } -} diff --git a/api-server/src/main/java/io/enmasse/api/v1/types/APIGroupList.java b/api-server/src/main/java/io/enmasse/api/v1/types/APIGroupList.java deleted file mode 100644 index e2960dc2cba..00000000000 --- a/api-server/src/main/java/io/enmasse/api/v1/types/APIGroupList.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.api.v1.types; - -import com.fasterxml.jackson.annotation.JsonInclude; -import com.fasterxml.jackson.annotation.JsonProperty; -import com.fasterxml.jackson.annotation.JsonPropertyOrder; - -import javax.validation.Valid; -import javax.validation.constraints.NotNull; -import java.util.List; - -@JsonInclude(JsonInclude.Include.NON_NULL) -@JsonPropertyOrder({ - "apiVersion", - "kind" -}) -public class APIGroupList { - @NotNull - @JsonProperty("apiVersion") - private String apiVersion = "v1"; - - @NotNull - @JsonProperty("kind") - private String kind = "APIGroupList"; - - @JsonProperty("groups") - private List<@Valid APIGroup> groups; - - public APIGroupList(List groups) { - this.groups = groups; - } - - @JsonProperty("apiVersion") - public String getApiVersion() { - return apiVersion; - } - - @JsonProperty("kind") - public String getKind() { - return kind; - } - - @JsonProperty("groups") - public List getGroups() { - return groups; - } -} diff --git a/api-server/src/main/java/io/enmasse/api/v1/types/APIGroupVersion.java b/api-server/src/main/java/io/enmasse/api/v1/types/APIGroupVersion.java deleted file mode 100644 index 3802b76833e..00000000000 --- a/api-server/src/main/java/io/enmasse/api/v1/types/APIGroupVersion.java +++ /dev/null @@ -1,32 +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.api.v1.types; - -import com.fasterxml.jackson.annotation.JsonInclude; -import com.fasterxml.jackson.annotation.JsonProperty; - -@JsonInclude(JsonInclude.Include.NON_NULL) -public class APIGroupVersion { - @JsonProperty("groupVersion") - private String groupVersion; - - @JsonProperty("version") - private String version; - - public APIGroupVersion(String groupVersion, String version) { - this.groupVersion = groupVersion; - this.version = version; - } - - @JsonProperty("groupVersion") - public String getGroupVersion() { - return groupVersion; - } - - @JsonProperty("version") - public String getVersion() { - return version; - } -} diff --git a/api-server/src/main/java/io/enmasse/api/v1/types/APIResource.java b/api-server/src/main/java/io/enmasse/api/v1/types/APIResource.java deleted file mode 100644 index 3a4947562c2..00000000000 --- a/api-server/src/main/java/io/enmasse/api/v1/types/APIResource.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.api.v1.types; - -import com.fasterxml.jackson.annotation.JsonInclude; -import com.fasterxml.jackson.annotation.JsonProperty; - -import java.util.List; - -@JsonInclude(JsonInclude.Include.NON_NULL) -public class APIResource -{ - @JsonProperty("name") - private String name; - - @JsonProperty("singularname") - private String singularname = ""; - - @JsonProperty("namespaced") - private boolean namespaced = false; - - @JsonProperty("kind") - private String kind; - - @JsonProperty("verbs") - private List verbs; - - public APIResource(String name, String singularname, boolean namespaced, String kind, List verbs) { - this.name = name; - this.singularname = singularname; - this.namespaced = namespaced; - this.kind = kind; - this.verbs = verbs; - } - - @JsonProperty("name") - public String getName() { - return name; - } - - @JsonProperty("singularname") - private String getSingularname() { - return singularname; - } - - @JsonProperty("namespaced") - private boolean getNamespaced() { - return namespaced; - } - - @JsonProperty("kind") - public String getKind() { - return kind; - } - - @JsonProperty("verbs") - private List getVerbs() { - return verbs; - } -} diff --git a/api-server/src/main/java/io/enmasse/api/v1/types/APIResourceList.java b/api-server/src/main/java/io/enmasse/api/v1/types/APIResourceList.java deleted file mode 100644 index 0be9e5e640a..00000000000 --- a/api-server/src/main/java/io/enmasse/api/v1/types/APIResourceList.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.api.v1.types; - -import com.fasterxml.jackson.annotation.JsonInclude; -import com.fasterxml.jackson.annotation.JsonProperty; -import com.fasterxml.jackson.annotation.JsonPropertyOrder; - -import javax.validation.Valid; -import javax.validation.constraints.NotNull; -import java.util.List; - -@JsonInclude(JsonInclude.Include.NON_NULL) -@JsonPropertyOrder({ - "apiVersion", - "kind" -}) -public class APIResourceList { - @NotNull - @JsonProperty("apiVersion") - private String apiVersion = "v1"; - - @NotNull - @JsonProperty("kind") - private String kind = "APIResourceList"; - - @JsonProperty("groupVersion") - private String groupVersion; - - @JsonProperty("resources") - private List<@Valid APIResource> resources; - - public APIResourceList(String groupVersion, List resources) { - this.groupVersion = groupVersion; - this.resources = resources; - } - - @JsonProperty("apiVersion") - public String getApiVersion() { - return apiVersion; - } - - @JsonProperty("kind") - public String getKind() { - return kind; - } - - @JsonProperty("groupVersion") - public String getGroupVersion() { - return groupVersion; - } - - @JsonProperty("resources") - private List getResources() { - return resources; - } -} diff --git a/api-server/src/main/resources/logback.xml b/api-server/src/main/resources/logback.xml deleted file mode 100644 index 9ed3476bbee..00000000000 --- a/api-server/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/api-server/src/test/java/io/enmasse/api/server/HTTPServerTest.java b/api-server/src/test/java/io/enmasse/api/server/HTTPServerTest.java deleted file mode 100644 index 4aab7ceae9f..00000000000 --- a/api-server/src/test/java/io/enmasse/api/server/HTTPServerTest.java +++ /dev/null @@ -1,300 +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.api.server; - -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.api.auth.ApiHeaderConfig; -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.metrics.api.Metrics; -import io.enmasse.user.api.UserApi; -import io.enmasse.user.model.v1.Operation; -import io.enmasse.user.model.v1.UserAuthenticationBuilder; -import io.enmasse.user.model.v1.UserAuthenticationType; -import io.enmasse.user.model.v1.UserAuthorizationBuilder; -import io.enmasse.user.model.v1.UserBuilder; -import io.enmasse.user.model.v1.UserList; -import io.enmasse.user.model.v1.UserSpecBuilder; -import io.fabric8.kubernetes.api.model.ObjectMetaBuilder; -import io.vertx.core.Future; -import io.vertx.core.Vertx; -import io.vertx.core.buffer.Buffer; -import io.vertx.core.http.HttpClient; -import io.vertx.core.http.HttpClientOptions; -import io.vertx.core.http.HttpClientRequest; -import io.vertx.core.http.HttpClientResponse; -import io.vertx.core.http.HttpMethod; -import io.vertx.core.json.JsonArray; -import io.vertx.core.json.JsonObject; -import io.vertx.junit5.Checkpoint; -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.junit.jupiter.params.ParameterizedTest; -import org.junit.jupiter.params.provider.MethodSource; - -import java.time.Clock; -import java.util.Arrays; -import java.util.Collections; -import java.util.Optional; -import java.util.concurrent.TimeUnit; -import java.util.function.BiConsumer; - -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertTrue; -import static org.mockito.ArgumentMatchers.any; -import static org.mockito.ArgumentMatchers.anyString; -import static org.mockito.ArgumentMatchers.eq; -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.when; - -@ExtendWith(VertxExtension.class) -@SuppressWarnings("deprecation") -public class HTTPServerTest { - - private Vertx vertx; - private TestAddressSpaceApi addressSpaceApi; - private AddressSpace addressSpace; - private HTTPServer server; - - public static String[] apiVersions() { - return new String[] {"v1alpha1", "v1beta1"}; - } - - @BeforeEach - public void setup(VertxTestContext context) throws Exception { - vertx = Vertx.vertx(); - addressSpaceApi = new TestAddressSpaceApi(); - addressSpace = createAddressSpace("ns", "myinstance"); - addressSpaceApi.createAddressSpace(addressSpace); - - 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)); - - UserApi userApi = mock(UserApi.class); - UserList users = new UserList(); - users.getItems().add(new UserBuilder() - .withMetadata(new ObjectMetaBuilder() - .withName("myinstance.user1") - .withNamespace("myinstance") - .build()) - .withSpec(new UserSpecBuilder() - .withUsername("user1") - .withAuthentication(new UserAuthenticationBuilder() - .withType(UserAuthenticationType.password) - .withPassword("admin") - .build()) - .withAuthorization(Arrays.asList(new UserAuthorizationBuilder() - .withAddresses(Arrays.asList("queue1")) - .withOperations(Arrays.asList(Operation.send, Operation.recv)) - .build())) - .build()) - .build()); - when(userApi.listUsers(any(), any())).thenReturn(users); - when(userApi.listAllUsers(any())).thenReturn(users); - - ApiServerOptions options = new ApiServerOptions(); - options.setVersion("1.0"); - options.setCertDir("/doesnotexist"); - - 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)); - when(authenticationServiceRegistry.listAuthenticationServices()).thenReturn(Collections.singletonList(authenticationService)); - - ResteasyDeploymentFactory resteasyDeploymentFactory = new ResteasyDeploymentFactory(addressSpaceApi, new TestSchemaProvider(), authApi, userApi, Clock.systemUTC(), authenticationServiceRegistry, ApiHeaderConfig.DEFAULT_HEADERS_CONFIG, new Metrics(), false); - this.server = new HTTPServer(options, resteasyDeploymentFactory, null, null, 0); - vertx.deployVerticle(this.server, context.succeeding(arg -> context.completeNow())); - } - - @AfterEach - public void teardown(VertxTestContext context) { - vertx.close(context.succeeding(arg -> context.completeNow())); - } - - private int port () { - return this.server.getActualPort(); - } - - private AddressSpace createAddressSpace(String namespace, String name) { - return new AddressSpaceBuilder() - - .withNewMetadata() - .withName(name) - .withNamespace(namespace) - .endMetadata() - - .withNewSpec() - .withType("type1") - .withPlan("myplan") - - .addNewEndpoint() - .withName("foo") - .withService("messaging") - .endEndpoint() - - .endSpec() - - .withNewStatus(false) - - .build(); - } - - private static HttpClientRequest putAuthzToken(HttpClientRequest request) { - request.putHeader("Authorization", "Bearer mytoken"); - return request; - } - - private void runSingleRequest(final VertxTestContext context, final String apiVersion, final HttpMethod method, final String uri, final JsonObject payload, final BiConsumer responseConsumer) throws Throwable { - final HttpClient client = vertx.createHttpClient(); - - try { - - HttpClientRequest req = client.request(method, port(), "localhost", uri, response -> { - response.bodyHandler(buffer -> { - responseConsumer.accept(response, buffer); - if(!context.completed()) { - context.completeNow(); - } - }); - }); - req.putHeader("Content-Type", "application/json"); - putAuthzToken(req); - - if ( payload != null ) { - req.end(payload.toBuffer()); - } else { - req.end(); - } - - assertTrue(context.awaitCompletion(60, TimeUnit.SECONDS)); - if (context.failed()) { - throw context.causeOfFailure(); - } - - } finally { - client.close(); - } - } - - @ParameterizedTest - @MethodSource("apiVersions") - public void testApiResources(String apiVersion, VertxTestContext context) throws Throwable { - - final Checkpoint checkpoint = context.checkpoint(1); - HttpClient client = vertx.createHttpClient(); - - try { - - Future f = Future.succeededFuture(); - - f = f.compose(v -> { - Future next = Future.future(); - - HttpClientRequest rootReq = client.get(port(), "localhost", "/apis/user.enmasse.io/" + apiVersion, response -> { - next.tryComplete(); - context.verify(() -> assertEquals(200, response.statusCode())); - response.bodyHandler(buffer -> { - JsonObject data = buffer.toJsonObject(); - context.verify(() -> assertTrue(data.containsKey("resources"))); - JsonArray resources = data.getJsonArray("resources"); - context.verify(() -> assertEquals(1, resources.size())); - checkpoint.flag(); - }); - }); - putAuthzToken(rootReq); - rootReq.end(); - - return next; - }); - - assertTrue(context.awaitCompletion(60, TimeUnit.SECONDS)); - if (context.failed()) { - throw context.causeOfFailure(); - } - } finally { - client.close(); - } - } - - @ParameterizedTest - @MethodSource("apiVersions") - public void testUserApi(String apiVersion, VertxTestContext context) throws Throwable { - - runSingleRequest(context, apiVersion, HttpMethod.GET, "/apis/user.enmasse.io/" + apiVersion + "/namespaces/ns/messagingusers", null, (response, buffer) -> { - context.verify(() -> { - assertEquals(200, response.statusCode()); - JsonObject data = buffer.toJsonObject(); - assertTrue(data.containsKey("items")); - assertEquals(1, data.getJsonArray("items").size()); - assertEquals("myinstance.user1", data.getJsonArray("items").getJsonObject(0).getJsonObject("metadata").getString("name")); - }); - }); - - } - - @ParameterizedTest - @MethodSource("apiVersions") - public void testUserApiNonNamespaces(String apiVersion, VertxTestContext context) throws Throwable { - - runSingleRequest(context, apiVersion, HttpMethod.GET, "/apis/user.enmasse.io/" + apiVersion + "/messagingusers", null, (response, buffer) -> { - context.verify(() -> { - assertEquals(200, response.statusCode()); - JsonObject data = buffer.toJsonObject(); - assertTrue(data.containsKey("items")); - assertEquals(1, data.getJsonArray("items").size()); - assertEquals("myinstance.user1", data.getJsonArray("items").getJsonObject(0).getJsonObject("metadata").getString("name")); - }); - }); - - } - - @Test - public void testOpenApiSpec(VertxTestContext context) throws Throwable { - HttpClientOptions options = new HttpClientOptions(); - HttpClient client = vertx.createHttpClient(options); - try { - HttpClientRequest request = client.get(port(), "localhost", "/swagger.json", response -> { - response.bodyHandler(buffer -> { - JsonObject data = buffer.toJsonObject(); - context.verify(() -> assertTrue(data.containsKey("paths"))); - context.completeNow(); - }); - }); - putAuthzToken(request); - request.end(); - - assertTrue(context.awaitCompletion(60, TimeUnit.SECONDS)); - if (context.failed()) { - throw context.causeOfFailure(); - } - } finally { - client.close(); - } - } -} diff --git a/api-server/src/test/java/io/enmasse/api/server/TestSchemaProvider.java b/api-server/src/test/java/io/enmasse/api/server/TestSchemaProvider.java deleted file mode 100644 index 90e4862b70d..00000000000 --- a/api-server/src/test/java/io/enmasse/api/server/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.api.server; - -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/api-server/src/test/java/io/enmasse/api/v1/http/HttpUserServiceTest.java b/api-server/src/test/java/io/enmasse/api/v1/http/HttpUserServiceTest.java deleted file mode 100644 index a49e1c33c52..00000000000 --- a/api-server/src/test/java/io/enmasse/api/v1/http/HttpUserServiceTest.java +++ /dev/null @@ -1,454 +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.api.v1.http; - -import static org.hamcrest.CoreMatchers.hasItem; -import static org.hamcrest.CoreMatchers.is; -import static org.hamcrest.MatcherAssert.assertThat; -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.Mockito.mock; -import static org.mockito.Mockito.when; - -import java.time.Clock; -import java.util.Arrays; -import java.util.Collections; -import java.util.Optional; -import java.util.concurrent.Callable; - -import javax.ws.rs.core.Response; -import javax.ws.rs.core.SecurityContext; - -import com.fasterxml.jackson.databind.ObjectMapper; -import com.github.fge.jsonpatch.JsonPatch; -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.AuthenticationServiceRegistry; -import org.jboss.resteasy.spi.ResteasyUriInfo; -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Test; - -import io.enmasse.address.model.AddressSpaceBuilder; -import io.enmasse.api.common.DefaultExceptionMapper; -import io.enmasse.api.common.Status; -import io.enmasse.config.AnnotationKeys; -import io.enmasse.k8s.api.TestAddressSpaceApi; -import io.enmasse.k8s.model.v1beta1.Table; -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.UserAuthorizationBuilder; -import io.enmasse.user.model.v1.UserBuilder; -import io.enmasse.user.model.v1.UserList; -import io.enmasse.user.model.v1.UserSpecBuilder; - -public class HttpUserServiceTest { - private ObjectMapper mapper = new ObjectMapper(); - private HttpUserService userService; - private TestAddressSpaceApi addressSpaceApi; - private TestUserApi userApi; - private User u1; - private User u2; - private DefaultExceptionMapper exceptionMapper = new DefaultExceptionMapper(); - private SecurityContext securityContext; - - @BeforeEach - public void setup() { - addressSpaceApi = new TestAddressSpaceApi(); - userApi = new TestUserApi(); - securityContext = mock(SecurityContext.class); - when(securityContext.isUserInRole(any())).thenReturn(true); - - AuthenticationServiceRegistry authenticationServiceRegistry = mock(AuthenticationServiceRegistry.class); - AuthenticationService authenticationService = new AuthenticationServiceBuilder() - .withNewMetadata() - .withName("standard") - .endMetadata() - .withNewSpec() - .withType(AuthenticationServiceType.standard) - .endSpec() - .withNewStatus() - .withHost("example") - .withPort(5671) - .endStatus() - .build(); - when(authenticationServiceRegistry.findAuthenticationService(any())).thenReturn(Optional.of(authenticationService)); - when(authenticationServiceRegistry.resolveDefaultAuthenticationService()).thenReturn(Optional.of(authenticationService)); - when(authenticationServiceRegistry.listAuthenticationServices()).thenReturn(Collections.singletonList(authenticationService)); - - this.userService = new HttpUserService(addressSpaceApi, userApi, authenticationServiceRegistry, Clock.systemUTC()); - addressSpaceApi.createAddressSpace(new AddressSpaceBuilder() - .withNewMetadata() - .withName("myspace") - .withNamespace("ns1") - .addToAnnotations(AnnotationKeys.REALM_NAME, "r1") - .endMetadata() - - .withNewSpec() - .withType("type1") - .withPlan("myplan") - .endSpec() - - .build()); - - addressSpaceApi.createAddressSpace(new AddressSpaceBuilder() - .withNewMetadata() - .withName("otherspace") - .withNamespace("ns2") - .addToAnnotations(AnnotationKeys.REALM_NAME, "r2") - .endMetadata() - - .withNewSpec() - .withType("type1") - .withPlan("myplan") - .endSpec() - .build()); - - u1 = new UserBuilder() - .withNewMetadata() - .withName("myspace.user1") - .withNamespace("ns1") - .endMetadata() - - .withSpec(new UserSpecBuilder() - .withUsername("user1") - .withAuthentication(new UserAuthenticationBuilder() - .withType(UserAuthenticationType.password) - .withPassword("p4ssw0rd") - .build()) - .withAuthorization(Arrays.asList( - new UserAuthorizationBuilder() - .withAddresses(Arrays.asList("queue1", "topic1")) - .withOperations(Arrays.asList(Operation.send, Operation.recv)) - .build(), - new UserAuthorizationBuilder() - .withAddresses(Arrays.asList("direct*")) - .withOperations(Arrays.asList(Operation.view)) - .build())) - .build()) - .build(); - - u2 = new UserBuilder() - .withNewMetadata() - .withName("otherspace.user2") - .withNamespace("ns2") - .endMetadata() - - .withSpec(new UserSpecBuilder() - .withUsername("user2") - .withAuthentication(new UserAuthenticationBuilder() - .withType(UserAuthenticationType.password) - .withPassword("pswd") - .build()) - .withAuthorization(Arrays.asList( - new UserAuthorizationBuilder() - .withOperations(Arrays.asList(Operation.manage)) - .build())) - .build()) - .build(); - - userApi.createUser(authenticationService, "r1", u1); - userApi.createUser(authenticationService, "r2", u2); - } - - private Response invoke(Callable fn) { - try { - return fn.call(); - } catch (Exception e) { - return exceptionMapper.toResponse(e); - } - } - - @Test - public void testList() { - Response response = invoke(() -> userService.getUserList(securityContext, null, "ns1", null)); - - assertThat(response.getStatus(), is(200)); - UserList list = (UserList) response.getEntity(); - - assertThat(list.getItems().size(), is(1)); - assertThat(list.getItems(), hasItem(u1)); - } - - @Test - public void testListTable() { - Response response = invoke(() -> userService.getUserList(securityContext, "application/json;as=Table;g=meta.k8s.io;v=v1beta1", "ns1", null)); - - assertThat(response.getStatus(), is(200)); - Table table = (Table) response.getEntity(); - - assertThat(table.getColumnDefinitions().size(), is(4)); - assertThat(table.getRows().size(), is(1)); - } - - @Test - public void testGetByUser() { - Response response = invoke(() -> userService.getUser(securityContext, null, "ns1", "myspace.user1")); - - assertThat(response.getStatus(), is(200)); - User user = (User) response.getEntity(); - - assertThat(user, is(u1)); - } - - @Test - public void testGetByUserTable() { - Response response = invoke(() -> userService.getUser(securityContext, "application/json;as=Table;g=meta.k8s.io;v=v1beta1", "ns1", "myspace.user1")); - - Table table = (Table) response.getEntity(); - - assertThat(response.getStatus(), is(200)); - assertThat(table.getColumnDefinitions().size(), is(4)); - assertThat(table.getRows().get(0).getObject().getMetadata().getName(), is(u1.getMetadata().getName())); - } - - @Test - public void testGetByUserNotFound() { - Response response = invoke(() -> userService.getUser(securityContext, null, "ns1", "myspace.user2")); - - assertThat(response.getStatus(), is(404)); - } - - @Test - public void testListException() { - userApi.throwException = true; - Response response = invoke(() -> userService.getUserList(securityContext, null, "ns1", null)); - assertThat(response.getStatus(), is(500)); - } - - @Test - public void testGetException() { - userApi.throwException = true; - Response response = invoke(() -> userService.getUser(securityContext, null, "ns1", "myspace.user1")); - assertThat(response.getStatus(), is(500)); - } - - @Test - public void testCreate() { - User u3 = new UserBuilder() - .withNewMetadata() - .withName("myspace.user3") - .withNamespace("ns1") - .endMetadata() - - .withSpec(new UserSpecBuilder() - .withUsername("user3") - .withAuthentication(new UserAuthenticationBuilder() - .withType(UserAuthenticationType.password) - .withPassword("p4ssw0rd") - .build()) - .withAuthorization(Arrays.asList( - new UserAuthorizationBuilder() - .withAddresses(Arrays.asList("queue1", "topic1")) - .withOperations(Arrays.asList(Operation.send, Operation.recv)) - .build())) - .build()) - .build(); - Response response = invoke(() -> userService.createUser(securityContext, new ResteasyUriInfo("http://localhost:8443/", null, "/"), "ns1", u3)); - assertThat(response.getStatus(), is(201)); - - assertThat(userApi.listUsers(null, "ns1").getItems(), hasItem(u3)); - } - - @Test - public void testCreateException() { - userApi.throwException = true; - User u3 = new UserBuilder() - .withNewMetadata() - .withName("myspace.user3") - .withNamespace("ns1") - .endMetadata() - - .withSpec(new UserSpecBuilder() - .withUsername("user3") - .withAuthentication(new UserAuthenticationBuilder() - .withType(UserAuthenticationType.password) - .withPassword("p4ssw0rd") - .build()) - .withAuthorization(Arrays.asList( - new UserAuthorizationBuilder() - .withAddresses(Arrays.asList("queue1", "topic1")) - .withOperations(Arrays.asList(Operation.send, Operation.recv)) - .build())) - .build()) - .build(); - Response response = invoke(() -> userService.createUser(securityContext, null, "ns1", u3)); - assertThat(response.getStatus(), is(500)); - } - - @Test - public void testPutException() { - userApi.throwException = true; - User u3 = new UserBuilder() - .withNewMetadata() - .withName("myspace.user1") - .withNamespace("ns1") - .endMetadata() - - .withSpec(new UserSpecBuilder() - .withUsername("user1") - .withAuthentication(new UserAuthenticationBuilder() - .withType(UserAuthenticationType.password) - .withPassword("p4ssw0rd") - .build()) - .withAuthorization(Arrays.asList( - new UserAuthorizationBuilder() - .withAddresses(Arrays.asList("queue1", "topic1")) - .withOperations(Arrays.asList(Operation.send, Operation.recv)) - .build())) - .build()) - .build(); - Response response = invoke(() -> userService.replaceUser(securityContext, "ns1", "myspace.user1", u3)); - assertThat(response.getStatus(), is(500)); - } - - @Test - public void testPutNotMatchingName() { - User u3 = new UserBuilder() - .withNewMetadata() - .withName("myspace.user3") - .withNamespace("ns1") - .endMetadata() - - .withSpec(new UserSpecBuilder() - .withUsername("user3") - .withAuthentication(new UserAuthenticationBuilder() - .withType(UserAuthenticationType.password) - .withPassword("p4ssw0rd") - .build()) - .withAuthorization(Arrays.asList( - new UserAuthorizationBuilder() - .withAddresses(Arrays.asList("queue1", "topic1")) - .withOperations(Arrays.asList(Operation.send, Operation.recv)) - .build())) - .build()) - .build(); - Response response = invoke(() -> userService.replaceUser(securityContext, "ns1", "myspace.user1", u3)); - assertThat(response.getStatus(), is(400)); - } - - @Test - public void testPutNotExists() { - User u3 = new UserBuilder() - .withNewMetadata() - .withName("myspace.user3") - .withNamespace("ns1") - .endMetadata() - - .withSpec(new UserSpecBuilder() - .withUsername("user3") - .withAuthentication(new UserAuthenticationBuilder() - .withType(UserAuthenticationType.password) - .withPassword("p4ssw0rd") - .build()) - .withAuthorization(Arrays.asList( - new UserAuthorizationBuilder() - .withAddresses(Arrays.asList("queue1", "topic1")) - .withOperations(Arrays.asList(Operation.send, Operation.recv)) - .build())) - .build()) - .build(); - Response response = invoke(() -> userService.replaceUser(securityContext, "ns1", "myspace.user3", u3)); - assertThat(response.getStatus(), is(404)); - } - - @Test - public void testPut() { - User u3 = new UserBuilder() - .withNewMetadata() - .withName("myspace.user1") - .withNamespace("ns1") - .endMetadata() - - .withSpec(new UserSpecBuilder() - .withUsername("user1") - .withAuthentication(new UserAuthenticationBuilder() - .withType(UserAuthenticationType.password) - .withPassword("p4ssw0rd") - .build()) - .withAuthorization(Arrays.asList( - new UserAuthorizationBuilder() - .withAddresses(Arrays.asList("queue2", "topic2")) - .withOperations(Arrays.asList(Operation.send, Operation.recv)) - .build())) - .build()) - .build(); - Response response = invoke(() -> userService.replaceUser(securityContext, "ns1", "myspace.user1", u3)); - assertThat(response.getStatus(), is(200)); - } - - @Test - public void testDelete() { - Response response = invoke(() -> userService.deleteUser(securityContext, "ns1", "myspace.user1")); - assertThat(response.getStatus(), is(200)); - assertThat(((Status) response.getEntity()).getStatusCode(), is(200)); - - assertTrue(userApi.listUsers(null, "ns1").getItems().isEmpty()); - assertFalse(userApi.listUsers(null, "ns2").getItems().isEmpty()); - } - - @Test - public void testDeleteException() { - userApi.throwException = true; - Response response = invoke(() -> userService.deleteUser(securityContext, "ns1", "myspace.user1")); - assertThat(response.getStatus(), is(500)); - } - - @Test - public void deleteAllUsers() { - Response response = invoke(() -> userService.deleteUsers(securityContext, "unknown")); - assertThat(response.getStatus(), is(200)); - assertThat(userApi.listUsers(null, "ns1").getItems().size(), is(1)); - - response = invoke(() -> userService.deleteUsers(securityContext, "ns1")); - assertThat(response.getStatus(), is(200)); - assertThat(userApi.listUsers(null, "ns1").getItems().size(), is(0)); - } - - @Test - public void jsonPatch_RFC6902() throws Exception { - final JsonPatch patch = mapper.readValue("[{\"op\":\"add\",\"path\":\"/spec/authentication/password\",\"value\":\"dorado\"}]", JsonPatch.class); - - Response response = userService.patchUser(securityContext, u1.getMetadata().getNamespace(), u1.getMetadata().getName(), patch); - assertThat(response.getStatus(), is(200)); - assertThat(((User) response.getEntity()).getSpec().getAuthentication().getPassword(), is("dorado")); - } - - @Test - public void jsonMergePatch_RFC7386() throws Exception { - final JsonPatch patch = mapper.readValue("[{\"op\":\"add\",\"path\":\"/spec/authentication/password\",\"value\":\"dorado\"}]", JsonPatch.class); - - Response response = userService.patchUser(securityContext, u1.getMetadata().getNamespace(), u1.getMetadata().getName(), patch); - assertThat(response.getStatus(), is(200)); - assertThat(((User) response.getEntity()).getSpec().getAuthentication().getPassword(), is("dorado")); - } - - @Test - public void patchUserNotFound() throws Exception { - final JsonPatch patch = mapper.readValue("[{\"op\":\"add\",\"path\":\"/metadata/annotations/fish\",\"value\":\"dorado\"}]", JsonPatch.class); - - Response response = userService.patchUser(securityContext, "myns", "myspace.unknown", patch); - assertThat(response.getStatus(), is(404)); - } - - @Test - public void patchImmutable() throws Exception { - - final JsonPatch patch = mapper.readValue("[" + - "{\"op\":\"replace\",\"path\":\"/metadata/name\",\"value\":\"newname\"}," + - "{\"op\":\"replace\",\"path\":\"/metadata/namespace\",\"value\":\"newnamespace\"}" + - "]", JsonPatch.class); - - Response response = userService.patchUser(securityContext, u1.getMetadata().getNamespace(), u1.getMetadata().getName(), patch); - assertThat(response.getStatus(), is(200)); - User updated = (User) response.getEntity(); - assertThat(updated.getMetadata().getName(), is(u1.getMetadata().getName())); - assertThat(updated.getMetadata().getNamespace(), is(u1.getMetadata().getNamespace())); - } - -} diff --git a/api-server/src/test/java/io/enmasse/api/v1/http/TestUserApi.java b/api-server/src/test/java/io/enmasse/api/v1/http/TestUserApi.java deleted file mode 100644 index 4d342d4b4ec..00000000000 --- a/api-server/src/test/java/io/enmasse/api/v1/http/TestUserApi.java +++ /dev/null @@ -1,134 +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.api.v1.http; - -import io.enmasse.admin.model.v1.AuthenticationService; -import io.enmasse.user.api.UserApi; -import io.enmasse.user.model.v1.User; -import io.enmasse.user.model.v1.UserList; - -import java.util.ArrayList; -import java.util.HashMap; -import java.util.Map; -import java.util.Optional; - -public class TestUserApi implements UserApi { - private final Map> userMap = new HashMap<>(); - public boolean throwException = false; - - @Override - public boolean isAvailable(AuthenticationService authenticationService) { - return true; - } - - @Override - public Optional getUserWithName(AuthenticationService authenticationService, String realm, String name) { - if (throwException) { - throw new RuntimeException("exception"); - } - return userMap.get(realm).values().stream() - .filter(user -> user.getMetadata().getName().equals(name)) - .findFirst(); - } - - @Override - public void createUser(AuthenticationService authenticationService, String realm, User user) { - if (throwException) { - throw new RuntimeException("exception"); - } - String name = user.getSpec().getUsername(); - userMap.computeIfAbsent(realm, k -> new HashMap<>()).put(name, user); - - } - - @Override - public boolean replaceUser(AuthenticationService authenticationService, String realm, User user) { - if (throwException) { - throw new RuntimeException("exception"); - } - - String name = user.getSpec().getUsername(); - Map users = userMap.computeIfAbsent(realm, k -> new HashMap<>()); - if (!users.containsKey(name)) { - return false; - } - users.put(name, user); - return true; - } - - @Override - public void deleteUser(AuthenticationService authenticationService, String realm, User user) { - if (throwException) { - throw new RuntimeException("exception"); - } - Map m = userMap.get(realm); - if (m != null) { - m.remove(user.getSpec().getUsername()); - } - } - - @Override - public boolean realmExists(AuthenticationService authenticationService, String realm) { - return userMap.containsKey(realm); - } - - @Override - public UserList listUsers(AuthenticationService authenticationService, String namespace) { - if (throwException) { - throw new RuntimeException("exception"); - } - UserList list = new UserList(); - for (Map users : userMap.values()) { - for (User user : users.values()) { - if (user.getMetadata().getNamespace().equals(namespace)) { - list.getItems().add(user); - } - } - } - return list; - } - - @Override - public UserList listUsersWithLabels(AuthenticationService authenticationService, String namespace, Map labels) { - if (throwException) { - throw new RuntimeException("exception"); - } - return listUsers(authenticationService, namespace); - } - - @Override - public UserList listAllUsers(AuthenticationService authenticationService) { - if (throwException) { - throw new RuntimeException("exception"); - } - UserList list = new UserList(); - for (Map users : userMap.values()) { - list.getItems().addAll(users.values()); - } - return list; - } - - @Override - public UserList listAllUsersWithLabels(AuthenticationService authenticationService, Map labels) { - if (throwException) { - throw new RuntimeException("exception"); - } - return listAllUsers(authenticationService); - } - - @Override - public void deleteUsers(AuthenticationService authenticationService, String namespace) { - if (throwException) { - throw new RuntimeException("exception"); - } - for (Map users : userMap.values()) { - for (User user : new ArrayList<>(users.values())) { - if (user.getMetadata().getNamespace().equals(namespace)) { - users.remove(user.getSpec().getUsername()); - } - } - } - } -} diff --git a/api-server/src/test/resources/logback-test.xml b/api-server/src/test/resources/logback-test.xml deleted file mode 100644 index 0037f119770..00000000000 --- a/api-server/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/Makefile b/documentation/Makefile index 00749308e27..434a61b5f35 100644 --- a/documentation/Makefile +++ b/documentation/Makefile @@ -6,7 +6,8 @@ ASCIIDOCTOR_FLAGS ?= -v -a EnMasseVersion=$(VERSION) -t -dbook $(ASCIIDOCT .PHONY: swagger swagger: build/swagger2markup.jar - java -jar build/swagger2markup.jar convert -i $(TOPDIR)/api-server/target/classes/swagger.json -f common/restapi-reference + mvn process-resources + java -jar build/swagger2markup.jar convert -i target/classes/swagger.json -f common/restapi-reference build/swagger2markup.jar: mkdir -p build diff --git a/documentation/assemblies/assembly-installing-manual-steps.adoc b/documentation/assemblies/assembly-installing-manual-steps.adoc deleted file mode 100644 index 7d8d5c63e80..00000000000 --- a/documentation/assemblies/assembly-installing-manual-steps.adoc +++ /dev/null @@ -1,22 +0,0 @@ -// This assembly is included in the following assemblies: -// -// assembly-installing.adoc -// Commented out because this file is an assembly within an assembly: - -[id='installing-using-manual-steps-{context}'] -= Installing {ProductName} manually - -The manual deployment procedure can be performed on any platform -supporting the {KubePlatform} client. - -include::../modules/proc-creating-project.adoc[leveloffset=+1] - -include::../modules/proc-deploying-example-authservice.adoc[leveloffset=+1] - -include::../modules/proc-deploying-address-space-controller.adoc[leveloffset=+1] - -include::../modules/proc-deploying-example-plans.adoc[leveloffset=+1] - -include::../modules/proc-deploying-api-server.adoc[leveloffset=+1] - -include::../modules/proc-deploying-service-broker.adoc[leveloffset=+1] diff --git a/documentation/pom.xml b/documentation/pom.xml new file mode 100644 index 00000000000..6e90d9524c3 --- /dev/null +++ b/documentation/pom.xml @@ -0,0 +1,19 @@ + + + + io.enmasse + enmasse + 0.31-SNAPSHOT + + 4.0.0 + documentation + + + + src/main/resources + true + + + + diff --git a/api-server/src/main/resources/swagger.json b/documentation/src/main/resources/swagger.json similarity index 100% rename from api-server/src/main/resources/swagger.json rename to documentation/src/main/resources/swagger.json diff --git a/pom.xml b/pom.xml index a0e294eb7af..a259514c6f7 100644 --- a/pom.xml +++ b/pom.xml @@ -111,6 +111,7 @@ api-model metrics-api keycloak-user-api + documentation agent k8s-api k8s-api-testutil @@ -123,7 +124,6 @@ mqtt-lwt standard-controller address-space-controller - api-server keycloak-plugin systemtests broker-plugin 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 d8db98e42cb..48f4aaf24cb 100644 --- a/systemtests/src/main/java/io/enmasse/systemtest/logs/GlobalLogCollector.java +++ b/systemtests/src/main/java/io/enmasse/systemtest/logs/GlobalLogCollector.java @@ -232,11 +232,6 @@ private void collectRouterInfo(Pod pod, Optional container, String files } } - public void collectApiServerJmapLog() { - log.info("Collecting jmap from api server"); - kubernetes.listPods(Collections.singletonMap("component", "api-server")).forEach(this::collectJmap); - } - private void collectJmap(Pod pod) { KubeCMDClient.runOnPod( pod.getMetadata().getNamespace(), diff --git a/systemtests/src/main/java/io/enmasse/systemtest/operator/OperatorManager.java b/systemtests/src/main/java/io/enmasse/systemtest/operator/OperatorManager.java index f47ab8a1977..3760dce4c67 100644 --- a/systemtests/src/main/java/io/enmasse/systemtest/operator/OperatorManager.java +++ b/systemtests/src/main/java/io/enmasse/systemtest/operator/OperatorManager.java @@ -114,18 +114,6 @@ public void deleteEnmasseOlm() throws Exception { public void installOperators() throws Exception { LOGGER.info("Installing enmasse operators from: {}", Environment.getInstance().getTemplatesPath()); kube.createNamespace(kube.getInfraNamespace(), Collections.singletonMap("allowed", "true")); - if (kube instanceof Minikube) { - CertBundle apiServertCert = CertificateUtils.createCertBundle("api-server." + kube.getInfraNamespace() + ".svc.cluster.local"); - Secret secret = new SecretBuilder() - .editOrNewMetadata() - .withName("api-server-cert") - .endMetadata() - .addToData("tls.key", apiServertCert.getKeyB64()) - .addToData("tls.crt", apiServertCert.getCertB64()) - - .build(); - kube.createSecret(kube.getInfraNamespace(), secret); - } KubeCMDClient.applyFromFile(kube.getInfraNamespace(), Paths.get(Environment.getInstance().getTemplatesPath(), "install", "bundles", productName)); } 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 a9bb3730277..1dc202e7890 100644 --- a/systemtests/src/main/java/io/enmasse/systemtest/platform/OpenShift.java +++ b/systemtests/src/main/java/io/enmasse/systemtest/platform/OpenShift.java @@ -64,8 +64,7 @@ public Endpoint getRestEndpoint() { if (TestUtils.resolvable(endpoint)) { return endpoint; } else { - log.info("Route endpoint didn't resolve, falling back to service endpoint"); - return getEndpoint("api-server", infraNamespace, "https"); + throw new IllegalStateExeception("Unable to get REST endpoint") } } diff --git a/systemtests/src/test/java/io/enmasse/systemtest/isolated/CommonTest.java b/systemtests/src/test/java/io/enmasse/systemtest/isolated/CommonTest.java index 3365bb75da9..463a8f1515a 100644 --- a/systemtests/src/test/java/io/enmasse/systemtest/isolated/CommonTest.java +++ b/systemtests/src/test/java/io/enmasse/systemtest/isolated/CommonTest.java @@ -357,7 +357,6 @@ void testMonitoringTools() throws Exception { @Tag(NON_PR) void testMessagingDuringRestartComponents() throws Exception { List