Skip to content

Commit

Permalink
Merge pull request #37 from orange-cloudfoundry/31_support_volume_mount
Browse files Browse the repository at this point in the history
Add support for volume mount
  • Loading branch information
s-bortolussi authored Jan 31, 2017
2 parents ff48e25 + 5b8367b commit d8a04fb
Show file tree
Hide file tree
Showing 31 changed files with 926 additions and 117 deletions.
35 changes: 30 additions & 5 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -117,7 +117,7 @@ among the cloudfoundry installation to register with (among orgs and spaces).
* SERVICES[{SERVICE_ID}]_TAGS (String holding an array-of-strings, multiple tags are separated by comma,
as ```tag1,tag2,tag3```, default is ```[]```)
* SERVICES[{SERVICE_ID}]_REQUIRES (String holding an array-of-strings, multiple requires are separated by comma,
as ```syslog_drain, route_forwarding```, default is ```[]```). A list of permissions that the user would have to give the service, if they provision it. The only permission currently supported is syslog_drain.
as ```syslog_drain, volume_mount```, default is ```[]```). A list of permissions that the user would have to give the service, if they provision it. The only permission currently supported is syslog_drain.
* SERVICES[{SERVICE_ID}]_DASHBOARD_URL (String, no default). The URL of a web-based management user interface for the service instance.
* SERVICES[{SERVICE_ID}]_METADATA_DISPLAY_NAME (String, default is SERVICES_ID_NAME). The user-facing name of the service.
* SERVICES[{SERVICE_ID}]_METADATA_IMAGE_URL (String, default is "")
Expand All @@ -138,9 +138,9 @@ A number of catalog variables are not configureable, the broker always return th
* plan_updateable: false
* dashboard_client: ````{}```` (empty)

## Bound credentials
## credentials binding

The returned credentials are identical for all bound service instances of a specific plan~~, with at least one define~~.
The returned credentials are identical for all bound service instances of a specific plan.

The credentials could be defined for a service, it will be applied for all plans of the service.
It is configured by the following environment variables:
Expand All @@ -165,9 +165,9 @@ the same format as 'cf cups', e.g. ```'{"username":"admin","password":"pa55woRD"

This is mapped to [spring-cloud-cloudfoundry-service-broker](https://github.com/spring-cloud/spring-cloud-cloudfoundry-service-broker/blob/master/src%2Fmain%2Fjava%2Forg%2Fspringframework%2Fcloud%2Fservicebroker%2Fmodel%2FCreateServiceInstanceBindingResponse.java#L35)

## Bound syslog_drain_url
## syslog_drain_url binding

The returned syslog_drain_url is identical for all bound service instances of a specific plan~~, with at least one define~~.
The returned syslog_drain_url is identical for all bound service instances of a specific plan.

The syslog_drain_url could be defined for a service, it will be applied for all plans of the service.
It is configured by the following environment variables:
Expand All @@ -180,6 +180,31 @@ It is configured by the following environment variables:

If syslog_drain_url has been defined, ```SERVICES[{SERVICE_ID}]_REQUIRES``` property with a value ```syslog_drain``` must be declared in the Catalog endpoint or the service broker would consider the configuration invalid.

## volume_mount binding

The returned volume_mount is identical for all bound service instances of a specific plan.

The volume_mount could be defined for a service, it will be applied for all plans of the service.
It is configured by the following environment variables:
* SERVICES[{SERVICE_ID}]\_VOLUME_MOUNT[{INDEX}]_CONTAINER_DIR String.
* SERVICES[{SERVICE_ID}]\_VOLUME_MOUNT[{INDEX}]_DRIVER String.
* SERVICES[{SERVICE_ID}]\_VOLUME_MOUNT[{INDEX}]_MODE String.
* SERVICES[{SERVICE_ID}]\_VOLUME_MOUNT[{INDEX}]_DEVICE_TYPE String.
* SERVICES[{SERVICE_ID}]\_VOLUME_MOUNT[{INDEX}]_DEVICE_VOLUME_ID String.
* SERVICES[{SERVICE_ID}]\_VOLUME_MOUNT[{INDEX}]_DEVICE_MOUNT_CONFIG_[{MOUNT_KEY}] String.

The volume_mount could also be defined for a particular plan, if it contains conflict volume_mount key between the service
volume_mount and plan volume_mount, the values of the plan volume_mount will be taken.
It is configured by the following environment variables:
* SERVICES[{SERVICE_ID}]\_PLANS\[{PLAN_ID}]_VOLUME_MOUNT[{INDEX}]_CONTAINER_DIR String.
* SERVICES[{SERVICE_ID}]\_PLANS\[{PLAN_ID}]_VOLUME_MOUNT[{INDEX}]_DRIVER String.
* SERVICES[{SERVICE_ID}]\_PLANS\[{PLAN_ID}]_VOLUME_MOUNT[{INDEX}]_MODE String.
* SERVICES[{SERVICE_ID}]\_PLANS\[{PLAN_ID}]_VOLUME_MOUNT[{INDEX}]_DEVICE_TYPE String.
* SERVICES[{SERVICE_ID}]\_PLANS\[{PLAN_ID}]_VOLUME_MOUNT[{INDEX}]_DEVICE_VOLUME_ID String.
* SERVICES[{SERVICE_ID}]\_PLANS\[{PLAN_ID}]_VOLUME_MOUNT[{INDEX}]_DEVICE_MOUNT_CONFIG_[{MOUNT_KEY}] String.

If volume_mount has been defined, ```SERVICES[{SERVICE_ID}]_REQUIRES``` property with a value ```volume_mount``` must be declared in the Catalog endpoint or the service broker would consider the configuration invalid.

## Authentication

The service broker authenticates calls coming from Cloud Foundry through basic auth
Expand Down
11 changes: 9 additions & 2 deletions pom.xml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<project xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://maven.apache.org/POM/4.0.0"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>static-creds-broker</groupId>
<artifactId>static-creds-broker</artifactId>
Expand All @@ -19,6 +19,7 @@
<lombockVersion>1.16.12</lombockVersion>
<fest-assert.version>1.4</fest-assert.version>
<jgiven.version>0.14.0</jgiven.version>
<orika-core.version>1.5.0</orika-core.version>
</properties>

<build>
Expand Down Expand Up @@ -86,6 +87,12 @@
<version>${springCloudServiceBrokerVersion}</version>
</dependency>

<dependency>
<groupId>ma.glasnost.orika</groupId>
<artifactId>orika-core</artifactId>
<version>${orika-core.version}</version>
</dependency>

<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

import lombok.ToString;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.cloud.servicebroker.model.ServiceDefinitionRequires;
import org.springframework.stereotype.Component;

import javax.annotation.PostConstruct;
Expand All @@ -22,7 +23,6 @@
public class CatalogSettings {

public static final String NO_SERVICE_ERROR = "Invalid configuration. No service has been defined";
public static final String SYSLOG_DRAIN_REQUIRES = "syslog_drain";
@NotNull
@Size(min = 1, message = NO_SERVICE_ERROR)
@Valid
Expand Down Expand Up @@ -52,10 +52,11 @@ public void init() {
setDefaultPlanDescriptions();
assertPlanCredentialsExists();
assertSyslogDrainUrlRequiresExists();
assertVolumeMountRequiresExists();
}

private void assertPlanCredentialsExists() {
final Predicate<Plan> planWithoutCredential = plan -> plan.getFullCredentials() == null || plan.getFullCredentials().isEmpty();
final Predicate<Plan> planWithoutCredential = plan -> !plan.getFullCredentials().isPresent();
final Predicate<Service> serviceWithoutCredential = service -> !service.getFullCredentials().isPresent();

services.values().stream()
Expand All @@ -77,12 +78,29 @@ private void assertSyslogDrainUrlRequiresExists() {
});
}

private void assertVolumeMountRequiresExists() {
services.values().stream()
.flatMap(service -> service.getPlans().values().stream().filter(plan -> volumeMountExists(service, plan) && requiresVolumeMountNotPresent(service)))
.findFirst()
.ifPresent(plan -> {
throw new InvalidVolumeMountException(services);
});
}

private boolean syslogDrainUrlHasText(Service service, Plan plan) {
return plan.getSyslogDrainUrl() != null || service.getSyslogDrainUrl() != null;
}

private boolean requiresSyslogDrainUrlNotPresent(Service service) {
return service.getRequires() == null || !service.getRequires().contains(SYSLOG_DRAIN_REQUIRES);
return service.getRequires() == null || !service.getRequires().contains(ServiceDefinitionRequires.SERVICE_REQUIRES_SYSLOG_DRAIN.toString());
}

private boolean volumeMountExists(Service service, Plan plan) {
return (plan.getVolumeMounts() != null && !plan.getVolumeMounts().isEmpty()) || (service.getVolumeMounts() != null && !service.getVolumeMounts().isEmpty());
}

private boolean requiresVolumeMountNotPresent(Service service) {
return service.getRequires() == null || !service.getRequires().contains(ServiceDefinitionRequires.SERVICE_REQUIRES_VOLUME_MOUNT.toString());
}

private void setDefaultServiceDisplayName() {
Expand Down Expand Up @@ -153,20 +171,28 @@ private void setDefaultPlan() {
});
}

class NoCredentialException extends IllegalStateException {
public class NoCredentialException extends IllegalStateException {


public NoCredentialException(Plan plan) {
super("No credential has been set for plan " + plan);
}
}

class InvalidSyslogDrainUrlException extends IllegalStateException {
public class InvalidSyslogDrainUrlException extends IllegalStateException {


public InvalidSyslogDrainUrlException(Map<String, Service> services) {
super(String.format("%s includes a syslog_drain_url but \"requires\":[\"syslog_drain\"] is not present", services));
}
}

public class InvalidVolumeMountException extends IllegalStateException {


public InvalidVolumeMountException(Map<String, Service> services) {
super(String.format("%s includes a volume_mount but \"requires\":[\"volume_mount\"] is not present", services));
}
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,8 @@
@ToString
@EqualsAndHashCode
@Builder
public class ServicePlanDetail {
@AllArgsConstructor
public class CredentialsServicePlanBinding extends ServicePlanBinding {

/**
* The URL to which Cloud Foundry should drain logs for the bound application.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,7 @@
import javax.validation.Valid;
import javax.validation.constraints.NotNull;
import java.io.IOException;
import java.util.HashMap;
import java.util.Map;
import java.util.*;

/**
* Cloud Foundry plan
Expand Down Expand Up @@ -58,18 +57,27 @@ public class Plan {
*/
private String dashboardUrl;

/**
* The details of the volume mounts available to applications.
*/
private List<VolumeMountProperties> volumeMounts = new ArrayList<>();

public Plan() {
}

public Plan(String id) {
this.id = id;
}

public Map<String, Object> getFullCredentials() {
public Optional<Map<String, Object>> getFullCredentials() {
final Map<String, Object> full = new HashMap<>();
full.putAll(credentials);
full.putAll(credentialsJson);
return full;
if (credentials != null) {
full.putAll(credentials);
}
if (credentialsJson != null) {
full.putAll(credentialsJson);
}
return full.isEmpty() ? Optional.empty() : Optional.of(full);
}

public void setMetadata(String metadataJson) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,15 +5,13 @@
import lombok.Setter;
import lombok.ToString;
import org.hibernate.validator.constraints.NotEmpty;
import org.springframework.boot.context.properties.NestedConfigurationProperty;
import org.springframework.boot.json.JsonParser;
import org.springframework.boot.json.JsonParserFactory;
import org.springframework.cloud.servicebroker.model.ServiceDefinitionRequires;

import javax.validation.Valid;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.*;

/**
* Cloud Foundry Service
Expand Down Expand Up @@ -71,6 +69,12 @@ public class Service {
*/
private String dashboardUrl;

/**
* The details of the volume mounts available to applications.
*/
@NestedConfigurationProperty
private List<VolumeMountProperties> volumeMounts = new ArrayList<>();

public Service() {
}

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
/*
* *
* * Copyright (C) 2015 Orange
* * Licensed under the Apache License, Version 2.0 (the "License");
* * you may not use this file except in compliance with the License.
* * You may obtain a copy of the License at
* * http://www.apache.org/licenses/LICENSE-2.0
* * Unless required by applicable law or agreed to in writing, software
* * distributed under the License is distributed on an "AS IS" BASIS,
* * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* * See the License for the specific language governing permissions and
* * limitations under the License.
*
*/

package com.orange.servicebroker.staticcreds.domain;

/**
* @author Sebastien Bortolussi
*/
public class ServicePlanBinding {
}
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,8 @@
/**
* Created by YSBU7453 on 04/04/2016.
*/
public interface ServicePlanDetailRepository {
public interface ServicePlanBindingRepository {

Optional<ServicePlanDetail> find(String servicePlanId);
Optional<ServicePlanBinding> find(String servicePlanId);

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
/*
* *
* * Copyright (C) 2015 Orange
* * Licensed under the Apache License, Version 2.0 (the "License");
* * you may not use this file except in compliance with the License.
* * You may obtain a copy of the License at
* * http://www.apache.org/licenses/LICENSE-2.0
* * Unless required by applicable law or agreed to in writing, software
* * distributed under the License is distributed on an "AS IS" BASIS,
* * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* * See the License for the specific language governing permissions and
* * limitations under the License.
*
*/

package com.orange.servicebroker.staticcreds.domain;

import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;

import java.util.Map;

@Data
@AllArgsConstructor
@NoArgsConstructor
public class SharedVolumeDeviceProperties extends VolumeDeviceProperties {

private String volumeId;

private Map<String, Object> mountConfig;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
/*
* *
* * Copyright (C) 2015 Orange
* * Licensed under the Apache License, Version 2.0 (the "License");
* * you may not use this file except in compliance with the License.
* * You may obtain a copy of the License at
* * http://www.apache.org/licenses/LICENSE-2.0
* * Unless required by applicable law or agreed to in writing, software
* * distributed under the License is distributed on an "AS IS" BASIS,
* * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* * See the License for the specific language governing permissions and
* * limitations under the License.
*
*/

package com.orange.servicebroker.staticcreds.domain;

import lombok.EqualsAndHashCode;

@EqualsAndHashCode
public class VolumeDeviceProperties {
}
Loading

0 comments on commit d8a04fb

Please sign in to comment.