package org.cloudfoundry.community.servicebroker.postgresql; import com.google.common.collect.ImmutableMap; import com.jayway.restassured.http.ContentType; import com.jayway.restassured.response.ValidatableResponse; import org.apache.http.HttpStatus; import org.cloudfoundry.community.servicebroker.ServiceBrokerV2IntegrationTestBase; import org.cloudfoundry.community.servicebroker.model.Plan; import org.cloudfoundry.community.servicebroker.model.ServiceDefinition; import org.cloudfoundry.community.servicebroker.postgresql.config.Application; import org.cloudfoundry.community.servicebroker.postgresql.config.BrokerConfiguration; import org.cloudfoundry.community.servicebroker.postgresql.service.PostgreSQLDatabase; import org.junit.Before; import org.junit.FixMethodOrder; import org.junit.Test; import org.junit.runners.MethodSorters; import org.springframework.beans.factory.annotation.Value; import org.springframework.boot.test.SpringApplicationConfiguration; import java.sql.Connection; import java.sql.DatabaseMetaData; import java.sql.DriverManager; import java.sql.ResultSet; import java.util.ArrayList; import java.util.List; import java.util.Map; import static com.jayway.restassured.RestAssured.given; import static org.hamcrest.CoreMatchers.*; import static org.junit.Assert.*; @FixMethodOrder(MethodSorters.NAME_ASCENDING) @SpringApplicationConfiguration(classes = Application.class) public class PostgreSQLServiceBrokerV2IntegrationTests extends ServiceBrokerV2IntegrationTestBase { @Value("${MASTER_JDBC_URL}") private String jdbcUrl; private Connection conn; @Override @Before public void setUp() throws Exception { super.setUp(); conn = DriverManager.getConnection(this.jdbcUrl); } /** * Sanity check, to make sure that the 'service' table required for this Service Broker is created. */ @Test public void case0_checkThatServiceTableIsCreated() throws Exception { assertTrue(checkTableExists("service")); } /** * cf marketplace * cf create-service-broker * <p> * Fetch Catalog (GET /v2/catalog) */ @Override @Test public void case1_fetchCatalogSucceedsWithCredentials() throws Exception { // same as super code, but we need the response here ValidatableResponse response = given().auth().basic(username, password).header(apiVersionHeader).when().get(fetchCatalogPath).then().statusCode(HttpStatus.SC_OK); BrokerConfiguration brokerConfiguration = new BrokerConfiguration(); ServiceDefinition serviceDefinition = brokerConfiguration.catalog().getServiceDefinitions().get(0); response.body("services[0].id", equalTo(serviceDefinition.getId())); response.body("services[0].name", equalTo(serviceDefinition.getName())); response.body("services[0].description", equalTo(serviceDefinition.getDescription())); response.body("services[0].requires", equalTo(serviceDefinition.getRequires())); response.body("services[0].tags", equalTo(serviceDefinition.getTags())); List<String> planIds = new ArrayList<String>(); for(Plan plan: serviceDefinition.getPlans()) { planIds.add(plan.getId()); } response.body("services[0].plans.id", equalTo(planIds)); } /** * cf create-service * <p> * Provision Instance (PUT /v2/service_instances/:id) */ @Override @Test public void case2_provisionInstanceSucceedsWithCredentials() throws Exception { super.case2_provisionInstanceSucceedsWithCredentials(); assertTrue(checkDatabaseExists(instanceId)); assertTrue(checkRoleExists(instanceId)); assertTrue(checkRoleIsDatabaseOwner(instanceId, instanceId)); Map<String, String> serviceResult = PostgreSQLDatabase.executePreparedSelect("SELECT * FROM service WHERE serviceinstanceid = ?", ImmutableMap.of(1, instanceId)); assertThat(serviceResult.get("organizationguid"), is(organizationGuid)); assertThat(serviceResult.get("planid"), is(planId)); assertThat(serviceResult.get("spaceguid"), is(spaceGuid)); assertThat(serviceResult.get("servicedefinitionid"), is(serviceId)); assertThat(serviceResult.get("serviceinstanceid"), is(instanceId)); } /** * cf bind-service * <p> * Create Binding (PUT /v2/service_instances/:instance_id/service_bindings/:id) */ @Override @Test public void case3_createBindingSucceedsWithCredentials() throws Exception { // same as super code, but we need the response here String createBindingPath = String.format(createOrRemoveBindingBasePath, instanceId, serviceId); String request_body = "{\n" + " \"plan_id\": \"" + planId + "\",\n" + " \"service_id\": \"" + serviceId + "\",\n" + " \"app_guid\": \"" + appGuid + "\"\n" + "}"; ValidatableResponse response = given().auth().basic(username, password).header(apiVersionHeader).request().contentType(ContentType.JSON).body(request_body).when().put(createBindingPath).then().statusCode(HttpStatus.SC_CREATED); response.body("credentials.uri", containsString("postgres://" + instanceId)); response.body("syslog_drain_url", is(nullValue())); } /** * cf unbind-service * <p> * Remove Binding (DELETE /v2/service_instances/:instance_id/service_bindings/:id) */ @Override @Test public void case4_removeBindingSucceedsWithCredentials() throws Exception { // same as super code, but we need the response here String removeBindingPath = String.format(createOrRemoveBindingBasePath, instanceId, serviceId) + "?service_id=" + serviceId + "&plan_id=" + planId; ValidatableResponse response = given().auth().basic(username, password).header(apiVersionHeader).when().delete(removeBindingPath).then().statusCode(HttpStatus.SC_OK); // response body is empty json response.body(equalTo("{}")); } /** * cf delete-service * <p> * Remove Instance (DELETE /v2/service_instances/:id) */ @Override @Test public void case5_removeInstanceSucceedsWithCredentials() throws Exception { super.case5_removeInstanceSucceedsWithCredentials(); assertFalse(checkDatabaseExists(instanceId)); assertFalse(checkRoleExists(instanceId)); assertFalse(checkRoleIsDatabaseOwner(instanceId, instanceId)); Map<String, String> serviceResult = PostgreSQLDatabase.executePreparedSelect("SELECT * FROM service WHERE serviceinstanceid = ?", ImmutableMap.of(1, instanceId)); assertTrue(serviceResult.isEmpty()); } private boolean checkTableExists(String tableName) throws Exception { DatabaseMetaData md = conn.getMetaData(); ResultSet rs = md.getTables(null, null, tableName, null); // ResultSet.last() followed by ResultSet.getRow() will give you the row count rs.last(); int rowCount = rs.getRow(); return rowCount == 1; } private boolean checkDatabaseExists(String databaseName) throws Exception { Map<String, String> pgDatabaseResult = PostgreSQLDatabase.executePreparedSelect("SELECT * FROM pg_catalog.pg_database WHERE datname = ?", ImmutableMap.of(1, databaseName)); return pgDatabaseResult.size() > 0; } private boolean checkRoleExists(String roleName) throws Exception { Map<String, String> pgRoleResult = PostgreSQLDatabase.executePreparedSelect("SELECT * FROM pg_catalog.pg_roles WHERE rolname = ?", ImmutableMap.of(1, roleName)); return pgRoleResult.size() > 0; } private boolean checkRoleIsDatabaseOwner(String roleName, String databaseName) throws Exception { Map<String, String> pgRoleIsDatabaseOwnerResult = PostgreSQLDatabase.executePreparedSelect("SELECT d.datname as name, pg_catalog.pg_get_userbyid(d.datdba) as owner FROM pg_catalog.pg_database d WHERE d.datname = ?", ImmutableMap.of(1, databaseName)); String owner = pgRoleIsDatabaseOwnerResult.get("owner"); return (owner != null) ? owner.equals(roleName) : false; } }