Skip to content

Commit 6667c56

Browse files
authored
Issue 1296 fix cf labels not working (#1306)
* move label and annotations under metadata node
1 parent a08176f commit 6667c56

File tree

4 files changed

+163
-16
lines changed

4 files changed

+163
-16
lines changed

cloudfoundry-operations/src/main/java/org/cloudfoundry/operations/applications/ApplicationManifestUtilsV3.java

Lines changed: 45 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -26,12 +26,14 @@
2626
import java.nio.file.Files;
2727
import java.nio.file.Path;
2828
import java.nio.file.StandardOpenOption;
29+
import java.util.HashMap;
2930
import java.util.List;
3031
import java.util.Map;
3132
import java.util.Optional;
3233
import java.util.TreeMap;
3334
import java.util.regex.Pattern;
3435
import java.util.stream.Stream;
36+
import org.cloudfoundry.client.v3.Metadata;
3537
import org.cloudfoundry.client.v3.processes.HealthCheckType;
3638
import org.cloudfoundry.client.v3.processes.ReadinessHealthCheckType;
3739
import org.yaml.snakeyaml.DumperOptions;
@@ -172,8 +174,14 @@ private static ManifestV3Application.Builder toApplicationManifest(
172174
variables,
173175
raw -> getSidecar((Map<String, Object>) raw, variables),
174176
builder::sidecar);
175-
as(application, "labels", variables, Map.class::cast, builder::labels);
176-
as(application, "annotations", variables, Map.class::cast, builder::annotations);
177+
178+
as(
179+
application,
180+
"metadata",
181+
variables,
182+
raw -> getMetadata((Map<String, Object>) raw, variables),
183+
builder::metadata);
184+
177185
asBoolean(application, "default-route", variables, builder::defaultRoute);
178186

179187
return builder;
@@ -253,6 +261,31 @@ private static ManifestV3Service getService(Object raw, Map<String, String> vari
253261
return builder.build();
254262
}
255263

264+
private static Metadata getMetadata(Map<String, Object> raw, Map<String, String> variables) {
265+
266+
if (raw == null) return null;
267+
268+
Map<String, String> labels = new HashMap<>();
269+
Map<String, String> annotations = new HashMap<>();
270+
271+
asMap(raw, "labels", variables, String.class::cast, labels::put);
272+
273+
asMap(raw, "annotations", variables, String.class::cast, annotations::put);
274+
275+
if (labels.isEmpty() && annotations.isEmpty()) {
276+
return null;
277+
}
278+
279+
Metadata.Builder builder = Metadata.builder();
280+
if (!labels.isEmpty()) {
281+
builder.labels(labels);
282+
}
283+
if (!annotations.isEmpty()) {
284+
builder.annotations(annotations);
285+
}
286+
return builder.build();
287+
}
288+
256289
private static Map<String, Object> toYaml(ManifestV3 manifest) {
257290
Map<String, Object> yaml = new TreeMap<>();
258291
yaml.put("version", manifest.getVersion());
@@ -282,8 +315,8 @@ private static Map<String, Object> toApplicationYaml(ManifestV3Application appli
282315
"sidecars",
283316
convertCollection(
284317
application.getSidecars(), ApplicationManifestUtilsV3::toSidecarsYaml));
285-
putIfPresent(yaml, "labels", application.getLabels());
286-
putIfPresent(yaml, "annotations", application.getAnnotations());
318+
319+
putIfPresent(yaml, "metadata", toMetadataYaml(application.getMetadata()));
287320
return yaml;
288321
}
289322

@@ -337,4 +370,12 @@ private static Map<String, Object> toProcessYaml(ManifestV3Process process) {
337370
putIfPresent(yaml, "timeout", process.getTimeout());
338371
return yaml;
339372
}
373+
374+
private static Map<String, Object> toMetadataYaml(Metadata metadata) {
375+
if (metadata == null) return null;
376+
Map<String, Object> map = new HashMap<>();
377+
putIfPresent(map, "labels", metadata.getLabels());
378+
putIfPresent(map, "annotations", metadata.getAnnotations());
379+
return map.isEmpty() ? null : map;
380+
}
340381
}

cloudfoundry-operations/src/main/java/org/cloudfoundry/operations/applications/_ManifestV3Application.java

Lines changed: 3 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -16,10 +16,9 @@
1616

1717
package org.cloudfoundry.operations.applications;
1818

19-
20-
import org.cloudfoundry.AllowNulls;
2119
import org.cloudfoundry.Nullable;
2220
import org.immutables.value.Value;
21+
import org.cloudfoundry.client.v3.Metadata;
2322

2423
import java.util.List;
2524
import java.util.Map;
@@ -31,25 +30,17 @@
3130
@Value.Immutable
3231
abstract class _ManifestV3Application extends _ApplicationManifestCommon {
3332

34-
/**
35-
* The annotations configured for this application
36-
*/
37-
@AllowNulls
38-
@Nullable
39-
abstract Map<String, String> getAnnotations();
40-
4133
/**
4234
* Generate a default route based on the application name
4335
*/
4436
@Nullable
4537
abstract Boolean getDefaultRoute();
4638

4739
/**
48-
* The labels configured for this application
40+
* The metadata for this application
4941
*/
50-
@AllowNulls
5142
@Nullable
52-
abstract Map<String, String> getLabels();
43+
abstract Metadata getMetadata();
5344

5445
/**
5546
* The collection of processes configured for this application

cloudfoundry-operations/src/test/java/org/cloudfoundry/operations/applications/ApplicationManifestUtilsV3Test.java

Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
import java.io.IOException;
66
import java.nio.file.Files;
77
import java.nio.file.Path;
8+
import org.cloudfoundry.client.v3.Metadata;
89
import org.cloudfoundry.client.v3.processes.ReadinessHealthCheckType;
910
import org.junit.jupiter.api.Test;
1011

@@ -60,6 +61,62 @@ void testWithDockerApp() throws IOException {
6061
assertSerializeDeserialize(manifest);
6162
}
6263

64+
@Test
65+
void testWithMetadata() throws IOException {
66+
ManifestV3 manifest =
67+
ManifestV3.builder()
68+
.application(
69+
ManifestV3Application.builder()
70+
.name("test-app")
71+
.metadata(
72+
Metadata.builder()
73+
.label("test-label", "test-label-value")
74+
.annotation(
75+
"test-annotation",
76+
"test-annotation-value")
77+
.build())
78+
.build())
79+
.build();
80+
81+
assertSerializeDeserialize(manifest);
82+
}
83+
84+
@Test
85+
void testWithMetadataOnlyLabel() throws IOException {
86+
ManifestV3 manifest =
87+
ManifestV3.builder()
88+
.application(
89+
ManifestV3Application.builder()
90+
.name("test-app")
91+
.metadata(
92+
Metadata.builder()
93+
.label("test-label", "test-label-value")
94+
.build())
95+
.build())
96+
.build();
97+
98+
assertSerializeDeserialize(manifest);
99+
}
100+
101+
@Test
102+
void testWithMetadataOnlyAnnotation() throws IOException {
103+
ManifestV3 manifest =
104+
ManifestV3.builder()
105+
.application(
106+
ManifestV3Application.builder()
107+
.name("test-app")
108+
.metadata(
109+
Metadata.builder()
110+
.annotation(
111+
"test-annotation",
112+
"test-annotation-value")
113+
.build())
114+
.build())
115+
.build();
116+
117+
assertSerializeDeserialize(manifest);
118+
}
119+
63120
private void assertSerializeDeserialize(ManifestV3 manifest) throws IOException {
64121
Path file = Files.createTempFile("test-manifest-", ".yml");
65122
ApplicationManifestUtilsV3.write(file, manifest);

integration-test/src/test/java/org/cloudfoundry/operations/ApplicationsTest.java

Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@
2929
import org.cloudfoundry.CleanupCloudFoundryAfterClass;
3030
import org.cloudfoundry.CloudFoundryVersion;
3131
import org.cloudfoundry.IfCloudFoundryVersion;
32+
import org.cloudfoundry.client.CloudFoundryClient;
3233
import org.cloudfoundry.logcache.v1.Envelope;
3334
import org.cloudfoundry.logcache.v1.EnvelopeBatch;
3435
import org.cloudfoundry.logcache.v1.EnvelopeType;
@@ -108,6 +109,7 @@ public final class ApplicationsTest extends AbstractIntegrationTest {
108109
@Autowired private String serviceName;
109110

110111
@Autowired private LogCacheClient logCacheClient;
112+
@Autowired private CloudFoundryClient cloudFoundryClient;
111113

112114
// To create a service in #pushBindService, the Service Broker must be installed first.
113115
// We ensure it is by loading the serviceBrokerId @Lazy bean.
@@ -789,6 +791,62 @@ public void pushManifestV3() throws IOException {
789791
.verify(Duration.ofMinutes(5));
790792
}
791793

794+
@Test
795+
@IfCloudFoundryVersion(greaterThanOrEqualTo = CloudFoundryVersion.PCF_4_v2)
796+
public void pushManifestV3WithMetadata() throws IOException {
797+
String applicationName = this.nameFactory.getApplicationName();
798+
Map<String, String> labels = Collections.singletonMap("test-label", "test-label-value");
799+
Map<String, String> annotations =
800+
Collections.singletonMap("test-annotation", "test-annotation-value");
801+
802+
ManifestV3 manifest =
803+
ManifestV3.builder()
804+
.application(
805+
ManifestV3Application.builder()
806+
.buildpack("staticfile_buildpack")
807+
.disk(512)
808+
.healthCheckType(ApplicationHealthCheck.PORT)
809+
.memory(64)
810+
.name(applicationName)
811+
.path(
812+
new ClassPathResource("test-application.zip")
813+
.getFile()
814+
.toPath())
815+
.metadata(
816+
org.cloudfoundry.client.v3.Metadata.builder()
817+
.labels(labels)
818+
.annotations(annotations)
819+
.build())
820+
.build())
821+
.build();
822+
823+
this.cloudFoundryOperations
824+
.applications()
825+
.pushManifestV3(PushManifestV3Request.builder().manifest(manifest).build())
826+
.then(
827+
this.cloudFoundryOperations
828+
.applications()
829+
.get(GetApplicationRequest.builder().name(applicationName).build()))
830+
.map(ApplicationDetail::getId)
831+
.flatMap(
832+
id ->
833+
this.cloudFoundryClient
834+
.applicationsV3()
835+
.get(
836+
org.cloudfoundry.client.v3.applications
837+
.GetApplicationRequest.builder()
838+
.applicationId(id)
839+
.build()))
840+
.as(StepVerifier::create)
841+
.expectNextMatches(
842+
createdApp ->
843+
labels.equals(createdApp.getMetadata().getLabels())
844+
&& annotations.equals(
845+
createdApp.getMetadata().getAnnotations()))
846+
.expectComplete()
847+
.verify(Duration.ofMinutes(5));
848+
}
849+
792850
@Test
793851
@IfCloudFoundryVersion(
794852
greaterThanOrEqualTo =

0 commit comments

Comments
 (0)