Skip to content

Commit b2cdf80

Browse files
committed
YARN-9281. Add express upgrade button to Appcatalog UI. Contributed by Eric Yang
1 parent ebbda18 commit b2cdf80

File tree

13 files changed

+336
-24
lines changed

13 files changed

+336
-24
lines changed

hadoop-yarn-project/hadoop-yarn/hadoop-yarn-applications/hadoop-yarn-applications-catalog/hadoop-yarn-applications-catalog-webapp/pom.xml

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -487,5 +487,4 @@
487487
</build>
488488
</profile>
489489
</profiles>
490-
491490
</project>

hadoop-yarn-project/hadoop-yarn/hadoop-yarn-applications/hadoop-yarn-applications-catalog/hadoop-yarn-applications-catalog-webapp/src/main/java/org/apache/hadoop/yarn/appcatalog/application/AppCatalogSolrClient.java

Lines changed: 62 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -291,13 +291,12 @@ public void deployApp(String id, Service service) throws SolrServerException,
291291
docs.add(request);
292292
}
293293

294-
// Commit Solr changes.
295-
UpdateResponse detailsResponse = solr.add(docs);
296-
if (detailsResponse.getStatus() != 0) {
294+
try {
295+
commitSolrChanges(solr, docs);
296+
} catch (IOException e) {
297297
throw new IOException("Unable to register docker instance "
298-
+ "with application entry.");
298+
+ "with application entry.", e);
299299
}
300-
solr.commit();
301300
}
302301

303302
private SolrInputDocument incrementDownload(SolrDocument doc,
@@ -350,16 +349,10 @@ public void register(Application app) throws IOException {
350349
buffer.setField("yarnfile_s", mapper.writeValueAsString(yarnApp));
351350

352351
docs.add(buffer);
353-
// Commit Solr changes.
354-
UpdateResponse detailsResponse = solr.add(docs);
355-
if (detailsResponse.getStatus() != 0) {
356-
throw new IOException("Unable to register application " +
357-
"in Application Store.");
358-
}
359-
solr.commit();
352+
commitSolrChanges(solr, docs);
360353
} catch (SolrServerException | IOException e) {
361354
throw new IOException("Unable to register application " +
362-
"in Application Store. "+ e.getMessage());
355+
"in Application Store. ", e);
363356
}
364357
}
365358

@@ -389,16 +382,64 @@ protected void register(AppStoreEntry app) throws IOException {
389382
buffer.setField("yarnfile_s", mapper.writeValueAsString(yarnApp));
390383

391384
docs.add(buffer);
392-
// Commit Solr changes.
393-
UpdateResponse detailsResponse = solr.add(docs);
394-
if (detailsResponse.getStatus() != 0) {
395-
throw new IOException("Unable to register application " +
396-
"in Application Store.");
397-
}
398-
solr.commit();
385+
commitSolrChanges(solr, docs);
399386
} catch (SolrServerException | IOException e) {
400387
throw new IOException("Unable to register application " +
401-
"in Application Store. "+ e.getMessage());
388+
"in Application Store. ", e);
402389
}
403390
}
391+
392+
public void upgradeApp(Service service) throws IOException,
393+
SolrServerException {
394+
ObjectMapper mapper = new ObjectMapper();
395+
mapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
396+
Collection<SolrInputDocument> docs = new HashSet<SolrInputDocument>();
397+
SolrClient solr = getSolrClient();
398+
if (service!=null) {
399+
String name = service.getName();
400+
String app = "";
401+
SolrQuery query = new SolrQuery();
402+
query.setQuery("id:" + name);
403+
query.setFilterQueries("type_s:AppEntry");
404+
query.setRows(1);
405+
406+
QueryResponse response;
407+
try {
408+
response = solr.query(query);
409+
Iterator<SolrDocument> appList = response.getResults().listIterator();
410+
while (appList.hasNext()) {
411+
SolrDocument d = appList.next();
412+
app = d.get("app_s").toString();
413+
}
414+
} catch (SolrServerException | IOException e) {
415+
LOG.error("Error in finding deployed application: " + name, e);
416+
}
417+
// Register deployed application instance with AppList
418+
SolrInputDocument request = new SolrInputDocument();
419+
request.addField("type_s", "AppEntry");
420+
request.addField("id", name);
421+
request.addField("name_s", name);
422+
request.addField("app_s", app);
423+
request.addField("yarnfile_s", mapper.writeValueAsString(service));
424+
docs.add(request);
425+
}
426+
try {
427+
commitSolrChanges(solr, docs);
428+
} catch (IOException e) {
429+
throw new IOException("Unable to register docker instance "
430+
+ "with application entry.", e);
431+
}
432+
}
433+
434+
private void commitSolrChanges(SolrClient solr,
435+
Collection<SolrInputDocument> docs)
436+
throws IOException, SolrServerException {
437+
// Commit Solr changes.
438+
UpdateResponse detailsResponse = solr.add(docs);
439+
if (detailsResponse.getStatus() != 0) {
440+
throw new IOException("Failed to commit document in solr, status code: "
441+
+ detailsResponse.getStatus());
442+
}
443+
solr.commit();
444+
}
404445
}

hadoop-yarn-project/hadoop-yarn/hadoop-yarn-applications/hadoop-yarn-applications-catalog/hadoop-yarn-applications-catalog-webapp/src/main/java/org/apache/hadoop/yarn/appcatalog/application/YarnServiceClient.java

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@
2727
import org.apache.hadoop.security.UserGroupInformation;
2828
import org.apache.hadoop.yarn.appcatalog.model.AppEntry;
2929
import org.apache.hadoop.yarn.service.api.records.Service;
30+
import org.apache.hadoop.yarn.service.api.records.ServiceState;
3031
import org.apache.hadoop.yarn.service.api.records.KerberosPrincipal;
3132
import org.apache.hadoop.yarn.service.client.ApiServiceClient;
3233

@@ -171,4 +172,25 @@ public void getStatus(AppEntry entry) {
171172
LOG.error("Error in fetching application status: ", e);
172173
}
173174
}
175+
176+
public void upgradeApp(Service app) throws JsonProcessingException {
177+
ObjectMapper mapper = new ObjectMapper();
178+
mapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
179+
String appInstanceId = app.getName();
180+
app.setState(ServiceState.EXPRESS_UPGRADING);
181+
String yarnFile = mapper.writeValueAsString(app);
182+
ClientResponse response;
183+
try {
184+
response = asc.getApiClient(asc.getServicePath(appInstanceId))
185+
.put(ClientResponse.class, yarnFile);
186+
if (response.getStatus() >= 299) {
187+
String message = response.getEntity(String.class);
188+
throw new RuntimeException("Failed : HTTP error code : "
189+
+ response.getStatus() + " error: " + message);
190+
}
191+
} catch (UniformInterfaceException | ClientHandlerException
192+
| IOException e) {
193+
LOG.error("Error in stopping application: ", e);
194+
}
195+
}
174196
}

hadoop-yarn-project/hadoop-yarn/hadoop-yarn-applications/hadoop-yarn-applications-catalog/hadoop-yarn-applications-catalog-webapp/src/main/java/org/apache/hadoop/yarn/appcatalog/controller/AppDetailsController.java

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,8 +18,12 @@
1818

1919
package org.apache.hadoop.yarn.appcatalog.controller;
2020

21+
import java.io.IOException;
22+
23+
import javax.ws.rs.Consumes;
2124
import javax.ws.rs.GET;
2225
import javax.ws.rs.POST;
26+
import javax.ws.rs.PUT;
2327
import javax.ws.rs.Path;
2428
import javax.ws.rs.PathParam;
2529
import javax.ws.rs.Produces;
@@ -32,6 +36,7 @@
3236
import org.apache.hadoop.yarn.appcatalog.model.AppEntry;
3337
import org.apache.hadoop.yarn.service.api.records.Service;
3438
import org.apache.hadoop.yarn.service.api.records.ServiceState;
39+
import org.apache.solr.client.solrj.SolrServerException;
3540

3641
import com.fasterxml.jackson.core.JsonProcessingException;
3742

@@ -262,4 +267,33 @@ public Response restartApp(@PathParam("id") String id) {
262267
}
263268
return Response.ok().build();
264269
}
270+
271+
/**
272+
* Upgrade an application.
273+
*
274+
* @apiGroup AppDetailController
275+
* @apiName upgradeApp
276+
* @api {put} /app_details/upgrade/{id} Upgrade one instance of application.
277+
* @apiParam {String} id Application Name to upgrade.
278+
* @apiSuccess {String} text
279+
* @apiError BadRequest Requested application does not upgrade.
280+
* @return Web response code
281+
*/
282+
@Path("upgrade/{id}")
283+
@PUT
284+
@Consumes(MediaType.APPLICATION_JSON)
285+
@Produces(MediaType.APPLICATION_JSON)
286+
public Response upgradeApp(@PathParam("id") String id, Service app) {
287+
try {
288+
AppCatalogSolrClient sc = new AppCatalogSolrClient();
289+
sc.upgradeApp(app);
290+
YarnServiceClient yc = new YarnServiceClient();
291+
yc.upgradeApp(app);
292+
} catch (IOException | SolrServerException e) {
293+
return Response.status(Status.BAD_REQUEST).entity(e.toString()).build();
294+
}
295+
String output = "{\"status\":\"Application upgrade requested.\",\"id\":\"" +
296+
app.getName() + "\"}";
297+
return Response.status(Status.ACCEPTED).entity(output).build();
298+
}
265299
}

hadoop-yarn-project/hadoop-yarn/hadoop-yarn-applications/hadoop-yarn-applications-catalog/hadoop-yarn-applications-catalog-webapp/src/main/javascript/app.js

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,9 @@ app.config(['$routeProvider',
5353
}).when('/deploy/:id', {
5454
templateUrl: 'partials/deploy.html',
5555
controller: 'DeployAppController'
56+
}).when('/upgrade/:id', {
57+
templateUrl: 'partials/upgrade.html',
58+
controller: 'UpgradeAppController'
5659
}).otherwise({
5760
redirectTo: '/'
5861
});

hadoop-yarn-project/hadoop-yarn/hadoop-yarn-applications/hadoop-yarn-applications-catalog/hadoop-yarn-applications-catalog-webapp/src/main/javascript/controllers.js

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -127,6 +127,10 @@ controllers.controller("AppDetailsController", [ '$scope', '$interval', '$rootSc
127127
}, errorCallback);
128128
}
129129

130+
$scope.upgradeApp = function(id) {
131+
window.location = '/#!/upgrade/' + id;
132+
}
133+
130134
$scope.canDeployApp = function() {
131135
return true;
132136
};
@@ -306,6 +310,56 @@ controllers.controller("DeployAppController", [ '$scope', '$rootScope', '$http',
306310

307311
}]);
308312

313+
controllers.controller("UpgradeAppController", [ '$scope', '$rootScope', '$http',
314+
'$routeParams', function($scope, $rootScope, $http, $routeParams) {
315+
$scope.message = null;
316+
$scope.error = null;
317+
$scope.appName = $routeParams.id;
318+
$scope.refreshAppDetails = function() {
319+
$http({
320+
method : 'GET',
321+
url : '/v1/app_details/status/' + $scope.appName
322+
}).then(successCallback, errorCallback);
323+
}
324+
325+
$scope.upgradeApp = function(app) {
326+
$rootScope.$emit("showLoadScreen", {});
327+
$http({
328+
method : 'PUT',
329+
url : '/v1/app_details/upgrade/' + $scope.appName,
330+
data : JSON.stringify($scope.details)
331+
}).then(function(data, status, headers, config) {
332+
$rootScope.$emit("RefreshAppList", {});
333+
window.location = '/#!/app/' + data.data.id;
334+
}, function(data, status, headers, config) {
335+
$rootScope.$emit("hideLoadScreen", {});
336+
$scope.error = data.data;
337+
$('#error-message').html(data.data);
338+
$('#myModal').modal('show');
339+
console.log('error', data, status);
340+
});
341+
}
342+
343+
function successCallback(response) {
344+
if (response.data.yarnfile.components.length!=0) {
345+
$scope.details = response.data.yarnfile;
346+
} else {
347+
// When application is in accepted or failed state, it does not
348+
// have components detail, hence we update states only.
349+
$scope.details.state = response.data.yarnfile.state;
350+
}
351+
}
352+
353+
function errorCallback(response) {
354+
$rootScope.$emit("hideLoadScreen", {});
355+
$scope.error = "Error in getting application detail.";
356+
$('#error-message').html($scope.error);
357+
$('#myModal').modal('show');
358+
}
359+
360+
$scope.refreshAppDetails();
361+
}]);
362+
309363
controllers.controller("LoadScreenController", [ '$scope', '$rootScope', '$http', function($scope, $rootScope, $http) {
310364
$scope.loadScreen = "hide";
311365

hadoop-yarn-project/hadoop-yarn/hadoop-yarn-applications/hadoop-yarn-applications-catalog/hadoop-yarn-applications-catalog-webapp/src/main/webapp/css/bootstrap-hadoop.css

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -184,6 +184,11 @@
184184
background-color: #FFF;
185185
box-shadow: 0 0 2px 0 #1391c1;
186186
}
187+
.btn-secondary:visited {
188+
color: #429929;
189+
background-color: #FFF;
190+
box-shadow: 0 0 2px 0 #1391c1;
191+
}
187192
.btn-secondary[disabled],
188193
.btn-secondary:focus[disabled],
189194
.btn-secondary.disabled,

hadoop-yarn-project/hadoop-yarn/hadoop-yarn-applications/hadoop-yarn-applications-catalog/hadoop-yarn-applications-catalog-webapp/src/main/webapp/partials/details.html

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,8 +14,9 @@
1414
<div class="container content">
1515
<div class="row">
1616
<div class="col-xs-12 col-md-12 col-lg-12">
17+
<a ng-click="restartApp(appName)" class="btn btn-secondary"><span class="glyphicon glyphicon-play"></span> Start</a>
1718
<a ng-click="stopApp(appName)" class="btn btn-secondary"><span class="glyphicon glyphicon-stop"></span> Stop</a>
18-
<a ng-click="restartApp(appName)" class="btn btn-secondary"><span class="glyphicon glyphicon-refresh"></span> Start</a>
19+
<a ng-click="upgradeApp(appName)" class="btn btn-secondary"><span class="glyphicon glyphicon-circle-arrow-up"></span> Upgrade</a>
1920
<div style="display:inline-block;" ng-repeat="(key, value) in details.yarnfile.quicklinks">
2021
<a href="{{value}}" class="btn btn-secondary" ng-hide="checkServiceLink()"><span class="glyphicon glyphicon-new-window"></span> {{key}}</a>
2122
</div>

0 commit comments

Comments
 (0)