diff --git a/.github/workflows/ci-java-8-and-11.yml b/.github/workflows/ci-java-8-and-11.yml
index 27082a618ad..468a54178ab 100644
--- a/.github/workflows/ci-java-8-and-11.yml
+++ b/.github/workflows/ci-java-8-and-11.yml
@@ -33,7 +33,9 @@ jobs:
restore-keys: ${{ runner.os }}-m2
- name: Build with Maven
run: ./mvnw clean package -Dgpg.skip
+ - name: Compile integration tests
+ run: ./mvnw -Pintegration-test test-compile compile
- if: ${{ matrix.java != 8 }}
name: Check style with Spotless
- run: ./mvnw spotless:check
+ run: ./mvnw spotless:check -Pintegration-test
diff --git a/.idea/.name b/.idea/.name
deleted file mode 100644
index 3817c35541d..00000000000
--- a/.idea/.name
+++ /dev/null
@@ -1 +0,0 @@
-cloudfoundry-java-client
\ No newline at end of file
diff --git a/.idea/codeStyles/Project.xml b/.idea/codeStyles/Project.xml
deleted file mode 100644
index f4ec93d14cd..00000000000
--- a/.idea/codeStyles/Project.xml
+++ /dev/null
@@ -1,449 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- true
- true
- LOGGER
- true
- true
-
-
-
-
-
-
-
-
- true
- true
- true
- true
-
-
- BY_NAME
-
-
-
-
-
-
- true
- true
- true
- true
-
-
- BY_NAME
-
-
-
-
-
-
- true
- true
- true
- true
-
-
- BY_NAME
-
-
-
-
-
-
- true
- true
- true
- true
-
-
- BY_NAME
-
-
-
-
-
-
- true
- true
- true
-
-
- BY_NAME
-
-
-
-
-
-
- true
- true
- true
-
-
- BY_NAME
-
-
-
-
-
-
- true
- true
- true
-
-
- BY_NAME
-
-
-
-
-
-
- true
- true
- true
-
-
- BY_NAME
-
-
-
-
-
-
- true
- true
-
-
-
-
-
-
-
-
- true
- true
- logger
- true
-
-
-
-
-
-
-
-
- true
- true
- true
-
-
- BY_NAME
-
-
-
-
-
-
- true
- true
- true
-
-
- BY_NAME
-
-
-
-
-
-
- true
- true
- true
-
-
- BY_NAME
-
-
-
-
-
-
- true
- true
- true
-
-
- BY_NAME
-
-
-
-
-
-
- true
- true
-
-
- BY_NAME
-
-
-
-
-
-
- true
- true
-
-
- BY_NAME
-
-
-
-
-
-
- true
- true
-
-
- BY_NAME
-
-
-
-
-
-
- true
- true
-
-
- BY_NAME
-
-
-
-
-
- true
-
- BY_NAME
-
-
-
-
-
-
-
- true
- true
-
-
- BY_NAME
-
-
-
-
-
-
- true
- true
-
-
- BY_NAME
-
-
-
-
-
-
- true
- true
-
-
- BY_NAME
-
-
-
-
-
-
- true
- true
-
-
- BY_NAME
-
-
-
-
-
-
- true
- true
- true
-
-
- BY_NAME
-
-
-
-
-
-
- true
- true
-
-
- BY_NAME
-
-
-
-
-
-
- true
- true
- true
-
-
- BY_NAME
-
-
-
-
-
-
- true
- true
-
-
- BY_NAME
-
-
-
-
-
-
- true
- true
- true
-
-
- BY_NAME
-
-
-
-
-
-
- true
- true
-
-
- BY_NAME
-
-
-
-
-
-
- true
- true
- true
-
-
- BY_NAME
-
-
-
-
-
-
- true
- true
-
-
- BY_NAME
-
-
-
-
-
- true
-
- BY_NAME
-
-
-
-
-
- true
-
- BY_NAME
-
-
-
-
-
-
- true
- true
-
-
- BY_NAME
-
-
-
-
-
- true
-
- BY_NAME
-
-
-
-
-
-
-
-
-
-
-
-
-
\ No newline at end of file
diff --git a/.idea/codeStyles/codeStyleConfig.xml b/.idea/codeStyles/codeStyleConfig.xml
deleted file mode 100644
index 79ee123c2b2..00000000000
--- a/.idea/codeStyles/codeStyleConfig.xml
+++ /dev/null
@@ -1,5 +0,0 @@
-
-
-
-
-
\ No newline at end of file
diff --git a/.idea/compiler.xml b/.idea/compiler.xml
deleted file mode 100644
index 1d6ce3c640e..00000000000
--- a/.idea/compiler.xml
+++ /dev/null
@@ -1,31 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
\ No newline at end of file
diff --git a/.idea/copyright/Apache_License__Version_2_0.xml b/.idea/copyright/Apache_License__Version_2_0.xml
deleted file mode 100644
index 893ada61a7c..00000000000
--- a/.idea/copyright/Apache_License__Version_2_0.xml
+++ /dev/null
@@ -1,9 +0,0 @@
-
-
-
-
-
-
-
-
-
\ No newline at end of file
diff --git a/.idea/copyright/profiles_settings.xml b/.idea/copyright/profiles_settings.xml
deleted file mode 100644
index 2fca47b2b32..00000000000
--- a/.idea/copyright/profiles_settings.xml
+++ /dev/null
@@ -1,7 +0,0 @@
-
-
-
-
-
-
-
\ No newline at end of file
diff --git a/.idea/dictionaries/bhale.xml b/.idea/dictionaries/bhale.xml
deleted file mode 100644
index f5368121b65..00000000000
--- a/.idea/dictionaries/bhale.xml
+++ /dev/null
@@ -1,21 +0,0 @@
-
-
-
- deserialized
- dropsonde
- evictor
- gibi
- keepalive
- kibi
- mibi
- mibibyte
- relativize
- scim
- tcproutes
- timespan
- unshare
- upsert
- yhoo
-
-
-
\ No newline at end of file
diff --git a/.idea/dictionaries/project.xml b/.idea/dictionaries/project.xml
deleted file mode 100644
index 2e81ec68354..00000000000
--- a/.idea/dictionaries/project.xml
+++ /dev/null
@@ -1,45 +0,0 @@
-
-
-
- acfbae
- actee
- actees
- adff
- backoff
- bufs
- buildpacks
- cflinuxfs
- codec
- containermetrics
- ctxt
- datas
- diegos
- firehose
- gradle
- guids
- metadatas
- micropcf
- mkactivity
- mkcalendar
- mkredirectref
- mkworkspace
- okhttp
- orderpatch
- procfile
- recentlogs
- restage
- showmethod
- spacejump
- springapps
- staticfile
- textsearch
- truster
- truststore
- uncheckout
- unmap
- updateredirectref
- uptime
- whatuuid
-
-
-
diff --git a/.idea/encodings.xml b/.idea/encodings.xml
deleted file mode 100644
index f939b983d38..00000000000
--- a/.idea/encodings.xml
+++ /dev/null
@@ -1,22 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
\ No newline at end of file
diff --git a/.idea/google-java-format.xml b/.idea/google-java-format.xml
deleted file mode 100644
index 2aa056da349..00000000000
--- a/.idea/google-java-format.xml
+++ /dev/null
@@ -1,6 +0,0 @@
-
-
-
-
-
-
\ No newline at end of file
diff --git a/.idea/inspectionProfiles/Project_Default.xml b/.idea/inspectionProfiles/Project_Default.xml
deleted file mode 100644
index 212e4222738..00000000000
--- a/.idea/inspectionProfiles/Project_Default.xml
+++ /dev/null
@@ -1,29 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
\ No newline at end of file
diff --git a/.idea/inspectionProfiles/profiles_settings.xml b/.idea/inspectionProfiles/profiles_settings.xml
deleted file mode 100644
index 3b312839bf2..00000000000
--- a/.idea/inspectionProfiles/profiles_settings.xml
+++ /dev/null
@@ -1,7 +0,0 @@
-
-
-
-
-
-
-
\ No newline at end of file
diff --git a/.idea/kotlinc.xml b/.idea/kotlinc.xml
deleted file mode 100644
index 1c24f9a8de4..00000000000
--- a/.idea/kotlinc.xml
+++ /dev/null
@@ -1,7 +0,0 @@
-
-
-
-
-
-
-
\ No newline at end of file
diff --git a/.idea/misc.xml b/.idea/misc.xml
deleted file mode 100644
index a1043f84492..00000000000
--- a/.idea/misc.xml
+++ /dev/null
@@ -1,24 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
\ No newline at end of file
diff --git a/.idea/runConfigurations.xml b/.idea/runConfigurations.xml
deleted file mode 100644
index 797acea53eb..00000000000
--- a/.idea/runConfigurations.xml
+++ /dev/null
@@ -1,10 +0,0 @@
-
-
-
-
-
-
-
-
-
-
\ No newline at end of file
diff --git a/.idea/runConfigurations/TestServiceBroker.xml b/.idea/runConfigurations/TestServiceBroker.xml
deleted file mode 100644
index de50e7e26f1..00000000000
--- a/.idea/runConfigurations/TestServiceBroker.xml
+++ /dev/null
@@ -1,10 +0,0 @@
-
-
-
-
-
-
-
-
-
-
\ No newline at end of file
diff --git a/.idea/runConfigurations/cloudfoundry_client.xml b/.idea/runConfigurations/cloudfoundry_client.xml
deleted file mode 100644
index 00e5b6ea595..00000000000
--- a/.idea/runConfigurations/cloudfoundry_client.xml
+++ /dev/null
@@ -1,23 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
\ No newline at end of file
diff --git a/.idea/runConfigurations/cloudfoundry_client_reactor.xml b/.idea/runConfigurations/cloudfoundry_client_reactor.xml
deleted file mode 100644
index de82ed2bbc0..00000000000
--- a/.idea/runConfigurations/cloudfoundry_client_reactor.xml
+++ /dev/null
@@ -1,25 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
\ No newline at end of file
diff --git a/.idea/runConfigurations/cloudfoundry_operations.xml b/.idea/runConfigurations/cloudfoundry_operations.xml
deleted file mode 100644
index 03d3cad96d4..00000000000
--- a/.idea/runConfigurations/cloudfoundry_operations.xml
+++ /dev/null
@@ -1,25 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
\ No newline at end of file
diff --git a/.idea/runConfigurations/cloudfoundry_util.xml b/.idea/runConfigurations/cloudfoundry_util.xml
deleted file mode 100644
index 7a32364ae65..00000000000
--- a/.idea/runConfigurations/cloudfoundry_util.xml
+++ /dev/null
@@ -1,23 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
\ No newline at end of file
diff --git a/.idea/runConfigurations/integration_test.xml b/.idea/runConfigurations/integration_test.xml
deleted file mode 100644
index d8fd72e7b89..00000000000
--- a/.idea/runConfigurations/integration_test.xml
+++ /dev/null
@@ -1,26 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
\ No newline at end of file
diff --git a/.idea/runConfigurations/test_service_broker__package_.xml b/.idea/runConfigurations/test_service_broker__package_.xml
deleted file mode 100644
index b1d3fbf9241..00000000000
--- a/.idea/runConfigurations/test_service_broker__package_.xml
+++ /dev/null
@@ -1,29 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
\ No newline at end of file
diff --git a/.idea/uiDesigner.xml b/.idea/uiDesigner.xml
deleted file mode 100644
index e96534fb27b..00000000000
--- a/.idea/uiDesigner.xml
+++ /dev/null
@@ -1,124 +0,0 @@
-
-
-
-
- -
-
-
- -
-
-
- -
-
-
- -
-
-
- -
-
-
-
-
-
- -
-
-
-
-
-
- -
-
-
-
-
-
- -
-
-
-
-
-
- -
-
-
-
-
- -
-
-
-
-
- -
-
-
-
-
- -
-
-
-
-
- -
-
-
-
-
- -
-
-
-
-
- -
-
-
- -
-
-
-
-
- -
-
-
-
-
- -
-
-
-
-
- -
-
-
-
-
- -
-
-
-
-
- -
-
-
- -
-
-
- -
-
-
- -
-
-
- -
-
-
-
-
- -
-
-
- -
-
-
-
-
-
\ No newline at end of file
diff --git a/.idea/vcs.xml b/.idea/vcs.xml
deleted file mode 100644
index 590a612f67c..00000000000
--- a/.idea/vcs.xml
+++ /dev/null
@@ -1,7 +0,0 @@
-
-
-
-
-
-
-
\ No newline at end of file
diff --git a/ci/integration-test.sh b/ci/integration-test.sh
index 789f612213d..4091ca6c7ca 100755
--- a/ci/integration-test.sh
+++ b/ci/integration-test.sh
@@ -35,4 +35,4 @@ TEST_ADMIN_CLIENTSECRET=$(jq -n -r --argjson credentials "${UAA_CREDS}" '$creden
export TEST_ADMIN_CLIENTSECRET
cd cf-java-client
-./mvnw -q -P integration-test test
+./mvnw -P integration-test test
diff --git a/cloudfoundry-client-reactor/src/main/java/org/cloudfoundry/reactor/client/_ReactorCloudFoundryClient.java b/cloudfoundry-client-reactor/src/main/java/org/cloudfoundry/reactor/client/_ReactorCloudFoundryClient.java
index 29d208aad47..319a1bcfd6f 100644
--- a/cloudfoundry-client-reactor/src/main/java/org/cloudfoundry/reactor/client/_ReactorCloudFoundryClient.java
+++ b/cloudfoundry-client-reactor/src/main/java/org/cloudfoundry/reactor/client/_ReactorCloudFoundryClient.java
@@ -34,6 +34,7 @@
import org.cloudfoundry.client.v2.routemappings.RouteMappings;
import org.cloudfoundry.client.v2.routes.Routes;
import org.cloudfoundry.client.v2.securitygroups.SecurityGroups;
+import org.cloudfoundry.client.v3.securitygroups.SecurityGroupsV3;
import org.cloudfoundry.client.v2.servicebindings.ServiceBindingsV2;
import org.cloudfoundry.client.v2.servicebrokers.ServiceBrokers;
import org.cloudfoundry.client.v2.serviceinstances.ServiceInstances;
@@ -91,6 +92,7 @@
import org.cloudfoundry.reactor.client.v2.routemappings.ReactorRouteMappings;
import org.cloudfoundry.reactor.client.v2.routes.ReactorRoutes;
import org.cloudfoundry.reactor.client.v2.securitygroups.ReactorSecurityGroups;
+import org.cloudfoundry.reactor.client.v3.securitygroups.ReactorSecurityGroupsV3;
import org.cloudfoundry.reactor.client.v2.servicebindings.ReactorServiceBindingsV2;
import org.cloudfoundry.reactor.client.v2.servicebrokers.ReactorServiceBrokers;
import org.cloudfoundry.reactor.client.v2.serviceinstances.ReactorServiceInstances;
@@ -151,7 +153,8 @@ public AdminV3 adminV3() {
@Override
@Value.Derived
public ApplicationUsageEvents applicationUsageEvents() {
- return new ReactorApplicationUsageEvents(getConnectionContext(), getRootV2(), getTokenProvider(), getRequestTags());
+ return new ReactorApplicationUsageEvents(getConnectionContext(), getRootV2(), getTokenProvider(),
+ getRequestTags());
}
@Override
@@ -228,7 +231,8 @@ public Droplets droplets() {
@Override
@Value.Derived
public EnvironmentVariableGroups environmentVariableGroups() {
- return new ReactorEnvironmentVariableGroups(getConnectionContext(), getRootV2(), getTokenProvider(), getRequestTags());
+ return new ReactorEnvironmentVariableGroups(getConnectionContext(), getRootV2(), getTokenProvider(),
+ getRequestTags());
}
@Override
@@ -270,7 +274,8 @@ public JobsV3 jobsV3() {
@Override
@Value.Derived
public OrganizationQuotaDefinitions organizationQuotaDefinitions() {
- return new ReactorOrganizationQuotaDefinitions(getConnectionContext(), getRootV2(), getTokenProvider(), getRequestTags());
+ return new ReactorOrganizationQuotaDefinitions(getConnectionContext(), getRootV2(), getTokenProvider(),
+ getRequestTags());
}
@Override
@@ -345,6 +350,12 @@ public SecurityGroups securityGroups() {
return new ReactorSecurityGroups(getConnectionContext(), getRootV2(), getTokenProvider(), getRequestTags());
}
+ @Override
+ @Value.Derived
+ public SecurityGroupsV3 securityGroupsV3() {
+ return new ReactorSecurityGroupsV3(getConnectionContext(), getRootV3(), getTokenProvider(), getRequestTags());
+ }
+
@Override
@Value.Derived
public ServiceBindingsV2 serviceBindingsV2() {
@@ -366,7 +377,7 @@ public ServiceBrokers serviceBrokers() {
@Override
@Value.Derived
public ServiceBrokersV3 serviceBrokersV3() {
- return new ReactorServiceBrokersV3(getConnectionContext(), getRootV3(), getTokenProvider(), getRequestTags());
+ return new ReactorServiceBrokersV3(getConnectionContext(), getRootV3(), getTokenProvider(), getRequestTags());
}
@Override
@@ -396,7 +407,8 @@ public ServiceOfferingsV3 serviceOfferingsV3() {
@Override
@Value.Derived
public ServicePlanVisibilities servicePlanVisibilities() {
- return new ReactorServicePlanVisibilities(getConnectionContext(), getRootV2(), getTokenProvider(), getRequestTags());
+ return new ReactorServicePlanVisibilities(getConnectionContext(), getRootV2(), getTokenProvider(),
+ getRequestTags());
}
@Override
@@ -432,7 +444,8 @@ public SharedDomains sharedDomains() {
@Override
@Value.Derived
public SpaceQuotaDefinitions spaceQuotaDefinitions() {
- return new ReactorSpaceQuotaDefinitions(getConnectionContext(), getRootV2(), getTokenProvider(), getRequestTags());
+ return new ReactorSpaceQuotaDefinitions(getConnectionContext(), getRootV2(), getTokenProvider(),
+ getRequestTags());
}
@Override
@@ -468,7 +481,8 @@ public Tasks tasks() {
@Override
@Value.Derived
public UserProvidedServiceInstances userProvidedServiceInstances() {
- return new ReactorUserProvidedServiceInstances(getConnectionContext(), getRootV2(), getTokenProvider(), getRequestTags());
+ return new ReactorUserProvidedServiceInstances(getConnectionContext(), getRootV2(), getTokenProvider(),
+ getRequestTags());
}
@Override
@@ -483,7 +497,8 @@ public Users users() {
abstract ConnectionContext getConnectionContext();
/**
- * Map of http header name and value which will be added to every request to the controller
+ * Map of http header name and value which will be added to every request to the
+ * controller
*/
@Value.Default
Map getRequestTags() {
diff --git a/cloudfoundry-client-reactor/src/main/java/org/cloudfoundry/reactor/client/v3/securitygroups/ReactorSecurityGroupsV3.java b/cloudfoundry-client-reactor/src/main/java/org/cloudfoundry/reactor/client/v3/securitygroups/ReactorSecurityGroupsV3.java
new file mode 100644
index 00000000000..adea2b5f86a
--- /dev/null
+++ b/cloudfoundry-client-reactor/src/main/java/org/cloudfoundry/reactor/client/v3/securitygroups/ReactorSecurityGroupsV3.java
@@ -0,0 +1,201 @@
+/*
+ * Copyright 2013-2023 the original author or authors.
+ *
+ * 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 org.cloudfoundry.reactor.client.v3.securitygroups;
+
+import java.util.Map;
+import org.cloudfoundry.client.v3.securitygroups.BindRunningSecurityGroupRequest;
+import org.cloudfoundry.client.v3.securitygroups.BindRunningSecurityGroupResponse;
+import org.cloudfoundry.client.v3.securitygroups.BindStagingSecurityGroupRequest;
+import org.cloudfoundry.client.v3.securitygroups.BindStagingSecurityGroupResponse;
+import org.cloudfoundry.client.v3.securitygroups.CreateSecurityGroupRequest;
+import org.cloudfoundry.client.v3.securitygroups.CreateSecurityGroupResponse;
+import org.cloudfoundry.client.v3.securitygroups.DeleteSecurityGroupRequest;
+import org.cloudfoundry.client.v3.securitygroups.GetSecurityGroupRequest;
+import org.cloudfoundry.client.v3.securitygroups.GetSecurityGroupResponse;
+import org.cloudfoundry.client.v3.securitygroups.ListRunningSecurityGroupsRequest;
+import org.cloudfoundry.client.v3.securitygroups.ListRunningSecurityGroupsResponse;
+import org.cloudfoundry.client.v3.securitygroups.ListSecurityGroupsRequest;
+import org.cloudfoundry.client.v3.securitygroups.ListSecurityGroupsResponse;
+import org.cloudfoundry.client.v3.securitygroups.ListStagingSecurityGroupsRequest;
+import org.cloudfoundry.client.v3.securitygroups.ListStagingSecurityGroupsResponse;
+import org.cloudfoundry.client.v3.securitygroups.SecurityGroupsV3;
+import org.cloudfoundry.client.v3.securitygroups.UnbindRunningSecurityGroupRequest;
+import org.cloudfoundry.client.v3.securitygroups.UnbindStagingSecurityGroupRequest;
+import org.cloudfoundry.client.v3.securitygroups.UpdateSecurityGroupRequest;
+import org.cloudfoundry.client.v3.securitygroups.UpdateSecurityGroupResponse;
+import org.cloudfoundry.client.v3.servicebindings.ServiceBindingsV3;
+import org.cloudfoundry.reactor.ConnectionContext;
+import org.cloudfoundry.reactor.TokenProvider;
+import org.cloudfoundry.reactor.client.v3.AbstractClientV3Operations;
+import reactor.core.publisher.Mono;
+
+/**
+ * The Reactor-based implementation of {@link ServiceBindingsV3}
+ */
+public final class ReactorSecurityGroupsV3 extends AbstractClientV3Operations
+ implements SecurityGroupsV3 {
+
+ /**
+ * Creates an instance
+ *
+ * @param connectionContext the {@link ConnectionContext} to use when communicating with the
+ * server
+ * @param root the root URI of the server. Typically something like
+ * {@code https://api.run.pivotal.io}.
+ * @param tokenProvider the {@link TokenProvider} to use when communicating with the server
+ * @param requestTags map with custom http headers which will be added to web request
+ */
+ public ReactorSecurityGroupsV3(
+ ConnectionContext connectionContext,
+ Mono root,
+ TokenProvider tokenProvider,
+ Map requestTags) {
+ super(connectionContext, root, tokenProvider, requestTags);
+ }
+
+ @Override
+ public Mono create(CreateSecurityGroupRequest request) {
+ return post(
+ request,
+ CreateSecurityGroupResponse.class,
+ builder -> builder.pathSegment("security_groups"))
+ .checkpoint();
+ }
+
+ @Override
+ public Mono get(GetSecurityGroupRequest request) {
+ return get(
+ request,
+ GetSecurityGroupResponse.class,
+ builder ->
+ builder.pathSegment(
+ "security_groups", request.getSecurityGroupId()))
+ .checkpoint();
+ }
+
+ @Override
+ public Mono list(ListSecurityGroupsRequest request) {
+ return get(
+ request,
+ ListSecurityGroupsResponse.class,
+ builder -> builder.pathSegment("security_groups"))
+ .checkpoint();
+ }
+
+ @Override
+ public Mono update(UpdateSecurityGroupRequest request) {
+ return patch(
+ request,
+ UpdateSecurityGroupResponse.class,
+ builder ->
+ builder.pathSegment(
+ "security_groups", request.getSecurityGroupId()))
+ .checkpoint();
+ }
+
+ @Override
+ public Mono delete(DeleteSecurityGroupRequest request) {
+ return delete(
+ request,
+ builder ->
+ builder.pathSegment(
+ "security_groups", request.getSecurityGroupId()))
+ .checkpoint();
+ }
+
+ @Override
+ public Mono bindRunningSecurityGroup(
+ BindRunningSecurityGroupRequest request) {
+ return post(
+ request,
+ BindRunningSecurityGroupResponse.class,
+ builder ->
+ builder.pathSegment(
+ "security_groups",
+ request.getSecurityGroupId(),
+ "relationships",
+ "running_spaces"))
+ .checkpoint();
+ }
+
+ @Override
+ public Mono bindStagingSecurityGroup(
+ BindStagingSecurityGroupRequest request) {
+ return post(
+ request,
+ BindStagingSecurityGroupResponse.class,
+ builder ->
+ builder.pathSegment(
+ "security_groups",
+ request.getSecurityGroupId(),
+ "relationships",
+ "staging_spaces"))
+ .checkpoint();
+ }
+
+ @Override
+ public Mono unbindStagingSecurityGroup(UnbindStagingSecurityGroupRequest request) {
+ return delete(
+ request,
+ Void.class,
+ builder ->
+ builder.pathSegment(
+ "security_groups",
+ request.getSecurityGroupId(),
+ "relationships",
+ "staging_spaces",
+ request.getSpaceId()))
+ .checkpoint();
+ }
+
+ @Override
+ public Mono unbindRunningSecurityGroup(UnbindRunningSecurityGroupRequest request) {
+ return delete(
+ request,
+ Void.class,
+ builder ->
+ builder.pathSegment(
+ "security_groups",
+ request.getSecurityGroupId(),
+ "relationships",
+ "running_spaces",
+ request.getSpaceId()))
+ .checkpoint();
+ }
+
+ @Override
+ public Mono listRunning(
+ ListRunningSecurityGroupsRequest request) {
+ return get(
+ request,
+ ListRunningSecurityGroupsResponse.class,
+ builder ->
+ builder.pathSegment(
+ "spaces", request.getSpaceId(), "running_security_groups"))
+ .checkpoint();
+ }
+
+ @Override
+ public Mono listStaging(
+ ListStagingSecurityGroupsRequest request) {
+ return get(
+ request,
+ ListStagingSecurityGroupsResponse.class,
+ builder ->
+ builder.pathSegment(
+ "spaces", request.getSpaceId(), "staging_security_groups"))
+ .checkpoint();
+ }
+}
diff --git a/cloudfoundry-client-reactor/src/test/java/org/cloudfoundry/reactor/client/v3/securitygroups/ReactorSecurityGroupsV3Test.java b/cloudfoundry-client-reactor/src/test/java/org/cloudfoundry/reactor/client/v3/securitygroups/ReactorSecurityGroupsV3Test.java
new file mode 100644
index 00000000000..7ffeece9ffc
--- /dev/null
+++ b/cloudfoundry-client-reactor/src/test/java/org/cloudfoundry/reactor/client/v3/securitygroups/ReactorSecurityGroupsV3Test.java
@@ -0,0 +1,885 @@
+/*
+ * Copyright 2013-2021 the original author or authors.
+ *
+ * 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 org.cloudfoundry.reactor.client.v3.securitygroups;
+
+import static io.netty.handler.codec.http.HttpMethod.DELETE;
+import static io.netty.handler.codec.http.HttpMethod.GET;
+import static io.netty.handler.codec.http.HttpMethod.PATCH;
+import static io.netty.handler.codec.http.HttpMethod.POST;
+import static io.netty.handler.codec.http.HttpResponseStatus.ACCEPTED;
+import static io.netty.handler.codec.http.HttpResponseStatus.CREATED;
+import static io.netty.handler.codec.http.HttpResponseStatus.NO_CONTENT;
+import static io.netty.handler.codec.http.HttpResponseStatus.OK;
+
+import java.time.Duration;
+import java.util.Collections;
+import org.cloudfoundry.client.v3.Link;
+import org.cloudfoundry.client.v3.Pagination;
+import org.cloudfoundry.client.v3.Relationship;
+import org.cloudfoundry.client.v3.ToManyRelationship;
+import org.cloudfoundry.client.v3.securitygroups.BindRunningSecurityGroupRequest;
+import org.cloudfoundry.client.v3.securitygroups.BindRunningSecurityGroupResponse;
+import org.cloudfoundry.client.v3.securitygroups.BindStagingSecurityGroupRequest;
+import org.cloudfoundry.client.v3.securitygroups.BindStagingSecurityGroupResponse;
+import org.cloudfoundry.client.v3.securitygroups.CreateSecurityGroupRequest;
+import org.cloudfoundry.client.v3.securitygroups.CreateSecurityGroupResponse;
+import org.cloudfoundry.client.v3.securitygroups.DeleteSecurityGroupRequest;
+import org.cloudfoundry.client.v3.securitygroups.GetSecurityGroupRequest;
+import org.cloudfoundry.client.v3.securitygroups.GetSecurityGroupResponse;
+import org.cloudfoundry.client.v3.securitygroups.GloballyEnabled;
+import org.cloudfoundry.client.v3.securitygroups.ListRunningSecurityGroupsRequest;
+import org.cloudfoundry.client.v3.securitygroups.ListRunningSecurityGroupsResponse;
+import org.cloudfoundry.client.v3.securitygroups.ListSecurityGroupsRequest;
+import org.cloudfoundry.client.v3.securitygroups.ListSecurityGroupsResponse;
+import org.cloudfoundry.client.v3.securitygroups.ListStagingSecurityGroupsRequest;
+import org.cloudfoundry.client.v3.securitygroups.ListStagingSecurityGroupsResponse;
+import org.cloudfoundry.client.v3.securitygroups.Protocol;
+import org.cloudfoundry.client.v3.securitygroups.Relationships;
+import org.cloudfoundry.client.v3.securitygroups.Rule;
+import org.cloudfoundry.client.v3.securitygroups.SecurityGroupResource;
+import org.cloudfoundry.client.v3.securitygroups.UnbindRunningSecurityGroupRequest;
+import org.cloudfoundry.client.v3.securitygroups.UnbindStagingSecurityGroupRequest;
+import org.cloudfoundry.client.v3.securitygroups.UpdateSecurityGroupRequest;
+import org.cloudfoundry.client.v3.securitygroups.UpdateSecurityGroupResponse;
+import org.cloudfoundry.reactor.InteractionContext;
+import org.cloudfoundry.reactor.TestRequest;
+import org.cloudfoundry.reactor.TestResponse;
+import org.cloudfoundry.reactor.client.AbstractClientApiTest;
+import org.junit.jupiter.api.Test;
+import reactor.test.StepVerifier;
+
+public final class ReactorSecurityGroupsV3Test extends AbstractClientApiTest {
+
+ private final ReactorSecurityGroupsV3 securityGroups =
+ new ReactorSecurityGroupsV3(
+ CONNECTION_CONTEXT, this.root, TOKEN_PROVIDER, Collections.emptyMap());
+
+ @Test
+ public void create() {
+ mockRequest(
+ InteractionContext.builder()
+ .request(
+ TestRequest.builder()
+ .method(POST)
+ .path("/security_groups")
+ .payload(
+ "fixtures/client/v3/security_groups/POST_request.json")
+ .build())
+ .response(
+ TestResponse.builder()
+ .status(CREATED)
+ .payload(
+ "fixtures/client/v3/security_groups/POST_response.json")
+ .build())
+ .build());
+ this.securityGroups
+ .create(
+ CreateSecurityGroupRequest.builder()
+ .rules(
+ Rule.builder()
+ .protocol(Protocol.TCP)
+ .destination("10.10.10.0/24")
+ .ports("443,80,8080")
+ .build())
+ .name("my-group0")
+ .rules(
+ Rule.builder()
+ .protocol(Protocol.ICMP)
+ .destination("10.10.10.0/24")
+ .description(
+ "Allow ping requests to private services")
+ .type(8)
+ .code(0)
+ .build())
+ .build())
+ .as(StepVerifier::create)
+ .expectNext(
+ CreateSecurityGroupResponse.builder()
+ .name("my-group0")
+ .id("b85a788e-671f-4549-814d-e34cdb2f539a")
+ .createdAt("2020-02-20T17:42:08Z")
+ .updatedAt("2020-02-20T17:42:08Z")
+ .globallyEnabled(
+ GloballyEnabled.builder()
+ .staging(false)
+ .running(true)
+ .build())
+ .rules(
+ Rule.builder()
+ .protocol(Protocol.TCP)
+ .destination("10.10.10.0/24")
+ .ports("443,80,8080")
+ .build())
+ .rules(
+ Rule.builder()
+ .protocol(Protocol.ICMP)
+ .destination("10.10.10.0/24")
+ .description(
+ "Allow ping requests to private services")
+ .type(8)
+ .code(0)
+ .build())
+ .relationships(
+ Relationships.builder()
+ .stagingSpaces(
+ ToManyRelationship.builder()
+ .data(
+ Relationship.builder()
+ .id("space-guid-1")
+ .build())
+ .data(
+ Relationship.builder()
+ .id("space-guid-2")
+ .build())
+ .build())
+ .runningSpaces(ToManyRelationship.builder().build())
+ .build())
+ .link(
+ "self",
+ Link.builder()
+ .href(
+ "https://api.example.org/v3/security_groups/b85a788e-671f-4549-814d-e34cdb2f539a")
+ .build())
+ .build())
+ .expectComplete()
+ .verify(Duration.ofSeconds(5));
+ }
+
+ @Test
+ public void get() {
+ mockRequest(
+ InteractionContext.builder()
+ .request(
+ TestRequest.builder()
+ .method(GET)
+ .path(
+ "/security_groups/b85a788e-671f-4549-814d-e34cdb2f539a")
+ .build())
+ .response(
+ TestResponse.builder()
+ .status(OK)
+ .payload(
+ "fixtures/client/v3/security_groups/GET_{id}_response.json")
+ .build())
+ .build());
+ this.securityGroups
+ .get(
+ GetSecurityGroupRequest.builder()
+ .securityGroupId("b85a788e-671f-4549-814d-e34cdb2f539a")
+ .build())
+ .as(StepVerifier::create)
+ .expectNext(
+ GetSecurityGroupResponse.builder()
+ .name("my-group0")
+ .id("b85a788e-671f-4549-814d-e34cdb2f539a")
+ .createdAt("2020-02-20T17:42:08Z")
+ .updatedAt("2020-02-20T17:42:08Z")
+ .globallyEnabled(
+ GloballyEnabled.builder()
+ .staging(false)
+ .running(true)
+ .build())
+ .rules(
+ Rule.builder()
+ .protocol(Protocol.TCP)
+ .destination("10.10.10.0/24")
+ .ports("443,80,8080")
+ .build())
+ .rules(
+ Rule.builder()
+ .protocol(Protocol.ICMP)
+ .destination("10.10.10.0/24")
+ .description(
+ "Allow ping requests to private services")
+ .type(8)
+ .code(0)
+ .build())
+ .relationships(
+ Relationships.builder()
+ .stagingSpaces(
+ ToManyRelationship.builder()
+ .data(
+ Relationship.builder()
+ .id("space-guid-1")
+ .build())
+ .data(
+ Relationship.builder()
+ .id("space-guid-2")
+ .build())
+ .build())
+ .runningSpaces(ToManyRelationship.builder().build())
+ .build())
+ .link(
+ "self",
+ Link.builder()
+ .href(
+ "https://api.example.org/v3/security_groups/b85a788e-671f-4549-814d-e34cdb2f539a")
+ .build())
+ .build())
+ .expectComplete()
+ .verify(Duration.ofSeconds(5));
+ }
+
+ @Test
+ public void list() {
+ mockRequest(
+ InteractionContext.builder()
+ .request(TestRequest.builder().method(GET).path("/security_groups").build())
+ .response(
+ TestResponse.builder()
+ .status(OK)
+ .payload(
+ "fixtures/client/v3/security_groups/GET_response.json")
+ .build())
+ .build());
+
+ this.securityGroups
+ .list(ListSecurityGroupsRequest.builder().build())
+ .as(StepVerifier::create)
+ .expectNext(
+ ListSecurityGroupsResponse.builder()
+ .pagination(
+ Pagination.builder()
+ .totalResults(1)
+ .totalPages(1)
+ .first(
+ Link.builder()
+ .href(
+ "https://api.example.org/v3/security_groups?page=1&per_page=50")
+ .build())
+ .last(
+ Link.builder()
+ .href(
+ "https://api.example.org/v3/security_groups?page=1&per_page=50")
+ .build())
+ .build())
+ .resource(
+ SecurityGroupResource.builder()
+ .name("my-group0")
+ .id("b85a788e-671f-4549-814d-e34cdb2f539a")
+ .createdAt("2020-02-20T17:42:08Z")
+ .updatedAt("2020-02-20T17:42:08Z")
+ .globallyEnabled(
+ GloballyEnabled.builder()
+ .staging(false)
+ .running(true)
+ .build())
+ .rules(
+ Rule.builder()
+ .protocol(Protocol.TCP)
+ .destination("10.10.10.0/24")
+ .ports("443,80,8080")
+ .build())
+ .rules(
+ Rule.builder()
+ .protocol(Protocol.ICMP)
+ .destination("10.10.10.0/24")
+ .description(
+ "Allow ping requests to"
+ + " private services")
+ .type(8)
+ .code(0)
+ .build())
+ .relationships(
+ Relationships.builder()
+ .stagingSpaces(
+ ToManyRelationship.builder()
+ .data(
+ Relationship
+ .builder()
+ .id(
+ "space-guid-1")
+ .build())
+ .data(
+ Relationship
+ .builder()
+ .id(
+ "space-guid-2")
+ .build())
+ .build())
+ .runningSpaces(
+ ToManyRelationship.builder()
+ .build())
+ .build())
+ .link(
+ "self",
+ Link.builder()
+ .href(
+ "https://api.example.org/v3/security_groups/b85a788e-671f-4549-814d-e34cdb2f539a")
+ .build())
+ .build())
+ .resource(
+ SecurityGroupResource.builder()
+ .name("my-group1")
+ .id("a89a788e-671f-4549-814d-e34c1b2f533a")
+ .createdAt("2020-02-20T17:42:08Z")
+ .updatedAt("2020-02-20T17:42:08Z")
+ .relationships(
+ Relationships.builder()
+ .stagingSpaces(
+ ToManyRelationship.builder()
+ .build())
+ .runningSpaces(
+ ToManyRelationship.builder()
+ .build())
+ .build())
+ .globallyEnabled(
+ GloballyEnabled.builder()
+ .staging(true)
+ .running(true)
+ .build())
+ .globallyEnabled(
+ GloballyEnabled.builder()
+ .staging(true)
+ .running(true)
+ .build())
+ .link(
+ "self",
+ Link.builder()
+ .href(
+ "https://api.example.org/v3/security_groups/a89a788e-671f-4549-814d-e34c1b2f533a")
+ .build())
+ .build())
+ .build())
+ .expectComplete()
+ .verify(Duration.ofSeconds(5));
+ }
+
+ @Test
+ public void update() {
+ mockRequest(
+ InteractionContext.builder()
+ .request(
+ TestRequest.builder()
+ .method(PATCH)
+ .path(
+ "/security_groups/b85a788e-671f-4549-814d-e34cdb2f539a")
+ .payload(
+ "fixtures/client/v3/security_groups/PATCH_{id}_request.json")
+ .build())
+ .response(
+ TestResponse.builder()
+ .status(OK)
+ .payload(
+ "fixtures/client/v3/security_groups/PATCH_{id}_response.json")
+ .build())
+ .build());
+ this.securityGroups
+ .update(
+ UpdateSecurityGroupRequest.builder()
+ .name("my-group0")
+ .securityGroupId("b85a788e-671f-4549-814d-e34cdb2f539a")
+ .globallyEnabled(GloballyEnabled.builder().running(true).build())
+ .rules(
+ Rule.builder()
+ .protocol(Protocol.TCP)
+ .destination("10.10.10.0/24")
+ .ports("443,80,8080")
+ .build())
+ .rules(
+ Rule.builder()
+ .protocol(Protocol.ICMP)
+ .destination("10.10.10.0/24")
+ .description(
+ "Allow ping requests to private services")
+ .type(8)
+ .code(0)
+ .build())
+ .build())
+ .as(StepVerifier::create)
+ .expectNext(
+ UpdateSecurityGroupResponse.builder()
+ .name("my-group0")
+ .id("b85a788e-671f-4549-814d-e34cdb2f539a")
+ .createdAt("2020-02-20T17:42:08Z")
+ .updatedAt("2020-02-20T17:42:08Z")
+ .globallyEnabled(
+ GloballyEnabled.builder()
+ .staging(false)
+ .running(true)
+ .build())
+ .rules(
+ Rule.builder()
+ .protocol(Protocol.TCP)
+ .destination("10.10.10.0/24")
+ .ports("443,80,8080")
+ .build())
+ .rules(
+ Rule.builder()
+ .protocol(Protocol.ICMP)
+ .destination("10.10.10.0/24")
+ .description(
+ "Allow ping requests to private services")
+ .type(8)
+ .code(0)
+ .build())
+ .relationships(
+ Relationships.builder()
+ .stagingSpaces(
+ ToManyRelationship.builder()
+ .data(
+ Relationship.builder()
+ .id("space-guid-1")
+ .build())
+ .data(
+ Relationship.builder()
+ .id("space-guid-2")
+ .build())
+ .build())
+ .runningSpaces(ToManyRelationship.builder().build())
+ .build())
+ .link(
+ "self",
+ Link.builder()
+ .href(
+ "https://api.example.org/v3/security_groups/b85a788e-671f-4549-814d-e34cdb2f539a")
+ .build())
+ .build())
+ .expectComplete()
+ .verify(Duration.ofSeconds(5));
+ }
+
+ @Test
+ public void delete() {
+ mockRequest(
+ InteractionContext.builder()
+ .request(
+ TestRequest.builder()
+ .method(DELETE)
+ .path(
+ "/security_groups/b85a788e-671f-4549-814d-e34cdb2f539a")
+ .build())
+ .response(
+ TestResponse.builder()
+ .status(ACCEPTED)
+ .header(
+ "Location",
+ "https://api.example.org/v3/jobs/b85a788e-671f-4549-814d-e34cdb2f539a")
+ .build())
+ .build());
+
+ this.securityGroups
+ .delete(
+ DeleteSecurityGroupRequest.builder()
+ .securityGroupId("b85a788e-671f-4549-814d-e34cdb2f539a")
+ .build())
+ .as(StepVerifier::create)
+ .expectNext("b85a788e-671f-4549-814d-e34cdb2f539a")
+ .expectComplete()
+ .verify(Duration.ofSeconds(5));
+ }
+
+ @Test
+ public void bindStagingSecurityGroup() {
+ mockRequest(
+ InteractionContext.builder()
+ .request(
+ TestRequest.builder()
+ .method(POST)
+ .path(
+ "/security_groups/b85a788e-671f-4549-814d-e34cdb2f539a/relationships/staging_spaces")
+ .payload(
+ "fixtures/client/v3/security_groups/bind_staging/POST_request.json")
+ .build())
+ .response(
+ TestResponse.builder()
+ .status(OK)
+ .payload(
+ "fixtures/client/v3/security_groups/bind_staging/POST_response.json")
+ .build())
+ .build());
+ this.securityGroups
+ .bindStagingSecurityGroup(
+ BindStagingSecurityGroupRequest.builder()
+ .securityGroupId("b85a788e-671f-4549-814d-e34cdb2f539a")
+ .boundSpaces(Relationship.builder().id("space-guid1").build())
+ .boundSpaces(Relationship.builder().id("space-guid2").build())
+ .build())
+ .as(StepVerifier::create)
+ .expectNext(
+ BindStagingSecurityGroupResponse.builder()
+ .boundSpaces(Relationship.builder().id("space-guid1").build())
+ .boundSpaces(Relationship.builder().id("space-guid2").build())
+ .boundSpaces(
+ Relationship.builder().id("previous-space-guid").build())
+ .link(
+ "self",
+ Link.builder()
+ .href(
+ "https://api.example.org/v3/security_groups/b85a788e-671f-4549-814d-e34cdb2f539a/relationships/staging_spaces")
+ .build())
+ .build())
+ .expectComplete()
+ .verify(Duration.ofSeconds(5));
+ }
+
+ @Test
+ public void bindRunningSecurityGroup() {
+ mockRequest(
+ InteractionContext.builder()
+ .request(
+ TestRequest.builder()
+ .method(POST)
+ .path(
+ "/security_groups/b85a788e-671f-4549-814d-e34cdb2f539a/relationships/running_spaces")
+ .payload(
+ "fixtures/client/v3/security_groups/bind_running/POST_request.json")
+ .build())
+ .response(
+ TestResponse.builder()
+ .status(OK)
+ .payload(
+ "fixtures/client/v3/security_groups/bind_running/POST_response.json")
+ .build())
+ .build());
+ this.securityGroups
+ .bindRunningSecurityGroup(
+ BindRunningSecurityGroupRequest.builder()
+ .securityGroupId("b85a788e-671f-4549-814d-e34cdb2f539a")
+ .boundSpaces(Relationship.builder().id("space-guid1").build())
+ .boundSpaces(Relationship.builder().id("space-guid2").build())
+ .build())
+ .as(StepVerifier::create)
+ .expectNext(
+ BindRunningSecurityGroupResponse.builder()
+ .boundSpaces(Relationship.builder().id("space-guid1").build())
+ .boundSpaces(Relationship.builder().id("space-guid2").build())
+ .boundSpaces(
+ Relationship.builder().id("previous-space-guid").build())
+ .link(
+ "self",
+ Link.builder()
+ .href(
+ "https://api.example.org/v3/security_groups/b85a788e-671f-4549-814d-e34cdb2f539a/relationships/running_spaces")
+ .build())
+ .build())
+ .expectComplete()
+ .verify(Duration.ofSeconds(5));
+ }
+
+ @Test
+ public void unbindStagingSecurityGroup() {
+ mockRequest(
+ InteractionContext.builder()
+ .request(
+ TestRequest.builder()
+ .method(DELETE)
+ .path(
+ "/security_groups/b85a788e-671f-4549-814d-e34cdb2f539a/relationships/staging_spaces/space-guid-1")
+ .build())
+ .response(TestResponse.builder().status(NO_CONTENT).build())
+ .build());
+ this.securityGroups
+ .unbindStagingSecurityGroup(
+ UnbindStagingSecurityGroupRequest.builder()
+ .securityGroupId("b85a788e-671f-4549-814d-e34cdb2f539a")
+ .spaceId("space-guid-1")
+ .build())
+ .as(StepVerifier::create)
+ .expectNextCount(0)
+ .verifyComplete();
+ }
+
+ @Test
+ public void unbindRunningSecurityGroup() {
+ mockRequest(
+ InteractionContext.builder()
+ .request(
+ TestRequest.builder()
+ .method(DELETE)
+ .path(
+ "/security_groups/b85a788e-671f-4549-814d-e34cdb2f539a/relationships/running_spaces/space-guid-1")
+ .build())
+ .response(TestResponse.builder().status(NO_CONTENT).build())
+ .build());
+ this.securityGroups
+ .unbindRunningSecurityGroup(
+ UnbindRunningSecurityGroupRequest.builder()
+ .securityGroupId("b85a788e-671f-4549-814d-e34cdb2f539a")
+ .spaceId("space-guid-1")
+ .build())
+ .as(StepVerifier::create)
+ .expectNextCount(0)
+ .verifyComplete();
+ }
+
+ @Test
+ public void listRunning() {
+ mockRequest(
+ InteractionContext.builder()
+ .request(
+ TestRequest.builder()
+ .method(GET)
+ .path(
+ "/spaces/c5048979-53b9-4d2a-9fca-78e6bc07c041/running_security_groups")
+ .build())
+ .response(
+ TestResponse.builder()
+ .status(OK)
+ .payload(
+ "fixtures/client/v3/security_groups/GET_running_{id}_response.json")
+ .build())
+ .build());
+
+ this.securityGroups
+ .listRunning(
+ ListRunningSecurityGroupsRequest.builder()
+ .spaceId("c5048979-53b9-4d2a-9fca-78e6bc07c041")
+ .build())
+ .as(StepVerifier::create)
+ .expectNext(
+ ListRunningSecurityGroupsResponse.builder()
+ .pagination(
+ Pagination.builder()
+ .totalResults(1)
+ .totalPages(1)
+ .first(
+ Link.builder()
+ .href(
+ "https://api.example.org/v3/spaces/c5048979-53b9-4d2a-9fca-78e6bc07c041/running_security_groups?page=1&per_page=50")
+ .build())
+ .last(
+ Link.builder()
+ .href(
+ "https://api.example.org/v3/spaces/c5048979-53b9-4d2a-9fca-78e6bc07c041/running_security_groups?page=1&per_page=50")
+ .build())
+ .build())
+ .resource(
+ SecurityGroupResource.builder()
+ .name("my-group0")
+ .id("b85a788e-671f-4549-814d-e34cdb2f539a")
+ .createdAt("2020-02-20T17:42:08Z")
+ .updatedAt("2020-02-20T17:42:08Z")
+ .globallyEnabled(
+ GloballyEnabled.builder()
+ .staging(false)
+ .running(true)
+ .build())
+ .rules(
+ Rule.builder()
+ .protocol(Protocol.TCP)
+ .destination("10.10.10.0/24")
+ .ports("443,80,8080")
+ .build())
+ .rules(
+ Rule.builder()
+ .protocol(Protocol.ICMP)
+ .destination("10.10.10.0/24")
+ .description(
+ "Allow ping requests to"
+ + " private services")
+ .type(8)
+ .code(0)
+ .build())
+ .relationships(
+ Relationships.builder()
+ .stagingSpaces(
+ ToManyRelationship.builder()
+ .build())
+ .runningSpaces(
+ ToManyRelationship.builder()
+ .data(
+ Relationship
+ .builder()
+ .id(
+ "space-guid-1")
+ .build())
+ .data(
+ Relationship
+ .builder()
+ .id(
+ "space-guid-2")
+ .build())
+ .build())
+ .build())
+ .link(
+ "self",
+ Link.builder()
+ .href(
+ "https://api.example.org/v3/security_groups/b85a788e-671f-4549-814d-e34cdb2f539a")
+ .build())
+ .build())
+ .resource(
+ SecurityGroupResource.builder()
+ .name("my-group1")
+ .id("a89a788e-671f-4549-814d-e34c1b2f533a")
+ .createdAt("2020-02-20T17:42:08Z")
+ .updatedAt("2020-02-20T17:42:08Z")
+ .relationships(
+ Relationships.builder()
+ .stagingSpaces(
+ ToManyRelationship.builder()
+ .build())
+ .runningSpaces(
+ ToManyRelationship.builder()
+ .build())
+ .build())
+ .globallyEnabled(
+ GloballyEnabled.builder()
+ .staging(true)
+ .running(true)
+ .build())
+ .globallyEnabled(
+ GloballyEnabled.builder()
+ .staging(true)
+ .running(true)
+ .build())
+ .link(
+ "self",
+ Link.builder()
+ .href(
+ "https://api.example.org/v3/security_groups/a89a788e-671f-4549-814d-e34c1b2f533a")
+ .build())
+ .build())
+ .build())
+ .expectComplete()
+ .verify(Duration.ofSeconds(5));
+ }
+
+ @Test
+ public void listStaging() {
+ mockRequest(
+ InteractionContext.builder()
+ .request(
+ TestRequest.builder()
+ .method(GET)
+ .path(
+ "/spaces/c5048979-53b9-4d2a-9fca-78e6bc07c041/staging_security_groups")
+ .build())
+ .response(
+ TestResponse.builder()
+ .status(OK)
+ .payload(
+ "fixtures/client/v3/security_groups/GET_staging_{id}_response.json")
+ .build())
+ .build());
+
+ this.securityGroups
+ .listStaging(
+ ListStagingSecurityGroupsRequest.builder()
+ .spaceId("c5048979-53b9-4d2a-9fca-78e6bc07c041")
+ .build())
+ .as(StepVerifier::create)
+ .expectNext(
+ ListStagingSecurityGroupsResponse.builder()
+ .pagination(
+ Pagination.builder()
+ .totalResults(1)
+ .totalPages(1)
+ .first(
+ Link.builder()
+ .href(
+ "https://api.example.org/v3/spaces/c5048979-53b9-4d2a-9fca-78e6bc07c041/staging_security_groups?page=1&per_page=50")
+ .build())
+ .last(
+ Link.builder()
+ .href(
+ "https://api.example.org/v3/spaces/c5048979-53b9-4d2a-9fca-78e6bc07c041/staging_security_groups?page=1&per_page=50")
+ .build())
+ .build())
+ .resource(
+ SecurityGroupResource.builder()
+ .name("my-group0")
+ .id("b85a788e-671f-4549-814d-e34cdb2f539a")
+ .createdAt("2020-02-20T17:42:08Z")
+ .updatedAt("2020-02-20T17:42:08Z")
+ .globallyEnabled(
+ GloballyEnabled.builder()
+ .staging(true)
+ .running(false)
+ .build())
+ .rules(
+ Rule.builder()
+ .protocol(Protocol.TCP)
+ .destination("10.10.10.0/24")
+ .ports("443,80,8080")
+ .build())
+ .rules(
+ Rule.builder()
+ .protocol(Protocol.ICMP)
+ .destination("10.10.10.0/24")
+ .description(
+ "Allow ping requests to"
+ + " private services")
+ .type(8)
+ .code(0)
+ .build())
+ .relationships(
+ Relationships.builder()
+ .stagingSpaces(
+ ToManyRelationship.builder()
+ .data(
+ Relationship
+ .builder()
+ .id(
+ "space-guid-1")
+ .build())
+ .data(
+ Relationship
+ .builder()
+ .id(
+ "space-guid-2")
+ .build())
+ .build())
+ .runningSpaces(
+ ToManyRelationship.builder()
+ .build())
+ .build())
+ .link(
+ "self",
+ Link.builder()
+ .href(
+ "https://api.example.org/v3/security_groups/b85a788e-671f-4549-814d-e34cdb2f539a")
+ .build())
+ .build())
+ .resource(
+ SecurityGroupResource.builder()
+ .name("my-group1")
+ .id("a89a788e-671f-4549-814d-e34c1b2f533a")
+ .createdAt("2020-02-20T17:42:08Z")
+ .updatedAt("2020-02-20T17:42:08Z")
+ .relationships(
+ Relationships.builder()
+ .stagingSpaces(
+ ToManyRelationship.builder()
+ .build())
+ .runningSpaces(
+ ToManyRelationship.builder()
+ .build())
+ .build())
+ .globallyEnabled(
+ GloballyEnabled.builder()
+ .staging(true)
+ .running(true)
+ .build())
+ .globallyEnabled(
+ GloballyEnabled.builder()
+ .staging(true)
+ .running(true)
+ .build())
+ .link(
+ "self",
+ Link.builder()
+ .href(
+ "https://api.example.org/v3/security_groups/a89a788e-671f-4549-814d-e34c1b2f533a")
+ .build())
+ .build())
+ .build())
+ .expectComplete()
+ .verify(Duration.ofSeconds(5));
+ }
+}
diff --git a/cloudfoundry-client-reactor/src/test/resources/fixtures/client/v3/security_groups/GET_response.json b/cloudfoundry-client-reactor/src/test/resources/fixtures/client/v3/security_groups/GET_response.json
new file mode 100644
index 00000000000..9e39d26c2a1
--- /dev/null
+++ b/cloudfoundry-client-reactor/src/test/resources/fixtures/client/v3/security_groups/GET_response.json
@@ -0,0 +1,84 @@
+{
+ "pagination": {
+ "total_results": 1,
+ "total_pages": 1,
+ "first": {
+ "href": "https://api.example.org/v3/security_groups?page=1&per_page=50"
+ },
+ "last": {
+ "href": "https://api.example.org/v3/security_groups?page=1&per_page=50"
+ },
+ "next": null,
+ "previous": null
+ },
+ "resources": [
+ {
+ "guid": "b85a788e-671f-4549-814d-e34cdb2f539a",
+ "created_at": "2020-02-20T17:42:08Z",
+ "updated_at": "2020-02-20T17:42:08Z",
+ "name": "my-group0",
+ "globally_enabled": {
+ "running": true,
+ "staging": false
+ },
+ "rules": [
+ {
+ "protocol": "tcp",
+ "destination": "10.10.10.0/24",
+ "ports": "443,80,8080"
+ },
+ {
+ "protocol": "icmp",
+ "destination": "10.10.10.0/24",
+ "type": 8,
+ "code": 0,
+ "description": "Allow ping requests to private services"
+ }
+ ],
+ "relationships": {
+ "staging_spaces": {
+ "data": [
+ {
+ "guid": "space-guid-1"
+ },
+ {
+ "guid": "space-guid-2"
+ }
+ ]
+ },
+ "running_spaces": {
+ "data": []
+ }
+ },
+ "links": {
+ "self": {
+ "href": "https://api.example.org/v3/security_groups/b85a788e-671f-4549-814d-e34cdb2f539a"
+ }
+ }
+ },
+ {
+ "guid": "a89a788e-671f-4549-814d-e34c1b2f533a",
+ "created_at": "2020-02-20T17:42:08Z",
+ "updated_at": "2020-02-20T17:42:08Z",
+ "name": "my-group1",
+ "globally_enabled": {
+ "running": true,
+ "staging": true
+ },
+ "rules": [],
+ "relationships": {
+ "staging_spaces": {
+ "data": []
+ },
+ "running_spaces": {
+ "data": []
+ }
+ },
+ "links": {
+ "self": {
+ "href": "https://api.example.org/v3/security_groups/a89a788e-671f-4549-814d-e34c1b2f533a"
+ }
+ }
+ }
+ ]
+}
\ No newline at end of file
diff --git a/cloudfoundry-client-reactor/src/test/resources/fixtures/client/v3/security_groups/GET_running_{id}_response.json b/cloudfoundry-client-reactor/src/test/resources/fixtures/client/v3/security_groups/GET_running_{id}_response.json
new file mode 100644
index 00000000000..c02b6b95267
--- /dev/null
+++ b/cloudfoundry-client-reactor/src/test/resources/fixtures/client/v3/security_groups/GET_running_{id}_response.json
@@ -0,0 +1,84 @@
+{
+ "pagination": {
+ "total_results": 1,
+ "total_pages": 1,
+ "first": {
+ "href": "https://api.example.org/v3/spaces/c5048979-53b9-4d2a-9fca-78e6bc07c041/running_security_groups?page=1&per_page=50"
+ },
+ "last": {
+ "href": "https://api.example.org/v3/spaces/c5048979-53b9-4d2a-9fca-78e6bc07c041/running_security_groups?page=1&per_page=50"
+ },
+ "next": null,
+ "previous": null
+ },
+ "resources": [
+ {
+ "guid": "b85a788e-671f-4549-814d-e34cdb2f539a",
+ "created_at": "2020-02-20T17:42:08Z",
+ "updated_at": "2020-02-20T17:42:08Z",
+ "name": "my-group0",
+ "globally_enabled": {
+ "running": true,
+ "staging": false
+ },
+ "rules": [
+ {
+ "protocol": "tcp",
+ "destination": "10.10.10.0/24",
+ "ports": "443,80,8080"
+ },
+ {
+ "protocol": "icmp",
+ "destination": "10.10.10.0/24",
+ "type": 8,
+ "code": 0,
+ "description": "Allow ping requests to private services"
+ }
+ ],
+ "relationships": {
+ "staging_spaces": {
+ "data": []
+ },
+ "running_spaces": {
+ "data": [
+ {
+ "guid": "space-guid-1"
+ },
+ {
+ "guid": "space-guid-2"
+ }
+ ]
+ }
+ },
+ "links": {
+ "self": {
+ "href": "https://api.example.org/v3/security_groups/b85a788e-671f-4549-814d-e34cdb2f539a"
+ }
+ }
+ },
+ {
+ "guid": "a89a788e-671f-4549-814d-e34c1b2f533a",
+ "created_at": "2020-02-20T17:42:08Z",
+ "updated_at": "2020-02-20T17:42:08Z",
+ "name": "my-group1",
+ "globally_enabled": {
+ "running": true,
+ "staging": true
+ },
+ "rules": [],
+ "relationships": {
+ "staging_spaces": {
+ "data": []
+ },
+ "running_spaces": {
+ "data": []
+ }
+ },
+ "links": {
+ "self": {
+ "href": "https://api.example.org/v3/security_groups/a89a788e-671f-4549-814d-e34c1b2f533a"
+ }
+ }
+ }
+ ]
+}
\ No newline at end of file
diff --git a/cloudfoundry-client-reactor/src/test/resources/fixtures/client/v3/security_groups/GET_staging_{id}_response.json b/cloudfoundry-client-reactor/src/test/resources/fixtures/client/v3/security_groups/GET_staging_{id}_response.json
new file mode 100644
index 00000000000..b9dede15381
--- /dev/null
+++ b/cloudfoundry-client-reactor/src/test/resources/fixtures/client/v3/security_groups/GET_staging_{id}_response.json
@@ -0,0 +1,84 @@
+{
+ "pagination": {
+ "total_results": 1,
+ "total_pages": 1,
+ "first": {
+ "href": "https://api.example.org/v3/spaces/c5048979-53b9-4d2a-9fca-78e6bc07c041/staging_security_groups?page=1&per_page=50"
+ },
+ "last": {
+ "href": "https://api.example.org/v3/spaces/c5048979-53b9-4d2a-9fca-78e6bc07c041/staging_security_groups?page=1&per_page=50"
+ },
+ "next": null,
+ "previous": null
+ },
+ "resources": [
+ {
+ "guid": "b85a788e-671f-4549-814d-e34cdb2f539a",
+ "created_at": "2020-02-20T17:42:08Z",
+ "updated_at": "2020-02-20T17:42:08Z",
+ "name": "my-group0",
+ "globally_enabled": {
+ "running": false,
+ "staging": true
+ },
+ "rules": [
+ {
+ "protocol": "tcp",
+ "destination": "10.10.10.0/24",
+ "ports": "443,80,8080"
+ },
+ {
+ "protocol": "icmp",
+ "destination": "10.10.10.0/24",
+ "type": 8,
+ "code": 0,
+ "description": "Allow ping requests to private services"
+ }
+ ],
+ "relationships": {
+ "staging_spaces": {
+ "data": [
+ {
+ "guid": "space-guid-1"
+ },
+ {
+ "guid": "space-guid-2"
+ }
+ ]
+ },
+ "running_spaces": {
+ "data": []
+ }
+ },
+ "links": {
+ "self": {
+ "href": "https://api.example.org/v3/security_groups/b85a788e-671f-4549-814d-e34cdb2f539a"
+ }
+ }
+ },
+ {
+ "guid": "a89a788e-671f-4549-814d-e34c1b2f533a",
+ "created_at": "2020-02-20T17:42:08Z",
+ "updated_at": "2020-02-20T17:42:08Z",
+ "name": "my-group1",
+ "globally_enabled": {
+ "running": true,
+ "staging": true
+ },
+ "rules": [],
+ "relationships": {
+ "staging_spaces": {
+ "data": []
+ },
+ "running_spaces": {
+ "data": []
+ }
+ },
+ "links": {
+ "self": {
+ "href": "https://api.example.org/v3/security_groups/a89a788e-671f-4549-814d-e34c1b2f533a"
+ }
+ }
+ }
+ ]
+}
\ No newline at end of file
diff --git a/cloudfoundry-client-reactor/src/test/resources/fixtures/client/v3/security_groups/GET_{id}_response.json b/cloudfoundry-client-reactor/src/test/resources/fixtures/client/v3/security_groups/GET_{id}_response.json
new file mode 100644
index 00000000000..5c1290a8e35
--- /dev/null
+++ b/cloudfoundry-client-reactor/src/test/resources/fixtures/client/v3/security_groups/GET_{id}_response.json
@@ -0,0 +1,44 @@
+{
+ "guid": "b85a788e-671f-4549-814d-e34cdb2f539a",
+ "created_at": "2020-02-20T17:42:08Z",
+ "updated_at": "2020-02-20T17:42:08Z",
+ "name": "my-group0",
+ "globally_enabled": {
+ "running": true,
+ "staging": false
+ },
+ "rules": [
+ {
+ "protocol": "tcp",
+ "destination": "10.10.10.0/24",
+ "ports": "443,80,8080"
+ },
+ {
+ "protocol": "icmp",
+ "destination": "10.10.10.0/24",
+ "type": 8,
+ "code": 0,
+ "description": "Allow ping requests to private services"
+ }
+ ],
+ "relationships": {
+ "staging_spaces": {
+ "data": [
+ {
+ "guid": "space-guid-1"
+ },
+ {
+ "guid": "space-guid-2"
+ }
+ ]
+ },
+ "running_spaces": {
+ "data": []
+ }
+ },
+ "links": {
+ "self": {
+ "href": "https://api.example.org/v3/security_groups/b85a788e-671f-4549-814d-e34cdb2f539a"
+ }
+ }
+}
\ No newline at end of file
diff --git a/cloudfoundry-client-reactor/src/test/resources/fixtures/client/v3/security_groups/PATCH_{id}_request.json b/cloudfoundry-client-reactor/src/test/resources/fixtures/client/v3/security_groups/PATCH_{id}_request.json
new file mode 100644
index 00000000000..7b146d9a9b5
--- /dev/null
+++ b/cloudfoundry-client-reactor/src/test/resources/fixtures/client/v3/security_groups/PATCH_{id}_request.json
@@ -0,0 +1,20 @@
+{
+ "name": "my-group0",
+ "globally_enabled": {
+ "running": true
+ },
+ "rules": [
+ {
+ "protocol": "tcp",
+ "destination": "10.10.10.0/24",
+ "ports": "443,80,8080"
+ },
+ {
+ "protocol": "icmp",
+ "destination": "10.10.10.0/24",
+ "type": 8,
+ "code": 0,
+ "description": "Allow ping requests to private services"
+ }
+ ]
+}
\ No newline at end of file
diff --git a/cloudfoundry-client-reactor/src/test/resources/fixtures/client/v3/security_groups/PATCH_{id}_response.json b/cloudfoundry-client-reactor/src/test/resources/fixtures/client/v3/security_groups/PATCH_{id}_response.json
new file mode 100644
index 00000000000..5c1290a8e35
--- /dev/null
+++ b/cloudfoundry-client-reactor/src/test/resources/fixtures/client/v3/security_groups/PATCH_{id}_response.json
@@ -0,0 +1,44 @@
+{
+ "guid": "b85a788e-671f-4549-814d-e34cdb2f539a",
+ "created_at": "2020-02-20T17:42:08Z",
+ "updated_at": "2020-02-20T17:42:08Z",
+ "name": "my-group0",
+ "globally_enabled": {
+ "running": true,
+ "staging": false
+ },
+ "rules": [
+ {
+ "protocol": "tcp",
+ "destination": "10.10.10.0/24",
+ "ports": "443,80,8080"
+ },
+ {
+ "protocol": "icmp",
+ "destination": "10.10.10.0/24",
+ "type": 8,
+ "code": 0,
+ "description": "Allow ping requests to private services"
+ }
+ ],
+ "relationships": {
+ "staging_spaces": {
+ "data": [
+ {
+ "guid": "space-guid-1"
+ },
+ {
+ "guid": "space-guid-2"
+ }
+ ]
+ },
+ "running_spaces": {
+ "data": []
+ }
+ },
+ "links": {
+ "self": {
+ "href": "https://api.example.org/v3/security_groups/b85a788e-671f-4549-814d-e34cdb2f539a"
+ }
+ }
+}
\ No newline at end of file
diff --git a/cloudfoundry-client-reactor/src/test/resources/fixtures/client/v3/security_groups/POST_request.json b/cloudfoundry-client-reactor/src/test/resources/fixtures/client/v3/security_groups/POST_request.json
new file mode 100644
index 00000000000..35e74431db8
--- /dev/null
+++ b/cloudfoundry-client-reactor/src/test/resources/fixtures/client/v3/security_groups/POST_request.json
@@ -0,0 +1,17 @@
+{
+ "name": "my-group0",
+ "rules": [
+ {
+ "protocol": "tcp",
+ "destination": "10.10.10.0/24",
+ "ports": "443,80,8080"
+ },
+ {
+ "protocol": "icmp",
+ "destination": "10.10.10.0/24",
+ "type": 8,
+ "code": 0,
+ "description": "Allow ping requests to private services"
+ }
+ ]
+}
\ No newline at end of file
diff --git a/cloudfoundry-client-reactor/src/test/resources/fixtures/client/v3/security_groups/POST_response.json b/cloudfoundry-client-reactor/src/test/resources/fixtures/client/v3/security_groups/POST_response.json
new file mode 100644
index 00000000000..5c1290a8e35
--- /dev/null
+++ b/cloudfoundry-client-reactor/src/test/resources/fixtures/client/v3/security_groups/POST_response.json
@@ -0,0 +1,44 @@
+{
+ "guid": "b85a788e-671f-4549-814d-e34cdb2f539a",
+ "created_at": "2020-02-20T17:42:08Z",
+ "updated_at": "2020-02-20T17:42:08Z",
+ "name": "my-group0",
+ "globally_enabled": {
+ "running": true,
+ "staging": false
+ },
+ "rules": [
+ {
+ "protocol": "tcp",
+ "destination": "10.10.10.0/24",
+ "ports": "443,80,8080"
+ },
+ {
+ "protocol": "icmp",
+ "destination": "10.10.10.0/24",
+ "type": 8,
+ "code": 0,
+ "description": "Allow ping requests to private services"
+ }
+ ],
+ "relationships": {
+ "staging_spaces": {
+ "data": [
+ {
+ "guid": "space-guid-1"
+ },
+ {
+ "guid": "space-guid-2"
+ }
+ ]
+ },
+ "running_spaces": {
+ "data": []
+ }
+ },
+ "links": {
+ "self": {
+ "href": "https://api.example.org/v3/security_groups/b85a788e-671f-4549-814d-e34cdb2f539a"
+ }
+ }
+}
\ No newline at end of file
diff --git a/cloudfoundry-client-reactor/src/test/resources/fixtures/client/v3/security_groups/bind_running/POST_request.json b/cloudfoundry-client-reactor/src/test/resources/fixtures/client/v3/security_groups/bind_running/POST_request.json
new file mode 100644
index 00000000000..eaa31060cad
--- /dev/null
+++ b/cloudfoundry-client-reactor/src/test/resources/fixtures/client/v3/security_groups/bind_running/POST_request.json
@@ -0,0 +1,10 @@
+{
+ "data": [
+ {
+ "guid": "space-guid1"
+ },
+ {
+ "guid": "space-guid2"
+ }
+ ]
+}
\ No newline at end of file
diff --git a/cloudfoundry-client-reactor/src/test/resources/fixtures/client/v3/security_groups/bind_running/POST_response.json b/cloudfoundry-client-reactor/src/test/resources/fixtures/client/v3/security_groups/bind_running/POST_response.json
new file mode 100644
index 00000000000..35ce11babff
--- /dev/null
+++ b/cloudfoundry-client-reactor/src/test/resources/fixtures/client/v3/security_groups/bind_running/POST_response.json
@@ -0,0 +1,18 @@
+{
+ "data": [
+ {
+ "guid": "space-guid1"
+ },
+ {
+ "guid": "space-guid2"
+ },
+ {
+ "guid": "previous-space-guid"
+ }
+ ],
+ "links": {
+ "self": {
+ "href": "https://api.example.org/v3/security_groups/b85a788e-671f-4549-814d-e34cdb2f539a/relationships/running_spaces"
+ }
+ }
+}
\ No newline at end of file
diff --git a/cloudfoundry-client-reactor/src/test/resources/fixtures/client/v3/security_groups/bind_staging/POST_request.json b/cloudfoundry-client-reactor/src/test/resources/fixtures/client/v3/security_groups/bind_staging/POST_request.json
new file mode 100644
index 00000000000..eaa31060cad
--- /dev/null
+++ b/cloudfoundry-client-reactor/src/test/resources/fixtures/client/v3/security_groups/bind_staging/POST_request.json
@@ -0,0 +1,10 @@
+{
+ "data": [
+ {
+ "guid": "space-guid1"
+ },
+ {
+ "guid": "space-guid2"
+ }
+ ]
+}
\ No newline at end of file
diff --git a/cloudfoundry-client-reactor/src/test/resources/fixtures/client/v3/security_groups/bind_staging/POST_response.json b/cloudfoundry-client-reactor/src/test/resources/fixtures/client/v3/security_groups/bind_staging/POST_response.json
new file mode 100644
index 00000000000..06b4b5914d5
--- /dev/null
+++ b/cloudfoundry-client-reactor/src/test/resources/fixtures/client/v3/security_groups/bind_staging/POST_response.json
@@ -0,0 +1,18 @@
+{
+ "data": [
+ {
+ "guid": "space-guid1"
+ },
+ {
+ "guid": "space-guid2"
+ },
+ {
+ "guid": "previous-space-guid"
+ }
+ ],
+ "links": {
+ "self": {
+ "href": "https://api.example.org/v3/security_groups/b85a788e-671f-4549-814d-e34cdb2f539a/relationships/staging_spaces"
+ }
+ }
+}
\ No newline at end of file
diff --git a/cloudfoundry-client/src/main/java/org/cloudfoundry/client/CloudFoundryClient.java b/cloudfoundry-client/src/main/java/org/cloudfoundry/client/CloudFoundryClient.java
index 5e487ed3769..446319cf4b3 100644
--- a/cloudfoundry-client/src/main/java/org/cloudfoundry/client/CloudFoundryClient.java
+++ b/cloudfoundry-client/src/main/java/org/cloudfoundry/client/CloudFoundryClient.java
@@ -63,6 +63,7 @@
import org.cloudfoundry.client.v3.resourcematch.ResourceMatchV3;
import org.cloudfoundry.client.v3.roles.RolesV3;
import org.cloudfoundry.client.v3.routes.RoutesV3;
+import org.cloudfoundry.client.v3.securitygroups.SecurityGroupsV3;
import org.cloudfoundry.client.v3.servicebindings.ServiceBindingsV3;
import org.cloudfoundry.client.v3.servicebrokers.ServiceBrokersV3;
import org.cloudfoundry.client.v3.serviceinstances.ServiceInstancesV3;
@@ -247,6 +248,11 @@ public interface CloudFoundryClient {
*/
SecurityGroups securityGroups();
+ /**
+ * Main entry point to the Cloud Foundry Security Groups V3 Client API
+ */
+ SecurityGroupsV3 securityGroupsV3();
+
/**
* Main entry point to the Cloud Foundry Service Bindings V2 Client API
*/
@@ -348,7 +354,8 @@ public interface CloudFoundryClient {
Tasks tasks();
/**
- * Main entry point to the Cloud Foundry User Provided Service Instances Client API
+ * Main entry point to the Cloud Foundry User Provided Service Instances Client
+ * API
*/
UserProvidedServiceInstances userProvidedServiceInstances();
diff --git a/cloudfoundry-client/src/main/java/org/cloudfoundry/client/v3/securitygroups/AbstractBindSecurityGroupRequest.java b/cloudfoundry-client/src/main/java/org/cloudfoundry/client/v3/securitygroups/AbstractBindSecurityGroupRequest.java
new file mode 100644
index 00000000000..daef64716e7
--- /dev/null
+++ b/cloudfoundry-client/src/main/java/org/cloudfoundry/client/v3/securitygroups/AbstractBindSecurityGroupRequest.java
@@ -0,0 +1,38 @@
+/*
+ * Copyright 2013-2023 the original author or authors.
+ *
+ * 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 org.cloudfoundry.client.v3.securitygroups;
+
+import com.fasterxml.jackson.annotation.JsonIgnore;
+import com.fasterxml.jackson.annotation.JsonProperty;
+import com.fasterxml.jackson.databind.annotation.JsonSerialize;
+import java.util.List;
+import org.cloudfoundry.client.v3.Relationship;
+
+@JsonSerialize
+public abstract class AbstractBindSecurityGroupRequest {
+
+ /**
+ * The Security Group id
+ */
+ @JsonIgnore
+ abstract String getSecurityGroupId();
+
+ /**
+ * A relationship to the spaces where the security_group is applied to
+ * applications during runtime
+ */
+ @JsonProperty("data")
+ abstract List getBoundSpaces();
+}
diff --git a/cloudfoundry-client/src/main/java/org/cloudfoundry/client/v3/securitygroups/AbstractBindSecurityGroupResponse.java b/cloudfoundry-client/src/main/java/org/cloudfoundry/client/v3/securitygroups/AbstractBindSecurityGroupResponse.java
new file mode 100644
index 00000000000..65838436474
--- /dev/null
+++ b/cloudfoundry-client/src/main/java/org/cloudfoundry/client/v3/securitygroups/AbstractBindSecurityGroupResponse.java
@@ -0,0 +1,41 @@
+/*
+ * Copyright 2013-2023 the original author or authors.
+ *
+ * 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 org.cloudfoundry.client.v3.securitygroups;
+
+import com.fasterxml.jackson.annotation.JsonProperty;
+import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
+import java.util.List;
+import java.util.Map;
+import org.cloudfoundry.AllowNulls;
+import org.cloudfoundry.client.v3.Link;
+import org.cloudfoundry.client.v3.Relationship;
+
+@JsonDeserialize
+public abstract class AbstractBindSecurityGroupResponse {
+
+ /**
+ * A relationship to the spaces where the security_group is applied to applications during
+ * runtime
+ */
+ @JsonProperty("data")
+ abstract List getBoundSpaces();
+
+ /**
+ * The links
+ */
+ @AllowNulls
+ @JsonProperty("links")
+ public abstract Map getLinks();
+}
diff --git a/cloudfoundry-client/src/main/java/org/cloudfoundry/client/v3/securitygroups/AbstractListSecurityGroupRequest.java b/cloudfoundry-client/src/main/java/org/cloudfoundry/client/v3/securitygroups/AbstractListSecurityGroupRequest.java
new file mode 100644
index 00000000000..1a29e8e83cb
--- /dev/null
+++ b/cloudfoundry-client/src/main/java/org/cloudfoundry/client/v3/securitygroups/AbstractListSecurityGroupRequest.java
@@ -0,0 +1,43 @@
+/*
+ * Copyright 2013-2023 the original author or authors.
+ *
+ * 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 org.cloudfoundry.client.v3.securitygroups;
+
+import com.fasterxml.jackson.annotation.JsonIgnore;
+import java.util.List;
+import org.cloudfoundry.Nullable;
+import org.cloudfoundry.client.v2.PaginatedRequest;
+import org.cloudfoundry.client.v3.FilterParameter;
+
+public abstract class AbstractListSecurityGroupRequest extends PaginatedRequest {
+
+ /**
+ * The Space id
+ */
+ @JsonIgnore
+ abstract String getSpaceId();
+
+ /**
+ * The security group ids filter
+ */
+ @FilterParameter("guids")
+ @Nullable
+ abstract List getSecurityGroupIds();
+
+ /**
+ * The security group names filter
+ */
+ @FilterParameter("names")
+ @Nullable
+ abstract List getNames();
+}
diff --git a/cloudfoundry-client/src/main/java/org/cloudfoundry/client/v3/securitygroups/AbstractUnbindSecurityGroupRequest.java b/cloudfoundry-client/src/main/java/org/cloudfoundry/client/v3/securitygroups/AbstractUnbindSecurityGroupRequest.java
new file mode 100644
index 00000000000..7903a2bdacf
--- /dev/null
+++ b/cloudfoundry-client/src/main/java/org/cloudfoundry/client/v3/securitygroups/AbstractUnbindSecurityGroupRequest.java
@@ -0,0 +1,32 @@
+/*
+ * Copyright 2013-2023 the original author or authors.
+ *
+ * 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 org.cloudfoundry.client.v3.securitygroups;
+
+import com.fasterxml.jackson.annotation.JsonIgnore;
+
+public abstract class AbstractUnbindSecurityGroupRequest {
+
+ /**
+ * The Security Group id
+ */
+ @JsonIgnore
+ abstract String getSecurityGroupId();
+
+ /**
+ * The Space id
+ */
+ @JsonIgnore
+ abstract String getSpaceId();
+}
diff --git a/cloudfoundry-client/src/main/java/org/cloudfoundry/client/v3/securitygroups/Protocol.java b/cloudfoundry-client/src/main/java/org/cloudfoundry/client/v3/securitygroups/Protocol.java
new file mode 100644
index 00000000000..7bd2e1eb0e3
--- /dev/null
+++ b/cloudfoundry-client/src/main/java/org/cloudfoundry/client/v3/securitygroups/Protocol.java
@@ -0,0 +1,76 @@
+/*
+ * Copyright 2013-2023 the original author or authors.
+ *
+ * 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 org.cloudfoundry.client.v3.securitygroups;
+
+import com.fasterxml.jackson.annotation.JsonCreator;
+import com.fasterxml.jackson.annotation.JsonValue;
+
+/**
+ * The protocol of a Security Group
+ */
+public enum Protocol {
+
+ /**
+ * All protocols
+ */
+ ALL("all"),
+
+ /**
+ * ICMP protocol
+ */
+ ICMP("icmp"),
+
+ /**
+ * TCP protocol
+ */
+ TCP("tcp"),
+
+ /**
+ * UDP protocol
+ */
+ UDP("udp");
+
+ private final String value;
+
+ Protocol(String value) {
+ this.value = value;
+ }
+
+ @JsonCreator
+ public static Protocol from(String s) {
+ switch (s.toLowerCase()) {
+ case "all":
+ return ALL;
+ case "icmp":
+ return ICMP;
+ case "tcp":
+ return TCP;
+ case "udp":
+ return UDP;
+ default:
+ throw new IllegalArgumentException(String.format("Unknown protocol: %s", s));
+ }
+ }
+
+ @JsonValue
+ public String getValue() {
+ return this.value;
+ }
+
+ @Override
+ public String toString() {
+ return getValue();
+ }
+}
diff --git a/cloudfoundry-client/src/main/java/org/cloudfoundry/client/v3/securitygroups/SecurityGroup.java b/cloudfoundry-client/src/main/java/org/cloudfoundry/client/v3/securitygroups/SecurityGroup.java
new file mode 100644
index 00000000000..0ac31fc985f
--- /dev/null
+++ b/cloudfoundry-client/src/main/java/org/cloudfoundry/client/v3/securitygroups/SecurityGroup.java
@@ -0,0 +1,51 @@
+/*
+ * Copyright 2013-2023 the original author or authors.
+ *
+ * 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 org.cloudfoundry.client.v3.securitygroups;
+
+import com.fasterxml.jackson.annotation.JsonProperty;
+import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
+import java.util.List;
+import org.cloudfoundry.client.v3.Resource;
+
+/**
+ * The entity response payload for the Security Group resource
+ */
+@JsonDeserialize
+public abstract class SecurityGroup extends Resource {
+
+ /**
+ * The name
+ */
+ @JsonProperty("name")
+ abstract String getName();
+
+ /**
+ * The globally enabled
+ */
+ @JsonProperty("globally_enabled")
+ abstract GloballyEnabled getGloballyEnabled();
+
+ /**
+ * The rules
+ */
+ @JsonProperty("rules")
+ abstract List getRules();
+
+ /**
+ * The space relationships
+ */
+ @JsonProperty("relationships")
+ abstract Relationships getRelationships();
+}
diff --git a/cloudfoundry-client/src/main/java/org/cloudfoundry/client/v3/securitygroups/SecurityGroupsV3.java b/cloudfoundry-client/src/main/java/org/cloudfoundry/client/v3/securitygroups/SecurityGroupsV3.java
new file mode 100644
index 00000000000..88e5e6d743c
--- /dev/null
+++ b/cloudfoundry-client/src/main/java/org/cloudfoundry/client/v3/securitygroups/SecurityGroupsV3.java
@@ -0,0 +1,136 @@
+/*
+ * Copyright 2013-2021 the original author or authors.
+ *
+ * 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 org.cloudfoundry.client.v3.securitygroups;
+
+import reactor.core.publisher.Mono;
+
+public interface SecurityGroupsV3 {
+
+ /**
+ * Makes the Creating
+ * a Security Group request.
+ *
+ * @param request the Create Security Group request
+ * @return the response from the Create Security Group request
+ */
+ Mono create(CreateSecurityGroupRequest request);
+
+ /**
+ * Makes the Get
+ * a Security Group request.
+ *
+ * @param request the Get Security Group request
+ * @return the response from the Get Security Group request
+ */
+ Mono get(GetSecurityGroupRequest request);
+
+ /**
+ * Makes the List
+ * Security Groups request
+ *
+ * @param request the List Security Group request
+ * @return the response from the List Security Group request
+ */
+ Mono list(ListSecurityGroupsRequest request);
+
+ /**
+ * Makes the Update
+ * Security Groups request
+ *
+ * @param request the Update Security Group request
+ * @return the response from the Update Security Group request
+ */
+ Mono update(UpdateSecurityGroupRequest request);
+
+ /**
+ * Makes the Delete
+ * Security Groups request
+ *
+ * @param request the Delete Security Group request
+ * @return the response from the Delete Security Group request
+ */
+ Mono delete(DeleteSecurityGroupRequest request);
+
+ /**
+ * Makes the Bind
+ * Staging Security Group request
+ *
+ * @param request the Bind Staging Security Group request
+ * @return the response from the Bind Staging Security Group request
+ */
+ Mono bindStagingSecurityGroup(
+ BindStagingSecurityGroupRequest request);
+
+ /**
+ * Makes the Bind
+ * Running Security Group request
+ *
+ * @param request the Bind Running Security Group request
+ * @return the response from the Bind Running Security Group request
+ */
+ Mono bindRunningSecurityGroup(
+ BindRunningSecurityGroupRequest request);
+
+ /**
+ * Makes the Unbind
+ * Staging
+ * Security Group request
+ *
+ * @param request the Unbind Staging Security Group request
+ * @return the response from the Unbind staging Security Group request
+ */
+ Mono unbindStagingSecurityGroup(UnbindStagingSecurityGroupRequest request);
+
+ /**
+ * Makes the Unbind
+ * Running
+ * Security Groups request
+ *
+ * @param request the Unbind Staging Running Security Group request
+ * @return the response from the Unbind Running Security Group request
+ */
+ Mono unbindRunningSecurityGroup(UnbindRunningSecurityGroupRequest request);
+
+ /**
+ * Makes the List
+ * Running Security Groups request
+ *
+ * @param request the List Staging Security Group request
+ * @return the response from the List Staging Security Group request
+ */
+ Mono listStaging(ListStagingSecurityGroupsRequest request);
+
+ /**
+ * Makes the List
+ * Running Security Groups request
+ *
+ * @param request the List Staging Security Group request
+ * @return the response from the List Running Security Group request
+ */
+ Mono listRunning(ListRunningSecurityGroupsRequest request);
+}
diff --git a/cloudfoundry-client/src/main/java/org/cloudfoundry/client/v3/securitygroups/_BindRunningSecurityGroupRequest.java b/cloudfoundry-client/src/main/java/org/cloudfoundry/client/v3/securitygroups/_BindRunningSecurityGroupRequest.java
new file mode 100644
index 00000000000..0e14195fc77
--- /dev/null
+++ b/cloudfoundry-client/src/main/java/org/cloudfoundry/client/v3/securitygroups/_BindRunningSecurityGroupRequest.java
@@ -0,0 +1,25 @@
+/*
+ * Copyright 2013-2023 the original author or authors.
+ *
+ * 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 org.cloudfoundry.client.v3.securitygroups;
+
+import org.immutables.value.Value;
+
+import com.fasterxml.jackson.databind.annotation.JsonSerialize;
+
+@JsonSerialize
+@Value.Immutable
+abstract class _BindRunningSecurityGroupRequest extends AbstractBindSecurityGroupRequest {
+
+}
diff --git a/cloudfoundry-client/src/main/java/org/cloudfoundry/client/v3/securitygroups/_BindRunningSecurityGroupResponse.java b/cloudfoundry-client/src/main/java/org/cloudfoundry/client/v3/securitygroups/_BindRunningSecurityGroupResponse.java
new file mode 100644
index 00000000000..e299671f9af
--- /dev/null
+++ b/cloudfoundry-client/src/main/java/org/cloudfoundry/client/v3/securitygroups/_BindRunningSecurityGroupResponse.java
@@ -0,0 +1,25 @@
+/*
+ * Copyright 2013-2023 the original author or authors.
+ *
+ * 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 org.cloudfoundry.client.v3.securitygroups;
+
+import org.immutables.value.Value;
+
+import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
+
+@JsonDeserialize
+@Value.Immutable
+public abstract class _BindRunningSecurityGroupResponse extends AbstractBindSecurityGroupResponse {
+
+}
diff --git a/cloudfoundry-client/src/main/java/org/cloudfoundry/client/v3/securitygroups/_BindStagingSecurityGroupRequest.java b/cloudfoundry-client/src/main/java/org/cloudfoundry/client/v3/securitygroups/_BindStagingSecurityGroupRequest.java
new file mode 100644
index 00000000000..36ce1fcadae
--- /dev/null
+++ b/cloudfoundry-client/src/main/java/org/cloudfoundry/client/v3/securitygroups/_BindStagingSecurityGroupRequest.java
@@ -0,0 +1,24 @@
+/*
+ * Copyright 2013-2023 the original author or authors.
+ *
+ * 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 org.cloudfoundry.client.v3.securitygroups;
+
+import org.immutables.value.Value;
+import com.fasterxml.jackson.databind.annotation.JsonSerialize;
+
+@JsonSerialize
+@Value.Immutable
+abstract class _BindStagingSecurityGroupRequest extends AbstractBindSecurityGroupRequest {
+
+}
diff --git a/cloudfoundry-client/src/main/java/org/cloudfoundry/client/v3/securitygroups/_BindStagingSecurityGroupResponse.java b/cloudfoundry-client/src/main/java/org/cloudfoundry/client/v3/securitygroups/_BindStagingSecurityGroupResponse.java
new file mode 100644
index 00000000000..920c3eee7d4
--- /dev/null
+++ b/cloudfoundry-client/src/main/java/org/cloudfoundry/client/v3/securitygroups/_BindStagingSecurityGroupResponse.java
@@ -0,0 +1,24 @@
+/*
+ * Copyright 2013-2023 the original author or authors.
+ *
+ * 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 org.cloudfoundry.client.v3.securitygroups;
+
+import org.immutables.value.Value;
+import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
+
+@JsonDeserialize
+@Value.Immutable
+public abstract class _BindStagingSecurityGroupResponse extends AbstractBindSecurityGroupResponse {
+
+}
diff --git a/cloudfoundry-client/src/main/java/org/cloudfoundry/client/v3/securitygroups/_CreateSecurityGroupRequest.java b/cloudfoundry-client/src/main/java/org/cloudfoundry/client/v3/securitygroups/_CreateSecurityGroupRequest.java
new file mode 100644
index 00000000000..e2d6b4f6c02
--- /dev/null
+++ b/cloudfoundry-client/src/main/java/org/cloudfoundry/client/v3/securitygroups/_CreateSecurityGroupRequest.java
@@ -0,0 +1,60 @@
+/*
+ * Copyright 2013-2021 the original author or authors.
+ *
+ * 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 org.cloudfoundry.client.v3.securitygroups;
+
+import com.fasterxml.jackson.annotation.JsonProperty;
+import com.fasterxml.jackson.databind.annotation.JsonSerialize;
+import org.cloudfoundry.Nullable;
+import org.immutables.value.Value;
+
+import java.util.List;
+
+/**
+ * The request payload for the Create a Security Group operation
+ */
+@JsonSerialize
+@Value.Immutable
+abstract class _CreateSecurityGroupRequest {
+
+ /**
+ * The security group name
+ */
+ @JsonProperty("name")
+ abstract String getName();
+
+ /**
+ * the security group glbally enabled field
+ */
+ @JsonProperty("globally_enabled")
+ @Nullable
+ abstract GloballyEnabled getGloballyEnabled();
+
+ /**
+ * The security group rules
+ */
+ @JsonProperty("rules")
+ @Nullable
+ abstract List getRules();
+
+ /**
+ * The security group relationships
+ */
+ @JsonProperty("relationships")
+ @Nullable
+ abstract Relationships getRelationships();
+
+}
diff --git a/cloudfoundry-client/src/main/java/org/cloudfoundry/client/v3/securitygroups/_CreateSecurityGroupResponse.java b/cloudfoundry-client/src/main/java/org/cloudfoundry/client/v3/securitygroups/_CreateSecurityGroupResponse.java
new file mode 100644
index 00000000000..5b7f6f8ccd9
--- /dev/null
+++ b/cloudfoundry-client/src/main/java/org/cloudfoundry/client/v3/securitygroups/_CreateSecurityGroupResponse.java
@@ -0,0 +1,27 @@
+/*
+ * Copyright 2013-2023 the original author or authors.
+ *
+ * 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 org.cloudfoundry.client.v3.securitygroups;
+
+import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
+import org.immutables.value.Value;
+
+/**
+ * The response payload for the Creating a Security Group operation
+ */
+@JsonDeserialize
+@Value.Immutable
+abstract class _CreateSecurityGroupResponse extends SecurityGroup {
+
+}
diff --git a/cloudfoundry-client/src/main/java/org/cloudfoundry/client/v3/securitygroups/_DeleteSecurityGroupRequest.java b/cloudfoundry-client/src/main/java/org/cloudfoundry/client/v3/securitygroups/_DeleteSecurityGroupRequest.java
new file mode 100644
index 00000000000..48b0e7d20e2
--- /dev/null
+++ b/cloudfoundry-client/src/main/java/org/cloudfoundry/client/v3/securitygroups/_DeleteSecurityGroupRequest.java
@@ -0,0 +1,32 @@
+/*
+ * Copyright 2013-2023 the original author or authors.
+ *
+ * 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 org.cloudfoundry.client.v3.securitygroups;
+
+import com.fasterxml.jackson.annotation.JsonIgnore;
+import org.immutables.value.Value;
+
+/**
+ * The request payload for the Delete Security Group operation
+ */
+@Value.Immutable
+abstract class _DeleteSecurityGroupRequest {
+
+ /**
+ * The Security Group id
+ */
+ @JsonIgnore
+ abstract String getSecurityGroupId();
+
+}
diff --git a/cloudfoundry-client/src/main/java/org/cloudfoundry/client/v3/securitygroups/_GetSecurityGroupRequest.java b/cloudfoundry-client/src/main/java/org/cloudfoundry/client/v3/securitygroups/_GetSecurityGroupRequest.java
new file mode 100644
index 00000000000..e001a6dbf9c
--- /dev/null
+++ b/cloudfoundry-client/src/main/java/org/cloudfoundry/client/v3/securitygroups/_GetSecurityGroupRequest.java
@@ -0,0 +1,34 @@
+/*
+ * Copyright 2013-2023 the original author or authors.
+ *
+ * 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 org.cloudfoundry.client.v3.securitygroups;
+
+import com.fasterxml.jackson.databind.annotation.JsonSerialize;
+import com.fasterxml.jackson.annotation.JsonIgnore;
+import org.immutables.value.Value;
+
+/**
+ * The request payload for the Get Security Group operation
+ */
+@JsonSerialize
+@Value.Immutable
+abstract class _GetSecurityGroupRequest {
+
+ /**
+ * The Security Group id
+ */
+ @JsonIgnore
+ abstract String getSecurityGroupId();
+
+}
diff --git a/cloudfoundry-client/src/main/java/org/cloudfoundry/client/v3/securitygroups/_GetSecurityGroupResponse.java b/cloudfoundry-client/src/main/java/org/cloudfoundry/client/v3/securitygroups/_GetSecurityGroupResponse.java
new file mode 100644
index 00000000000..aab7d9d91ed
--- /dev/null
+++ b/cloudfoundry-client/src/main/java/org/cloudfoundry/client/v3/securitygroups/_GetSecurityGroupResponse.java
@@ -0,0 +1,27 @@
+/*
+ * Copyright 2013-2023 the original author or authors.
+ *
+ * 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 org.cloudfoundry.client.v3.securitygroups;
+
+import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
+import org.immutables.value.Value;
+
+/**
+ * The response payload for the get Security Group operation
+ */
+@JsonDeserialize
+@Value.Immutable
+abstract class _GetSecurityGroupResponse extends SecurityGroup {
+
+}
diff --git a/cloudfoundry-client/src/main/java/org/cloudfoundry/client/v3/securitygroups/_GloballyEnabled.java b/cloudfoundry-client/src/main/java/org/cloudfoundry/client/v3/securitygroups/_GloballyEnabled.java
new file mode 100644
index 00000000000..bf89201cbb2
--- /dev/null
+++ b/cloudfoundry-client/src/main/java/org/cloudfoundry/client/v3/securitygroups/_GloballyEnabled.java
@@ -0,0 +1,43 @@
+/*
+ * Copyright 2013-2023 the original author or authors.
+ *
+ * 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 org.cloudfoundry.client.v3.securitygroups;
+
+import com.fasterxml.jackson.annotation.JsonProperty;
+import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
+
+import org.cloudfoundry.Nullable;
+import org.immutables.value.Value;
+
+/**
+ * Controls if the group is applied globally to the lifecycle of all applications
+ */
+@JsonDeserialize
+@Value.Immutable
+abstract class _GloballyEnabled {
+
+ /**
+ * Specifies whether the group should be applied globally to all running applications
+ */
+ @JsonProperty("running")
+ @Nullable
+ abstract Boolean getRunning();
+
+ /**
+ * Specifies whether the group should be applied globally to all staging applications
+ */
+ @JsonProperty("staging")
+ @Nullable
+ abstract Boolean getStaging();
+
+}
diff --git a/cloudfoundry-client/src/main/java/org/cloudfoundry/client/v3/securitygroups/_ListRunningSecurityGroupsRequest.java b/cloudfoundry-client/src/main/java/org/cloudfoundry/client/v3/securitygroups/_ListRunningSecurityGroupsRequest.java
new file mode 100644
index 00000000000..45e37d9a8f9
--- /dev/null
+++ b/cloudfoundry-client/src/main/java/org/cloudfoundry/client/v3/securitygroups/_ListRunningSecurityGroupsRequest.java
@@ -0,0 +1,27 @@
+/*
+ * Copyright 2013-2021 the original author or authors.
+ *
+ * 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 org.cloudfoundry.client.v3.securitygroups;
+
+import org.immutables.value.Value;
+
+
+/**
+ * The request payload for the List running Security Group operation
+ */
+
+@Value.Immutable
+abstract class _ListRunningSecurityGroupsRequest extends AbstractListSecurityGroupRequest {
+
+}
diff --git a/cloudfoundry-client/src/main/java/org/cloudfoundry/client/v3/securitygroups/_ListRunningSecurityGroupsResponse.java b/cloudfoundry-client/src/main/java/org/cloudfoundry/client/v3/securitygroups/_ListRunningSecurityGroupsResponse.java
new file mode 100644
index 00000000000..0a6ec345a27
--- /dev/null
+++ b/cloudfoundry-client/src/main/java/org/cloudfoundry/client/v3/securitygroups/_ListRunningSecurityGroupsResponse.java
@@ -0,0 +1,29 @@
+/*
+ * Copyright 2013-2023 the original author or authors.
+ *
+ * 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 org.cloudfoundry.client.v3.securitygroups;
+
+import org.immutables.value.Value;
+
+import org.cloudfoundry.client.v3.PaginatedResponse;
+import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
+
+/**
+ * The response payload for the List Security Groups operation
+ */
+@JsonDeserialize
+@Value.Immutable
+abstract class _ListRunningSecurityGroupsResponse extends PaginatedResponse {
+
+}
diff --git a/cloudfoundry-client/src/main/java/org/cloudfoundry/client/v3/securitygroups/_ListSecurityGroupsRequest.java b/cloudfoundry-client/src/main/java/org/cloudfoundry/client/v3/securitygroups/_ListSecurityGroupsRequest.java
new file mode 100644
index 00000000000..2ec1c16369e
--- /dev/null
+++ b/cloudfoundry-client/src/main/java/org/cloudfoundry/client/v3/securitygroups/_ListSecurityGroupsRequest.java
@@ -0,0 +1,70 @@
+/*
+ * Copyright 2023 the original author or authors.
+ *
+ * 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 org.cloudfoundry.client.v3.securitygroups;
+
+import org.cloudfoundry.client.v2.PaginatedRequest;
+import org.immutables.value.Value;
+import org.cloudfoundry.client.v3.FilterParameter;
+import org.cloudfoundry.Nullable;
+import java.util.List;
+import com.fasterxml.jackson.databind.annotation.JsonSerialize;
+
+/**
+ * The request payload for the List Security Group operation
+ */
+@JsonSerialize
+@Value.Immutable
+abstract class _ListSecurityGroupsRequest extends PaginatedRequest {
+
+ /**
+ * The security group ids filter
+ */
+ @FilterParameter("guids")
+ abstract List getSecurityGroupIds();
+
+ /**
+ * The security group names filter
+ */
+ @FilterParameter("names")
+ abstract List getNames();
+
+ /**
+ * the security group globally enabled running filter
+ */
+ @FilterParameter("globally_enabled_running")
+ @Nullable
+ abstract Boolean getGloballyEnabledRunning();
+
+ /**
+ * the security group globally enabled staging filter
+ */
+ @FilterParameter("globally_enabled_staging")
+ @Nullable
+ abstract Boolean getGloballyEnabledStagingBoolean();
+
+ /**
+ * the security group running_space_guids filter
+ */
+ @FilterParameter("running_space_guids")
+ @Nullable
+ abstract List getRunningSpaceIds();
+
+ /**
+ * the security group staging_space_guids filter
+ */
+ @FilterParameter("staging_space_guids")
+ @Nullable
+ abstract List getStagingSpaceIds();
+}
diff --git a/cloudfoundry-client/src/main/java/org/cloudfoundry/client/v3/securitygroups/_ListSecurityGroupsResponse.java b/cloudfoundry-client/src/main/java/org/cloudfoundry/client/v3/securitygroups/_ListSecurityGroupsResponse.java
new file mode 100644
index 00000000000..f19bfc76136
--- /dev/null
+++ b/cloudfoundry-client/src/main/java/org/cloudfoundry/client/v3/securitygroups/_ListSecurityGroupsResponse.java
@@ -0,0 +1,31 @@
+/*
+ * Copyright 2013-2021 the original author or authors.
+ *
+ * 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 org.cloudfoundry.client.v3.securitygroups;
+
+import org.immutables.value.Value;
+
+import org.cloudfoundry.client.v3.PaginatedResponse;
+import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
+
+/**
+ * The response payload for the List Security Groups operation
+ */
+@JsonDeserialize
+@Value.Immutable
+abstract class _ListSecurityGroupsResponse extends PaginatedResponse {
+
+}
diff --git a/cloudfoundry-client/src/main/java/org/cloudfoundry/client/v3/securitygroups/_ListStagingSecurityGroupsRequest.java b/cloudfoundry-client/src/main/java/org/cloudfoundry/client/v3/securitygroups/_ListStagingSecurityGroupsRequest.java
new file mode 100644
index 00000000000..d85b8c9c01c
--- /dev/null
+++ b/cloudfoundry-client/src/main/java/org/cloudfoundry/client/v3/securitygroups/_ListStagingSecurityGroupsRequest.java
@@ -0,0 +1,28 @@
+/*
+ * Copyright 2013-2023 the original author or authors.
+ *
+ * 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 org.cloudfoundry.client.v3.securitygroups;
+
+import org.immutables.value.Value;
+import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
+
+
+/**
+ * The request payload for the List staging Security Group operation
+ */
+@JsonDeserialize
+@Value.Immutable
+abstract class _ListStagingSecurityGroupsRequest extends AbstractListSecurityGroupRequest {
+
+}
diff --git a/cloudfoundry-client/src/main/java/org/cloudfoundry/client/v3/securitygroups/_ListStagingSecurityGroupsResponse.java b/cloudfoundry-client/src/main/java/org/cloudfoundry/client/v3/securitygroups/_ListStagingSecurityGroupsResponse.java
new file mode 100644
index 00000000000..1109b7a60a7
--- /dev/null
+++ b/cloudfoundry-client/src/main/java/org/cloudfoundry/client/v3/securitygroups/_ListStagingSecurityGroupsResponse.java
@@ -0,0 +1,29 @@
+/*
+ * Copyright 2013-2023 the original author or authors.
+ *
+ * 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 org.cloudfoundry.client.v3.securitygroups;
+
+import org.immutables.value.Value;
+
+import org.cloudfoundry.client.v3.PaginatedResponse;
+import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
+
+/**
+ * The response payload for the List Security Groups operation
+ */
+@JsonDeserialize
+@Value.Immutable
+abstract class _ListStagingSecurityGroupsResponse extends PaginatedResponse {
+
+}
diff --git a/cloudfoundry-client/src/main/java/org/cloudfoundry/client/v3/securitygroups/_Relationships.java b/cloudfoundry-client/src/main/java/org/cloudfoundry/client/v3/securitygroups/_Relationships.java
new file mode 100644
index 00000000000..e9d782cbac7
--- /dev/null
+++ b/cloudfoundry-client/src/main/java/org/cloudfoundry/client/v3/securitygroups/_Relationships.java
@@ -0,0 +1,44 @@
+/*
+ * Copyright 2013-2023 the original author or authors.
+ *
+ * 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 org.cloudfoundry.client.v3.securitygroups;
+
+import com.fasterxml.jackson.annotation.JsonProperty;
+import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
+import org.cloudfoundry.client.v3.ToManyRelationship;
+import org.immutables.value.Value;
+
+/**
+ * Holds relationships to running/staging spaces where security groups is
+ * applied
+ */
+@JsonDeserialize
+@Value.Immutable
+abstract class _Relationships {
+
+ /**
+ * A relationship to the spaces where the security_group is applied to
+ * applications during runtime
+ */
+ @JsonProperty("running_spaces")
+ abstract ToManyRelationship getRunningSpaces();
+
+ /**
+ * A relationship to the spaces where the security_group is applied to
+ * applications during runtime
+ */
+ @JsonProperty("staging_spaces")
+ abstract ToManyRelationship getStagingSpaces();
+
+}
diff --git a/cloudfoundry-client/src/main/java/org/cloudfoundry/client/v3/securitygroups/_Rule.java b/cloudfoundry-client/src/main/java/org/cloudfoundry/client/v3/securitygroups/_Rule.java
new file mode 100644
index 00000000000..b24119e0f89
--- /dev/null
+++ b/cloudfoundry-client/src/main/java/org/cloudfoundry/client/v3/securitygroups/_Rule.java
@@ -0,0 +1,78 @@
+/*
+ * Copyright 2013-2023 the original author or authors.
+ *
+ * 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 org.cloudfoundry.client.v3.securitygroups;
+
+import com.fasterxml.jackson.annotation.JsonProperty;
+import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
+import org.cloudfoundry.Nullable;
+import org.immutables.value.Value;
+
+/**
+ * A security group rule
+ */
+@JsonDeserialize
+@Value.Immutable
+abstract class _Rule {
+
+ /**
+ * The code control signal for icmp
+ */
+ @JsonProperty("code")
+ @Nullable
+ abstract Integer getCode();
+
+ /**
+ * The description of the rule
+ */
+ @JsonProperty("description")
+ @Nullable
+ abstract String getDescription();
+
+ /**
+ * The destination
+ */
+ @JsonProperty("destination")
+ @Nullable
+ abstract String getDestination();
+
+ /**
+ * Enables logging for the egress rule
+ */
+ @JsonProperty("log")
+ @Nullable
+ abstract Boolean getLog();
+
+ /**
+ * The ports
+ */
+ @JsonProperty("ports")
+ @Nullable
+ abstract String getPorts();
+
+ /**
+ * The protocol
+ */
+ @JsonProperty("protocol")
+ @Nullable
+ abstract Protocol getProtocol();
+
+ /**
+ * The type control signal for icmp
+ */
+ @JsonProperty("type")
+ @Nullable
+ abstract Integer getType();
+
+}
diff --git a/cloudfoundry-client/src/main/java/org/cloudfoundry/client/v3/securitygroups/_SecurityGroupResource.java b/cloudfoundry-client/src/main/java/org/cloudfoundry/client/v3/securitygroups/_SecurityGroupResource.java
new file mode 100644
index 00000000000..339559f111f
--- /dev/null
+++ b/cloudfoundry-client/src/main/java/org/cloudfoundry/client/v3/securitygroups/_SecurityGroupResource.java
@@ -0,0 +1,30 @@
+/*
+ * Copyright 2013-2023 the original author or authors.
+ *
+ * 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 org.cloudfoundry.client.v3.securitygroups;
+
+import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
+import org.immutables.value.Value;
+
+
+/**
+ * The Resource response payload for the List SecurityGroups operation
+ */
+@JsonDeserialize
+@Value.Immutable
+abstract class _SecurityGroupResource extends SecurityGroup {
+
+}
diff --git a/cloudfoundry-client/src/main/java/org/cloudfoundry/client/v3/securitygroups/_UnbindRunningSecurityGroupRequest.java b/cloudfoundry-client/src/main/java/org/cloudfoundry/client/v3/securitygroups/_UnbindRunningSecurityGroupRequest.java
new file mode 100644
index 00000000000..b9666f90422
--- /dev/null
+++ b/cloudfoundry-client/src/main/java/org/cloudfoundry/client/v3/securitygroups/_UnbindRunningSecurityGroupRequest.java
@@ -0,0 +1,25 @@
+/*
+ * Copyright 2013-2023 the original author or authors.
+ *
+ * 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 org.cloudfoundry.client.v3.securitygroups;
+
+import org.immutables.value.Value;
+
+/**
+ * The request payload for the Unbind Running Security Group operation
+ */
+@Value.Immutable
+abstract class _UnbindRunningSecurityGroupRequest extends AbstractUnbindSecurityGroupRequest {
+
+}
diff --git a/cloudfoundry-client/src/main/java/org/cloudfoundry/client/v3/securitygroups/_UnbindStagingSecurityGroupRequest.java b/cloudfoundry-client/src/main/java/org/cloudfoundry/client/v3/securitygroups/_UnbindStagingSecurityGroupRequest.java
new file mode 100644
index 00000000000..7c0f3a34b88
--- /dev/null
+++ b/cloudfoundry-client/src/main/java/org/cloudfoundry/client/v3/securitygroups/_UnbindStagingSecurityGroupRequest.java
@@ -0,0 +1,25 @@
+/*
+ * Copyright 2013-2023 the original author or authors.
+ *
+ * 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 org.cloudfoundry.client.v3.securitygroups;
+
+import org.immutables.value.Value;
+
+/**
+ * The request payload for the Unbind Staging Security Group operation
+ */
+@Value.Immutable
+abstract class _UnbindStagingSecurityGroupRequest extends AbstractUnbindSecurityGroupRequest {
+
+}
diff --git a/cloudfoundry-client/src/main/java/org/cloudfoundry/client/v3/securitygroups/_UpdateSecurityGroupRequest.java b/cloudfoundry-client/src/main/java/org/cloudfoundry/client/v3/securitygroups/_UpdateSecurityGroupRequest.java
new file mode 100644
index 00000000000..a5ce3eed999
--- /dev/null
+++ b/cloudfoundry-client/src/main/java/org/cloudfoundry/client/v3/securitygroups/_UpdateSecurityGroupRequest.java
@@ -0,0 +1,53 @@
+/*
+ * Copyright 2013-2023 the original author or authors.
+ *
+ * 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 org.cloudfoundry.client.v3.securitygroups;
+
+import org.immutables.value.Value;
+
+import java.util.List;
+import com.fasterxml.jackson.databind.annotation.JsonSerialize;
+import com.fasterxml.jackson.annotation.JsonProperty;
+import com.fasterxml.jackson.annotation.JsonIgnore;
+
+/**
+ * The request payload for the Create a Security Group operation
+ */
+@JsonSerialize
+@Value.Immutable
+abstract class _UpdateSecurityGroupRequest {
+
+ /**
+ * Id of the security group
+ */
+ @JsonIgnore
+ abstract String getSecurityGroupId();
+
+ /**
+ * Name of the security group
+ */
+ @JsonProperty("name")
+ abstract String getName();
+
+ /**
+ * Object that controls if the group is applied globally to the lifecycle of all applications
+ */
+ @JsonProperty("globally_enabled")
+ abstract GloballyEnabled getGloballyEnabled();
+
+ /**
+ * Rules that will be applied by this security group
+ */
+ @JsonProperty("rules")
+ abstract List getRules();
+}
diff --git a/cloudfoundry-client/src/main/java/org/cloudfoundry/client/v3/securitygroups/_UpdateSecurityGroupResponse.java b/cloudfoundry-client/src/main/java/org/cloudfoundry/client/v3/securitygroups/_UpdateSecurityGroupResponse.java
new file mode 100644
index 00000000000..42f5dfa05e7
--- /dev/null
+++ b/cloudfoundry-client/src/main/java/org/cloudfoundry/client/v3/securitygroups/_UpdateSecurityGroupResponse.java
@@ -0,0 +1,26 @@
+/*
+ * Copyright 2013-2023 the original author or authors.
+ *
+ * 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 org.cloudfoundry.client.v3.securitygroups;
+
+import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
+import org.immutables.value.Value;
+
+/**
+ * The response payload for the Update a Security Group operation
+ */
+@JsonDeserialize
+@Value.Immutable
+abstract class _UpdateSecurityGroupResponse extends SecurityGroup {
+
+}
diff --git a/cloudfoundry-client/src/test/java/org/cloudfoundry/client/v3/securitygroups/BindRunningSecurityGroupRequestTest.java b/cloudfoundry-client/src/test/java/org/cloudfoundry/client/v3/securitygroups/BindRunningSecurityGroupRequestTest.java
new file mode 100644
index 00000000000..8c3e12640e7
--- /dev/null
+++ b/cloudfoundry-client/src/test/java/org/cloudfoundry/client/v3/securitygroups/BindRunningSecurityGroupRequestTest.java
@@ -0,0 +1,39 @@
+/*
+ * Copyright 2013-2023 the original author or authors.
+ *
+ * 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 org.cloudfoundry.client.v3.securitygroups;
+
+import static org.junit.jupiter.api.Assertions.assertThrows;
+
+import org.cloudfoundry.client.v3.Relationship;
+import org.junit.jupiter.api.Test;
+
+public class BindRunningSecurityGroupRequestTest {
+
+ @Test
+ public void noSecurityGroupId() {
+ assertThrows(
+ IllegalStateException.class,
+ () -> {
+ BindRunningSecurityGroupRequest.builder().build();
+ });
+ }
+
+ @Test
+ public void valid() {
+ BindRunningSecurityGroupRequest.builder()
+ .securityGroupId("b85a788e-671f-4549-814d-e34cdb2f539a")
+ .boundSpaces(Relationship.builder().id("space-guid-1").build())
+ .build();
+ }
+}
diff --git a/cloudfoundry-client/src/test/java/org/cloudfoundry/client/v3/securitygroups/BindStagingSecurityGroupRequestTest.java b/cloudfoundry-client/src/test/java/org/cloudfoundry/client/v3/securitygroups/BindStagingSecurityGroupRequestTest.java
new file mode 100644
index 00000000000..ff5a4f3ba96
--- /dev/null
+++ b/cloudfoundry-client/src/test/java/org/cloudfoundry/client/v3/securitygroups/BindStagingSecurityGroupRequestTest.java
@@ -0,0 +1,39 @@
+/*
+ * Copyright 2013-2023 the original author or authors.
+ *
+ * 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 org.cloudfoundry.client.v3.securitygroups;
+
+import static org.junit.jupiter.api.Assertions.assertThrows;
+
+import org.cloudfoundry.client.v3.Relationship;
+import org.junit.jupiter.api.Test;
+
+public class BindStagingSecurityGroupRequestTest {
+
+ @Test
+ public void noSecurityGroupId() {
+ assertThrows(
+ IllegalStateException.class,
+ () -> {
+ BindStagingSecurityGroupRequest.builder().build();
+ });
+ }
+
+ @Test
+ public void valid() {
+ BindStagingSecurityGroupRequest.builder()
+ .securityGroupId("b85a788e-671f-4549-814d-e34cdb2f539a")
+ .boundSpaces(Relationship.builder().id("space-guid-1").build())
+ .build();
+ }
+}
diff --git a/cloudfoundry-client/src/test/java/org/cloudfoundry/client/v3/securitygroups/CreateSecurityGroupRequestTest.java b/cloudfoundry-client/src/test/java/org/cloudfoundry/client/v3/securitygroups/CreateSecurityGroupRequestTest.java
new file mode 100644
index 00000000000..292cec1b67d
--- /dev/null
+++ b/cloudfoundry-client/src/test/java/org/cloudfoundry/client/v3/securitygroups/CreateSecurityGroupRequestTest.java
@@ -0,0 +1,31 @@
+/*
+ * Copyright 2013-2023 the original author or authors.
+ *
+ * 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 org.cloudfoundry.client.v3.securitygroups;
+
+import static org.junit.jupiter.api.Assertions.assertThrows;
+
+import org.junit.jupiter.api.Test;
+
+public class CreateSecurityGroupRequestTest {
+
+ @Test
+ public void noName() {
+ assertThrows(
+ IllegalStateException.class,
+ () -> {
+ CreateSecurityGroupRequest.builder().rule(Rule.builder().build()).build();
+ });
+ }
+}
diff --git a/cloudfoundry-client/src/test/java/org/cloudfoundry/client/v3/securitygroups/DeleteSecurityGroupRequestTest.java b/cloudfoundry-client/src/test/java/org/cloudfoundry/client/v3/securitygroups/DeleteSecurityGroupRequestTest.java
new file mode 100644
index 00000000000..d3fb5b8bec9
--- /dev/null
+++ b/cloudfoundry-client/src/test/java/org/cloudfoundry/client/v3/securitygroups/DeleteSecurityGroupRequestTest.java
@@ -0,0 +1,37 @@
+/*
+ * Copyright 2013-2023 the original author or authors.
+ *
+ * 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 org.cloudfoundry.client.v3.securitygroups;
+
+import static org.junit.jupiter.api.Assertions.assertThrows;
+
+import org.junit.jupiter.api.Test;
+
+public class DeleteSecurityGroupRequestTest {
+
+ @Test
+ public void noSecurityGroupId() {
+ assertThrows(
+ IllegalStateException.class,
+ () -> {
+ DeleteSecurityGroupRequest.builder().build();
+ });
+ }
+
+ @Test
+ public void valid() {
+ DeleteSecurityGroupRequest.builder()
+ .securityGroupId("b85a788e-671f-4549-814d-e34cdb2f539a")
+ .build();
+ }
+}
diff --git a/cloudfoundry-client/src/test/java/org/cloudfoundry/client/v3/securitygroups/GetSecurityGroupRequestTest.java b/cloudfoundry-client/src/test/java/org/cloudfoundry/client/v3/securitygroups/GetSecurityGroupRequestTest.java
new file mode 100644
index 00000000000..435873e76a8
--- /dev/null
+++ b/cloudfoundry-client/src/test/java/org/cloudfoundry/client/v3/securitygroups/GetSecurityGroupRequestTest.java
@@ -0,0 +1,38 @@
+/*
+ * Copyright 2013-2023 the original author or authors.
+ *
+ * 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 org.cloudfoundry.client.v3.securitygroups;
+
+import static org.junit.jupiter.api.Assertions.assertThrows;
+
+import org.junit.jupiter.api.Test;
+
+public class GetSecurityGroupRequestTest {
+
+ @Test
+ public void noSecurityGroupId() {
+ assertThrows(
+ IllegalStateException.class,
+ () -> {
+ GetSecurityGroupRequest.builder().build();
+ });
+ }
+
+ @Test
+ public void valid() {
+ GetSecurityGroupRequest.builder()
+ .securityGroupId("b85a788e-671f-4549-814d-e34cdb2f539a")
+ .build();
+ }
+}
diff --git a/cloudfoundry-client/src/test/java/org/cloudfoundry/client/v3/securitygroups/ListRunningSecurityGroupsRequestTest.java b/cloudfoundry-client/src/test/java/org/cloudfoundry/client/v3/securitygroups/ListRunningSecurityGroupsRequestTest.java
new file mode 100644
index 00000000000..3f60cfc93f1
--- /dev/null
+++ b/cloudfoundry-client/src/test/java/org/cloudfoundry/client/v3/securitygroups/ListRunningSecurityGroupsRequestTest.java
@@ -0,0 +1,35 @@
+/*
+ * Copyright 2013-2023 the original author or authors.
+ *
+ * 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 org.cloudfoundry.client.v3.securitygroups;
+
+import static org.junit.jupiter.api.Assertions.assertThrows;
+
+import org.junit.jupiter.api.Test;
+
+public class ListRunningSecurityGroupsRequestTest {
+
+ @Test
+ public void noSpaceID() {
+ assertThrows(
+ IllegalStateException.class,
+ () -> {
+ ListRunningSecurityGroupsRequest.builder().build();
+ });
+ }
+
+ @Test
+ public void valid() {
+ ListRunningSecurityGroupsRequest.builder().spaceId("space-giud1").build();
+ }
+}
diff --git a/cloudfoundry-client/src/test/java/org/cloudfoundry/client/v3/securitygroups/ListSecurityGroupsRequestTest.java b/cloudfoundry-client/src/test/java/org/cloudfoundry/client/v3/securitygroups/ListSecurityGroupsRequestTest.java
new file mode 100644
index 00000000000..9d8ccd1f696
--- /dev/null
+++ b/cloudfoundry-client/src/test/java/org/cloudfoundry/client/v3/securitygroups/ListSecurityGroupsRequestTest.java
@@ -0,0 +1,24 @@
+/*
+ * Copyright 2013-2023 the original author or authors.
+ *
+ * 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 org.cloudfoundry.client.v3.securitygroups;
+
+import org.junit.jupiter.api.Test;
+
+public class ListSecurityGroupsRequestTest {
+
+ @Test
+ public void valid() {
+ ListSecurityGroupsRequest.builder().build();
+ }
+}
diff --git a/cloudfoundry-client/src/test/java/org/cloudfoundry/client/v3/securitygroups/ListStagingSecurityGroupsRequestTest.java b/cloudfoundry-client/src/test/java/org/cloudfoundry/client/v3/securitygroups/ListStagingSecurityGroupsRequestTest.java
new file mode 100644
index 00000000000..9ffde0a435f
--- /dev/null
+++ b/cloudfoundry-client/src/test/java/org/cloudfoundry/client/v3/securitygroups/ListStagingSecurityGroupsRequestTest.java
@@ -0,0 +1,35 @@
+/*
+ * Copyright 2013-2023 the original author or authors.
+ *
+ * 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 org.cloudfoundry.client.v3.securitygroups;
+
+import static org.junit.jupiter.api.Assertions.assertThrows;
+
+import org.junit.jupiter.api.Test;
+
+public class ListStagingSecurityGroupsRequestTest {
+
+ @Test
+ public void noSpaceID() {
+ assertThrows(
+ IllegalStateException.class,
+ () -> {
+ ListStagingSecurityGroupsRequest.builder().build();
+ });
+ }
+
+ @Test
+ public void valid() {
+ ListStagingSecurityGroupsRequest.builder().spaceId("space-giud1").build();
+ }
+}
diff --git a/cloudfoundry-client/src/test/java/org/cloudfoundry/client/v3/securitygroups/UnbindRunningSecurityGroupRequestTest.java b/cloudfoundry-client/src/test/java/org/cloudfoundry/client/v3/securitygroups/UnbindRunningSecurityGroupRequestTest.java
new file mode 100644
index 00000000000..6bacd1e034e
--- /dev/null
+++ b/cloudfoundry-client/src/test/java/org/cloudfoundry/client/v3/securitygroups/UnbindRunningSecurityGroupRequestTest.java
@@ -0,0 +1,49 @@
+/*
+ * Copyright 2013-2023 the original author or authors.
+ *
+ * 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 org.cloudfoundry.client.v3.securitygroups;
+
+import static org.junit.jupiter.api.Assertions.assertThrows;
+
+import org.junit.jupiter.api.Test;
+
+public class UnbindRunningSecurityGroupRequestTest {
+
+ @Test
+ public void noSecurityGroupId() {
+ assertThrows(
+ IllegalStateException.class,
+ () -> {
+ UnbindRunningSecurityGroupRequest.builder().build();
+ });
+ }
+
+ @Test
+ public void noSpaceId() {
+ assertThrows(
+ IllegalStateException.class,
+ () -> {
+ UnbindRunningSecurityGroupRequest.builder()
+ .securityGroupId("b85a788e-671f-4549-814d-e34cdb2f539a")
+ .build();
+ });
+ }
+
+ @Test
+ public void valid() {
+ UnbindRunningSecurityGroupRequest.builder()
+ .securityGroupId("b85a788e-671f-4549-814d-e34cdb2f539a")
+ .spaceId("space-guid2")
+ .build();
+ }
+}
diff --git a/cloudfoundry-client/src/test/java/org/cloudfoundry/client/v3/securitygroups/UnbindStagingSecurityGroupRequestTest.java b/cloudfoundry-client/src/test/java/org/cloudfoundry/client/v3/securitygroups/UnbindStagingSecurityGroupRequestTest.java
new file mode 100644
index 00000000000..9ab1c7fa1d4
--- /dev/null
+++ b/cloudfoundry-client/src/test/java/org/cloudfoundry/client/v3/securitygroups/UnbindStagingSecurityGroupRequestTest.java
@@ -0,0 +1,49 @@
+/*
+ * Copyright 2013-2023 the original author or authors.
+ *
+ * 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 org.cloudfoundry.client.v3.securitygroups;
+
+import static org.junit.jupiter.api.Assertions.assertThrows;
+
+import org.junit.jupiter.api.Test;
+
+public class UnbindStagingSecurityGroupRequestTest {
+
+ @Test
+ public void noSecurityGroupId() {
+ assertThrows(
+ IllegalStateException.class,
+ () -> {
+ UnbindStagingSecurityGroupRequest.builder().build();
+ });
+ }
+
+ @Test
+ public void noSpaceId() {
+ assertThrows(
+ IllegalStateException.class,
+ () -> {
+ UnbindStagingSecurityGroupRequest.builder()
+ .securityGroupId("b85a788e-671f-4549-814d-e34cdb2f539a")
+ .build();
+ });
+ }
+
+ @Test
+ public void valid() {
+ UnbindStagingSecurityGroupRequest.builder()
+ .securityGroupId("b85a788e-671f-4549-814d-e34cdb2f539a")
+ .spaceId("space-guid2")
+ .build();
+ }
+}
diff --git a/cloudfoundry-client/src/test/java/org/cloudfoundry/client/v3/securitygroups/UpdateSecurityGroupRequestTest.java b/cloudfoundry-client/src/test/java/org/cloudfoundry/client/v3/securitygroups/UpdateSecurityGroupRequestTest.java
new file mode 100644
index 00000000000..d0b18172b85
--- /dev/null
+++ b/cloudfoundry-client/src/test/java/org/cloudfoundry/client/v3/securitygroups/UpdateSecurityGroupRequestTest.java
@@ -0,0 +1,53 @@
+/*
+ * Copyright 2013-2023 the original author or authors.
+ *
+ * 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 org.cloudfoundry.client.v3.securitygroups;
+
+import static org.junit.jupiter.api.Assertions.assertThrows;
+
+import org.junit.jupiter.api.Test;
+
+public class UpdateSecurityGroupRequestTest {
+
+ @Test
+ public void noName() {
+ assertThrows(
+ IllegalStateException.class,
+ () -> {
+ UpdateSecurityGroupRequest.builder().build();
+ });
+ }
+
+ @Test
+ public void valid() {
+ UpdateSecurityGroupRequest.builder()
+ .name("my-group0")
+ .securityGroupId("b85a788e-671f-4549-814d-e34cdb2f539a")
+ .globallyEnabled(GloballyEnabled.builder().running(true).build())
+ .rules(
+ Rule.builder()
+ .protocol(Protocol.TCP)
+ .destination("10.10.10.0/24")
+ .ports("443,80,8080")
+ .build())
+ .rules(
+ Rule.builder()
+ .protocol(Protocol.ICMP)
+ .destination("10.10.10.0/24")
+ .description("Allow ping requests to private services")
+ .type(8)
+ .code(0)
+ .build())
+ .build();
+ }
+}
diff --git a/cloudfoundry-operations/src/main/java/org/cloudfoundry/operations/_DefaultCloudFoundryOperations.java b/cloudfoundry-operations/src/main/java/org/cloudfoundry/operations/_DefaultCloudFoundryOperations.java
index 35d76a3fb0f..299b4bf5e41 100644
--- a/cloudfoundry-operations/src/main/java/org/cloudfoundry/operations/_DefaultCloudFoundryOperations.java
+++ b/cloudfoundry-operations/src/main/java/org/cloudfoundry/operations/_DefaultCloudFoundryOperations.java
@@ -18,10 +18,10 @@
import org.cloudfoundry.Nullable;
import org.cloudfoundry.client.CloudFoundryClient;
-import org.cloudfoundry.client.v2.organizations.ListOrganizationsRequest;
-import org.cloudfoundry.client.v2.organizations.OrganizationResource;
-import org.cloudfoundry.client.v2.spaces.ListSpacesRequest;
-import org.cloudfoundry.client.v2.spaces.SpaceResource;
+import org.cloudfoundry.client.v3.organizations.ListOrganizationsRequest;
+import org.cloudfoundry.client.v3.organizations.OrganizationResource;
+import org.cloudfoundry.client.v3.spaces.ListSpacesRequest;
+import org.cloudfoundry.client.v3.spaces.SpaceResource;
import org.cloudfoundry.doppler.DopplerClient;
import org.cloudfoundry.networking.NetworkingClient;
import org.cloudfoundry.operations.advanced.Advanced;
@@ -56,7 +56,6 @@
import org.cloudfoundry.uaa.UaaClient;
import org.cloudfoundry.util.ExceptionUtils;
import org.cloudfoundry.util.PaginationUtils;
-import org.cloudfoundry.util.ResourceUtils;
import org.immutables.value.Value;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;
@@ -211,7 +210,7 @@ Mono getOrganizationId() {
if (hasText(organization)) {
Mono cached = getOrganization(getCloudFoundryClientPublisher(), organization)
- .map(ResourceUtils::getId);
+ .map(OrganizationResource::getId);
return getCacheDuration()
.map(cached::cache)
@@ -247,7 +246,7 @@ Mono getSpaceId() {
if (hasText(getSpace())) {
Mono cached = getOrganizationId()
.flatMap(organizationId -> getSpace(getCloudFoundryClientPublisher(), organizationId, space))
- .map(ResourceUtils::getId);
+ .map(SpaceResource::getId);
return getCacheDuration()
.map(cached::cache)
@@ -308,7 +307,7 @@ private static boolean hasText(CharSequence str) {
private static Flux requestOrganizations(Mono cloudFoundryClientPublisher, String organization) {
return cloudFoundryClientPublisher
.flatMapMany(cloudFoundryClient -> PaginationUtils
- .requestClientV2Resources(page -> cloudFoundryClient.organizations()
+ .requestClientV3Resources(page -> cloudFoundryClient.organizationsV3()
.list(ListOrganizationsRequest.builder()
.name(organization)
.page(page)
@@ -318,7 +317,7 @@ private static Flux requestOrganizations(Mono requestSpaces(Mono cloudFoundryClientPublisher, String organizationId, String space) {
return cloudFoundryClientPublisher
.flatMapMany(cloudFoundryClient -> PaginationUtils
- .requestClientV2Resources(page -> cloudFoundryClient.spaces()
+ .requestClientV3Resources(page -> cloudFoundryClient.spacesV3()
.list(ListSpacesRequest.builder()
.organizationId(organizationId)
.name(space)
diff --git a/integration-test/pom.xml b/integration-test/pom.xml
index e1b541e1502..c7f8e86a6e5 100644
--- a/integration-test/pom.xml
+++ b/integration-test/pom.xml
@@ -76,6 +76,18 @@
${project.version}
test
+
+ org.hamcrest
+ hamcrest-junit
+ 2.0.0.0
+ test
+
+
+ junit
+ junit
+
+
+
org.immutables
value
@@ -91,11 +103,6 @@
spring-boot-starter-test
test
-
- org.junit.vintage
- junit-vintage-engine
- test
-
diff --git a/integration-test/src/test/java/org/cloudfoundry/AbstractIntegrationTest.java b/integration-test/src/test/java/org/cloudfoundry/AbstractIntegrationTest.java
index b84496bff21..c677804e71e 100644
--- a/integration-test/src/test/java/org/cloudfoundry/AbstractIntegrationTest.java
+++ b/integration-test/src/test/java/org/cloudfoundry/AbstractIntegrationTest.java
@@ -16,57 +16,57 @@
package org.cloudfoundry;
-import org.junit.After;
-import org.junit.Before;
-import org.junit.Rule;
-import org.junit.rules.TestName;
-import org.junit.runner.RunWith;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-import org.springframework.beans.factory.annotation.Autowired;
-import org.springframework.test.context.ContextConfiguration;
-import org.springframework.test.context.junit4.SpringRunner;
-import reactor.core.publisher.Mono;
-import reactor.util.function.Tuple2;
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.cloudfoundry.util.tuple.TupleUtils.consumer;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
+import java.lang.reflect.Method;
+import java.util.Optional;
import java.util.function.Consumer;
+import org.junit.jupiter.api.AfterEach;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.TestInfo;
+import org.junit.jupiter.api.extension.RegisterExtension;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.test.context.junit.jupiter.SpringJUnitConfig;
+import reactor.core.publisher.Mono;
+import reactor.util.function.Tuple2;
-import static org.assertj.core.api.Assertions.assertThat;
-import static org.cloudfoundry.util.tuple.TupleUtils.consumer;
-
-@RunWith(SpringRunner.class)
-@ContextConfiguration(classes = IntegrationTestConfiguration.class)
+@SpringJUnitConfig(classes = IntegrationTestConfiguration.class)
public abstract class AbstractIntegrationTest {
private final Logger logger = LoggerFactory.getLogger("cloudfoundry-client.test");
- @Rule
- public final TestName testName = new TestName();
+ public String testName;
- @Autowired
- @Rule
- public CloudFoundryVersionConditionalRule cloudFoundryVersion;
+ @Autowired protected NameFactory nameFactory;
- @Autowired
- protected NameFactory nameFactory;
+ @Autowired @RegisterExtension
+ public CloudFoundryVersionConditionalRule cloudFoundryVersionConditionalRule;
- @Before
- public void testEntry() {
+ @BeforeEach
+ public void testEntry(TestInfo testInfo) {
+ Optional testMethod = testInfo.getTestMethod();
+ if (testMethod.isPresent()) {
+ this.testName = testMethod.get().getName();
+ }
this.logger.debug(">> {} <<", getTestName());
}
- @After
+ @AfterEach
public final void testExit() {
this.logger.debug("<< {} >>", getTestName());
}
protected static Mono getBytes(String path) {
- try (InputStream in = new FileInputStream(new File("src/test/resources", path)); ByteArrayOutputStream out = new ByteArrayOutputStream()) {
+ try (InputStream in = new FileInputStream(new File("src/test/resources", path));
+ ByteArrayOutputStream out = new ByteArrayOutputStream()) {
byte[] buffer = new byte[8192];
int len;
@@ -85,7 +85,6 @@ protected static Consumer> tupleEquality() {
}
private String getTestName() {
- return String.format("%s.%s", this.getClass().getSimpleName(), this.testName.getMethodName());
+ return String.format("%s.%s", this.getClass().getSimpleName(), this.testName);
}
-
}
diff --git a/integration-test/src/test/java/org/cloudfoundry/ApplicationUtils.java b/integration-test/src/test/java/org/cloudfoundry/ApplicationUtils.java
index a3817e55cf6..0efd69a03ff 100644
--- a/integration-test/src/test/java/org/cloudfoundry/ApplicationUtils.java
+++ b/integration-test/src/test/java/org/cloudfoundry/ApplicationUtils.java
@@ -16,6 +16,15 @@
package org.cloudfoundry;
+import static org.cloudfoundry.util.DelayUtils.exponentialBackOff;
+import static org.cloudfoundry.util.tuple.TupleUtils.function;
+
+import java.nio.file.Path;
+import java.time.Duration;
+import java.util.Collection;
+import java.util.Map;
+import java.util.Optional;
+import java.util.stream.Collectors;
import org.cloudfoundry.client.CloudFoundryClient;
import org.cloudfoundry.client.v2.applications.ApplicationInstanceInfo;
import org.cloudfoundry.client.v2.applications.ApplicationInstancesRequest;
@@ -41,151 +50,246 @@
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;
-import java.nio.file.Path;
-import java.time.Duration;
-import java.util.Collection;
-import java.util.Map;
-import java.util.Optional;
-import java.util.stream.Collectors;
-
-import static org.cloudfoundry.util.DelayUtils.exponentialBackOff;
-import static org.cloudfoundry.util.tuple.TupleUtils.function;
-
public class ApplicationUtils {
- static Mono pushApplication(CloudFoundryClient cloudFoundryClient, Path applicationBits, String applicationName, Collection boundServices,
- Map env, String hostName, String spaceId) {
+ static Mono pushApplication(
+ CloudFoundryClient cloudFoundryClient,
+ Path applicationBits,
+ String applicationName,
+ Collection boundServices,
+ Map env,
+ String hostName,
+ String spaceId) {
return getSharedDomain(cloudFoundryClient)
- .flatMap(domain -> Mono
- .zip(createApplicationId(cloudFoundryClient, spaceId, applicationName),
- createRouteId(cloudFoundryClient, ResourceUtils.getId(domain), spaceId, hostName))
- .flatMap(function((applicationId, routeId) -> Mono
- .zip(requestAssociateApplicationRoute(cloudFoundryClient, applicationId, routeId),
- bindServices(cloudFoundryClient, applicationId, boundServices))
- .thenReturn(applicationId)))
- .flatMap(applicationId -> createRunningApplication(cloudFoundryClient, applicationBits, applicationId, env)
- .map(ignore -> new ApplicationUtils.ApplicationMetadata(applicationId, spaceId, String.format("https://%s.%s", hostName, ResourceUtils.getEntity(domain).getName())))));
-
- }
-
- private static Mono bindServices(CloudFoundryClient cloudFoundryClient, String applicationId, Collection boundServices) {
- return Mono
- .zip(boundServices.stream()
- .map(serviceInstanceId -> cloudFoundryClient
- .serviceBindingsV2()
- .create(CreateServiceBindingRequest.builder()
- .applicationId(applicationId)
- .serviceInstanceId(serviceInstanceId)
- .build()))
- .collect(Collectors.toList()), a -> Mono.just(applicationId))
- .thenReturn(applicationId);
- }
-
- private static Mono createApplicationId(CloudFoundryClient cloudFoundryClient, String spaceId, String applicationName) {
+ .flatMap(
+ domain ->
+ Mono.zip(
+ createApplicationId(
+ cloudFoundryClient,
+ spaceId,
+ applicationName),
+ createRouteId(
+ cloudFoundryClient,
+ ResourceUtils.getId(domain),
+ spaceId,
+ hostName))
+ .flatMap(
+ function(
+ (applicationId, routeId) ->
+ Mono.zip(
+ requestAssociateApplicationRoute(
+ cloudFoundryClient,
+ applicationId,
+ routeId),
+ bindServices(
+ cloudFoundryClient,
+ applicationId,
+ boundServices))
+ .thenReturn(applicationId)))
+ .flatMap(
+ applicationId ->
+ createRunningApplication(
+ cloudFoundryClient,
+ applicationBits,
+ applicationId,
+ env)
+ .map(
+ ignore ->
+ new ApplicationUtils
+ .ApplicationMetadata(
+ applicationId,
+ spaceId,
+ String
+ .format(
+ "https://%s.%s",
+ hostName,
+ ResourceUtils
+ .getEntity(
+ domain)
+ .getName())))));
+ }
+
+ private static Mono bindServices(
+ CloudFoundryClient cloudFoundryClient,
+ String applicationId,
+ Collection boundServices) {
+ return Mono.zip(
+ boundServices.stream()
+ .map(
+ serviceInstanceId ->
+ cloudFoundryClient
+ .serviceBindingsV2()
+ .create(
+ CreateServiceBindingRequest
+ .builder()
+ .applicationId(
+ applicationId)
+ .serviceInstanceId(
+ serviceInstanceId)
+ .build()))
+ .collect(Collectors.toList()),
+ a -> Mono.just(applicationId))
+ .thenReturn(applicationId);
+ }
+
+ private static Mono createApplicationId(
+ CloudFoundryClient cloudFoundryClient, String spaceId, String applicationName) {
return requestCreateApplication(cloudFoundryClient, spaceId, applicationName)
- .map(ResourceUtils::getId);
+ .map(ResourceUtils::getId);
}
- private static Mono createRouteId(CloudFoundryClient cloudFoundryClient, String domainId, String spaceId, String hostName) {
+ private static Mono createRouteId(
+ CloudFoundryClient cloudFoundryClient,
+ String domainId,
+ String spaceId,
+ String hostName) {
return requestCreateRoute(cloudFoundryClient, domainId, spaceId, hostName)
- .map(ResourceUtils::getId);
+ .map(ResourceUtils::getId);
}
- private static Mono createRunningApplication(CloudFoundryClient cloudFoundryClient, Path applicationBits, String applicationId, Map env) {
- return ApplicationUtils.requestUploadApplication(cloudFoundryClient, applicationId, applicationBits)
- .flatMap(job -> JobUtils.waitForCompletion(cloudFoundryClient, Duration.ofMinutes(5), job))
- .then(requestUpdateApplication(cloudFoundryClient, applicationId, env, "STARTED"))
- .then(getApplicationPackageState(cloudFoundryClient, applicationId)
- .filter(state -> "STAGED".equals(state) || "FAILED".equals(state))
- .repeatWhenEmpty(exponentialBackOff(Duration.ofSeconds(1), Duration.ofSeconds(15), Duration.ofMinutes(5))))
- .then(getApplicationInstanceState(cloudFoundryClient, applicationId)
- .filter("RUNNING"::equals)
- .repeatWhenEmpty(exponentialBackOff(Duration.ofSeconds(1), Duration.ofSeconds(15), Duration.ofMinutes(5))));
+ private static Mono createRunningApplication(
+ CloudFoundryClient cloudFoundryClient,
+ Path applicationBits,
+ String applicationId,
+ Map env) {
+ return ApplicationUtils.requestUploadApplication(
+ cloudFoundryClient, applicationId, applicationBits)
+ .flatMap(
+ job ->
+ JobUtils.waitForCompletion(
+ cloudFoundryClient, Duration.ofMinutes(5), job))
+ .then(requestUpdateApplication(cloudFoundryClient, applicationId, env, "STARTED"))
+ .then(
+ getApplicationPackageState(cloudFoundryClient, applicationId)
+ .filter(state -> "STAGED".equals(state) || "FAILED".equals(state))
+ .repeatWhenEmpty(
+ exponentialBackOff(
+ Duration.ofSeconds(1),
+ Duration.ofSeconds(15),
+ Duration.ofMinutes(5))))
+ .then(
+ getApplicationInstanceState(cloudFoundryClient, applicationId)
+ .filter("RUNNING"::equals)
+ .repeatWhenEmpty(
+ exponentialBackOff(
+ Duration.ofSeconds(1),
+ Duration.ofSeconds(15),
+ Duration.ofMinutes(5))));
}
- private static Mono getApplicationPackageState(CloudFoundryClient cloudFoundryClient, String applicationId) {
+ private static Mono getApplicationPackageState(
+ CloudFoundryClient cloudFoundryClient, String applicationId) {
return requestGetApplication(cloudFoundryClient, applicationId)
- .map(response -> ResourceUtils.getEntity(response).getPackageState());
+ .map(response -> ResourceUtils.getEntity(response).getPackageState());
}
- private static Mono getApplicationInstanceState(CloudFoundryClient cloudFoundryClient, String applicationId) {
+ private static Mono getApplicationInstanceState(
+ CloudFoundryClient cloudFoundryClient, String applicationId) {
return requestApplicationInstances(cloudFoundryClient, applicationId)
- .flatMapMany(response -> Flux.fromIterable(response.getInstances().values()))
- .single()
- .map(ApplicationInstanceInfo::getState);
+ .flatMapMany(response -> Flux.fromIterable(response.getInstances().values()))
+ .single()
+ .map(ApplicationInstanceInfo::getState);
}
- private static Mono getSharedDomain(CloudFoundryClient cloudFoundryClient) {
+ private static Mono getSharedDomain(
+ CloudFoundryClient cloudFoundryClient) {
return requestListSharedDomains(cloudFoundryClient)
- .filter(resource -> !Optional.ofNullable(ResourceUtils.getEntity(resource).getInternal()).orElse(false))
- .next();
+ .filter(
+ resource ->
+ !Optional.ofNullable(
+ ResourceUtils.getEntity(resource).getInternal())
+ .orElse(false))
+ .next();
}
- private static Mono requestApplicationInstances(CloudFoundryClient cloudFoundryClient, String applicationId) {
- return cloudFoundryClient.applicationsV2()
- .instances(ApplicationInstancesRequest.builder()
- .applicationId(applicationId)
- .build());
+ private static Mono requestApplicationInstances(
+ CloudFoundryClient cloudFoundryClient, String applicationId) {
+ return cloudFoundryClient
+ .applicationsV2()
+ .instances(
+ ApplicationInstancesRequest.builder().applicationId(applicationId).build());
}
- private static Mono requestAssociateApplicationRoute(CloudFoundryClient cloudFoundryClient, String applicationId, String routeId) {
- return cloudFoundryClient.applicationsV2()
- .associateRoute(AssociateApplicationRouteRequest.builder()
- .applicationId(applicationId)
- .routeId(routeId)
- .build());
+ private static Mono requestAssociateApplicationRoute(
+ CloudFoundryClient cloudFoundryClient, String applicationId, String routeId) {
+ return cloudFoundryClient
+ .applicationsV2()
+ .associateRoute(
+ AssociateApplicationRouteRequest.builder()
+ .applicationId(applicationId)
+ .routeId(routeId)
+ .build());
}
- private static Mono requestCreateApplication(CloudFoundryClient cloudFoundryClient, String spaceId, String applicationName) {
- return cloudFoundryClient.applicationsV2()
- .create(CreateApplicationRequest.builder()
- .buildpack("https://github.com/cloudfoundry/java-buildpack.git")
- .memory(768)
- .name(applicationName)
- .spaceId(spaceId)
- .build());
+ private static Mono requestCreateApplication(
+ CloudFoundryClient cloudFoundryClient, String spaceId, String applicationName) {
+ return cloudFoundryClient
+ .applicationsV2()
+ .create(
+ CreateApplicationRequest.builder()
+ .buildpack("https://github.com/cloudfoundry/java-buildpack.git")
+ .memory(768)
+ .name(applicationName)
+ .spaceId(spaceId)
+ .build());
}
- private static Mono requestCreateRoute(CloudFoundryClient cloudFoundryClient, String domainId, String spaceId, String hostName) {
- return cloudFoundryClient.routes()
- .create(CreateRouteRequest.builder()
- .domainId(domainId)
- .host(hostName)
- .spaceId(spaceId)
- .build());
+ private static Mono requestCreateRoute(
+ CloudFoundryClient cloudFoundryClient,
+ String domainId,
+ String spaceId,
+ String hostName) {
+ return cloudFoundryClient
+ .routes()
+ .create(
+ CreateRouteRequest.builder()
+ .domainId(domainId)
+ .host(hostName)
+ .spaceId(spaceId)
+ .build());
}
- private static Mono requestGetApplication(CloudFoundryClient cloudFoundryClient, String applicationId) {
- return cloudFoundryClient.applicationsV2()
- .get(GetApplicationRequest.builder()
- .applicationId(applicationId)
- .build());
+ private static Mono requestGetApplication(
+ CloudFoundryClient cloudFoundryClient, String applicationId) {
+ return cloudFoundryClient
+ .applicationsV2()
+ .get(GetApplicationRequest.builder().applicationId(applicationId).build());
}
- private static Flux requestListSharedDomains(CloudFoundryClient cloudFoundryClient) {
- return PaginationUtils
- .requestClientV2Resources(page -> cloudFoundryClient.sharedDomains()
- .list(ListSharedDomainsRequest.builder()
- .page(page)
- .build()));
+ private static Flux requestListSharedDomains(
+ CloudFoundryClient cloudFoundryClient) {
+ return PaginationUtils.requestClientV2Resources(
+ page ->
+ cloudFoundryClient
+ .sharedDomains()
+ .list(ListSharedDomainsRequest.builder().page(page).build()));
}
- private static Mono requestUpdateApplication(CloudFoundryClient cloudFoundryClient, String applicationId, Map env, String state) {
- return cloudFoundryClient.applicationsV2()
- .update(UpdateApplicationRequest.builder()
- .applicationId(applicationId)
- .environmentJsons(env)
- .state(state)
- .build());
+ private static Mono requestUpdateApplication(
+ CloudFoundryClient cloudFoundryClient,
+ String applicationId,
+ Map env,
+ String state) {
+ return cloudFoundryClient
+ .applicationsV2()
+ .update(
+ UpdateApplicationRequest.builder()
+ .applicationId(applicationId)
+ .environmentJsons(env)
+ .state(state)
+ .build());
}
- private static Mono requestUploadApplication(CloudFoundryClient cloudFoundryClient, String applicationId, Path application) {
- return cloudFoundryClient.applicationsV2()
- .upload(UploadApplicationRequest.builder()
- .application(application)
- .applicationId(applicationId)
- .async(true)
- .build());
+ private static Mono requestUploadApplication(
+ CloudFoundryClient cloudFoundryClient, String applicationId, Path application) {
+ return cloudFoundryClient
+ .applicationsV2()
+ .upload(
+ UploadApplicationRequest.builder()
+ .application(application)
+ .applicationId(applicationId)
+ .async(true)
+ .build());
}
public static final class ApplicationMetadata {
@@ -201,8 +305,5 @@ private ApplicationMetadata(String applicationId, String spaceId, String uri) {
this.spaceId = spaceId;
this.uri = uri;
}
-
}
-
-
}
diff --git a/integration-test/src/test/java/org/cloudfoundry/CloudFoundryCleaner.java b/integration-test/src/test/java/org/cloudfoundry/CloudFoundryCleaner.java
index 3d97095f77d..c4fb056ad2a 100644
--- a/integration-test/src/test/java/org/cloudfoundry/CloudFoundryCleaner.java
+++ b/integration-test/src/test/java/org/cloudfoundry/CloudFoundryCleaner.java
@@ -16,7 +16,17 @@
package org.cloudfoundry;
+import static org.cloudfoundry.CloudFoundryVersion.PCF_1_12;
+import static org.cloudfoundry.CloudFoundryVersion.PCF_2_1;
+import static org.cloudfoundry.util.tuple.TupleUtils.function;
+import static org.cloudfoundry.util.tuple.TupleUtils.predicate;
+
import com.github.zafarkhaja.semver.Version;
+import java.time.Duration;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.function.Supplier;
+import javax.net.ssl.SSLException;
import org.cloudfoundry.client.CloudFoundryClient;
import org.cloudfoundry.client.v2.applications.ListApplicationServiceBindingsRequest;
import org.cloudfoundry.client.v2.applications.RemoveApplicationServiceBindingRequest;
@@ -111,33 +121,23 @@
import reactor.util.function.Tuples;
import reactor.util.retry.Retry;
-import javax.net.ssl.SSLException;
-import java.time.Duration;
-import java.util.HashMap;
-import java.util.Map;
-import java.util.function.Supplier;
-
-import static org.cloudfoundry.CloudFoundryVersion.PCF_1_12;
-import static org.cloudfoundry.CloudFoundryVersion.PCF_2_1;
-import static org.cloudfoundry.util.tuple.TupleUtils.function;
-import static org.cloudfoundry.util.tuple.TupleUtils.predicate;
-
final class CloudFoundryCleaner {
private static final Logger LOGGER = LoggerFactory.getLogger("cloudfoundry-client.test");
- private static final Map STANDARD_FEATURE_FLAGS = FluentMap.builder()
- .entry("app_bits_upload", true)
- .entry("app_scaling", true)
- .entry("diego_docker", true)
- .entry("private_domain_creation", true)
- .entry("route_creation", true)
- .entry("service_instance_creation", true)
- .entry("service_instance_sharing", true)
- .entry("set_roles_by_username", true)
- .entry("unset_roles_by_username", true)
- .entry("user_org_creation", false)
- .build();
+ private static final Map STANDARD_FEATURE_FLAGS =
+ FluentMap.builder()
+ .entry("app_bits_upload", true)
+ .entry("app_scaling", true)
+ .entry("diego_docker", true)
+ .entry("private_domain_creation", true)
+ .entry("route_creation", true)
+ .entry("service_instance_creation", true)
+ .entry("service_instance_sharing", true)
+ .entry("set_roles_by_username", true)
+ .entry("unset_roles_by_username", true)
+ .entry("user_org_creation", false)
+ .build();
private final CloudFoundryClient cloudFoundryClient;
@@ -149,7 +149,12 @@ final class CloudFoundryCleaner {
private final UaaClient uaaClient;
- CloudFoundryCleaner(CloudFoundryClient cloudFoundryClient, NameFactory nameFactory, NetworkingClient networkingClient, Version serverVersion, UaaClient uaaClient) {
+ CloudFoundryCleaner(
+ CloudFoundryClient cloudFoundryClient,
+ NameFactory nameFactory,
+ NetworkingClient networkingClient,
+ Version serverVersion,
+ UaaClient uaaClient) {
this.cloudFoundryClient = cloudFoundryClient;
this.nameFactory = nameFactory;
this.networkingClient = networkingClient;
@@ -159,598 +164,1282 @@ final class CloudFoundryCleaner {
void clean() {
Flux.empty()
- .thenMany(Mono.when( // Before Routes
- cleanServiceInstances(this.cloudFoundryClient, this.nameFactory, this.serverVersion),
- cleanUserProvidedServiceInstances(this.cloudFoundryClient, this.nameFactory)
- ))
- .thenMany(Mono.when( // No prerequisites
- cleanBuildpacks(this.cloudFoundryClient, this.nameFactory),
- cleanClients(this.uaaClient, this.nameFactory),
- cleanFeatureFlags(this.cloudFoundryClient),
- cleanGroups(this.uaaClient, this.nameFactory),
- cleanIdentityProviders(this.uaaClient, this.nameFactory),
- cleanIdentityZones(this.uaaClient, this.nameFactory),
- cleanNetworkingPolicies(this.networkingClient, this.nameFactory, this.serverVersion),
- cleanRoutes(this.cloudFoundryClient, this.nameFactory),
- cleanSecurityGroups(this.cloudFoundryClient, this.nameFactory),
- cleanServiceBrokers(this.cloudFoundryClient, this.nameFactory),
- cleanSpaceQuotaDefinitions(this.cloudFoundryClient, this.nameFactory),
- cleanStacks(this.cloudFoundryClient, this.nameFactory),
- cleanUsers(this.cloudFoundryClient, this.nameFactory)
- ))
- .thenMany(Mono.when(
- cleanApplicationsV3(this.cloudFoundryClient, this.nameFactory), // After Routes, cannot run with other cleanApps
- cleanUsers(this.uaaClient, this.nameFactory) // After CF Users
- ))
- .thenMany(Mono.when( // After Routes/Applications
- cleanPrivateDomains(this.cloudFoundryClient, this.nameFactory),
- cleanSharedDomains(this.cloudFoundryClient, this.nameFactory),
- cleanSpaces(this.cloudFoundryClient, this.nameFactory)
- ))
- .thenMany(cleanOrganizations(this.cloudFoundryClient, this.nameFactory)) // After Spaces
- .thenMany(cleanOrganizationQuotaDefinitions(this.cloudFoundryClient, this.nameFactory)) // After Organizations
- .retryWhen(Retry.max(5).filter(SSLException.class::isInstance))
- .doOnSubscribe(s -> LOGGER.debug(">> CLEANUP <<"))
- .doOnComplete(() -> LOGGER.debug("<< CLEANUP >>"))
- .then()
- .block(Duration.ofMinutes(30));
- }
-
- private static Flux cleanApplicationsV3(CloudFoundryClient cloudFoundryClient, NameFactory nameFactory) {
- return PaginationUtils
- .requestClientV3Resources(page -> cloudFoundryClient.applicationsV3()
- .list(ListApplicationsRequest.builder()
- .page(page)
- .build()))
- .filter(application -> nameFactory.isApplicationName(application.getName()))
- .delayUntil(application -> removeApplicationServiceBindings(cloudFoundryClient, application))
- .flatMap(application -> cloudFoundryClient.applicationsV3()
- .delete(DeleteApplicationRequest.builder()
- .applicationId(application.getId())
- .build())
+ .thenMany(
+ Mono.when( // Before Routes
+ cleanServiceInstances(
+ this.cloudFoundryClient,
+ this.nameFactory,
+ this.serverVersion),
+ cleanUserProvidedServiceInstances(
+ this.cloudFoundryClient, this.nameFactory)))
+ .thenMany(
+ Mono.when( // No prerequisites
+ cleanBuildpacks(this.cloudFoundryClient, this.nameFactory),
+ cleanClients(this.uaaClient, this.nameFactory),
+ cleanFeatureFlags(this.cloudFoundryClient),
+ cleanGroups(this.uaaClient, this.nameFactory),
+ cleanIdentityProviders(this.uaaClient, this.nameFactory),
+ cleanIdentityZones(this.uaaClient, this.nameFactory),
+ cleanNetworkingPolicies(
+ this.networkingClient,
+ this.nameFactory,
+ this.serverVersion),
+ cleanRoutes(this.cloudFoundryClient, this.nameFactory),
+ cleanSecurityGroups(this.cloudFoundryClient, this.nameFactory),
+ cleanServiceBrokers(this.cloudFoundryClient, this.nameFactory),
+ cleanSpaceQuotaDefinitions(
+ this.cloudFoundryClient, this.nameFactory),
+ cleanStacks(this.cloudFoundryClient, this.nameFactory),
+ cleanUsers(this.cloudFoundryClient, this.nameFactory)))
+ .thenMany(
+ Mono.when(
+ cleanApplicationsV3(
+ this.cloudFoundryClient,
+ this.nameFactory), // After Routes, cannot run with
+ // other cleanApps
+ cleanUsers(this.uaaClient, this.nameFactory) // After CF Users
+ ))
+ .thenMany(
+ Mono.when( // After Routes/Applications
+ cleanPrivateDomains(this.cloudFoundryClient, this.nameFactory),
+ cleanSharedDomains(this.cloudFoundryClient, this.nameFactory),
+ cleanSpaces(this.cloudFoundryClient, this.nameFactory)))
+ .thenMany(
+ cleanOrganizations(
+ this.cloudFoundryClient, this.nameFactory)) // After Spaces
+ .thenMany(
+ cleanOrganizationQuotaDefinitions(
+ this.cloudFoundryClient, this.nameFactory)) // After Organizations
+ .retryWhen(Retry.max(5).filter(SSLException.class::isInstance))
+ .doOnSubscribe(s -> LOGGER.debug(">> CLEANUP <<"))
+ .doOnComplete(() -> LOGGER.debug("<< CLEANUP >>"))
.then()
- .doOnError(t -> LOGGER.error("Unable to delete V3 application {}", application.getName(), t)));
+ .block(Duration.ofMinutes(30));
+ }
+
+ private static Flux cleanApplicationsV3(
+ CloudFoundryClient cloudFoundryClient, NameFactory nameFactory) {
+ return PaginationUtils.requestClientV3Resources(
+ page ->
+ cloudFoundryClient
+ .applicationsV3()
+ .list(ListApplicationsRequest.builder().page(page).build()))
+ .filter(application -> nameFactory.isApplicationName(application.getName()))
+ .delayUntil(
+ application ->
+ removeApplicationServiceBindings(cloudFoundryClient, application))
+ .flatMap(
+ application ->
+ cloudFoundryClient
+ .applicationsV3()
+ .delete(
+ DeleteApplicationRequest.builder()
+ .applicationId(application.getId())
+ .build())
+ .then()
+ .doOnError(
+ t ->
+ LOGGER.error(
+ "Unable to delete V3 application"
+ + " {}",
+ application.getName(),
+ t)));
}
- private static Flux cleanBuildpacks(CloudFoundryClient cloudFoundryClient, NameFactory nameFactory) {
- return PaginationUtils
- .requestClientV2Resources(page -> cloudFoundryClient.buildpacks()
- .list(ListBuildpacksRequest.builder()
- .page(page)
- .build()))
- .filter(buildpack -> nameFactory.isBuildpackName(ResourceUtils.getEntity(buildpack).getName()))
- .flatMap(buildpack -> cloudFoundryClient.buildpacks()
- .delete(DeleteBuildpackRequest.builder()
- .async(true)
- .buildpackId(ResourceUtils.getId(buildpack))
- .build())
- .doOnError(t -> LOGGER.error("Unable to delete buildpack {}", ResourceUtils.getEntity(buildpack).getName(), t)))
- .flatMap(job -> JobUtils.waitForCompletion(cloudFoundryClient, Duration.ofMinutes(5), job));
+ private static Flux cleanBuildpacks(
+ CloudFoundryClient cloudFoundryClient, NameFactory nameFactory) {
+ return PaginationUtils.requestClientV2Resources(
+ page ->
+ cloudFoundryClient
+ .buildpacks()
+ .list(ListBuildpacksRequest.builder().page(page).build()))
+ .filter(
+ buildpack ->
+ nameFactory.isBuildpackName(
+ ResourceUtils.getEntity(buildpack).getName()))
+ .flatMap(
+ buildpack ->
+ cloudFoundryClient
+ .buildpacks()
+ .delete(
+ DeleteBuildpackRequest.builder()
+ .async(true)
+ .buildpackId(ResourceUtils.getId(buildpack))
+ .build())
+ .doOnError(
+ t ->
+ LOGGER.error(
+ "Unable to delete buildpack {}",
+ ResourceUtils.getEntity(buildpack)
+ .getName(),
+ t)))
+ .flatMap(
+ job ->
+ JobUtils.waitForCompletion(
+ cloudFoundryClient, Duration.ofMinutes(5), job));
}
private static Flux cleanClients(UaaClient uaaClient, NameFactory nameFactory) {
- return PaginationUtils
- .requestUaaResources(startIndex -> uaaClient.clients()
- .list(ListClientsRequest.builder()
- .startIndex(startIndex)
- .build()))
- .filter(client -> nameFactory.isClientId(client.getClientId()))
- .flatMap(client -> uaaClient.clients()
- .delete(DeleteClientRequest.builder()
- .clientId(client.getClientId())
- .build())
- .doOnError(t -> LOGGER.error("Unable to delete client {}", client.getName(), t))
- .then());
+ return PaginationUtils.requestUaaResources(
+ startIndex ->
+ uaaClient
+ .clients()
+ .list(
+ ListClientsRequest.builder()
+ .startIndex(startIndex)
+ .build()))
+ .filter(client -> nameFactory.isClientId(client.getClientId()))
+ .flatMap(
+ client ->
+ uaaClient
+ .clients()
+ .delete(
+ DeleteClientRequest.builder()
+ .clientId(client.getClientId())
+ .build())
+ .doOnError(
+ t ->
+ LOGGER.error(
+ "Unable to delete client {}",
+ client.getName(),
+ t))
+ .then());
}
private static Flux cleanFeatureFlags(CloudFoundryClient cloudFoundryClient) {
- return cloudFoundryClient.featureFlags()
- .list(ListFeatureFlagsRequest.builder()
- .build())
- .flatMapIterable(ListFeatureFlagsResponse::getFeatureFlags)
- .filter(featureFlag -> STANDARD_FEATURE_FLAGS.containsKey(featureFlag.getName()))
- .filter(featureFlag -> STANDARD_FEATURE_FLAGS.get(featureFlag.getName()) != featureFlag.getEnabled())
- .flatMap(featureFlag -> cloudFoundryClient.featureFlags()
- .set(SetFeatureFlagRequest.builder()
- .name(featureFlag.getName())
- .enabled(STANDARD_FEATURE_FLAGS.get(featureFlag.getName()))
- .build())
- .doOnError(t -> LOGGER.error("Unable to set feature flag {} to {}", featureFlag.getName(), STANDARD_FEATURE_FLAGS.get(featureFlag.getName()), t))
- .then());
+ return cloudFoundryClient
+ .featureFlags()
+ .list(ListFeatureFlagsRequest.builder().build())
+ .flatMapIterable(ListFeatureFlagsResponse::getFeatureFlags)
+ .filter(featureFlag -> STANDARD_FEATURE_FLAGS.containsKey(featureFlag.getName()))
+ .filter(
+ featureFlag ->
+ STANDARD_FEATURE_FLAGS.get(featureFlag.getName())
+ != featureFlag.getEnabled())
+ .flatMap(
+ featureFlag ->
+ cloudFoundryClient
+ .featureFlags()
+ .set(
+ SetFeatureFlagRequest.builder()
+ .name(featureFlag.getName())
+ .enabled(
+ STANDARD_FEATURE_FLAGS.get(
+ featureFlag.getName()))
+ .build())
+ .doOnError(
+ t ->
+ LOGGER.error(
+ "Unable to set feature flag {} to"
+ + " {}",
+ featureFlag.getName(),
+ STANDARD_FEATURE_FLAGS.get(
+ featureFlag.getName()),
+ t))
+ .then());
}
private static Flux cleanGroups(UaaClient uaaClient, NameFactory nameFactory) {
- return PaginationUtils
- .requestUaaResources(startIndex -> uaaClient.groups()
- .list(ListGroupsRequest.builder()
- .startIndex(startIndex)
- .build()))
- .filter(group -> nameFactory.isGroupName(group.getDisplayName()))
- .sort((group1, group2) -> {
- if (containsMember(group1, group2)) {
- return -1;
- } else if (containsMember(group2, group1)) {
- return 1;
- } else {
- return 0;
- }
- })
- .concatMap(group -> uaaClient.groups()
- .delete(DeleteGroupRequest.builder()
- .groupId(group.getId())
- .version("*")
- .build())
- .doOnError(t -> LOGGER.error("Unable to delete group {}", group.getDisplayName(), t))
- .then());
+ return PaginationUtils.requestUaaResources(
+ startIndex ->
+ uaaClient
+ .groups()
+ .list(
+ ListGroupsRequest.builder()
+ .startIndex(startIndex)
+ .build()))
+ .filter(group -> nameFactory.isGroupName(group.getDisplayName()))
+ .sort(
+ (group1, group2) -> {
+ if (containsMember(group1, group2)) {
+ return -1;
+ } else if (containsMember(group2, group1)) {
+ return 1;
+ } else {
+ return 0;
+ }
+ })
+ .concatMap(
+ group ->
+ uaaClient
+ .groups()
+ .delete(
+ DeleteGroupRequest.builder()
+ .groupId(group.getId())
+ .version("*")
+ .build())
+ .doOnError(
+ t ->
+ LOGGER.error(
+ "Unable to delete group {}",
+ group.getDisplayName(),
+ t))
+ .then());
}
private static Flux cleanIdentityProviders(UaaClient uaaClient, NameFactory nameFactory) {
- return uaaClient.identityProviders()
- .list(ListIdentityProvidersRequest.builder()
- .build())
- .flatMapIterable(ListIdentityProvidersResponse::getIdentityProviders)
- .filter(provider -> nameFactory.isIdentityProviderName(provider.getName()))
- .flatMap(provider -> uaaClient.identityProviders()
- .delete(DeleteIdentityProviderRequest.builder()
- .identityProviderId(provider.getId())
- .build())
- .doOnError(t -> LOGGER.error("Unable to delete identity provider {}", provider.getName(), t))
- .then());
+ return uaaClient
+ .identityProviders()
+ .list(ListIdentityProvidersRequest.builder().build())
+ .flatMapIterable(ListIdentityProvidersResponse::getIdentityProviders)
+ .filter(provider -> nameFactory.isIdentityProviderName(provider.getName()))
+ .flatMap(
+ provider ->
+ uaaClient
+ .identityProviders()
+ .delete(
+ DeleteIdentityProviderRequest.builder()
+ .identityProviderId(provider.getId())
+ .build())
+ .doOnError(
+ t ->
+ LOGGER.error(
+ "Unable to delete identity provider"
+ + " {}",
+ provider.getName(),
+ t))
+ .then());
}
private static Flux cleanIdentityZones(UaaClient uaaClient, NameFactory nameFactory) {
- return uaaClient.identityZones()
- .list(ListIdentityZonesRequest.builder()
- .build())
- .flatMapIterable(ListIdentityZonesResponse::getIdentityZones)
- .filter(zone -> nameFactory.isIdentityZoneName(zone.getName()))
- .flatMap(zone -> uaaClient.identityZones()
- .delete(DeleteIdentityZoneRequest.builder()
- .identityZoneId(zone.getId())
- .build())
- .doOnError(t -> LOGGER.error("Unable to delete identity zone {}", zone.getName(), t))
- .then());
- }
-
- private static Flux cleanNetworkingPolicies(NetworkingClient networkingClient, NameFactory nameFactory, Version serverVersion) {
- return ifCfVersion(PCF_1_12, serverVersion, () -> networkingClient.policies()
- .list(ListPoliciesRequest.builder()
- .build())
- .flatMapIterable(ListPoliciesResponse::getPolicies)
- .filter(policy -> nameFactory.isPort(policy.getDestination().getPorts().getStart()))
- .filter(policy -> nameFactory.isPort(policy.getDestination().getPorts().getEnd()))
- .flatMap(policy -> networkingClient.policies()
- .delete(DeletePoliciesRequest.builder()
- .policy(Policy.builder()
- .destination(Destination.builder()
- .id(policy.getDestination().getId())
- .ports(Ports.builder()
- .end(policy.getDestination().getPorts().getEnd())
- .start(policy.getDestination().getPorts().getStart())
- .build())
- .protocol(policy.getDestination().getProtocol())
- .build())
- .source(Source.builder()
- .id(policy.getSource().getId())
- .build())
- .build())
- .build())
- .doOnError(t -> LOGGER.error("Unable to delete networking policy between {} and {}", policy.getSource().getId(), policy.getDestination().getId(), t))
- .then()));
- }
-
- private static Flux cleanOrganizationQuotaDefinitions(CloudFoundryClient cloudFoundryClient, NameFactory nameFactory) {
- return PaginationUtils
- .requestClientV2Resources(page -> cloudFoundryClient.organizationQuotaDefinitions()
- .list(ListOrganizationQuotaDefinitionsRequest.builder()
- .page(page)
- .build()))
- .filter(domain -> nameFactory.isQuotaDefinitionName(ResourceUtils.getEntity(domain).getName()))
- .flatMap(organizationQuotaDefinition -> cloudFoundryClient.organizationQuotaDefinitions()
- .delete(DeleteOrganizationQuotaDefinitionRequest.builder()
- .async(true)
- .organizationQuotaDefinitionId(ResourceUtils.getId(organizationQuotaDefinition))
- .build())
- .flatMapMany(job -> JobUtils.waitForCompletion(cloudFoundryClient, Duration.ofMinutes(5), job))
- .doOnError(t -> LOGGER.error("Unable to delete organization quota definition {}", ResourceUtils.getEntity(organizationQuotaDefinition).getName(), t)));
- }
-
- private static Flux cleanOrganizations(CloudFoundryClient cloudFoundryClient, NameFactory nameFactory) {
- return PaginationUtils
- .requestClientV2Resources(page -> cloudFoundryClient.organizations()
- .list(ListOrganizationsRequest.builder()
- .page(page)
- .build()))
- .filter(organization -> nameFactory.isOrganizationName(ResourceUtils.getEntity(organization).getName()))
- .flatMap(organization -> cloudFoundryClient.organizations()
- .delete(DeleteOrganizationRequest.builder()
- .async(true)
- .organizationId(ResourceUtils.getId(organization))
- .build())
- .flatMapMany(job -> JobUtils.waitForCompletion(cloudFoundryClient, Duration.ofMinutes(5), job))
- .doOnError(t -> LOGGER.error("Unable to delete organization {}", ResourceUtils.getEntity(organization).getName(), t)));
- }
-
- private static Flux cleanPrivateDomains(CloudFoundryClient cloudFoundryClient, NameFactory nameFactory) {
- return PaginationUtils
- .requestClientV2Resources(page -> cloudFoundryClient.privateDomains()
- .list(ListPrivateDomainsRequest.builder()
- .page(page)
- .build()))
- .filter(domain -> nameFactory.isDomainName(ResourceUtils.getEntity(domain).getName()))
- .flatMap(privateDomain -> cloudFoundryClient.privateDomains()
- .delete(DeletePrivateDomainRequest.builder()
- .async(true)
- .privateDomainId(ResourceUtils.getId(privateDomain))
- .build())
- .flatMapMany(job -> JobUtils.waitForCompletion(cloudFoundryClient, Duration.ofMinutes(5), job))
- .doOnError(t -> LOGGER.error("Unable to delete private domain {}", ResourceUtils.getEntity(privateDomain).getName(), t)));
- }
-
- private static Flux cleanRoutes(CloudFoundryClient cloudFoundryClient, NameFactory nameFactory) {
+ return uaaClient
+ .identityZones()
+ .list(ListIdentityZonesRequest.builder().build())
+ .flatMapIterable(ListIdentityZonesResponse::getIdentityZones)
+ .filter(zone -> nameFactory.isIdentityZoneName(zone.getName()))
+ .flatMap(
+ zone ->
+ uaaClient
+ .identityZones()
+ .delete(
+ DeleteIdentityZoneRequest.builder()
+ .identityZoneId(zone.getId())
+ .build())
+ .doOnError(
+ t ->
+ LOGGER.error(
+ "Unable to delete identity zone {}",
+ zone.getName(),
+ t))
+ .then());
+ }
+
+ private static Flux cleanNetworkingPolicies(
+ NetworkingClient networkingClient, NameFactory nameFactory, Version serverVersion) {
+ return ifCfVersion(
+ PCF_1_12,
+ serverVersion,
+ () ->
+ networkingClient
+ .policies()
+ .list(ListPoliciesRequest.builder().build())
+ .flatMapIterable(ListPoliciesResponse::getPolicies)
+ .filter(
+ policy ->
+ nameFactory.isPort(
+ policy.getDestination()
+ .getPorts()
+ .getStart()))
+ .filter(
+ policy ->
+ nameFactory.isPort(
+ policy.getDestination()
+ .getPorts()
+ .getEnd()))
+ .flatMap(
+ policy ->
+ networkingClient
+ .policies()
+ .delete(
+ DeletePoliciesRequest.builder()
+ .policy(
+ Policy.builder()
+ .destination(
+ Destination
+ .builder()
+ .id(
+ policy.getDestination()
+ .getId())
+ .ports(
+ Ports
+ .builder()
+ .end(
+ policy.getDestination()
+ .getPorts()
+ .getEnd())
+ .start(
+ policy.getDestination()
+ .getPorts()
+ .getStart())
+ .build())
+ .protocol(
+ policy.getDestination()
+ .getProtocol())
+ .build())
+ .source(
+ Source
+ .builder()
+ .id(
+ policy.getSource()
+ .getId())
+ .build())
+ .build())
+ .build())
+ .doOnError(
+ t ->
+ LOGGER.error(
+ "Unable to delete"
+ + " networking"
+ + " policy"
+ + " between {}"
+ + " and {}",
+ policy.getSource()
+ .getId(),
+ policy.getDestination()
+ .getId(),
+ t))
+ .then()));
+ }
+
+ private static Flux cleanOrganizationQuotaDefinitions(
+ CloudFoundryClient cloudFoundryClient, NameFactory nameFactory) {
+ return PaginationUtils.requestClientV2Resources(
+ page ->
+ cloudFoundryClient
+ .organizationQuotaDefinitions()
+ .list(
+ ListOrganizationQuotaDefinitionsRequest.builder()
+ .page(page)
+ .build()))
+ .filter(
+ domain ->
+ nameFactory.isQuotaDefinitionName(
+ ResourceUtils.getEntity(domain).getName()))
+ .flatMap(
+ organizationQuotaDefinition ->
+ cloudFoundryClient
+ .organizationQuotaDefinitions()
+ .delete(
+ DeleteOrganizationQuotaDefinitionRequest.builder()
+ .async(true)
+ .organizationQuotaDefinitionId(
+ ResourceUtils.getId(
+ organizationQuotaDefinition))
+ .build())
+ .flatMapMany(
+ job ->
+ JobUtils.waitForCompletion(
+ cloudFoundryClient,
+ Duration.ofMinutes(5),
+ job))
+ .doOnError(
+ t ->
+ LOGGER.error(
+ "Unable to delete organization"
+ + " quota definition {}",
+ ResourceUtils.getEntity(
+ organizationQuotaDefinition)
+ .getName(),
+ t)));
+ }
+
+ private static Flux cleanOrganizations(
+ CloudFoundryClient cloudFoundryClient, NameFactory nameFactory) {
+ return PaginationUtils.requestClientV2Resources(
+ page ->
+ cloudFoundryClient
+ .organizations()
+ .list(
+ ListOrganizationsRequest.builder()
+ .page(page)
+ .build()))
+ .filter(
+ organization ->
+ nameFactory.isOrganizationName(
+ ResourceUtils.getEntity(organization).getName()))
+ .flatMap(
+ organization ->
+ cloudFoundryClient
+ .organizations()
+ .delete(
+ DeleteOrganizationRequest.builder()
+ .async(true)
+ .organizationId(
+ ResourceUtils.getId(organization))
+ .build())
+ .flatMapMany(
+ job ->
+ JobUtils.waitForCompletion(
+ cloudFoundryClient,
+ Duration.ofMinutes(5),
+ job))
+ .doOnError(
+ t ->
+ LOGGER.error(
+ "Unable to delete organization {}",
+ ResourceUtils.getEntity(
+ organization)
+ .getName(),
+ t)));
+ }
+
+ private static Flux cleanPrivateDomains(
+ CloudFoundryClient cloudFoundryClient, NameFactory nameFactory) {
+ return PaginationUtils.requestClientV2Resources(
+ page ->
+ cloudFoundryClient
+ .privateDomains()
+ .list(
+ ListPrivateDomainsRequest.builder()
+ .page(page)
+ .build()))
+ .filter(
+ domain ->
+ nameFactory.isDomainName(ResourceUtils.getEntity(domain).getName()))
+ .flatMap(
+ privateDomain ->
+ cloudFoundryClient
+ .privateDomains()
+ .delete(
+ DeletePrivateDomainRequest.builder()
+ .async(true)
+ .privateDomainId(
+ ResourceUtils.getId(privateDomain))
+ .build())
+ .flatMapMany(
+ job ->
+ JobUtils.waitForCompletion(
+ cloudFoundryClient,
+ Duration.ofMinutes(5),
+ job))
+ .doOnError(
+ t ->
+ LOGGER.error(
+ "Unable to delete private domain"
+ + " {}",
+ ResourceUtils.getEntity(
+ privateDomain)
+ .getName(),
+ t)));
+ }
+
+ private static Flux cleanRoutes(
+ CloudFoundryClient cloudFoundryClient, NameFactory nameFactory) {
return getAllDomains(cloudFoundryClient)
- .flatMapMany(domains -> PaginationUtils
- .requestClientV2Resources(page -> cloudFoundryClient.routes()
- .list(ListRoutesRequest.builder()
- .page(page)
- .build()))
- .map(resource -> Tuples.of(domains, resource)))
- .filter(predicate((domains, route) -> nameFactory.isDomainName(domains.get(ResourceUtils.getEntity(route).getDomainId())) ||
- nameFactory.isApplicationName(ResourceUtils.getEntity(route).getHost()) ||
- nameFactory.isHostName(ResourceUtils.getEntity(route).getHost())))
- .flatMap(function((domains, route) -> cloudFoundryClient.routes()
- .delete(DeleteRouteRequest.builder()
- .async(true)
- .routeId(ResourceUtils.getId(route))
- .build())
- .flatMapMany(job -> JobUtils.waitForCompletion(cloudFoundryClient, Duration.ofMinutes(5), job))
- .doOnError(t -> {
- RouteEntity entity = ResourceUtils.getEntity(route);
- LOGGER.error("Unable to delete route {}.{}:{}{}", entity.getHost(), domains.get(entity.getDomainId()), entity.getPort(), entity.getPath(), t);
- })));
- }
-
- private static Flux cleanSecurityGroups(CloudFoundryClient cloudFoundryClient, NameFactory nameFactory) {
- return PaginationUtils
- .requestClientV2Resources(page -> cloudFoundryClient.securityGroups()
- .list(ListSecurityGroupsRequest.builder()
- .page(page)
- .build()))
- .filter(securityGroup -> nameFactory.isSecurityGroupName(ResourceUtils.getEntity(securityGroup).getName()))
- .flatMap(securityGroup -> cloudFoundryClient.securityGroups()
- .delete(DeleteSecurityGroupRequest.builder()
- .securityGroupId(ResourceUtils.getId(securityGroup))
- .build())
- .doOnError(t -> LOGGER.error("Unable to delete security group {}", ResourceUtils.getEntity(securityGroup).getName(), t))
- .then());
- }
-
- private static Flux cleanServiceBrokers(CloudFoundryClient cloudFoundryClient, NameFactory nameFactory) {
- return PaginationUtils
- .requestClientV2Resources(page -> cloudFoundryClient.serviceBrokers()
- .list(ListServiceBrokersRequest.builder()
- .page(page)
- .build()))
- .filter(serviceBroker -> nameFactory.isServiceBrokerName(ResourceUtils.getEntity(serviceBroker).getName()))
- .flatMap(serviceBroker -> cloudFoundryClient.serviceBrokers()
- .delete(DeleteServiceBrokerRequest.builder()
- .serviceBrokerId(ResourceUtils.getId(serviceBroker))
- .build())
- .doOnError(t -> LOGGER.error("Unable to delete service broker {}", ResourceUtils.getEntity(serviceBroker).getName(), t)));
- }
-
- private static Flux cleanServiceInstances(CloudFoundryClient cloudFoundryClient, NameFactory nameFactory, Version serverVersion) {
- return PaginationUtils
- .requestClientV2Resources(page -> cloudFoundryClient.serviceInstances()
- .list(ListServiceInstancesRequest.builder()
- .page(page)
- .build()))
- .filter(serviceInstance -> nameFactory.isServiceInstanceName(ResourceUtils.getEntity(serviceInstance).getName()))
- .delayUntil(serviceInstance -> removeRouteAssociations(cloudFoundryClient, serviceInstance))
- .delayUntil(serviceInstance -> removeServiceInstanceServiceBindings(cloudFoundryClient, serviceInstance))
- .delayUntil(serviceInstance -> removeServiceKeys(cloudFoundryClient, serviceInstance))
- .delayUntil(serviceInstance -> removeServiceShares(cloudFoundryClient, serviceInstance, serverVersion))
- .flatMap(serviceInstance -> cloudFoundryClient.serviceInstances()
- .delete(DeleteServiceInstanceRequest.builder()
- .async(true)
- .serviceInstanceId(ResourceUtils.getId(serviceInstance))
- .build())
- .flatMap(response -> {
- Object entity = response.getEntity();
- if (entity instanceof JobEntity) {
- return JobUtils.waitForCompletion(cloudFoundryClient, Duration.ofMinutes(5), (JobEntity) response.getEntity());
- } else {
- return LastOperationUtils
- .waitForCompletion(Duration.ofMinutes(5), () -> cloudFoundryClient.serviceInstances()
- .get(GetServiceInstanceRequest.builder()
- .serviceInstanceId(ResourceUtils.getId(serviceInstance))
- .build())
- .map(r -> ResourceUtils.getEntity(r).getLastOperation()));
- }
- })
- .doOnError(t -> LOGGER.error("Unable to delete service instance {}", ResourceUtils.getEntity(serviceInstance).getName(), t)));
- }
-
- private static Flux cleanSharedDomains(CloudFoundryClient cloudFoundryClient, NameFactory nameFactory) {
- return PaginationUtils.
- requestClientV2Resources(page -> cloudFoundryClient.sharedDomains()
- .list(ListSharedDomainsRequest.builder()
- .page(page)
- .build()))
- .filter(domain -> nameFactory.isDomainName(ResourceUtils.getEntity(domain).getName()))
- .flatMap(domain -> cloudFoundryClient.sharedDomains()
- .delete(DeleteSharedDomainRequest.builder()
- .async(true)
- .sharedDomainId(ResourceUtils.getId(domain))
- .build())
- .flatMapMany(job -> JobUtils.waitForCompletion(cloudFoundryClient, Duration.ofMinutes(5), job))
- .doOnError(t -> LOGGER.error("Unable to delete domain {}", ResourceUtils.getEntity(domain).getName(), t)));
- }
-
- private static Flux cleanSpaceQuotaDefinitions(CloudFoundryClient cloudFoundryClient, NameFactory nameFactory) {
- return PaginationUtils
- .requestClientV2Resources(page -> cloudFoundryClient.spaceQuotaDefinitions()
- .list(ListSpaceQuotaDefinitionsRequest.builder()
- .page(page)
- .build()))
- .filter(quota -> nameFactory.isQuotaDefinitionName(ResourceUtils.getEntity(quota).getName()))
- .flatMap(quota -> cloudFoundryClient.spaceQuotaDefinitions()
- .delete((DeleteSpaceQuotaDefinitionRequest.builder()
- .async(true)
- .spaceQuotaDefinitionId(ResourceUtils.getId(quota))
- .build()))
- .flatMapMany(job -> JobUtils.waitForCompletion(cloudFoundryClient, Duration.ofMinutes(5), job))
- .doOnError(t -> LOGGER.error("Unable to delete space quota definition {}", ResourceUtils.getEntity(quota).getName(), t)));
- }
-
- private static Flux cleanSpaces(CloudFoundryClient cloudFoundryClient, NameFactory nameFactory) {
- return PaginationUtils
- .requestClientV2Resources(page -> cloudFoundryClient.spaces()
- .list(ListSpacesRequest.builder()
- .page(page)
- .build()))
- .filter(space -> nameFactory.isSpaceName(ResourceUtils.getEntity(space).getName()))
- .delayUntil(space -> removeSpaceMetadata(cloudFoundryClient, space)) //TODO: Remove once the delete spaces endpoint handles spaces with metadata
- .flatMap(space -> cloudFoundryClient.spaces()
- .delete(DeleteSpaceRequest.builder()
- .async(true)
- .recursive(true)
- .spaceId(ResourceUtils.getId(space))
- .build())
- .flatMapMany(job -> JobUtils.waitForCompletion(cloudFoundryClient, Duration.ofMinutes(5), job))
- .doOnError(t -> LOGGER.error("Unable to delete space {}", ResourceUtils.getEntity(space).getName(), t)));
- }
-
- private static Flux cleanStacks(CloudFoundryClient cloudFoundryClient, NameFactory nameFactory) {
- return PaginationUtils
- .requestClientV2Resources(page -> cloudFoundryClient.stacks()
- .list(ListStacksRequest.builder()
- .page(page)
- .build()))
- .filter(stack -> nameFactory.isStackName(ResourceUtils.getEntity(stack).getName()))
- .flatMap(stack -> cloudFoundryClient.stacks()
- .delete((DeleteStackRequest.builder()
- .async(true)
- .stackId(ResourceUtils.getId(stack))
- .build()))
- .flatMapMany(job -> JobUtils.waitForCompletion(cloudFoundryClient, Duration.ofMinutes(5), job))
- .doOnError(t -> LOGGER.error("Unable to delete stack {}", ResourceUtils.getEntity(stack).getName(), t)));
- }
-
- private static Flux cleanUserProvidedServiceInstances(CloudFoundryClient cloudFoundryClient, NameFactory nameFactory) {
- return PaginationUtils
- .requestClientV2Resources(page -> cloudFoundryClient.userProvidedServiceInstances()
- .list(ListUserProvidedServiceInstancesRequest.builder()
- .page(page)
- .build()))
- .filter(userProvidedServiceInstance -> nameFactory.isServiceInstanceName(ResourceUtils.getEntity(userProvidedServiceInstance).getName()))
- .delayUntil(userProvidedServiceInstance -> removeRouteAssociations(cloudFoundryClient, userProvidedServiceInstance))
- .delayUntil(userProvidedServiceInstance -> removeUserProvidedServiceInstanceServiceBindings(cloudFoundryClient, userProvidedServiceInstance))
- .flatMap(userProvidedServiceInstance -> cloudFoundryClient.userProvidedServiceInstances()
- .delete(DeleteUserProvidedServiceInstanceRequest.builder()
- .userProvidedServiceInstanceId(ResourceUtils.getId(userProvidedServiceInstance))
- .build())
- .doOnError(t -> LOGGER.error("Unable to delete user provided service instance {}", ResourceUtils.getEntity(userProvidedServiceInstance).getName(), t)));
- }
-
- private static Flux cleanUsers(CloudFoundryClient cloudFoundryClient, NameFactory nameFactory) {
- return PaginationUtils
- .requestClientV2Resources(page -> cloudFoundryClient.users()
- .list(org.cloudfoundry.client.v2.users.ListUsersRequest.builder()
- .page(page)
- .build()))
- .filter(resource -> isCleanable(nameFactory, resource))
- .map(resource -> resource.getMetadata().getId())
- .flatMap(userId -> cloudFoundryClient.users()
- .delete(org.cloudfoundry.client.v2.users.DeleteUserRequest.builder()
- .async(true)
- .userId(userId)
- .build())
- .flatMapMany(job -> JobUtils.waitForCompletion(cloudFoundryClient, Duration.ofMinutes(5), job))
- .doOnError(t -> LOGGER.error("Unable to delete user {}", userId, t)));
+ .flatMapMany(
+ domains ->
+ PaginationUtils.requestClientV2Resources(
+ page ->
+ cloudFoundryClient
+ .routes()
+ .list(
+ ListRoutesRequest.builder()
+ .page(page)
+ .build()))
+ .map(resource -> Tuples.of(domains, resource)))
+ .filter(
+ predicate(
+ (domains, route) ->
+ nameFactory.isDomainName(
+ domains.get(
+ ResourceUtils.getEntity(route)
+ .getDomainId()))
+ || nameFactory.isApplicationName(
+ ResourceUtils.getEntity(route).getHost())
+ || nameFactory.isHostName(
+ ResourceUtils.getEntity(route).getHost())))
+ .flatMap(
+ function(
+ (domains, route) ->
+ cloudFoundryClient
+ .routes()
+ .delete(
+ DeleteRouteRequest.builder()
+ .async(true)
+ .routeId(ResourceUtils.getId(route))
+ .build())
+ .flatMapMany(
+ job ->
+ JobUtils.waitForCompletion(
+ cloudFoundryClient,
+ Duration.ofMinutes(5),
+ job))
+ .doOnError(
+ t -> {
+ RouteEntity entity =
+ ResourceUtils.getEntity(route);
+ LOGGER.error(
+ "Unable to delete route"
+ + " {}.{}:{}{}",
+ entity.getHost(),
+ domains.get(
+ entity.getDomainId()),
+ entity.getPort(),
+ entity.getPath(),
+ t);
+ })));
+ }
+
+ private static Flux cleanSecurityGroups(
+ CloudFoundryClient cloudFoundryClient, NameFactory nameFactory) {
+ return PaginationUtils.requestClientV2Resources(
+ page ->
+ cloudFoundryClient
+ .securityGroups()
+ .list(
+ ListSecurityGroupsRequest.builder()
+ .page(page)
+ .build()))
+ .filter(
+ securityGroup ->
+ nameFactory.isSecurityGroupName(
+ ResourceUtils.getEntity(securityGroup).getName()))
+ .flatMap(
+ securityGroup ->
+ cloudFoundryClient
+ .securityGroups()
+ .delete(
+ DeleteSecurityGroupRequest.builder()
+ .securityGroupId(
+ ResourceUtils.getId(securityGroup))
+ .build())
+ .doOnError(
+ t ->
+ LOGGER.error(
+ "Unable to delete security group"
+ + " {}",
+ ResourceUtils.getEntity(
+ securityGroup)
+ .getName(),
+ t))
+ .then());
+ }
+
+ private static Flux cleanServiceBrokers(
+ CloudFoundryClient cloudFoundryClient, NameFactory nameFactory) {
+ return PaginationUtils.requestClientV2Resources(
+ page ->
+ cloudFoundryClient
+ .serviceBrokers()
+ .list(
+ ListServiceBrokersRequest.builder()
+ .page(page)
+ .build()))
+ .filter(
+ serviceBroker ->
+ nameFactory.isServiceBrokerName(
+ ResourceUtils.getEntity(serviceBroker).getName()))
+ .flatMap(
+ serviceBroker ->
+ cloudFoundryClient
+ .serviceBrokers()
+ .delete(
+ DeleteServiceBrokerRequest.builder()
+ .serviceBrokerId(
+ ResourceUtils.getId(serviceBroker))
+ .build())
+ .doOnError(
+ t ->
+ LOGGER.error(
+ "Unable to delete service broker"
+ + " {}",
+ ResourceUtils.getEntity(
+ serviceBroker)
+ .getName(),
+ t)));
+ }
+
+ private static Flux cleanServiceInstances(
+ CloudFoundryClient cloudFoundryClient, NameFactory nameFactory, Version serverVersion) {
+ return PaginationUtils.requestClientV2Resources(
+ page ->
+ cloudFoundryClient
+ .serviceInstances()
+ .list(
+ ListServiceInstancesRequest.builder()
+ .page(page)
+ .build()))
+ .filter(
+ serviceInstance ->
+ nameFactory.isServiceInstanceName(
+ ResourceUtils.getEntity(serviceInstance).getName()))
+ .delayUntil(
+ serviceInstance ->
+ removeRouteAssociations(cloudFoundryClient, serviceInstance))
+ .delayUntil(
+ serviceInstance ->
+ removeServiceInstanceServiceBindings(
+ cloudFoundryClient, serviceInstance))
+ .delayUntil(
+ serviceInstance -> removeServiceKeys(cloudFoundryClient, serviceInstance))
+ .delayUntil(
+ serviceInstance ->
+ removeServiceShares(
+ cloudFoundryClient, serviceInstance, serverVersion))
+ .flatMap(
+ serviceInstance ->
+ cloudFoundryClient
+ .serviceInstances()
+ .delete(
+ DeleteServiceInstanceRequest.builder()
+ .async(true)
+ .serviceInstanceId(
+ ResourceUtils.getId(
+ serviceInstance))
+ .build())
+ .flatMap(
+ response -> {
+ Object entity = response.getEntity();
+ if (entity instanceof JobEntity) {
+ return JobUtils.waitForCompletion(
+ cloudFoundryClient,
+ Duration.ofMinutes(5),
+ (JobEntity) response.getEntity());
+ } else {
+ return LastOperationUtils.waitForCompletion(
+ Duration.ofMinutes(5),
+ () ->
+ cloudFoundryClient
+ .serviceInstances()
+ .get(
+ GetServiceInstanceRequest
+ .builder()
+ .serviceInstanceId(
+ ResourceUtils
+ .getId(
+ serviceInstance))
+ .build())
+ .map(
+ r ->
+ ResourceUtils
+ .getEntity(
+ r)
+ .getLastOperation()));
+ }
+ })
+ .doOnError(
+ t ->
+ LOGGER.error(
+ "Unable to delete service instance"
+ + " {}",
+ ResourceUtils.getEntity(
+ serviceInstance)
+ .getName(),
+ t)));
+ }
+
+ private static Flux cleanSharedDomains(
+ CloudFoundryClient cloudFoundryClient, NameFactory nameFactory) {
+ return PaginationUtils.requestClientV2Resources(
+ page ->
+ cloudFoundryClient
+ .sharedDomains()
+ .list(
+ ListSharedDomainsRequest.builder()
+ .page(page)
+ .build()))
+ .filter(
+ domain ->
+ nameFactory.isDomainName(ResourceUtils.getEntity(domain).getName()))
+ .flatMap(
+ domain ->
+ cloudFoundryClient
+ .sharedDomains()
+ .delete(
+ DeleteSharedDomainRequest.builder()
+ .async(true)
+ .sharedDomainId(ResourceUtils.getId(domain))
+ .build())
+ .flatMapMany(
+ job ->
+ JobUtils.waitForCompletion(
+ cloudFoundryClient,
+ Duration.ofMinutes(5),
+ job))
+ .doOnError(
+ t ->
+ LOGGER.error(
+ "Unable to delete domain {}",
+ ResourceUtils.getEntity(domain)
+ .getName(),
+ t)));
+ }
+
+ private static Flux cleanSpaceQuotaDefinitions(
+ CloudFoundryClient cloudFoundryClient, NameFactory nameFactory) {
+ return PaginationUtils.requestClientV2Resources(
+ page ->
+ cloudFoundryClient
+ .spaceQuotaDefinitions()
+ .list(
+ ListSpaceQuotaDefinitionsRequest.builder()
+ .page(page)
+ .build()))
+ .filter(
+ quota ->
+ nameFactory.isQuotaDefinitionName(
+ ResourceUtils.getEntity(quota).getName()))
+ .flatMap(
+ quota ->
+ cloudFoundryClient
+ .spaceQuotaDefinitions()
+ .delete(
+ (DeleteSpaceQuotaDefinitionRequest.builder()
+ .async(true)
+ .spaceQuotaDefinitionId(
+ ResourceUtils.getId(quota))
+ .build()))
+ .flatMapMany(
+ job ->
+ JobUtils.waitForCompletion(
+ cloudFoundryClient,
+ Duration.ofMinutes(5),
+ job))
+ .doOnError(
+ t ->
+ LOGGER.error(
+ "Unable to delete space quota"
+ + " definition {}",
+ ResourceUtils.getEntity(quota)
+ .getName(),
+ t)));
+ }
+
+ private static Flux cleanSpaces(
+ CloudFoundryClient cloudFoundryClient, NameFactory nameFactory) {
+ return PaginationUtils.requestClientV2Resources(
+ page ->
+ cloudFoundryClient
+ .spaces()
+ .list(ListSpacesRequest.builder().page(page).build()))
+ .filter(space -> nameFactory.isSpaceName(ResourceUtils.getEntity(space).getName()))
+ .delayUntil(
+ space ->
+ removeSpaceMetadata(
+ cloudFoundryClient,
+ space)) // TODO: Remove once the delete spaces endpoint
+ // handles spaces with metadata
+ .flatMap(
+ space ->
+ cloudFoundryClient
+ .spaces()
+ .delete(
+ DeleteSpaceRequest.builder()
+ .async(true)
+ .recursive(true)
+ .spaceId(ResourceUtils.getId(space))
+ .build())
+ .flatMapMany(
+ job ->
+ JobUtils.waitForCompletion(
+ cloudFoundryClient,
+ Duration.ofMinutes(5),
+ job))
+ .doOnError(
+ t ->
+ LOGGER.error(
+ "Unable to delete space {}",
+ ResourceUtils.getEntity(space)
+ .getName(),
+ t)));
+ }
+
+ private static Flux cleanStacks(
+ CloudFoundryClient cloudFoundryClient, NameFactory nameFactory) {
+ return PaginationUtils.requestClientV2Resources(
+ page ->
+ cloudFoundryClient
+ .stacks()
+ .list(ListStacksRequest.builder().page(page).build()))
+ .filter(stack -> nameFactory.isStackName(ResourceUtils.getEntity(stack).getName()))
+ .flatMap(
+ stack ->
+ cloudFoundryClient
+ .stacks()
+ .delete(
+ (DeleteStackRequest.builder()
+ .async(true)
+ .stackId(ResourceUtils.getId(stack))
+ .build()))
+ .flatMapMany(
+ job ->
+ JobUtils.waitForCompletion(
+ cloudFoundryClient,
+ Duration.ofMinutes(5),
+ job))
+ .doOnError(
+ t ->
+ LOGGER.error(
+ "Unable to delete stack {}",
+ ResourceUtils.getEntity(stack)
+ .getName(),
+ t)));
+ }
+
+ private static Flux cleanUserProvidedServiceInstances(
+ CloudFoundryClient cloudFoundryClient, NameFactory nameFactory) {
+ return PaginationUtils.requestClientV2Resources(
+ page ->
+ cloudFoundryClient
+ .userProvidedServiceInstances()
+ .list(
+ ListUserProvidedServiceInstancesRequest.builder()
+ .page(page)
+ .build()))
+ .filter(
+ userProvidedServiceInstance ->
+ nameFactory.isServiceInstanceName(
+ ResourceUtils.getEntity(userProvidedServiceInstance)
+ .getName()))
+ .delayUntil(
+ userProvidedServiceInstance ->
+ removeRouteAssociations(
+ cloudFoundryClient, userProvidedServiceInstance))
+ .delayUntil(
+ userProvidedServiceInstance ->
+ removeUserProvidedServiceInstanceServiceBindings(
+ cloudFoundryClient, userProvidedServiceInstance))
+ .flatMap(
+ userProvidedServiceInstance ->
+ cloudFoundryClient
+ .userProvidedServiceInstances()
+ .delete(
+ DeleteUserProvidedServiceInstanceRequest.builder()
+ .userProvidedServiceInstanceId(
+ ResourceUtils.getId(
+ userProvidedServiceInstance))
+ .build())
+ .doOnError(
+ t ->
+ LOGGER.error(
+ "Unable to delete user provided"
+ + " service instance {}",
+ ResourceUtils.getEntity(
+ userProvidedServiceInstance)
+ .getName(),
+ t)));
+ }
+
+ private static Flux cleanUsers(
+ CloudFoundryClient cloudFoundryClient, NameFactory nameFactory) {
+ return PaginationUtils.requestClientV2Resources(
+ page ->
+ cloudFoundryClient
+ .users()
+ .list(
+ org.cloudfoundry.client.v2.users.ListUsersRequest
+ .builder()
+ .page(page)
+ .build()))
+ .filter(resource -> isCleanable(nameFactory, resource))
+ .map(resource -> resource.getMetadata().getId())
+ .flatMap(
+ userId ->
+ cloudFoundryClient
+ .users()
+ .delete(
+ org.cloudfoundry.client.v2.users.DeleteUserRequest
+ .builder()
+ .async(true)
+ .userId(userId)
+ .build())
+ .flatMapMany(
+ job ->
+ JobUtils.waitForCompletion(
+ cloudFoundryClient,
+ Duration.ofMinutes(5),
+ job))
+ .doOnError(
+ t ->
+ LOGGER.error(
+ "Unable to delete user {}",
+ userId,
+ t)));
}
private static Flux cleanUsers(UaaClient uaaClient, NameFactory nameFactory) {
- return PaginationUtils
- .requestUaaResources(startIndex -> uaaClient.users()
- .list(ListUsersRequest.builder()
- .startIndex(startIndex)
- .build()))
- .filter(user -> nameFactory.isUserName(user.getUserName()))
- .flatMap(user -> uaaClient.users()
- .delete(DeleteUserRequest.builder()
- .userId(user.getId())
- .version("*")
- .build())
- .doOnError(t -> LOGGER.error("Unable to delete user {}", user.getName(), t))
- .then());
+ return PaginationUtils.requestUaaResources(
+ startIndex ->
+ uaaClient
+ .users()
+ .list(
+ ListUsersRequest.builder()
+ .startIndex(startIndex)
+ .build()))
+ .filter(user -> nameFactory.isUserName(user.getUserName()))
+ .flatMap(
+ user ->
+ uaaClient
+ .users()
+ .delete(
+ DeleteUserRequest.builder()
+ .userId(user.getId())
+ .version("*")
+ .build())
+ .doOnError(
+ t ->
+ LOGGER.error(
+ "Unable to delete user {}",
+ user.getName(),
+ t))
+ .then());
}
private static boolean containsMember(Group group, Group candidate) {
return group.getMembers().stream()
- .map(MemberSummary::getMemberId)
- .anyMatch(id -> candidate.getId().equals(id));
+ .map(MemberSummary::getMemberId)
+ .anyMatch(id -> candidate.getId().equals(id));
}
private static Mono