YARN-8964. [UI2] YARN ui2 should use clusters/{cluster name} for all ATSv2 REST APIs. Contributed by Akhil PB.

This commit is contained in:
Sunil G 2018-11-22 10:25:22 +05:30
parent 8d2789c5eb
commit 5fe1dbf195
16 changed files with 194 additions and 21 deletions

View File

@ -16,7 +16,6 @@
* limitations under the License.
*/
import DS from 'ember-data';
import Ember from 'ember';
import Converter from 'yarn-ui/utils/converter';
import RESTAbstractAdapter from './restabstract';
@ -26,8 +25,8 @@ import RESTAbstractAdapter from './restabstract';
* in plain text format and not JSON.
*/
export default RESTAbstractAdapter.extend({
address: "timelineV1WebAddress",
restNameSpace: "timeline",
address: "timelineWebAddress",
restNameSpace: "timelineV2Log",
serverName: "ATS",
headers: {
@ -36,10 +35,14 @@ export default RESTAbstractAdapter.extend({
urlForFindRecord(id/*, modelName, snapshot*/) {
var splits = Converter.splitForAppLogs(id);
var clusterId = this.get("env.app.clusterId");
var containerId = splits[0];
var logFile = splits[1];
if (splits[2]) {
clusterId = splits[2];
}
var url = this._buildURL();
url = url + '/containers/' + containerId + '/logs/' + logFile;
url = url + '/containers/' + containerId + '/logs/' + logFile + '?clusterid=' + clusterId;
console.log('log url' + url);
return url;
},

View File

@ -25,7 +25,11 @@ export default AbstractAdapter.extend({
urlForFindRecord(id/*, modelName, snapshot*/) {
var url = this._buildURL();
url = url + '/apps/' + id + '?fields=ALL';
var clusterId = this.get("env.app.clusterId");
if (clusterId) {
url += `/clusters/${clusterId}`;
}
url += '/apps/' + id + '?fields=ALL';
return url;
},

View File

@ -20,12 +20,16 @@ import AbstractAdapter from './abstract';
export default AbstractAdapter.extend({
address: "timelineWebAddress",
restNameSpace: "timelineService",
restNameSpace: "timelineV2",
serverName: "ATS",
urlForQuery(query/*, modelName*/) {
var url = this.buildURL();
url += '/' + query.appId + '/entities/COMPONENT_INSTANCE?fields=ALL';
var clusterId = this.get("env.app.clusterId");
if (clusterId) {
url += `/clusters/${clusterId}`;
}
url += '/apps/' + query.appId + '/entities/COMPONENT_INSTANCE?fields=ALL';
delete query.appId;
return url;
}

View File

@ -25,6 +25,10 @@ export default AbstractAdapter.extend({
serverName: "ATS",
pathForType(/*modelName*/) {
var clusterId = this.get("env.app.clusterId");
if (clusterId) {
return `clusters/${clusterId}/flows`;
}
return 'flows'; // move to some common place, return path by modelname.
},
});

View File

@ -19,15 +19,15 @@
import AbstractAdapter from './abstract';
export default AbstractAdapter.extend({
address: "timelineV1WebAddress",
// restNameSpace: "timelineV2", // Use ATSv2 when it supports log APIs.
restNameSpace: "timeline", //Using ATSv1.5 now, would be supported by ATSv2 very soon.
address: "timelineWebAddress",
restNameSpace: "timelineV2Log",
serverName: "ATS",
urlForQuery(query/*, modelName*/) {
var url = this._buildURL();
var containerId = query['containerId'];
var clusterId = this.get("env.app.clusterId");
delete query.containerId;
return url + '/containers/' + containerId + '/logs';
return url + '/containers/' + containerId + '/logs' + '?clusterid=' + clusterId;
}
});

View File

@ -20,12 +20,16 @@ import AbstractAdapter from './abstract';
export default AbstractAdapter.extend({
address: "timelineWebAddress",
restNameSpace: "timelineService",
restNameSpace: "timelineV2",
serverName: "ATS",
urlForQuery(query/*, modelName*/) {
var url = this.buildURL();
url += '/' + query.appId + '/entities/COMPONENT?fields=ALL';
var clusterId = this.get("env.app.clusterId");
if (clusterId) {
url += `/clusters/${clusterId}`;
}
url += '/apps/' + query.appId + '/entities/COMPONENT?fields=ALL';
delete query.appId;
return url;
}

View File

@ -20,12 +20,16 @@ import AbstractAdapter from './abstract';
export default AbstractAdapter.extend({
address: "timelineWebAddress",
restNameSpace: "timelineService",
restNameSpace: "timelineV2",
serverName: "ATS",
urlForQueryRecord(query/*, modelName*/) {
var url = this.buildURL();
url += '/' + query.appId + '/entities/SERVICE_ATTEMPT?fields=ALL';
var clusterId = this.get("env.app.clusterId");
if (clusterId) {
url += `/clusters/${clusterId}`;
}
url += '/apps/' + query.appId + '/entities/SERVICE_ATTEMPT?fields=ALL';
delete query.appId;
return url;
}

View File

@ -26,6 +26,10 @@ export default AbstractAdapter.extend({
urlForQuery(query/*, modelName*/) {
var url = this._buildURL();
var clusterId = this.get("env.app.clusterId")
if (clusterId) {
url += `/clusters/${clusterId}`;
}
var appId = query.appId;
query.fields = 'ALL';
delete query.appId;
@ -34,6 +38,10 @@ export default AbstractAdapter.extend({
urlForFindRecord(id/*, modelName, snapshot*/) {
var url = this._buildURL();
var clusterId = this.get("env.app.clusterId")
if (clusterId) {
url += `/clusters/${clusterId}`;
}
return url + '/apps/' + Converter.attemptIdToAppId(id) +
"/entities/YARN_APPLICATION_ATTEMPT/" + id + "?fields=ALL";
}

View File

@ -26,6 +26,10 @@ export default AbstractAdapter.extend({
urlForQuery(query/*, modelName*/){
var url = this._buildURL();
var clusterId = this.get("env.app.clusterId")
if (clusterId) {
url += `/clusters/${clusterId}`;
}
var app_attempt_id = query.app_attempt_id;
query.fields = 'ALL';
delete query.app_attempt_id;

View File

@ -84,9 +84,36 @@ function getSecurityURL(rmhost) {
return url;
}
function getClusterIdFromYARN(rmhost, application) {
var httpUrl = window.location.protocol + '//' +
(ENV.hosts.localBaseAddress? ENV.hosts.localBaseAddress + '/' : '') + rmhost;
httpUrl += '/conf?name=yarn.resourcemanager.cluster-id';
Ember.Logger.log("Get cluster-id URL is: " + httpUrl);
var clusterId = "";
$.ajax({
type: 'GET',
dataType: 'json',
async: false,
context: this,
url: httpUrl,
success: function(data) {
clusterId = data.property.value;
Ember.Logger.log("Cluster Id from RM: " + clusterId);
application.advanceReadiness();
},
error: function() {
application.advanceReadiness();
}
});
return clusterId;
}
function updateConfigs(application) {
var hostname = window.location.hostname;
var rmhost = hostname + (window.location.port ? ':' + window.location.port: '') + skipTrailingSlash(window.location.pathname);
var rmhost = hostname + (window.location.port ? ':' + window.location.port: '') +
skipTrailingSlash(window.location.pathname);
window.ENV = window.ENV || {};
window.ENV.hosts = window.ENV.hosts || {};
@ -103,6 +130,10 @@ function updateConfigs(application) {
var protocolSchemeFromRM = getYarnHttpProtocolScheme(rmhost, application);
Ember.Logger.log("Is protocol scheme https? " + (protocolSchemeFromRM == "HTTPS_ONLY"));
var isHttpsSchemeEnabled = (protocolSchemeFromRM == "HTTPS_ONLY");
var clusterIdFromYARN = getClusterIdFromYARN(rmhost, application);
ENV.clusterId = clusterIdFromYARN;
if(!ENV.hosts.timelineWebAddress) {
var timelinehost = "";
$.ajax({
@ -207,6 +238,7 @@ export default {
};
const skipTrailingSlash = function(path) {
path = path.replace('index.html', '');
path = path.replace('ui2/', '');
path = path.replace(/\/$/, '');
console.log('base url:' + path)

View File

@ -49,8 +49,10 @@ Router.map(function() {
{ path: '/yarn-node-containers/:node_id/:node_addr' });
this.route('yarn-node-container',
{ path: '/yarn-node-container/:node_id/:node_addr/:container_id' });
this.route('yarn-container-log', { path:
'/yarn-container-log/:node_id/:node_addr/:container_id/:filename' });
this.route('yarn-container-log',
{ path: '/yarn-container-log/:node_id/:node_addr/:container_id/:filename' });
this.route('yarn-log-service',
{ path: '/yarn-log-service/:cluster_id/:container_id/:filename' });
this.route('yarn-deploy-service');
this.route('cluster-overview');

View File

@ -0,0 +1,66 @@
/**
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you 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.
*/
import Ember from 'ember';
import Constants from 'yarn-ui/constants';
import AbstractRoute from './abstract';
export default AbstractRoute.extend({
model(param) {
var id = param.container_id + Constants.PARAM_SEPARATOR + param.filename
+ Constants.PARAM_SEPARATOR + param.cluster_id;
return Ember.RSVP.hash({
containerLog: this.store.findRecord('yarn-app-log', id),
containerInfo: { id: param.container_id }
}).then(function (hash) {
// Just return as its success.
return hash;
}, function (reason) {
if (reason.errors && reason.errors[0]) {
// This means HTTP error response was sent by adapter.
return reason;
} else {
// Assume empty response received from server.
return {
containerInfo: { id: param.container_id },
containerLog: {
logs: "",
containerID: param.container_id,
logFileName: param.filename
}
};
}
});
},
afterModel(model) {
// Handle errors and redirect if promise is rejected.
if (model.errors && model.errors[0]) {
if (parseInt(model.errors[0].status) === 404) {
this.replaceWith('/notfound');
} else {
this.replaceWith('/error');
}
}
},
unloadAll() {
this.store.unloadAll('yarn-app-log');
}
});

View File

@ -29,8 +29,8 @@ export default DS.JSONAPISerializer.extend({
type: primaryModelClass.modelName,
attributes: {
logs: payload,
containerID: splits[1],
logFileName: splits[2]
containerID: splits[0],
logFileName: splits[1]
}
};
return { data: convertedPayload };

View File

@ -0,0 +1,37 @@
{{!--
Licensed to the Apache Software Foundation (ASF) under one
or more contributor license agreements. See the NOTICE file
distributed with this work for additional information
regarding copyright ownership. The ASF licenses this file
to you 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.
--}}
<div class="col-md-12 container-fluid">
<div class="col-md-12">
<div class="panel panel-default">
<div class="panel-heading">
<h5 align="center">
<b>Log: {{model.containerLog.logFileName}} [ {{model.containerLog.containerID}} ]</b>
</h5>
</div>
<div class="panel-body">
{{#if model.containerLog.logs}}
<pre>{{model.containerLog.logs}}</pre>
{{else}}
<p>No logs were written in {{model.containerLog.logFileName}}.</p>
{{/if}}
</div>
</div>
</div>
</div>
{{outlet}}

View File

@ -119,7 +119,7 @@ export default {
if (splitLen < 2) {
return null;
}
return [splits[0], splits[1]];
return splits[2] ? [splits[0], splits[1], splits[2]] : [splits[0], splits[1]];
}
},
memoryToSimpliedUnit: function(mb) {

View File

@ -31,6 +31,7 @@ module.exports = { // YARN UI App configurations
cluster: 'ws/v1/cluster',
metrics: 'ws/v1/cluster/metrics',
timelineV2: 'ws/v2/timeline',
timelineV2Log: 'ws/v2/applicationlog',
dashService: 'app/v1/services',
node: '{nodeAddress}/ws/v1/node'
},