Skip to content

Commit db497a8

Browse files
[b/413037987] Add more fields to Yarn applications response (#880)
* add more fields to Cloudera YARN API * change Cloudera YARN application output * rename variable * remove unused code * rename ouput json YARN to Yarn * add unit test for json field name
1 parent df7817f commit db497a8

File tree

5 files changed

+357
-160
lines changed

5 files changed

+357
-160
lines changed

dumper/app/src/main/java/com/google/edwmigration/dumper/application/dumper/connector/cloudera/manager/AbstractClouderaYarnApplicationTask.java

Lines changed: 22 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -16,9 +16,10 @@
1616
*/
1717
package com.google.edwmigration.dumper.application.dumper.connector.cloudera.manager;
1818

19+
import com.fasterxml.jackson.databind.JsonNode;
20+
import com.fasterxml.jackson.databind.node.ArrayNode;
1921
import com.google.common.base.Preconditions;
2022
import com.google.edwmigration.dumper.application.dumper.connector.cloudera.manager.dto.ApiYARNApplicationDTO;
21-
import com.google.edwmigration.dumper.application.dumper.connector.cloudera.manager.dto.ApiYARNApplicationListDTO;
2223
import com.google.edwmigration.dumper.application.dumper.task.TaskCategory;
2324
import java.io.IOException;
2425
import java.net.URI;
@@ -27,6 +28,7 @@
2728
import java.time.ZoneId;
2829
import java.time.ZonedDateTime;
2930
import java.time.format.DateTimeFormatter;
31+
import java.util.ArrayList;
3032
import java.util.List;
3133
import java.util.function.Consumer;
3234
import javax.annotation.Nonnull;
@@ -111,14 +113,30 @@ private List<ApiYARNApplicationDTO> load(URI yarnAppURI) {
111113
"YARN application API returned HTTP status %d. Message: %s",
112114
statusCode, readFromStream(resp.getEntity().getContent())));
113115
}
114-
ApiYARNApplicationListDTO yarnAppListDto =
115-
parseJsonStreamToObject(resp.getEntity().getContent(), ApiYARNApplicationListDTO.class);
116-
return yarnAppListDto.getApplications();
116+
117+
JsonNode applicationsResponse = readJsonTree(resp.getEntity().getContent());
118+
JsonNode applicationsArray = applicationsResponse.at("/applications");
119+
if (!applicationsArray.isArray()) {
120+
throw new IllegalArgumentException(
121+
"Unexpected JSON response without `applications`. "
122+
+ "Response: "
123+
+ applicationsResponse);
124+
}
125+
return toDTOs((ArrayNode) applicationsArray);
117126
} catch (IOException ex) {
118127
throw new ClouderaConnectorException(ex.getMessage(), ex);
119128
}
120129
}
121130

131+
private List<ApiYARNApplicationDTO> toDTOs(ArrayNode applicationsArray) {
132+
List<ApiYARNApplicationDTO> yarnApplicationDTOs = new ArrayList<>();
133+
for (JsonNode application : applicationsArray) {
134+
yarnApplicationDTOs.add(new ApiYARNApplicationDTO(application));
135+
}
136+
137+
return yarnApplicationDTOs;
138+
}
139+
122140
private URI buildNextYARNApplicationPageURI(String clusterName, @Nullable String appType) {
123141
try {
124142
URIBuilder uriBuilder =

dumper/app/src/main/java/com/google/edwmigration/dumper/application/dumper/connector/cloudera/manager/dto/ApiYARNApplicationDTO.java

Lines changed: 23 additions & 117 deletions
Original file line numberDiff line numberDiff line change
@@ -16,146 +16,52 @@
1616
*/
1717
package com.google.edwmigration.dumper.application.dumper.connector.cloudera.manager.dto;
1818

19+
import com.fasterxml.jackson.annotation.JsonIgnore;
1920
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
2021
import com.fasterxml.jackson.annotation.JsonProperty;
21-
import java.util.List;
22-
import java.util.Map;
22+
import com.fasterxml.jackson.databind.JsonNode;
23+
import org.springframework.util.StringUtils;
2324

2425
/**
2526
* DTO class for the official Cloudera API <a
2627
* href="https://archive.cloudera.com/cm7/7.0.3/generic/jar/cm_api/apidocs/json_ApiYarnApplication.html">json</a>
28+
* with additional information about cluster.
2729
*/
2830
@JsonIgnoreProperties(ignoreUnknown = true)
2931
public class ApiYARNApplicationDTO {
30-
@JsonProperty(required = true)
31-
private String clusterName;
32-
33-
@JsonProperty(required = true)
34-
private String applicationId;
35-
36-
@JsonProperty(required = true)
37-
private String name;
38-
39-
@JsonProperty(required = true)
40-
private String state;
41-
42-
@JsonProperty(required = true)
43-
private String startTime;
44-
45-
@JsonProperty(required = true)
46-
private String endTime;
47-
48-
@JsonProperty(required = true)
49-
private String user;
50-
51-
@JsonProperty(required = true)
52-
private String pool;
5332

5433
@JsonProperty(required = true)
55-
private List<String> applicationTags;
56-
57-
@JsonProperty(required = true)
58-
private long allocatedMemorySeconds;
59-
60-
@JsonProperty(required = true)
61-
private long allocatedVcoreSeconds;
34+
private String clusterName;
6235

63-
@JsonProperty(required = true)
64-
private Map<String, String> attributes;
36+
/**
37+
* DTO class for the official Cloudera API <a
38+
* href="https://archive.cloudera.com/cm7/7.0.3/generic/jar/cm_api/apidocs/json_ApiYarnApplication.html">json</a>.
39+
* JsonNode used to support schema evaluation.
40+
*/
41+
private final JsonNode apiYarnApplication;
6542

66-
public String getClusterName() {
67-
return clusterName;
43+
public ApiYARNApplicationDTO(JsonNode apiYarnApplication) {
44+
this.apiYarnApplication = apiYarnApplication;
6845
}
6946

47+
@JsonIgnore
7048
public String getApplicationId() {
71-
return applicationId;
72-
}
73-
74-
public String getName() {
75-
return name;
76-
}
77-
78-
public String getState() {
79-
return state;
80-
}
81-
82-
public String getStartTime() {
83-
return startTime;
84-
}
85-
86-
public String getEndTime() {
87-
return endTime;
49+
if (apiYarnApplication == null) {
50+
return null;
51+
}
52+
String applicationId = apiYarnApplication.at("/applicationId").asText();
53+
return StringUtils.hasText(applicationId) ? applicationId : null;
8854
}
8955

90-
public String getUser() {
91-
return user;
92-
}
93-
94-
public String getPool() {
95-
return pool;
96-
}
97-
98-
public List<String> getApplicationTags() {
99-
return applicationTags;
100-
}
101-
102-
public long getAllocatedMemorySeconds() {
103-
return allocatedMemorySeconds;
104-
}
105-
106-
public long getAllocatedVcoreSeconds() {
107-
return allocatedVcoreSeconds;
108-
}
109-
110-
public Map<String, String> getAttributes() {
111-
return attributes;
56+
public String getClusterName() {
57+
return clusterName;
11258
}
11359

11460
public void setClusterName(String clusterName) {
11561
this.clusterName = clusterName;
11662
}
11763

118-
public void setApplicationId(String applicationId) {
119-
this.applicationId = applicationId;
120-
}
121-
122-
public void setName(String name) {
123-
this.name = name;
124-
}
125-
126-
public void setState(String state) {
127-
this.state = state;
128-
}
129-
130-
public void setStartTime(String startTime) {
131-
this.startTime = startTime;
132-
}
133-
134-
public void setEndTime(String endTime) {
135-
this.endTime = endTime;
136-
}
137-
138-
public void setUser(String user) {
139-
this.user = user;
140-
}
141-
142-
public void setPool(String pool) {
143-
this.pool = pool;
144-
}
145-
146-
public void setApplicationTags(List<String> applicationTags) {
147-
this.applicationTags = applicationTags;
148-
}
149-
150-
public void setAllocatedMemorySeconds(long allocatedMemorySeconds) {
151-
this.allocatedMemorySeconds = allocatedMemorySeconds;
152-
}
153-
154-
public void setAllocatedVcoreSeconds(long allocatedVcoreSeconds) {
155-
this.allocatedVcoreSeconds = allocatedVcoreSeconds;
156-
}
157-
158-
public void setAttributes(Map<String, String> attributes) {
159-
this.attributes = attributes;
64+
public JsonNode getApiYarnApplication() {
65+
return apiYarnApplication;
16066
}
16167
}

dumper/app/src/main/java/com/google/edwmigration/dumper/application/dumper/connector/cloudera/manager/dto/ApiYARNApplicationListDTO.java

Lines changed: 0 additions & 39 deletions
This file was deleted.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,71 @@
1+
/*
2+
* Copyright 2022-2025 Google LLC
3+
* Copyright 2013-2021 CompilerWorks
4+
*
5+
* Licensed under the Apache License, Version 2.0 (the "License");
6+
* you may not use this file except in compliance with the License.
7+
* You may obtain a copy of the License at
8+
*
9+
* http://www.apache.org/licenses/LICENSE-2.0
10+
*
11+
* Unless required by applicable law or agreed to in writing, software
12+
* distributed under the License is distributed on an "AS IS" BASIS,
13+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14+
* See the License for the specific language governing permissions and
15+
* limitations under the License.
16+
*/
17+
package com.google.edwmigration.dumper.application.dumper.connector.cloudera.manager.dto;
18+
19+
import static org.junit.Assert.assertEquals;
20+
import static org.junit.Assert.assertNull;
21+
22+
import com.fasterxml.jackson.databind.JsonNode;
23+
import com.fasterxml.jackson.databind.ObjectMapper;
24+
import java.io.IOException;
25+
import java.net.URISyntaxException;
26+
import java.nio.file.Files;
27+
import java.nio.file.Paths;
28+
import org.junit.Test;
29+
30+
public class ApiYARNApplicationDTOTest {
31+
private static final ObjectMapper objectMapper = new ObjectMapper();
32+
33+
@Test
34+
public void applicationIdExists() throws Exception {
35+
JsonNode jsonNode =
36+
objectMapper.readTree(readString("/cloudera/manager/dto/ApiYARNApplicationDTO.json"));
37+
ApiYARNApplicationDTO dto = new ApiYARNApplicationDTO(jsonNode);
38+
39+
assertEquals("job_1741847944157_0018", dto.getApplicationId());
40+
}
41+
42+
@Test
43+
public void applicationIdDoesNotExist() throws Exception {
44+
JsonNode jsonNode = objectMapper.readTree("{ \"prop\": \"val\"}");
45+
ApiYARNApplicationDTO dto = new ApiYARNApplicationDTO(jsonNode);
46+
47+
assertNull("ApplicationId must be null if doesn't exist", dto.getApplicationId());
48+
}
49+
50+
@Test
51+
public void nullJsonFieldHandled() {
52+
ApiYARNApplicationDTO dto = new ApiYARNApplicationDTO(null);
53+
54+
assertNull("ApplicationId must be null for null json", dto.getApplicationId());
55+
}
56+
57+
@Test
58+
public void jsonSerialization() throws Exception {
59+
JsonNode yarnApp = objectMapper.readTree("{ \"prop\": \"val\"}");
60+
ApiYARNApplicationDTO dto = new ApiYARNApplicationDTO(yarnApp);
61+
62+
JsonNode outputJsonl = objectMapper.convertValue(dto, JsonNode.class);
63+
64+
// json filed apiYarnApplication is expected on server side
65+
assertEquals(yarnApp, outputJsonl.at("/apiYarnApplication"));
66+
}
67+
68+
private String readString(String name) throws IOException, URISyntaxException {
69+
return new String(Files.readAllBytes(Paths.get(this.getClass().getResource(name).toURI())));
70+
}
71+
}

0 commit comments

Comments
 (0)