YARN-7092. Render application specific log under application tab in new YARN UI. Contributed by Akhil PB.

This commit is contained in:
Sunil G 2017-12-05 19:41:07 +05:30
parent f9f317b702
commit 99ccca341f
24 changed files with 857 additions and 52 deletions

View File

@ -0,0 +1,39 @@
/**
* 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 AbstractAdapter from './abstract';
export default AbstractAdapter.extend({
address: "timelineWebAddress",
// restNameSpace: "timelineV2", // Use ATSv2 when it supports log APIs.
restNameSpace: "timeline", //Using ATSv1.5 now, would be supported by ATSv2 very soon.
serverName: "ATS",
urlForQuery(query/*, modelName*/) {
var url = this._buildURL();
var containerId = query['containerId'];
delete query.containerId;
return url + '/containers/' + containerId + '/logs';
},
fetchLogFileContent(containerId, logFile) {
var url = this._buildURL();
url = url + '/containers/' + containerId + '/logs/' + logFile;
return Ember.$.ajax({url: url, type: 'GET', dataType: 'text'});
}
});

View File

@ -0,0 +1,64 @@
/**
* 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';
export default Ember.Component.extend({
classNames: ['pull-right'],
targetId: '',
initialClosedState: false,
didInsertElement() {
if (!this.get('targetId')) {
this.$('.toggle_switch').hide();
}
if (this.get('targetId') && this.get('initialClosedState')) {
this.$('.toggle_switch').show();
this.toggleToggleSwitchArrow();
Ember.$('#' + this.get('targetId')).removeClass('panel-collapsed').show();
}
},
toggleToggleSwitchArrow() {
let $toggleArrow = this.$('.toggle_switch').find('span');
if ($toggleArrow.hasClass('glyphicon-chevron-up')) {
$toggleArrow.removeClass('glyphicon-chevron-up').addClass('glyphicon-chevron-down');
} else {
$toggleArrow.removeClass('glyphicon-chevron-down').addClass('glyphicon-chevron-up');
}
},
toggleCollapsiblePanel() {
let $collapsiblePanel = Ember.$('#' + this.get('targetId'));
if ($collapsiblePanel.hasClass('panel-collapsed')) {
$collapsiblePanel.removeClass('panel-collapsed');
$collapsiblePanel.slideDown();
} else {
$collapsiblePanel.addClass('panel-collapsed');
$collapsiblePanel.slideUp();
}
},
actions: {
togglePanelCollapse() {
this.toggleToggleSwitchArrow();
this.toggleCollapsiblePanel();
}
}
});

View File

@ -472,9 +472,5 @@ export default Ember.Component.extend({
prop = 'http://' + prop;
}
return prop;
},
isDataEmpty: Ember.computed(function() {
return this.modelArr.length === 0;
})
}
});

View File

@ -22,7 +22,7 @@ export default Ember.Controller.extend({
queryParams: ["service"],
service: undefined,
breadcrumbs: Ember.computed("model.attempt.appId", function () {
breadcrumbs: Ember.computed("model.attempt.appId", "model.attempt.id", function () {
var appId = this.get("model.attempt.appId");
var attemptId = this.get("model.attempt.id");
var serviceName = this.get('service');

View File

@ -0,0 +1,204 @@
/**
* 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';
export default Ember.Controller.extend({
queryParams: ["service"],
service: undefined,
selectedAttemptId: '',
attemptContainerList: null,
selectedContainerId: '',
selectedLogFileName: '',
containerLogFiles: null,
selectedLogFileContent: '',
_isLoadingTopPanel: false,
_isLoadingBottomPanel: false,
actions: {
showContainersForAttemptId(attemptId) {
this.set('selectedAttemptId', '');
if (attemptId) {
this.set('_isLoadingTopPanel', true);
this.set('selectedAttemptId', attemptId);
this.fetchContainersForAttemptId(attemptId).then((hash) => {
let containers = null;
if (hash.rmContainers.get('length') > 0 && hash.rmContainers.get('content')) {
containers = (containers || []).concat(hash.rmContainers.get('content'));
}
if (hash.tsContainers.get('length') > 0 && hash.tsContainers.get('content')) {
containers = (containers || []).concat(hash.tsContainers.get('content'));
}
this.set('attemptContainerList', containers);
}).finally(() => {
this.set('_isLoadingTopPanel', false);
});
} else {
this.set('attemptContainerList', null);
this.set('selectedContainerId', '');
this.set('containerLogFiles', null);
this.set('selectedLogFileName', '');
this.set('selectedLogFileContent', '');
}
},
showLogFilesForContainerId(containerId) {
this.set('selectedContainerId', '');
this.set('containerLogFiles', null);
this.set('selectedLogFileName', '');
this.set('selectedLogFileContent', '');
if (containerId) {
this.set('_isLoadingBottomPanel', true);
this.set('selectedContainerId', containerId);
this.fetchLogFilesForContainerId(containerId).then((hash) => {
if (hash.logs.get('length') > 0) {
this.set('containerLogFiles', hash.logs);
} else {
this.set('containerLogFiles', null);
}
}).finally(() => {
this.set('_isLoadingBottomPanel', false);
});
}
},
showContentForLogFile(logFile) {
this.set('selectedLogFileName', '');
Ember.$("#logContentTextArea1234554321").val('');
this.set('showFullLog', false);
if (logFile) {
this.set('_isLoadingBottomPanel', true);
this.set('selectedLogFileName', logFile);
this.fetchContentForLogFile(this.get('selectedContainerId'), logFile).then((content) => {
this.set('selectedLogFileContent', content.trim());
}, () => {
this.set('selectedLogFileContent', '');
}).always(() => {
this.set('_isLoadingBottomPanel', false);
});
} else {
this.set('selectedLogFileContent', '');
}
},
findNextTextInLogContent() {
let searchInputElem = document.getElementById('logSeachInput98765');
this.send('searchTextInLogContent', searchInputElem.value);
},
searchTextInLogContent(searchText) {
Ember.$('body').scrollTop(278);
let textAreaElem = document.getElementById('logContentTextArea1234554321');
let logContent = textAreaElem.innerText;
let startIndex = this.searchTextStartIndex || 0;
if (startIndex === -1) {
startIndex = this.searchTextStartIndex = 0;
}
if (this.prevSearchText !== searchText) {
startIndex = this.searchTextStartIndex = 0;
}
if (searchText && searchText.trim()) {
searchText = searchText.trim();
this.prevSearchText = searchText;
if (startIndex === 0) {
startIndex = logContent.indexOf(searchText, 0);
}
let endIndex = startIndex + searchText.length;
if (document.createRange && window.getSelection) {
let range = document.createRange();
range.selectNodeContents(textAreaElem);
range.setStart(textAreaElem.childNodes.item(0), startIndex);
range.setEnd(textAreaElem.childNodes.item(0), endIndex);
let selection = window.getSelection();
selection.removeAllRanges();
selection.addRange(range);
}
this.searchTextStartIndex = logContent.indexOf(searchText, endIndex + 1);
} else {
this.searchTextStartIndex = 0;
}
},
showFullLogFileContent() {
this.set('showFullLog', true);
this.notifyPropertyChange('selectedLogFileContent');
}
},
attemptList: Ember.computed('model.attempts', function() {
let attempts = this.get('model.attempts');
let list = null;
if (attempts && attempts.get('length') && attempts.get('content')) {
list = [].concat(attempts.get('content'));
}
return list;
}),
fetchContainersForAttemptId(attemptId) {
return Ember.RSVP.hash({
rmContainers: this.store.query('yarn-container', {
app_attempt_id: attemptId
}).catch(function() {
return Ember.A();
}),
tsContainers: this.store.query('yarn-timeline-container', {
app_attempt_id: attemptId
}).catch(function() {
return Ember.A();
})
});
},
fetchLogFilesForContainerId(containerId) {
return Ember.RSVP.hash({
logs: this.store.query('yarn-log', {
containerId: containerId
}).catch(function() {
return Ember.A();
})
});
},
fetchContentForLogFile(containerId, logFile) {
let logAdapter = this.store.adapterFor('yarn-log');
return logAdapter.fetchLogFileContent(containerId, logFile);
},
resetAfterRefresh() {
this.set('selectedAttemptId', '');
this.set('attemptContainerList', null);
this.set('selectedContainerId', '');
this.set('selectedLogFileName', '');
this.set('containerLogFiles', null);
this.set('selectedLogFileContent', '');
},
showFullLog: false,
showLastFewLinesOfLogContent: Ember.computed('selectedLogFileContent', function() {
let content = this.get('selectedLogFileContent');
let lines = content.split('\n');
if (this.get('showFullLog') || lines.length < 10) {
return content;
}
return lines.slice(lines.length - 10).join('\n');
})
});

View File

@ -0,0 +1,27 @@
/**
* 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 DS from 'ember-data';
export default DS.Model.extend({
fileName: DS.attr('string'),
fileSize: DS.attr('string'),
lastModifiedTime: DS.attr('string'),
containerId: DS.attr('string'),
nodeId: DS.attr('string')
});

View File

@ -60,6 +60,7 @@ Router.map(function() {
this.route('components');
this.route('charts');
this.route('configs');
this.route('logs');
});
this.route('yarn-component-instances', function() {
this.route('info', {path: '/:component_name/info'});

View File

@ -26,11 +26,13 @@ export default AbstractRoute.extend(AppAttemptMixin, {
attempt: this.fetchAttemptInfoFromRMorATS(param.app_attempt_id, this.store),
rmContainers: this.store.query('yarn-container', {
app_attempt_id: param.app_attempt_id
}).catch(function() {
return Ember.A();
}),
tsContainers: this.store.query('yarn-timeline-container', {
app_attempt_id: param.app_attempt_id
}).catch(function() {
return [];
return Ember.A();
})
});
},

View File

@ -29,7 +29,9 @@ export default AbstractRoute.extend(AppAttemptMixin, {
return Ember.RSVP.hash({
appId: app_id,
serviceName: service,
attempts: this.fetchAttemptListFromRMorATS(app_id, this.store)
attempts: this.fetchAttemptListFromRMorATS(app_id, this.store).catch(function() {
return Ember.A();
})
});
},

View File

@ -0,0 +1,47 @@
/**
* 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 AbstractRoute from '../abstract';
import AppAttemptMixin from 'yarn-ui/mixins/app-attempt';
export default AbstractRoute.extend(AppAttemptMixin, {
model(param, transition) {
const { app_id } = this.paramsFor('yarn-app');
const { service } = param;
transition.send('updateBreadcrumbs', app_id, service, [{text: 'Logs'}]);
return Ember.RSVP.hash({
appId: app_id,
serviceName: service,
attempts: this.fetchAttemptListFromRMorATS(app_id, this.store).catch(function() {
return [];
})
});
},
unloadAll() {
this.store.unloadAll('yarn-app-attempt');
this.store.unloadAll('yarn-timeline-appattempt');
this.store.unloadAll('yarn-container');
this.store.unloadAll('yarn-timeline-container');
this.store.unloadAll('yarn-log');
if (this.controller) {
this.controller.resetAfterRefresh();
}
}
});

View File

@ -0,0 +1,48 @@
/**
* 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 DS from 'ember-data';
export default DS.JSONAPISerializer.extend({
internalNormalizeSingleResponse(store, primaryModelClass, payload, containerId, nodeId) {
var fixedPayload = {
id: "yarn_log_" + payload.fileName + "_" + Date.now(),
type: primaryModelClass.modelName,
attributes: {
fileName: payload.fileName,
fileSize: payload.fileSize,
lastModifiedTime: payload.lastModifiedTime,
containerId: containerId,
nodeId: nodeId
}
};
return fixedPayload;
},
normalizeArrayResponse(store, primaryModelClass, payload/*, id, requestType*/) {
var normalizedArrayResponse = {
data: []
};
if (payload && payload.containerLogsInfo && payload.containerLogsInfo.containerLogInfo) {
normalizedArrayResponse.data = payload.containerLogsInfo.containerLogInfo.map((paylog) => {
return this.internalNormalizeSingleResponse(store, primaryModelClass, paylog,
payload.containerLogsInfo.containerId, payload.containerLogsInfo.nodeId);
});
}
return normalizedArrayResponse;
}
});

View File

@ -0,0 +1,21 @@
{{!
* 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.
}}
<a class="pull-right toggle_switch" href="#" {{action "togglePanelCollapse"}}>
<span class="glyphicon glyphicon-chevron-up"></span>
</a>

View File

@ -25,49 +25,41 @@
Containers
{{/if}}
</div>
{{#if isDataEmpty}}
<ul class="nav nav-tabs" role="tablist">
<li class="active">
<a href="#graphViewTab" role="tab" data-toggle="tab">Graph View</a>
</li>
<li class="">
<a href="#gridViewTab" role="tab" data-toggle="tab">Grid View</a>
</li>
</ul>
<div class="panel-body">
<div class="tab-content">
<div role="tabpanel" class="tab-pane active" id="graphViewTab">
<br/><br/>
<div class="col-md-7 container-fluid" id={{parent-id}}></div>
<!-- diag info -->
<div class="col-md-5 container-fluid">
<div class="panel panel-default add-ellipsis attempt-info-panel">
<div class="panel-heading">
{{#if selected.link}}
{{#link-to selected.linkname selected.id (query-params service=serviceName)}}{{selected.id}}{{/link-to}}
{{else}}
{{selected.id}}
{{/if}}
</div>
{{#if attemptModel}}
{{app-attempt-table attempt=selected}}
<ul class="nav nav-tabs" role="tablist">
<li class="active">
<a href="#graphViewTab" role="tab" data-toggle="tab">Graph View</a>
</li>
<li class="">
<a href="#gridViewTab" role="tab" data-toggle="tab">Grid View</a>
</li>
</ul>
<div class="panel-body">
<div class="tab-content">
<div role="tabpanel" class="tab-pane active" id="graphViewTab">
<br/><br/>
<div class="col-md-7 container-fluid" id={{parent-id}}></div>
<!-- diag info -->
<div class="col-md-5 container-fluid">
<div class="panel panel-default add-ellipsis attempt-info-panel">
<div class="panel-heading">
{{#if selected.link}}
{{#link-to selected.linkname selected.id (query-params service=serviceName)}}{{selected.id}}{{/link-to}}
{{else}}
{{container-table container=selected}}
{{selected.id}}
{{/if}}
</div>
{{#if attemptModel}}
{{app-attempt-table attempt=selected}}
{{else}}
{{container-table container=selected}}
{{/if}}
</div>
</div>
<div role="tabpanel" class="tab-pane" id="gridViewTab">
{{em-table columns=gridColumns rows=gridRows}}
</div>
</div>
<div role="tabpanel" class="tab-pane" id="gridViewTab">
{{em-table columns=gridColumns rows=gridRows}}
</div>
</div>
{{else}}
<div class="panel-body">
<h4 class="text-center">No data available!</h4>
</div>
{{/if}}
</div>
</div>
</div>
{{outlet}}

View File

@ -48,7 +48,26 @@
</div>
<div class="row">
{{#if (or model.rmContainers model.tsContainers)}}
{{timeline-view parent-id="containers-timeline-div" my-id="timeline-view" height="400" rmModel=model.rmContainers tsModel=model.tsContainers label="shortAppAttemptId" attemptModel=false}}
{{timeline-view
parent-id="containers-timeline-div"
my-id="timeline-view"
height="400"
rmModel=model.rmContainers
tsModel=model.tsContainers
label="shortAppAttemptId"
attemptModel=false
}}
{{else}}
<div class="col-md-12 container-fluid">
<div class="panel panel-default">
<div class="panel-heading">
Containers
</div>
<div class="panel-body">
<h4 class="text-center">No data available!</h4>
</div>
</div>
</div>
{{/if}}
</div>
</div>

View File

@ -125,6 +125,9 @@
{{#link-to 'yarn-app.info' tagName="li" class=(if (eq target.currentPath 'yarn-app.info') "active")}}
{{#link-to 'yarn-app.info' appId (query-params service=model.serviceName)}}Diagnostics{{/link-to}}
{{/link-to}}
{{#link-to 'yarn-app.logs' tagName="li" class=(if (eq target.currentPath 'yarn-app.logs') "active")}}
{{#link-to 'yarn-app.logs' appId (query-params service=serviceName)}}Logs{{/link-to}}
{{/link-to}}
</ul>
</ul>
</div>

View File

@ -17,6 +17,7 @@
}}
<div>
{{#if model.attempts}}
{{timeline-view
parent-id="attempt-timeline-div"
my-id="timeline-view"
@ -26,4 +27,16 @@
attemptModel=true
serviceName=model.serviceName
}}
{{else}}
<div class="col-md-12 container-fluid">
<div class="panel panel-default">
<div class="panel-heading">
Application Attempts
</div>
<div class="panel-body">
<h4 class="text-center">No data available!</h4>
</div>
</div>
</div>
{{/if}}
</div>

View File

@ -0,0 +1,132 @@
{{!
* 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="row">
<div class="col-md-12">
<div class="panel panel-default">
<div class="panel-heading">
Logs {{collapsible-panel targetId="logFilesCollapsablePanel"}}
</div>
<div class="panel-body" id="logFilesCollapsablePanel">
{{#if _isLoadingTopPanel}}
<div class="text-center" style="z-index: 100; position: absolute; left: 46%;">
<img src="assets/images/spinner.gif" alt="Loading...">
</div>
{{/if}}
{{#if attemptList}}
<div class="row">
<div class="col-md-6">
<label>Choose attempt to fetch containers</label>
<select class="form-control" onchange={{action "showContainersForAttemptId" value="target.value"}} style="max-width:350px;">
<option value="" selected={{eq selectedAttemptId ''}}>None</option>
{{#each attemptList as |attempt|}}
<option value="{{attempt.id}}" selected={{eq selectedAttemptId attempt.id}}>{{attempt.id}}</option>
{{/each}}
</select>
</div>
{{#if attemptContainerList}}
<div class="col-md-6">
<label>Choose container to fetch logs</label>
<select class="form-control" onchange={{action "showLogFilesForContainerId" value="target.value"}} style="max-width:350px">
<option value="" selected={{eq selectedContainerId ''}}>None</option>
{{#each attemptContainerList as |container|}}
<option value="{{container.id}}" selected={{eq selectedContainerId container.id}}>{{container.id}}</option>
{{/each}}
</select>
</div>
{{else}}
{{#if (and selectedAttemptId (not _isLoadingTopPanel))}}
<div class="col-md-4">
<h4 class="text-center" style="margin-top:25px;">No container data available!</h4>
</div>
{{/if}}
{{/if}}
</div>
{{else}}
<div class="col-md-12">
<h4 class="text-center">No data available!</h4>
</div>
{{/if}}
</div>
</div>
</div>
</div>
{{#if selectedContainerId}}
<div class="row">
<div class="col-md-12">
<div class="panel panel-default" style="min-height:150px;">
<div class="panel-heading">
Log: {{selectedLogFileName}} [ {{selectedContainerId}} &amp; {{selectedAttemptId}} ]
{{collapsible-panel targetId="logContentCollapsablePanel"}}
</div>
<div class="panel-body" id="logContentCollapsablePanel">
{{#if _isLoadingBottomPanel}}
<div class="text-center" style="z-index: 100; position: absolute; left: 46%;">
<img src="assets/images/spinner.gif" alt="Loading...">
</div>
{{/if}}
{{#if containerLogFiles}}
<div class="row">
<div class="col-md-6" style="margin-bottom:20px;">
<label>Choose log for {{selectedContainerId}}</label>
<select class="form-control" onchange={{action "showContentForLogFile" value="target.value"}} style="max-width:350px">
<option value="" selected={{eq selectedLogFileName ''}}>None</option>
{{#each containerLogFiles as |file|}}
<option value="{{file.fileName}}" selected={{eq selectedLogFileName file.fileName}}>{{file.fileName}} - {{file.fileSize}} bytes</option>
{{/each}}
</select>
</div>
{{#if selectedLogFileName}}
<div class="col-md-6">
<label>Find in log</label>
<div class="input-group" style="max-width:350px;">
{{input
class="form-control"
value=""
enter="searchTextInLogContent"
id="logSeachInput98765"
}}
<span class="input-group-btn">
<button class="btn btn-default" {{action "findNextTextInLogContent"}}>Find</button>
</span>
</div>
</div>
{{/if}}
</div>
<div class="row">
{{#if selectedLogFileContent}}
{{#unless showFullLog}}
<div class="col-md-12">
<strong>Showing last 10 lines of log. Click <a href="#" {{action "showFullLogFileContent"}}>here</a> for full log.</strong>
</div>
{{/unless}}
<pre id="logContentTextArea1234554321" class="log-content-area">{{showLastFewLinesOfLogContent}}</pre>
{{/if}}
</div>
{{else}}
{{#unless _isLoadingBottomPanel}}
<div class="col-md-12" style="margin-top:20px;">
<h4 class="text-center">No log data available!</h4>
</div>
{{/unless}}
{{/if}}
</div>
</div>
</div>
</div>
{{/if}}

View File

@ -1,6 +1,6 @@
{
"https://github.com/DataTables/DataTables.git": {
"1.10.15": "1.10.15"
"1.10.15": "84d24d41477bac7657c9ddfa1d86245f9affc879"
},
"https://github.com/components/ember-data.git": {
"2.1.0": "d8b4d3092f67afe22d9d374c40d719d557915fa3"
@ -27,10 +27,10 @@
"0.1.7": "7bb21488563bd1bba23e903a812bf5815beddd1a"
},
"https://github.com/fgnass/spin.js.git": {
"2.3.2": "2.3.2"
"2.3.2": "5a9ea41651105302fe76d15cb294070c5863e759"
},
"https://github.com/ivaynberg/select2.git": {
"4.0.0": "4.0.0"
"4.0.0": "80eb44bec78568b152dc5547269c164cb7b5363c"
},
"https://github.com/jquery/jquery-dist.git": {
"2.1.4": "7751e69b615c6eca6f783a81e292a55725af6b85"
@ -39,20 +39,20 @@
"1.19.0": "467e7e34652ad7d5883ce9c568461cf8c5e172a8"
},
"https://github.com/mbostock-bower/d3-bower.git": {
"3.5.17": "3.5.17"
"3.5.17": "abe0262a205c9f3755c3a757de4dfd1d49f34b24"
},
"https://github.com/moment/moment-timezone.git": {
"0.5.0": "74a2e9378ecf4a31a168f3049f086565c8d66814"
},
"https://github.com/moment/moment.git": {
"2.10.6": "2.10.6",
"2.10.6": "446ce77eb08c5c862d7b0b11ef1d2e884d12e3d7",
"2.12.0": "d3d7488b4d60632854181cb0a9af325d57fb3d51"
},
"https://github.com/rwjblue/ember-qunit-builds.git": {
"0.4.16": "142c4066a5458bef9dfcb92b70152b9c01d79188"
},
"https://github.com/sreenaths/more-js.git": {
"0.8.2": "0.8.2"
"0.8.2": "015ab7c7dad2dc9edc0049b8b65aeb020fd20c51"
},
"https://github.com/sreenaths/snippet-ss.git": {
"1.11.0": "c1abc566f4e001b7f1939b6dbdd911eadc969cf9"

View File

@ -0,0 +1,43 @@
/**
* 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 { moduleForComponent, test } from 'ember-qunit';
import hbs from 'htmlbars-inline-precompile';
moduleForComponent('collapsible-panel', 'Integration | Component | collapsible panel', {
integration: true
});
test('it renders', function(assert) {
// Set any properties with this.set('myProperty', 'value');
// Handle any actions with this.on('myAction', function(val) { ... });" + EOL + EOL +
this.render(hbs`{{collapsible-panel}}`);
assert.equal(this.$().text().trim(), '');
// Template block usage:" + EOL +
this.render(hbs`
{{#collapsible-panel}}
template block text
{{/collapsible-panel}}
`);
assert.equal(this.$().text().trim(), 'template block text');
});

View File

@ -0,0 +1,30 @@
/**
* 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 { moduleFor, test } from 'ember-qunit';
moduleFor('adapter:yarn-log', 'Unit | Adapter | yarn log', {
// Specify the other units that are required for this test.
// needs: ['serializer:foo']
});
// Replace this with your real tests.
test('it exists', function(assert) {
let adapter = this.subject();
assert.ok(adapter);
});

View File

@ -0,0 +1,30 @@
/**
* 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 { moduleFor, test } from 'ember-qunit';
moduleFor('controller:yarn-app/logs', 'Unit | Controller | yarn app/logs', {
// Specify the other units that are required for this test.
// needs: ['controller:foo']
});
// Replace this with your real tests.
test('it exists', function(assert) {
let controller = this.subject();
assert.ok(controller);
});

View File

@ -0,0 +1,30 @@
/**
* 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 { moduleForModel, test } from 'ember-qunit';
moduleForModel('yarn-log', 'Unit | Model | yarn log', {
// Specify the other units that are required for this test.
needs: []
});
test('it exists', function(assert) {
let model = this.subject();
// let store = this.store();
assert.ok(!!model);
});

View File

@ -0,0 +1,29 @@
/**
* 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 { moduleFor, test } from 'ember-qunit';
moduleFor('route:yarn-app/logs', 'Unit | Route | yarn app/logs', {
// Specify the other units that are required for this test.
// needs: ['controller:foo']
});
test('it exists', function(assert) {
let route = this.subject();
assert.ok(route);
});

View File

@ -0,0 +1,33 @@
/**
* 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 { moduleForModel, test } from 'ember-qunit';
moduleForModel('yarn-log', 'Unit | Serializer | yarn log', {
// Specify the other units that are required for this test.
needs: ['serializer:yarn-log']
});
// Replace this with your real tests.
test('it serializes records', function(assert) {
let record = this.subject();
let serializedRecord = record.serialize();
assert.ok(serializedRecord);
});