├── .gitignore
├── src
├── kopf
│ ├── css
│ │ ├── benchmark.css
│ │ ├── hotthreads.css
│ │ ├── repository.css
│ │ ├── analysis.css
│ │ ├── explain.css
│ │ ├── cluster_health.css
│ │ ├── aliases.css
│ │ ├── nodes.css
│ │ ├── rest_client.css
│ │ └── navbar.css
│ ├── elastic
│ │ ├── elastic_client.js
│ │ ├── index_template.js
│ │ ├── node_stats.js
│ │ ├── hot_thread.js
│ │ ├── shard_stats.js
│ │ ├── warmer.js
│ │ ├── hot_threads.js
│ │ ├── token.js
│ │ ├── shard.js
│ │ ├── cluster_mapping.js
│ │ ├── snapshot.js
│ │ ├── cat_result.js
│ │ ├── cluster_health.js
│ │ ├── es_connection.js
│ │ ├── node_hot_threads.js
│ │ ├── version.js
│ │ ├── cluster_settings.js
│ │ ├── broken_cluster.js
│ │ ├── percolator.js
│ │ ├── node.js
│ │ ├── index.js
│ │ ├── editable_index_settings.js
│ │ ├── cluster_changes.js
│ │ ├── index_metadata.js
│ │ ├── repository.js
│ │ └── alias.js
│ ├── models
│ │ ├── modal_controls.js
│ │ ├── gist.js
│ │ ├── snapshot_filter.js
│ │ ├── warmer_filter.js
│ │ ├── request.js
│ │ ├── index_template_filter.js
│ │ ├── alias_filter.js
│ │ ├── ace_editor.js
│ │ ├── node_filter.js
│ │ ├── paginator.js
│ │ └── index_filter.js
│ ├── services
│ │ ├── aceeditor.js
│ │ ├── state.js
│ │ ├── debug.js
│ │ ├── clipboard.js
│ │ ├── host_history.js
│ │ ├── explain.js
│ │ ├── page.js
│ │ └── alerts.js
│ ├── directives
│ │ ├── static_include.js
│ │ ├── navbar_section.js
│ │ ├── json_tree.js
│ │ ├── pagination.js
│ │ └── sort_table.js
│ ├── controllers
│ │ ├── confirm_dialog.js
│ │ ├── cluster_stats.js
│ │ ├── alerts.js
│ │ ├── debug.js
│ │ ├── cat.js
│ │ ├── cluster_settings.js
│ │ ├── hotthreads.js
│ │ ├── nodes.js
│ │ ├── index_settings.js
│ │ ├── benchmark.js
│ │ ├── create_index.js
│ │ ├── navbar.js
│ │ ├── global.js
│ │ └── warmers.js
│ ├── filters
│ │ ├── starts_with.js
│ │ ├── bytes.js
│ │ └── time_interval.js
│ ├── util.js
│ ├── theme-kopf.js
│ └── kopf.js
└── lib
│ ├── bootstrap
│ └── fonts
│ │ ├── glyphicons-halflings-regular.eot
│ │ ├── glyphicons-halflings-regular.ttf
│ │ └── glyphicons-halflings-regular.woff
│ ├── jsontree
│ └── jsontree.min.js
│ ├── csv
│ └── csv.js
│ └── angular-tree-dnd
│ └── ng-tree-dnd.css
├── imgs
├── warmer.png
├── aliases.png
├── analysis.png
├── snapshot.png
├── percolator.png
├── rest_client.png
├── cluster_state.png
└── cluster_view.png
├── _site
├── favicon.ico
├── es-plugin.properties
├── font-awesome
│ ├── fonts
│ │ ├── FontAwesome.otf
│ │ ├── fontawesome-webfont.eot
│ │ ├── fontawesome-webfont.ttf
│ │ ├── fontawesome-webfont.woff
│ │ └── fontawesome-webfont.woff2
│ ├── less
│ │ ├── fixed-width.less
│ │ ├── bordered-pulled.less
│ │ ├── larger.less
│ │ ├── list.less
│ │ ├── font-awesome.less
│ │ ├── core.less
│ │ ├── stacked.less
│ │ ├── rotated-flipped.less
│ │ ├── path.less
│ │ ├── animated.less
│ │ └── mixins.less
│ └── scss
│ │ ├── _fixed-width.scss
│ │ ├── _bordered-pulled.scss
│ │ ├── _larger.scss
│ │ ├── _list.scss
│ │ ├── font-awesome.scss
│ │ ├── _core.scss
│ │ ├── _stacked.scss
│ │ ├── _rotated-flipped.scss
│ │ ├── _path.scss
│ │ ├── _animated.scss
│ │ └── _mixins.scss
├── kopf_external_settings.json
├── partials
│ ├── analysis
│ │ └── analysis_token.html
│ ├── snapshot
│ │ ├── url_repository.html
│ │ ├── azure_repository.html
│ │ ├── fs_repository.html
│ │ ├── hdfs_repository.html
│ │ ├── s3_repository.html
│ │ └── repositories_table.html
│ ├── cluster_overview
│ │ ├── index_unassigned.html
│ │ ├── filters.html
│ │ └── index_body.html
│ ├── snapshot.html
│ ├── debug.html
│ ├── main_alerts.html
│ ├── directives
│ │ └── pagination.html
│ ├── index_settings
│ │ ├── cache.html
│ │ ├── routing.html
│ │ ├── blocks.html
│ │ └── translog.html
│ ├── aliases
│ │ └── alias_details.html
│ ├── cluster_settings.html
│ ├── create_index.html
│ ├── index_settings.html
│ ├── cat.html
│ ├── cluster_settings
│ │ ├── cluster.html
│ │ └── recovery.html
│ └── cluster_stats.html
├── modals
│ ├── modal_info.html
│ └── confirm_dialog.html
├── index.html
└── dist
│ └── theme-kopf.js
├── plugin-descriptor.properties
├── CHANGELOG.md
├── tests
├── jasmine
│ ├── filters
│ │ ├── starts_with.js
│ │ ├── time_interval.tests.js
│ │ └── bytes.tests.js
│ ├── directives
│ │ └── navbar_section.tests.js
│ ├── alerts.tests.js
│ ├── services
│ │ ├── debug.tests.js
│ │ ├── explain.tests.js
│ │ ├── page.tests.js
│ │ └── external_settings.tests.js
│ ├── cat.tests.js
│ └── host_history.tests.js
├── es_connection.js
├── models
│ ├── cat_result.js
│ ├── url_autocomplete.js
│ └── node_filter.js
├── all.html
└── karma.config.js
├── docker
├── Dockerfile
├── run.sh
├── nginx.conf.tpl
└── README.md
├── LICENSE
└── package.json
/.gitignore:
--------------------------------------------------------------------------------
1 | node_modules
2 |
--------------------------------------------------------------------------------
/src/kopf/css/benchmark.css:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/src/kopf/elastic/elastic_client.js:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/src/kopf/css/hotthreads.css:
--------------------------------------------------------------------------------
1 | .hot-threads-output {
2 | white-space: pre;
3 | }
--------------------------------------------------------------------------------
/imgs/warmer.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/lmenezes/elasticsearch-kopf/HEAD/imgs/warmer.png
--------------------------------------------------------------------------------
/_site/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/lmenezes/elasticsearch-kopf/HEAD/_site/favicon.ico
--------------------------------------------------------------------------------
/imgs/aliases.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/lmenezes/elasticsearch-kopf/HEAD/imgs/aliases.png
--------------------------------------------------------------------------------
/imgs/analysis.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/lmenezes/elasticsearch-kopf/HEAD/imgs/analysis.png
--------------------------------------------------------------------------------
/imgs/snapshot.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/lmenezes/elasticsearch-kopf/HEAD/imgs/snapshot.png
--------------------------------------------------------------------------------
/imgs/percolator.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/lmenezes/elasticsearch-kopf/HEAD/imgs/percolator.png
--------------------------------------------------------------------------------
/imgs/rest_client.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/lmenezes/elasticsearch-kopf/HEAD/imgs/rest_client.png
--------------------------------------------------------------------------------
/imgs/cluster_state.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/lmenezes/elasticsearch-kopf/HEAD/imgs/cluster_state.png
--------------------------------------------------------------------------------
/imgs/cluster_view.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/lmenezes/elasticsearch-kopf/HEAD/imgs/cluster_view.png
--------------------------------------------------------------------------------
/src/kopf/css/repository.css:
--------------------------------------------------------------------------------
1 | select.input-sm-twoxheight {
2 | height: 100px;
3 | line-height: 30px;
4 | }
--------------------------------------------------------------------------------
/_site/es-plugin.properties:
--------------------------------------------------------------------------------
1 | description=kopf - simple web administration tool for ElasticSearch
2 | version=1.5.7-SNAPSHOT
--------------------------------------------------------------------------------
/src/kopf/elastic/index_template.js:
--------------------------------------------------------------------------------
1 | function IndexTemplate(name, body) {
2 | this.name = name;
3 | this.body = body;
4 | }
5 |
--------------------------------------------------------------------------------
/_site/font-awesome/fonts/FontAwesome.otf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/lmenezes/elasticsearch-kopf/HEAD/_site/font-awesome/fonts/FontAwesome.otf
--------------------------------------------------------------------------------
/plugin-descriptor.properties:
--------------------------------------------------------------------------------
1 | description=kopf - simple web administration tool for Elasticsearch
2 | version=2.0.1
3 | site=true
4 | name=kopf
5 |
--------------------------------------------------------------------------------
/src/kopf/elastic/node_stats.js:
--------------------------------------------------------------------------------
1 | function NodeStats(id, stats) {
2 | this.id = id;
3 | this.name = stats.name;
4 | this.stats = stats;
5 | }
6 |
--------------------------------------------------------------------------------
/src/kopf/elastic/hot_thread.js:
--------------------------------------------------------------------------------
1 | function HotThread(header) {
2 | this.header = header;
3 | this.subHeader = undefined;
4 | this.stack = [];
5 | }
6 |
--------------------------------------------------------------------------------
/_site/font-awesome/fonts/fontawesome-webfont.eot:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/lmenezes/elasticsearch-kopf/HEAD/_site/font-awesome/fonts/fontawesome-webfont.eot
--------------------------------------------------------------------------------
/_site/font-awesome/fonts/fontawesome-webfont.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/lmenezes/elasticsearch-kopf/HEAD/_site/font-awesome/fonts/fontawesome-webfont.ttf
--------------------------------------------------------------------------------
/_site/font-awesome/fonts/fontawesome-webfont.woff:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/lmenezes/elasticsearch-kopf/HEAD/_site/font-awesome/fonts/fontawesome-webfont.woff
--------------------------------------------------------------------------------
/_site/font-awesome/fonts/fontawesome-webfont.woff2:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/lmenezes/elasticsearch-kopf/HEAD/_site/font-awesome/fonts/fontawesome-webfont.woff2
--------------------------------------------------------------------------------
/src/kopf/elastic/shard_stats.js:
--------------------------------------------------------------------------------
1 | function ShardStats(shard, index, stats) {
2 | this.shard = shard;
3 | this.index = index;
4 | this.stats = stats;
5 | }
6 |
--------------------------------------------------------------------------------
/_site/kopf_external_settings.json:
--------------------------------------------------------------------------------
1 | {
2 | "elasticsearch_root_path": "",
3 | "with_credentials": false,
4 | "theme": "dark",
5 | "refresh_rate": 5000
6 | }
--------------------------------------------------------------------------------
/src/kopf/models/modal_controls.js:
--------------------------------------------------------------------------------
1 | function ModalControls() {
2 | this.alert = null;
3 | this.active = false;
4 | this.title = '';
5 | this.info = '';
6 | }
7 |
--------------------------------------------------------------------------------
/src/lib/bootstrap/fonts/glyphicons-halflings-regular.eot:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/lmenezes/elasticsearch-kopf/HEAD/src/lib/bootstrap/fonts/glyphicons-halflings-regular.eot
--------------------------------------------------------------------------------
/src/lib/bootstrap/fonts/glyphicons-halflings-regular.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/lmenezes/elasticsearch-kopf/HEAD/src/lib/bootstrap/fonts/glyphicons-halflings-regular.ttf
--------------------------------------------------------------------------------
/src/lib/bootstrap/fonts/glyphicons-halflings-regular.woff:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/lmenezes/elasticsearch-kopf/HEAD/src/lib/bootstrap/fonts/glyphicons-halflings-regular.woff
--------------------------------------------------------------------------------
/_site/font-awesome/less/fixed-width.less:
--------------------------------------------------------------------------------
1 | // Fixed Width Icons
2 | // -------------------------
3 | .@{fa-css-prefix}-fw {
4 | width: (18em / 14);
5 | text-align: center;
6 | }
7 |
--------------------------------------------------------------------------------
/src/kopf/elastic/warmer.js:
--------------------------------------------------------------------------------
1 | function Warmer(id, index, body) {
2 | this.id = id;
3 | this.index = index;
4 | this.source = body.source;
5 | this.types = body.types;
6 | }
7 |
--------------------------------------------------------------------------------
/_site/font-awesome/scss/_fixed-width.scss:
--------------------------------------------------------------------------------
1 | // Fixed Width Icons
2 | // -------------------------
3 | .#{$fa-css-prefix}-fw {
4 | width: (18em / 14);
5 | text-align: center;
6 | }
7 |
--------------------------------------------------------------------------------
/src/kopf/services/aceeditor.js:
--------------------------------------------------------------------------------
1 | kopf.factory('AceEditorService', function() {
2 |
3 | this.init = function(name) {
4 | return new AceEditor(name);
5 | };
6 |
7 | return this;
8 | });
9 |
--------------------------------------------------------------------------------
/src/kopf/elastic/hot_threads.js:
--------------------------------------------------------------------------------
1 | function HotThreads(data) {
2 | this.nodes_hot_threads = data.split(':::').slice(1).map(function(data) {
3 | return new NodeHotThreads(data);
4 | });
5 | }
6 |
--------------------------------------------------------------------------------
/src/kopf/directives/static_include.js:
--------------------------------------------------------------------------------
1 | kopf.directive('ngStaticInclude', function() {
2 | return {
3 | templateUrl: function(elem, attr) {
4 | return './partials/' + attr.file + '.html';
5 | }
6 | };
7 | });
8 |
--------------------------------------------------------------------------------
/_site/partials/analysis/analysis_token.html:
--------------------------------------------------------------------------------
1 |
2 | {{token.token}}
3 |
4 |
5 | pos: {{token.position}} start: {{token.start_offset}} end: {{token.end_offset}}
6 |
--------------------------------------------------------------------------------
/src/kopf/elastic/token.js:
--------------------------------------------------------------------------------
1 | /** TYPES **/
2 | function Token(token, startOffset, endOffset, position) {
3 | this.token = token;
4 | this.start_offset = startOffset;
5 | this.end_offset = endOffset;
6 | this.position = position;
7 | }
8 |
--------------------------------------------------------------------------------
/_site/partials/snapshot/url_repository.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
--------------------------------------------------------------------------------
/_site/partials/cluster_overview/index_unassigned.html:
--------------------------------------------------------------------------------
1 |
2 |
3 | {{shard.shard}}
4 |
5 |
--------------------------------------------------------------------------------
/CHANGELOG.md:
--------------------------------------------------------------------------------
1 |
2 | # 1.4.3 (2014-12-08)
3 |
4 |
5 | ## Bug Fixes
6 |
7 | - **SNAPSHOT:** Display list of indices from snapshot instead of list of current indices when restoring a snapshot
8 | ([https://github.com/lmenezes/elasticsearch-kopf/issues/211])
9 |
--------------------------------------------------------------------------------
/src/kopf/elastic/shard.js:
--------------------------------------------------------------------------------
1 | function Shard(routing) {
2 | this.primary = routing.primary;
3 | this.shard = routing.shard;
4 | this.state = routing.state;
5 | this.node = routing.node;
6 | this.index = routing.index;
7 | this.id = this.node + '_' + this.shard + '_' + this.index;
8 | }
9 |
--------------------------------------------------------------------------------
/_site/partials/snapshot.html:
--------------------------------------------------------------------------------
1 |
9 |
--------------------------------------------------------------------------------
/src/kopf/elastic/cluster_mapping.js:
--------------------------------------------------------------------------------
1 | function ClusterMapping(data) {
2 |
3 | this.getIndices = function() {
4 | return Object.keys(data);
5 | };
6 |
7 | this.getTypes = function(index) {
8 | var indexMapping = getProperty(data, index + '.mappings', {});
9 | return Object.keys(indexMapping);
10 | };
11 |
12 | }
13 |
--------------------------------------------------------------------------------
/_site/partials/debug.html:
--------------------------------------------------------------------------------
1 |
2 |
3 | {{visible ? 'hide' : 'show'}} log
4 |
5 |
6 | {{msg}}
7 |
8 |
--------------------------------------------------------------------------------
/src/kopf/models/gist.js:
--------------------------------------------------------------------------------
1 | function Gist(title, url) {
2 | this.timestamp = getTimeString(new Date());
3 | this.title = title;
4 | this.url = url;
5 |
6 | this.loadFromJSON = function(json) {
7 | this.title = json.title;
8 | this.url = json.url;
9 | this.timestamp = json.timestamp;
10 | return this;
11 | };
12 |
13 | }
14 |
--------------------------------------------------------------------------------
/tests/jasmine/filters/starts_with.js:
--------------------------------------------------------------------------------
1 | describe('filter', function() {
2 |
3 | beforeEach(module('kopf'));
4 |
5 | describe('startsWithFilter', function() {
6 |
7 | it('should convert boolean values to unicode checkmark or cross',
8 | inject(function(startsWith) {
9 | expect(startsWith(['abc', 'acd', 'abd'], 'a')).toBe(['fabc', 'acd',
10 | 'abd']);
11 | }));
12 | });
13 | });
--------------------------------------------------------------------------------
/src/kopf/controllers/confirm_dialog.js:
--------------------------------------------------------------------------------
1 | kopf.controller('ConfirmDialogController', ['$scope', 'ConfirmDialogService',
2 | function($scope, ConfirmDialogService) {
3 |
4 | $scope.dialog_service = ConfirmDialogService;
5 |
6 | $scope.close = function() {
7 | $scope.dialog_service.close();
8 | };
9 |
10 | $scope.confirm = function() {
11 | $scope.dialog_service.confirm();
12 | };
13 |
14 | }
15 | ]);
16 |
--------------------------------------------------------------------------------
/_site/font-awesome/less/bordered-pulled.less:
--------------------------------------------------------------------------------
1 | // Bordered & Pulled
2 | // -------------------------
3 |
4 | .@{fa-css-prefix}-border {
5 | padding: .2em .25em .15em;
6 | border: solid .08em @fa-border-color;
7 | border-radius: .1em;
8 | }
9 |
10 | .pull-right { float: right; }
11 | .pull-left { float: left; }
12 |
13 | .@{fa-css-prefix} {
14 | &.pull-left { margin-right: .3em; }
15 | &.pull-right { margin-left: .3em; }
16 | }
17 |
--------------------------------------------------------------------------------
/_site/font-awesome/scss/_bordered-pulled.scss:
--------------------------------------------------------------------------------
1 | // Bordered & Pulled
2 | // -------------------------
3 |
4 | .#{$fa-css-prefix}-border {
5 | padding: .2em .25em .15em;
6 | border: solid .08em $fa-border-color;
7 | border-radius: .1em;
8 | }
9 |
10 | .pull-right { float: right; }
11 | .pull-left { float: left; }
12 |
13 | .#{$fa-css-prefix} {
14 | &.pull-left { margin-right: .3em; }
15 | &.pull-right { margin-left: .3em; }
16 | }
17 |
--------------------------------------------------------------------------------
/src/kopf/controllers/cluster_stats.js:
--------------------------------------------------------------------------------
1 | kopf.controller('ClusterStatsController', ['$scope', 'ElasticService',
2 | function($scope, ElasticService) {
3 |
4 | $scope.cluster = undefined;
5 |
6 | $scope.$watch(
7 | function() {
8 | return ElasticService.cluster;
9 | },
10 | function(newValue, oldValue) {
11 | $scope.cluster = ElasticService.cluster;
12 | }
13 | );
14 |
15 | }
16 | ]);
17 |
--------------------------------------------------------------------------------
/_site/font-awesome/less/larger.less:
--------------------------------------------------------------------------------
1 | // Icon Sizes
2 | // -------------------------
3 |
4 | /* makes the font 33% larger relative to the icon container */
5 | .@{fa-css-prefix}-lg {
6 | font-size: (4em / 3);
7 | line-height: (3em / 4);
8 | vertical-align: -15%;
9 | }
10 | .@{fa-css-prefix}-2x { font-size: 2em; }
11 | .@{fa-css-prefix}-3x { font-size: 3em; }
12 | .@{fa-css-prefix}-4x { font-size: 4em; }
13 | .@{fa-css-prefix}-5x { font-size: 5em; }
14 |
--------------------------------------------------------------------------------
/_site/font-awesome/scss/_larger.scss:
--------------------------------------------------------------------------------
1 | // Icon Sizes
2 | // -------------------------
3 |
4 | /* makes the font 33% larger relative to the icon container */
5 | .#{$fa-css-prefix}-lg {
6 | font-size: (4em / 3);
7 | line-height: (3em / 4);
8 | vertical-align: -15%;
9 | }
10 | .#{$fa-css-prefix}-2x { font-size: 2em; }
11 | .#{$fa-css-prefix}-3x { font-size: 3em; }
12 | .#{$fa-css-prefix}-4x { font-size: 4em; }
13 | .#{$fa-css-prefix}-5x { font-size: 5em; }
14 |
--------------------------------------------------------------------------------
/src/kopf/css/analysis.css:
--------------------------------------------------------------------------------
1 | .analysis-token {
2 | border-radius: 1px 1px 1px 1px;
3 | margin-right: 10px;
4 | padding-left: 2px;
5 | padding-right: 2px;
6 | margin-top: 5px;
7 | float: left;
8 | }
9 | .analysis-token-token {
10 | font-size:11px;
11 | text-align: center;
12 | font-weight: 500;
13 | }
14 | .analysis-token-info {
15 | font-size:9px;
16 | display: inline;
17 | }
18 | .analysis-text-input {
19 | overflow: hidden;
20 | display: block;
21 | }
--------------------------------------------------------------------------------
/src/kopf/css/explain.css:
--------------------------------------------------------------------------------
1 | .explanation-result {
2 | border: 1px solid #444;
3 | border-radius: 4px;
4 | margin-bottom: 10px;
5 | margin-top: 10px;
6 | }
7 | .explanation-result-title {
8 | font-size: 14px;
9 | padding: 5px;
10 | }
11 | .explanation-result table {
12 | border-top: none;
13 | border-left: none;
14 | border-right: none;
15 | }
16 |
17 | .explanation-result .table thead > tr > th {
18 | border-bottom: 1px solid #444;
19 | }
20 |
--------------------------------------------------------------------------------
/src/kopf/filters/starts_with.js:
--------------------------------------------------------------------------------
1 | kopf.filter('startsWith', function() {
2 |
3 | function strStartsWith(str, prefix) {
4 | return (str + '').indexOf(prefix) === 0;
5 | }
6 |
7 | return function(elements, prefix) {
8 | var filtered = [];
9 | angular.forEach(elements, function(element) {
10 | if (strStartsWith(element, prefix)) {
11 | filtered.push(element);
12 | }
13 | });
14 |
15 | return filtered;
16 | };
17 | });
18 |
--------------------------------------------------------------------------------
/src/kopf/css/cluster_health.css:
--------------------------------------------------------------------------------
1 | .cluster-health-top-actions {
2 | text-align: right;
3 | }
4 | .cluster-health-content {
5 | font-family: monospace;
6 | white-space: pre;
7 | }
8 |
9 | .cluster-health-form-group {
10 | padding-left: 5px;
11 | float: right;
12 | }
13 | .gist-timestamp-col {
14 | width: 70px;
15 | }
16 | .gist-link-col {
17 | width: 240px;
18 | }
19 | .gist-title-col {
20 | width: auto;
21 | }
22 | .cluster-health-filters {
23 | padding-top: 6px;
24 | }
--------------------------------------------------------------------------------
/src/kopf/models/snapshot_filter.js:
--------------------------------------------------------------------------------
1 | function SnapshotFilter() {
2 |
3 | this.clone = function() {
4 | return new SnapshotFilter();
5 | };
6 |
7 | this.getSorting = function() {
8 | return undefined;
9 | };
10 |
11 | this.equals = function(other) {
12 | return other !== null;
13 | };
14 |
15 | this.isBlank = function() {
16 | return true;
17 | };
18 |
19 | this.matches = function(snapshot) {
20 | return true;
21 | };
22 |
23 | }
24 |
--------------------------------------------------------------------------------
/src/kopf/filters/bytes.js:
--------------------------------------------------------------------------------
1 | kopf.filter('bytes', function() {
2 |
3 | var UNITS = ['b', 'KB', 'MB', 'GB', 'TB', 'PB'];
4 |
5 | function stringify(bytes) {
6 | if (bytes > 0) {
7 | var e = Math.floor(Math.log(bytes) / Math.log(1024));
8 | return (bytes / Math.pow(1024, e)).toFixed(2) + UNITS[e];
9 | } else {
10 | return 0 + UNITS[0];
11 | }
12 | }
13 |
14 | return function(bytes) {
15 | return stringify(bytes);
16 | };
17 |
18 | });
19 |
--------------------------------------------------------------------------------
/src/kopf/elastic/snapshot.js:
--------------------------------------------------------------------------------
1 | function Snapshot(info) {
2 | this.name = info.snapshot;
3 | this.indices = info.indices;
4 | this.state = info.state;
5 | this.start_time = info.start_time;
6 | this.start_time_in_millis = info.start_time_in_millis;
7 | this.end_time = info.end_time;
8 | this.end_time_in_millis = info.end_time_in_millis;
9 | this.duration_in_millis = info.duration_in_millis;
10 | this.failures = info.failures;
11 | this.shards = info.shards;
12 | }
13 |
--------------------------------------------------------------------------------
/_site/font-awesome/less/list.less:
--------------------------------------------------------------------------------
1 | // List Icons
2 | // -------------------------
3 |
4 | .@{fa-css-prefix}-ul {
5 | padding-left: 0;
6 | margin-left: @fa-li-width;
7 | list-style-type: none;
8 | > li { position: relative; }
9 | }
10 | .@{fa-css-prefix}-li {
11 | position: absolute;
12 | left: -@fa-li-width;
13 | width: @fa-li-width;
14 | top: (2em / 14);
15 | text-align: center;
16 | &.@{fa-css-prefix}-lg {
17 | left: (-@fa-li-width + (4em / 14));
18 | }
19 | }
20 |
--------------------------------------------------------------------------------
/_site/font-awesome/scss/_list.scss:
--------------------------------------------------------------------------------
1 | // List Icons
2 | // -------------------------
3 |
4 | .#{$fa-css-prefix}-ul {
5 | padding-left: 0;
6 | margin-left: $fa-li-width;
7 | list-style-type: none;
8 | > li { position: relative; }
9 | }
10 | .#{$fa-css-prefix}-li {
11 | position: absolute;
12 | left: -$fa-li-width;
13 | width: $fa-li-width;
14 | top: (2em / 14);
15 | text-align: center;
16 | &.#{$fa-css-prefix}-lg {
17 | left: -$fa-li-width + (4em / 14);
18 | }
19 | }
20 |
--------------------------------------------------------------------------------
/src/kopf/css/aliases.css:
--------------------------------------------------------------------------------
1 | .aliases-index-alias {
2 | border: 1px solid;
3 | border-radius: 2px 2px 2px 2px;
4 | margin-left: 5px;
5 | padding-left: 3px;
6 | padding-right: 2px;
7 | line-height: 14px;
8 | margin-bottom: 2px;
9 | height: 16px;
10 | }
11 |
12 | .aliases-alias-cell {
13 | width: 200px;
14 | max-width: 200px;
15 | min-width: 200px;
16 | }
17 | .aliases-new-alias-input {
18 | overflow: hidden;
19 | }
20 | .aliases-new-alias-input > a {
21 | text-decoration: none;
22 | color: inherit;
23 | }
--------------------------------------------------------------------------------
/_site/font-awesome/scss/font-awesome.scss:
--------------------------------------------------------------------------------
1 | /*!
2 | * Font Awesome 4.3.0 by @davegandy - http://fontawesome.io - @fontawesome
3 | * License - http://fontawesome.io/license (Font: SIL OFL 1.1, CSS: MIT License)
4 | */
5 |
6 | @import "variables";
7 | @import "mixins";
8 | @import "path";
9 | @import "core";
10 | @import "larger";
11 | @import "fixed-width";
12 | @import "list";
13 | @import "bordered-pulled";
14 | @import "animated";
15 | @import "rotated-flipped";
16 | @import "stacked";
17 | @import "icons";
18 |
--------------------------------------------------------------------------------
/src/kopf/controllers/alerts.js:
--------------------------------------------------------------------------------
1 | kopf.controller('AlertsController', ['$scope', 'AlertService',
2 | function($scope, AlertService) {
3 |
4 | $scope.alerts = [];
5 |
6 | $scope.$watch(
7 | function() {
8 | return AlertService.alerts;
9 | },
10 | function(newValue, oldValue) {
11 | $scope.alerts = AlertService.alerts;
12 | }
13 | );
14 |
15 | $scope.remove = function(id) {
16 | AlertService.remove(id);
17 | };
18 |
19 | }
20 |
21 | ]);
22 |
--------------------------------------------------------------------------------
/src/kopf/controllers/debug.js:
--------------------------------------------------------------------------------
1 | kopf.controller('DebugController', ['$scope', 'DebugService',
2 | function($scope, DebugService) {
3 |
4 | $scope.messages = [];
5 |
6 | $scope.visible = false;
7 |
8 | $scope.$watch(
9 | function() {
10 | return $scope.visible ? DebugService.getUpdatedAt() : 0;
11 | },
12 | function(newValue, oldValue) {
13 | $scope.messages = $scope.visible ? DebugService.getMessages() : [];
14 | }
15 | );
16 |
17 | }
18 |
19 | ]);
20 |
--------------------------------------------------------------------------------
/src/kopf/services/state.js:
--------------------------------------------------------------------------------
1 | kopf.factory('AppState', function() {
2 |
3 | this.properties = {};
4 |
5 | this.getProperty = function(controller, property, defaultValue) {
6 | if (this.properties[controller] === undefined) {
7 | this.properties[controller] = {};
8 | }
9 | if (this.properties[controller][property] === undefined) {
10 | this.properties[controller][property] = defaultValue;
11 | }
12 | return this.properties[controller][property];
13 | };
14 |
15 | return this;
16 |
17 | });
18 |
--------------------------------------------------------------------------------
/_site/modals/modal_info.html:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/_site/font-awesome/less/font-awesome.less:
--------------------------------------------------------------------------------
1 | /*!
2 | * Font Awesome 4.3.0 by @davegandy - http://fontawesome.io - @fontawesome
3 | * License - http://fontawesome.io/license (Font: SIL OFL 1.1, CSS: MIT License)
4 | */
5 |
6 | @import "variables.less";
7 | @import "mixins.less";
8 | @import "path.less";
9 | @import "core.less";
10 | @import "larger.less";
11 | @import "fixed-width.less";
12 | @import "list.less";
13 | @import "bordered-pulled.less";
14 | @import "animated.less";
15 | @import "rotated-flipped.less";
16 | @import "stacked.less";
17 | @import "icons.less";
18 |
--------------------------------------------------------------------------------
/_site/font-awesome/less/core.less:
--------------------------------------------------------------------------------
1 | // Base Class Definition
2 | // -------------------------
3 |
4 | .@{fa-css-prefix} {
5 | display: inline-block;
6 | font: normal normal normal @fa-font-size-base/1 FontAwesome; // shortening font declaration
7 | font-size: inherit; // can't have font-size inherit on line above, so need to override
8 | text-rendering: auto; // optimizelegibility throws things off #1094
9 | -webkit-font-smoothing: antialiased;
10 | -moz-osx-font-smoothing: grayscale;
11 | transform: translate(0, 0); // ensures no half-pixel rendering in firefox
12 |
13 | }
14 |
--------------------------------------------------------------------------------
/_site/font-awesome/less/stacked.less:
--------------------------------------------------------------------------------
1 | // Stacked Icons
2 | // -------------------------
3 |
4 | .@{fa-css-prefix}-stack {
5 | position: relative;
6 | display: inline-block;
7 | width: 2em;
8 | height: 2em;
9 | line-height: 2em;
10 | vertical-align: middle;
11 | }
12 | .@{fa-css-prefix}-stack-1x, .@{fa-css-prefix}-stack-2x {
13 | position: absolute;
14 | left: 0;
15 | width: 100%;
16 | text-align: center;
17 | }
18 | .@{fa-css-prefix}-stack-1x { line-height: inherit; }
19 | .@{fa-css-prefix}-stack-2x { font-size: 2em; }
20 | .@{fa-css-prefix}-inverse { color: @fa-inverse; }
21 |
--------------------------------------------------------------------------------
/_site/font-awesome/scss/_core.scss:
--------------------------------------------------------------------------------
1 | // Base Class Definition
2 | // -------------------------
3 |
4 | .#{$fa-css-prefix} {
5 | display: inline-block;
6 | font: normal normal normal #{$fa-font-size-base}/1 FontAwesome; // shortening font declaration
7 | font-size: inherit; // can't have font-size inherit on line above, so need to override
8 | text-rendering: auto; // optimizelegibility throws things off #1094
9 | -webkit-font-smoothing: antialiased;
10 | -moz-osx-font-smoothing: grayscale;
11 | transform: translate(0, 0); // ensures no half-pixel rendering in firefox
12 |
13 | }
14 |
--------------------------------------------------------------------------------
/src/kopf/elastic/cat_result.js:
--------------------------------------------------------------------------------
1 | function CatResult(result) {
2 | var lines = result.split('\n');
3 | var header = lines[0];
4 | var columns = header.match(/\S+/g);
5 | var values = lines.slice(1, -1).map(function(line) {
6 | return columns.map(function(column, i) {
7 | var start = header.indexOf(column);
8 | var lastColumn = i < columns.length - 1;
9 | var end = lastColumn ? header.indexOf(columns[i + 1]) : undefined;
10 | return line.substring(start, end).trim();
11 | });
12 | });
13 |
14 | this.columns = columns;
15 | this.lines = values;
16 | }
17 |
--------------------------------------------------------------------------------
/_site/font-awesome/scss/_stacked.scss:
--------------------------------------------------------------------------------
1 | // Stacked Icons
2 | // -------------------------
3 |
4 | .#{$fa-css-prefix}-stack {
5 | position: relative;
6 | display: inline-block;
7 | width: 2em;
8 | height: 2em;
9 | line-height: 2em;
10 | vertical-align: middle;
11 | }
12 | .#{$fa-css-prefix}-stack-1x, .#{$fa-css-prefix}-stack-2x {
13 | position: absolute;
14 | left: 0;
15 | width: 100%;
16 | text-align: center;
17 | }
18 | .#{$fa-css-prefix}-stack-1x { line-height: inherit; }
19 | .#{$fa-css-prefix}-stack-2x { font-size: 2em; }
20 | .#{$fa-css-prefix}-inverse { color: $fa-inverse; }
21 |
--------------------------------------------------------------------------------
/src/kopf/css/nodes.css:
--------------------------------------------------------------------------------
1 | .node-stat {
2 | font-size: 36px;
3 | font-weight: 300;
4 | float: left;
5 | line-height: 40px;
6 | min-width: 70px;
7 | margin-right: 15px;
8 | }
9 |
10 | .node-stat-detail {
11 | float: left;
12 | line-height: 20px;
13 | }
14 |
15 | .node-labels {
16 | padding-left: 20px;
17 | }
18 |
19 | .node-label {
20 | display: inline;
21 | padding: .1em .3em .1em;
22 | font-size: 8px;
23 | line-height: 1;
24 | color: #ffffff;
25 | text-align: center;
26 | white-space: nowrap;
27 | vertical-align: baseline;
28 | border-radius: .25em;
29 | }
30 |
--------------------------------------------------------------------------------
/src/kopf/css/rest_client.css:
--------------------------------------------------------------------------------
1 | .rest-client-request-url {
2 | overflow: hidden;
3 | display: block;
4 | }
5 | .rest-client-request-method {
6 | width: 80px;
7 | float: right;
8 | margin-left: 10px;
9 | }
10 | .rest-client-execute {
11 | float: right;
12 | margin-left: 10px;
13 | line-height: 12px;
14 | }
15 | .rest-client-request-body {
16 | font-size: 11px;
17 | margin-top: 20px;
18 | }
19 | .request-client-request-body-options {
20 | overflow: hidden;
21 | display: block;
22 | }
23 | .history-icon {
24 | float: right;
25 | padding-left: 10px;
26 | font-size: 22px;
27 | }
28 |
29 | .history-icon > a:hover {
30 | text-decoration: none;
31 | }
--------------------------------------------------------------------------------
/src/kopf/models/warmer_filter.js:
--------------------------------------------------------------------------------
1 | function WarmerFilter(id) {
2 |
3 | this.id = id;
4 |
5 | this.clone = function() {
6 | return new WarmerFilter(this.id);
7 | };
8 |
9 | this.getSorting = function() {
10 | return undefined;
11 | };
12 |
13 | this.equals = function(other) {
14 | return other !== null && this.id == other.id;
15 | };
16 |
17 | this.isBlank = function() {
18 | return !notEmpty(this.id);
19 | };
20 |
21 | this.matches = function(warmer) {
22 | if (this.isBlank()) {
23 | return true;
24 | } else {
25 | return warmer.id.indexOf(this.id) != -1;
26 | }
27 | };
28 |
29 | }
30 |
--------------------------------------------------------------------------------
/src/kopf/directives/navbar_section.js:
--------------------------------------------------------------------------------
1 | kopf.directive('ngNavbarSection', ['$location', 'ElasticService',
2 | function($location, ElasticService) {
3 |
4 | return {
5 | template: function(elem, attrs) {
6 | if (!attrs.version || ElasticService.versionCheck(attrs.version)) {
7 | var target = attrs.target;
8 | var text = attrs.text;
9 | var icon = attrs.icon;
10 | return '' +
11 | ' ' + text +
12 | '';
13 | } else {
14 | return '';
15 | }
16 | }
17 | };
18 | }
19 |
20 | ]);
21 |
--------------------------------------------------------------------------------
/tests/es_connection.js:
--------------------------------------------------------------------------------
1 | test("Creating regular ES connection", function() {
2 | var con = new ESConnection("http://localhost:9200");
3 | ok(con.host == "http://localhost:9200", "Checking host");
4 | })
5 |
6 | test("Creating HTTPS ES connection", function() {
7 | var con = new ESConnection("https://localhost:9200");
8 | ok(con.host == "https://localhost:9200", "Checking host");
9 | })
10 |
11 | test("Creating ES connection with username + password", function() {
12 | var con = new ESConnection("http://foo:bar@localhost:9200");
13 | ok(con.host == "http://localhost:9200", "Checking host");
14 | ok(con.username == "foo", "Checking username");
15 | ok(con.password == "bar", "Checking password");
16 | })
--------------------------------------------------------------------------------
/docker/Dockerfile:
--------------------------------------------------------------------------------
1 | FROM nginx:1.9.4
2 |
3 | # upgrade
4 | RUN apt-get update && \
5 | apt-get upgrade -y && \
6 | apt-get install -y --no-install-recommends python-pip curl && \
7 | rm -rf /var/lib/apt/lists/* && \
8 | pip install envtpl
9 |
10 | # nginx
11 | ADD nginx.conf.tpl /etc/nginx/nginx.conf.tpl
12 |
13 | # run script
14 | ADD ./run.sh ./run.sh
15 |
16 | # kopf
17 | ENV KOPF_VERSION 2.0.1
18 | RUN curl -s -L "https://github.com/lmenezes/elasticsearch-kopf/archive/v${KOPF_VERSION}.tar.gz" | \
19 | tar xz -C /tmp && mv "/tmp/elasticsearch-kopf-${KOPF_VERSION}" /kopf
20 |
21 | # logs
22 | VOLUME ["/var/log/nginx"]
23 |
24 | # ports
25 | EXPOSE 80 443
26 |
27 | ENTRYPOINT ["/run.sh"]
28 |
--------------------------------------------------------------------------------
/tests/models/cat_result.js:
--------------------------------------------------------------------------------
1 | QUnit.test( "cat result when column starts after value(docs)", function( assert ) {
2 | var response =
3 | 'id host ip node \n' +
4 | 'FDr3acnmQkaaz-9m2YZxHw foobarfoobarfooba 10.8.36.70 foobarfoobarfoobarfoobarfooba \n';
5 | var result = new CatResult(response);
6 | var expectedColumns = ['id', 'host', 'ip', 'node'];
7 | var expectedValues = ['FDr3acnmQkaaz-9m2YZxHw', 'foobarfoobarfooba', '10.8.36.70', 'foobarfoobarfoobarfoobarfooba'];
8 | assert.deepEqual(result.columns, expectedColumns, "correctly parses columns");
9 | assert.deepEqual(result.lines[0], expectedValues, "correctly parses values");
10 | });
11 |
--------------------------------------------------------------------------------
/_site/font-awesome/less/rotated-flipped.less:
--------------------------------------------------------------------------------
1 | // Rotated & Flipped Icons
2 | // -------------------------
3 |
4 | .@{fa-css-prefix}-rotate-90 { .fa-icon-rotate(90deg, 1); }
5 | .@{fa-css-prefix}-rotate-180 { .fa-icon-rotate(180deg, 2); }
6 | .@{fa-css-prefix}-rotate-270 { .fa-icon-rotate(270deg, 3); }
7 |
8 | .@{fa-css-prefix}-flip-horizontal { .fa-icon-flip(-1, 1, 0); }
9 | .@{fa-css-prefix}-flip-vertical { .fa-icon-flip(1, -1, 2); }
10 |
11 | // Hook for IE8-9
12 | // -------------------------
13 |
14 | :root .@{fa-css-prefix}-rotate-90,
15 | :root .@{fa-css-prefix}-rotate-180,
16 | :root .@{fa-css-prefix}-rotate-270,
17 | :root .@{fa-css-prefix}-flip-horizontal,
18 | :root .@{fa-css-prefix}-flip-vertical {
19 | filter: none;
20 | }
21 |
--------------------------------------------------------------------------------
/docker/run.sh:
--------------------------------------------------------------------------------
1 | #!/bin/sh
2 |
3 | set -e
4 |
5 | envtpl --keep-template /etc/nginx/nginx.conf.tpl
6 |
7 | if [ ! -z "${KOPF_BASIC_AUTH_LOGIN}" ]; then
8 | echo "${KOPF_BASIC_AUTH_LOGIN}:${KOPF_BASIC_AUTH_PASSWORD}" > /etc/nginx/kopf.htpasswd
9 | fi
10 |
11 | KOPF_REFRESH_RATE="${KOPF_REFRESH_RATE:-5000}"
12 | KOPF_THEME="${KOPF_THEME:-dark}"
13 | KOPF_WITH_CREDENTIALS="${KOPF_WITH_CREDENTIALS:-false}"
14 | KOPF_ES_ROOT_PATH="${KOPF_ES_ROOT_PATH:-/es}"
15 |
16 | cat < /kopf/_site/kopf_external_settings.json
17 | {
18 | "elasticsearch_root_path": "${KOPF_ES_ROOT_PATH}",
19 | "with_credentials": ${KOPF_WITH_CREDENTIALS},
20 | "theme": "${KOPF_THEME}",
21 | "refresh_rate": ${KOPF_REFRESH_RATE}
22 | }
23 | EOF
24 |
25 | exec nginx
26 |
--------------------------------------------------------------------------------
/_site/partials/main_alerts.html:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/src/kopf/elastic/cluster_health.js:
--------------------------------------------------------------------------------
1 | function ClusterHealth(health) {
2 | this.status = health.status;
3 | this.cluster_name = health.cluster_name;
4 | this.initializing_shards = health.initializing_shards;
5 | this.active_primary_shards = health.active_primary_shards;
6 | this.active_shards = health.active_shards;
7 | this.relocating_shards = health.relocating_shards;
8 | this.unassigned_shards = health.unassigned_shards;
9 | this.number_of_nodes = health.number_of_nodes;
10 | this.number_of_data_nodes = health.number_of_data_nodes;
11 | this.timed_out = health.timed_out;
12 | this.shards = this.active_shards + this.relocating_shards +
13 | this.unassigned_shards + this.initializing_shards;
14 | this.fetched_at = getTimeString(new Date());
15 | }
16 |
--------------------------------------------------------------------------------
/src/kopf/filters/time_interval.js:
--------------------------------------------------------------------------------
1 | kopf.filter('timeInterval', function() {
2 |
3 | var UNITS = ['yr', 'mo', 'd', 'h', 'min'];
4 |
5 | var UNIT_MEASURE = {
6 | yr: 31536000000,
7 | mo: 2678400000,
8 | wk: 604800000,
9 | d: 86400000,
10 | h: 3600000,
11 | min: 60000
12 | };
13 |
14 | function stringify(seconds) {
15 |
16 | var result = 'less than a minute';
17 |
18 | for (var idx = 0; idx < UNITS.length; idx++) {
19 | var amount = Math.floor(seconds / UNIT_MEASURE[UNITS[idx]]);
20 | if (amount) {
21 | result = amount + UNITS[idx] + '.';
22 | break;
23 | }
24 | }
25 |
26 | return result;
27 | }
28 |
29 | return function(seconds) {
30 | return stringify(seconds);
31 | };
32 |
33 | });
34 |
--------------------------------------------------------------------------------
/tests/jasmine/filters/time_interval.tests.js:
--------------------------------------------------------------------------------
1 | describe('filter', function() {
2 |
3 | beforeEach(module('kopf'));
4 |
5 | describe('timeInterval', function() {
6 |
7 | it('should convert boolean values to unicode checkmark or cross',
8 | inject(function(timeIntervalFilter) {
9 | expect(timeIntervalFilter(100000)).toBe('1min.');
10 | expect(timeIntervalFilter(1000000)).toBe('16min.');
11 | expect(timeIntervalFilter(10000000)).toBe('2h.');
12 | expect(timeIntervalFilter(100000000)).toBe('1d.');
13 | expect(timeIntervalFilter(1000000000)).toBe('11d.');
14 | expect(timeIntervalFilter(10000000000)).toBe('3mo.');
15 | expect(timeIntervalFilter(100000000000)).toBe('3yr.');
16 | }));
17 | });
18 | });
--------------------------------------------------------------------------------
/_site/font-awesome/scss/_rotated-flipped.scss:
--------------------------------------------------------------------------------
1 | // Rotated & Flipped Icons
2 | // -------------------------
3 |
4 | .#{$fa-css-prefix}-rotate-90 { @include fa-icon-rotate(90deg, 1); }
5 | .#{$fa-css-prefix}-rotate-180 { @include fa-icon-rotate(180deg, 2); }
6 | .#{$fa-css-prefix}-rotate-270 { @include fa-icon-rotate(270deg, 3); }
7 |
8 | .#{$fa-css-prefix}-flip-horizontal { @include fa-icon-flip(-1, 1, 0); }
9 | .#{$fa-css-prefix}-flip-vertical { @include fa-icon-flip(1, -1, 2); }
10 |
11 | // Hook for IE8-9
12 | // -------------------------
13 |
14 | :root .#{$fa-css-prefix}-rotate-90,
15 | :root .#{$fa-css-prefix}-rotate-180,
16 | :root .#{$fa-css-prefix}-rotate-270,
17 | :root .#{$fa-css-prefix}-flip-horizontal,
18 | :root .#{$fa-css-prefix}-flip-vertical {
19 | filter: none;
20 | }
21 |
--------------------------------------------------------------------------------
/src/kopf/services/debug.js:
--------------------------------------------------------------------------------
1 | kopf.factory('DebugService', ['$filter', function($filter) {
2 |
3 | var MaxMessages = 1000;
4 |
5 | var messages = [];
6 |
7 | var updatedAt = 0;
8 |
9 | var addMessage = function(message) {
10 | var date = new Date();
11 | messages.push($filter('date')(date, '[yyyy-MM-dd HH:mm:ss] ') + message);
12 | if (messages.length > MaxMessages) {
13 | messages.shift();
14 | }
15 | updatedAt = date.getTime();
16 | };
17 |
18 | this.debug = function(message, data) {
19 | addMessage(message);
20 | if (data) {
21 | addMessage(JSON.stringify(data));
22 | }
23 | };
24 |
25 | this.getUpdatedAt = function() {
26 | return updatedAt;
27 | };
28 |
29 | this.getMessages = function() {
30 | return messages;
31 | };
32 |
33 | return this;
34 |
35 | }]);
36 |
--------------------------------------------------------------------------------
/_site/font-awesome/less/path.less:
--------------------------------------------------------------------------------
1 | /* FONT PATH
2 | * -------------------------- */
3 |
4 | @font-face {
5 | font-family: 'FontAwesome';
6 | src: url('@{fa-font-path}/fontawesome-webfont.eot?v=@{fa-version}');
7 | src: url('@{fa-font-path}/fontawesome-webfont.eot?#iefix&v=@{fa-version}') format('embedded-opentype'),
8 | url('@{fa-font-path}/fontawesome-webfont.woff2?v=@{fa-version}') format('woff2'),
9 | url('@{fa-font-path}/fontawesome-webfont.woff?v=@{fa-version}') format('woff'),
10 | url('@{fa-font-path}/fontawesome-webfont.ttf?v=@{fa-version}') format('truetype'),
11 | url('@{fa-font-path}/fontawesome-webfont.svg?v=@{fa-version}#fontawesomeregular') format('svg');
12 | // src: url('@{fa-font-path}/FontAwesome.otf') format('opentype'); // used when developing fonts
13 | font-weight: normal;
14 | font-style: normal;
15 | }
16 |
--------------------------------------------------------------------------------
/_site/font-awesome/scss/_path.scss:
--------------------------------------------------------------------------------
1 | /* FONT PATH
2 | * -------------------------- */
3 |
4 | @font-face {
5 | font-family: 'FontAwesome';
6 | src: url('#{$fa-font-path}/fontawesome-webfont.eot?v=#{$fa-version}');
7 | src: url('#{$fa-font-path}/fontawesome-webfont.eot?#iefix&v=#{$fa-version}') format('embedded-opentype'),
8 | url('#{$fa-font-path}/fontawesome-webfont.woff2?v=#{$fa-version}') format('woff2'),
9 | url('#{$fa-font-path}/fontawesome-webfont.woff?v=#{$fa-version}') format('woff'),
10 | url('#{$fa-font-path}/fontawesome-webfont.ttf?v=#{$fa-version}') format('truetype'),
11 | url('#{$fa-font-path}/fontawesome-webfont.svg?v=#{$fa-version}#fontawesomeregular') format('svg');
12 | // src: url('#{$fa-font-path}/FontAwesome.otf') format('opentype'); // used when developing fonts
13 | font-weight: normal;
14 | font-style: normal;
15 | }
16 |
--------------------------------------------------------------------------------
/src/kopf/services/clipboard.js:
--------------------------------------------------------------------------------
1 | kopf.factory('ClipboardService', ['AlertService', '$document', '$window',
2 | function(AlertService, $document, $window) {
3 | var textarea = angular.element($document[0].createElement('textarea'));
4 | textarea.css({
5 | position: 'absolute',
6 | left: '-9999px',
7 | top: (
8 | $window.pageYOffset || $document[0].documentElement.scrollTop
9 | ) + 'px'
10 | });
11 | textarea.attr({readonly: ''});
12 | angular.element($document[0].body).append(textarea);
13 |
14 | this.copy = function(value, success, failure) {
15 | try {
16 | textarea.val(value);
17 | textarea.select();
18 | $document[0].execCommand('copy');
19 | success();
20 | } catch (error) {
21 | failure();
22 | }
23 | };
24 |
25 | return this;
26 | }]);
27 |
--------------------------------------------------------------------------------
/_site/font-awesome/less/animated.less:
--------------------------------------------------------------------------------
1 | // Animated Icons
2 | // --------------------------
3 |
4 | .@{fa-css-prefix}-spin {
5 | -webkit-animation: fa-spin 2s infinite linear;
6 | animation: fa-spin 2s infinite linear;
7 | }
8 |
9 | .@{fa-css-prefix}-pulse {
10 | -webkit-animation: fa-spin 1s infinite steps(8);
11 | animation: fa-spin 1s infinite steps(8);
12 | }
13 |
14 | @-webkit-keyframes fa-spin {
15 | 0% {
16 | -webkit-transform: rotate(0deg);
17 | transform: rotate(0deg);
18 | }
19 | 100% {
20 | -webkit-transform: rotate(359deg);
21 | transform: rotate(359deg);
22 | }
23 | }
24 |
25 | @keyframes fa-spin {
26 | 0% {
27 | -webkit-transform: rotate(0deg);
28 | transform: rotate(0deg);
29 | }
30 | 100% {
31 | -webkit-transform: rotate(359deg);
32 | transform: rotate(359deg);
33 | }
34 | }
35 |
--------------------------------------------------------------------------------
/_site/font-awesome/scss/_animated.scss:
--------------------------------------------------------------------------------
1 | // Spinning Icons
2 | // --------------------------
3 |
4 | .#{$fa-css-prefix}-spin {
5 | -webkit-animation: fa-spin 2s infinite linear;
6 | animation: fa-spin 2s infinite linear;
7 | }
8 |
9 | .#{$fa-css-prefix}-pulse {
10 | -webkit-animation: fa-spin 1s infinite steps(8);
11 | animation: fa-spin 1s infinite steps(8);
12 | }
13 |
14 | @-webkit-keyframes fa-spin {
15 | 0% {
16 | -webkit-transform: rotate(0deg);
17 | transform: rotate(0deg);
18 | }
19 | 100% {
20 | -webkit-transform: rotate(359deg);
21 | transform: rotate(359deg);
22 | }
23 | }
24 |
25 | @keyframes fa-spin {
26 | 0% {
27 | -webkit-transform: rotate(0deg);
28 | transform: rotate(0deg);
29 | }
30 | 100% {
31 | -webkit-transform: rotate(359deg);
32 | transform: rotate(359deg);
33 | }
34 | }
35 |
--------------------------------------------------------------------------------
/src/kopf/directives/json_tree.js:
--------------------------------------------------------------------------------
1 | (function(kopf, JSONTree) {
2 | 'use strict';
3 | kopf.directive('kopfJsonTree', function($sce) {
4 | var directive = {
5 | restrict: 'E',
6 | template:'',
7 | scope: {
8 | kopfBind: '='
9 | },
10 | link: function(scope, element, attrs, requires) {
11 | scope.$watch('kopfBind', function(value) {
12 | var result;
13 | if (value) {
14 | try {
15 | result = JSONTree.create(value);
16 | } catch (invalidJsonError) {
17 | result = invalidJsonError;
18 | }
19 | } else {
20 | result = '';
21 | }
22 |
23 | scope.result = $sce.trustAsHtml(result);
24 | });
25 | }
26 | };
27 | return directive;
28 | });
29 | })(kopf, JSONTree);
30 |
--------------------------------------------------------------------------------
/_site/partials/directives/pagination.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 | {{page.first | number:0}}-{{page.last | number:0}} of {{page.total | number:0}} {{label || ''}}
16 |
17 |
--------------------------------------------------------------------------------
/tests/jasmine/filters/bytes.tests.js:
--------------------------------------------------------------------------------
1 | describe('filter', function() {
2 |
3 | beforeEach(module('kopf'));
4 |
5 | describe('bytes', function() {
6 |
7 | it('should convert boolean values to unicode checkmark or cross',
8 | inject(function(bytesFilter) {
9 | expect(bytesFilter(1)).toBe('1.00b');
10 | expect(bytesFilter(12)).toBe('12.00b');
11 | expect(bytesFilter(123)).toBe('123.00b');
12 | expect(bytesFilter(1234)).toBe('1.21KB');
13 | expect(bytesFilter(12345)).toBe('12.06KB');
14 | expect(bytesFilter(123456)).toBe('120.56KB');
15 | expect(bytesFilter(1234567)).toBe('1.18MB');
16 | expect(bytesFilter(12345678)).toBe('11.77MB');
17 | expect(bytesFilter(123456789)).toBe('117.74MB');
18 | expect(bytesFilter(1234567890)).toBe('1.15GB');
19 | expect(bytesFilter(1234567890000000)).toBe('1.10PB');
20 | }));
21 | });
22 | });
23 |
--------------------------------------------------------------------------------
/src/kopf/models/request.js:
--------------------------------------------------------------------------------
1 | function Request(path, method, body) {
2 | this.timestamp = getTimeString(new Date());
3 | this.path = path;
4 | this.method = method;
5 | this.body = body;
6 |
7 | this.clear = function() {
8 | this.path = '';
9 | this.method = '';
10 | this.body = '';
11 | };
12 |
13 | this.loadFromJSON = function(json) {
14 | if (isDefined(json.url)) {
15 | var url = json.url.substring(7);
16 | var path = url.substring(url.indexOf('/'));
17 | this.path = path;
18 | } else {
19 | this.path = json.path;
20 | }
21 | this.method = json.method;
22 | this.body = json.body;
23 | this.timestamp = json.timestamp;
24 | return this;
25 | };
26 |
27 | this.equals = function(request) {
28 | return (
29 | this.path === request.path &&
30 | this.method.toUpperCase() === request.method.toUpperCase() &&
31 | this.body === request.body
32 | );
33 | };
34 | }
35 |
--------------------------------------------------------------------------------
/src/kopf/elastic/es_connection.js:
--------------------------------------------------------------------------------
1 | // Expects URL according to /^(https|http):\/\/(\w+):(\w+)@(.*)/i;
2 | // Examples:
3 | // http://localhost:9200
4 | // http://user:password@localhost:9200
5 | // https://localhost:9200
6 | function ESConnection(url, withCredentials) {
7 | if (url.indexOf('http://') !== 0 && url.indexOf('https://') !== 0) {
8 | url = 'http://' + url;
9 | }
10 | var protectedUrl = /^(https|http):\/\/(\w+):(\w+)@(.*)/i;
11 | this.host = 'http://localhost:9200'; // default
12 | this.withCredentials = withCredentials;
13 | if (notEmpty(url)) {
14 | var connectionParts = protectedUrl.exec(url);
15 | if (isDefined(connectionParts)) {
16 | this.host = connectionParts[1] + '://' + connectionParts[4];
17 | this.username = connectionParts[2];
18 | this.password = connectionParts[3];
19 | this.auth = 'Basic ' + window.btoa(this.username + ':' + this.password);
20 | } else {
21 | this.host = url;
22 | }
23 | }
24 |
25 | }
26 |
--------------------------------------------------------------------------------
/_site/modals/confirm_dialog.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
8 |
9 |
{{dialog_service.body}}
10 |
11 |
19 |
20 |
21 |
--------------------------------------------------------------------------------
/tests/jasmine/directives/navbar_section.tests.js:
--------------------------------------------------------------------------------
1 | describe('ngNavbarSection', function() {
2 | var $compile, $rootScope, $location;
3 |
4 | beforeEach(module('kopf'));
5 |
6 | beforeEach(function() {
7 | module('kopf');
8 | module(function($provide) {
9 | $provide.value('ElasticService', {
10 | versionCheck: function() {
11 | return true;
12 | }
13 | });
14 | });
15 | });
16 |
17 | beforeEach(inject(function(_$compile_, _$rootScope_, _$location_) {
18 | $compile = _$compile_;
19 | $rootScope = _$rootScope_;
20 | $location = _$location_;
21 | this.scope = $rootScope.$new();
22 | }));
23 |
24 | it('Creates correct HTML', function() {
25 | var element = $compile('')($rootScope);
26 | $rootScope.$digest();
27 | expect(element.html()).toContain(' snapshot text');
28 | });
29 |
30 | });
31 |
--------------------------------------------------------------------------------
/_site/partials/index_settings/cache.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
--------------------------------------------------------------------------------
/_site/font-awesome/less/mixins.less:
--------------------------------------------------------------------------------
1 | // Mixins
2 | // --------------------------
3 |
4 | .fa-icon() {
5 | display: inline-block;
6 | font: normal normal normal @fa-font-size-base/1 FontAwesome; // shortening font declaration
7 | font-size: inherit; // can't have font-size inherit on line above, so need to override
8 | text-rendering: auto; // optimizelegibility throws things off #1094
9 | -webkit-font-smoothing: antialiased;
10 | -moz-osx-font-smoothing: grayscale;
11 | transform: translate(0, 0); // ensures no half-pixel rendering in firefox
12 |
13 | }
14 |
15 | .fa-icon-rotate(@degrees, @rotation) {
16 | filter: progid:DXImageTransform.Microsoft.BasicImage(rotation=@rotation);
17 | -webkit-transform: rotate(@degrees);
18 | -ms-transform: rotate(@degrees);
19 | transform: rotate(@degrees);
20 | }
21 |
22 | .fa-icon-flip(@horiz, @vert, @rotation) {
23 | filter: progid:DXImageTransform.Microsoft.BasicImage(rotation=@rotation, mirror=1);
24 | -webkit-transform: scale(@horiz, @vert);
25 | -ms-transform: scale(@horiz, @vert);
26 | transform: scale(@horiz, @vert);
27 | }
28 |
--------------------------------------------------------------------------------
/src/kopf/elastic/node_hot_threads.js:
--------------------------------------------------------------------------------
1 | function NodeHotThreads(data) {
2 | var lines = data.split('\n');
3 | this.header = lines[0];
4 | // pre 4859ce5d79a786b58b1cd2fb131614677efd6b91
5 | var BackwardCompatible = lines[1].indexOf('Hot threads at') == -1;
6 | var HeaderLines = BackwardCompatible ? 2 : 3;
7 | this.subHeader = BackwardCompatible ? undefined : lines[1];
8 | this.node = this.header.substring(
9 | this.header.indexOf('[') + 1,
10 | this.header.indexOf(']')
11 | );
12 | var threads = [];
13 | var thread;
14 | if (lines.length > HeaderLines) {
15 | lines.slice(HeaderLines).forEach(function(line) {
16 | var blankLine = line.trim().length === 0;
17 | if (thread) {
18 | if (thread.subHeader) {
19 | thread.stack.push(line);
20 | if (blankLine) {
21 | thread = undefined;
22 | }
23 | } else {
24 | thread.subHeader = line;
25 | }
26 | } else {
27 | thread = new HotThread(line);
28 | threads.push(thread);
29 | }
30 | });
31 | }
32 | this.threads = threads;
33 |
34 | }
35 |
--------------------------------------------------------------------------------
/src/kopf/models/index_template_filter.js:
--------------------------------------------------------------------------------
1 | function IndexTemplateFilter(name, template) {
2 |
3 | this.name = name;
4 | this.template = template;
5 |
6 | this.clone = function() {
7 | return new IndexTemplateFilter(name, template);
8 | };
9 |
10 | this.getSorting = function() {
11 | return function(a, b) {
12 | return a.name.localeCompare(b.name);
13 | };
14 | };
15 |
16 | this.equals = function(other) {
17 | return (other !== null &&
18 | this.name === other.name &&
19 | this.template === other.template);
20 | };
21 |
22 | this.isBlank = function() {
23 | return !notEmpty(this.name) && !notEmpty(this.template);
24 | };
25 |
26 | this.matches = function(template) {
27 | if (this.isBlank()) {
28 | return true;
29 | } else {
30 | var matches = true;
31 | if (notEmpty(this.name)) {
32 | matches = template.name.indexOf(this.name) != -1;
33 | }
34 | if (matches && notEmpty(this.template)) {
35 | matches = template.body.template.indexOf(this.template) != -1;
36 | }
37 | return matches;
38 | }
39 | };
40 |
41 | }
42 |
--------------------------------------------------------------------------------
/_site/font-awesome/scss/_mixins.scss:
--------------------------------------------------------------------------------
1 | // Mixins
2 | // --------------------------
3 |
4 | @mixin fa-icon() {
5 | display: inline-block;
6 | font: normal normal normal #{$fa-font-size-base}/1 FontAwesome; // shortening font declaration
7 | font-size: inherit; // can't have font-size inherit on line above, so need to override
8 | text-rendering: auto; // optimizelegibility throws things off #1094
9 | -webkit-font-smoothing: antialiased;
10 | -moz-osx-font-smoothing: grayscale;
11 | transform: translate(0, 0); // ensures no half-pixel rendering in firefox
12 |
13 | }
14 |
15 | @mixin fa-icon-rotate($degrees, $rotation) {
16 | filter: progid:DXImageTransform.Microsoft.BasicImage(rotation=#{$rotation});
17 | -webkit-transform: rotate($degrees);
18 | -ms-transform: rotate($degrees);
19 | transform: rotate($degrees);
20 | }
21 |
22 | @mixin fa-icon-flip($horiz, $vert, $rotation) {
23 | filter: progid:DXImageTransform.Microsoft.BasicImage(rotation=#{$rotation});
24 | -webkit-transform: scale($horiz, $vert);
25 | -ms-transform: scale($horiz, $vert);
26 | transform: scale($horiz, $vert);
27 | }
28 |
--------------------------------------------------------------------------------
/src/kopf/directives/pagination.js:
--------------------------------------------------------------------------------
1 | kopf.directive('ngPagination', ['$document', function($document) {
2 |
3 | return {
4 | scope: {
5 | paginator: '=paginator',
6 | page: '=page',
7 | label: '=label'
8 | },
9 | templateUrl: './partials/directives/pagination.html',
10 | link: function(scope, element, attrs) {
11 | var handler = function(event) {
12 | var $target = $(event.target);
13 | if ($target.is('input, textarea')) {
14 | return;
15 | }
16 | if (event.keyCode == 39 && scope.page.next) {
17 | scope.$apply(function() {
18 | scope.paginator.nextPage();
19 | event.preventDefault();
20 | });
21 | }
22 | if (event.keyCode == 37 && scope.page.previous) {
23 | scope.$apply(function() {
24 | scope.paginator.previousPage();
25 | event.preventDefault();
26 | });
27 | }
28 | };
29 |
30 | $document.bind('keydown', handler);
31 | element.on('$destroy', function() {
32 | $document.unbind('keydown', handler);
33 | });
34 | }
35 | };
36 | }]);
37 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | The MIT License (MIT)
2 |
3 | Copyright (c) 2014 Leonardo Menezes
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/src/kopf/controllers/cat.js:
--------------------------------------------------------------------------------
1 | kopf.controller('CatController', ['$scope', 'ElasticService', 'AlertService',
2 | function($scope, ElasticService, AlertService) {
3 |
4 | $scope.apis = [
5 | 'aliases',
6 | //'allocation',
7 | 'count',
8 | //'fielddata',
9 | //'health',
10 | //'indices',
11 | 'master',
12 | //'nodes',
13 | //'pending_tasks',
14 | 'plugins',
15 | 'recovery',
16 | //'thread_pool',
17 | //'shards',
18 | //'segments'
19 | ];
20 |
21 | $scope.api = '';
22 |
23 | $scope.result = undefined;
24 |
25 | $scope.execute = function() {
26 | if ($scope.api.length > 0) {
27 | ElasticService.executeCatRequest(
28 | $scope.api,
29 | function(result) {
30 | $scope.result = result;
31 | },
32 | function(error) {
33 | AlertService.error('Error while fetching data', error);
34 | $scope.result = undefined;
35 | }
36 | );
37 | } else {
38 | AlertService.error('You must select an API');
39 | }
40 | };
41 | }
42 |
43 | ]);
44 |
--------------------------------------------------------------------------------
/src/kopf/controllers/cluster_settings.js:
--------------------------------------------------------------------------------
1 | kopf.controller('ClusterSettingsController', ['$scope', '$location', '$timeout',
2 | 'AlertService', 'ElasticService',
3 | function($scope, $location, $timeout, AlertService, ElasticService) {
4 |
5 | $scope.initializeController = function() {
6 | $('#cluster_settings_option a').tab('show');
7 | $('#cluster_settings_tabs a:first').tab('show');
8 | $('.setting-info').popover();
9 | $scope.active_settings = 'transient'; // remember last active?
10 | $scope.settings = new ClusterSettings(ElasticService.cluster.settings);
11 | };
12 |
13 | $scope.save = function() {
14 | var settings = JSON.stringify($scope.settings, undefined, '');
15 | ElasticService.updateClusterSettings(settings,
16 | function(response) {
17 | AlertService.success('Cluster settings were successfully updated',
18 | response);
19 | ElasticService.refresh();
20 | },
21 | function(error) {
22 | AlertService.error('Error while updating cluster settings', error);
23 | }
24 | );
25 | };
26 | }
27 | ]);
28 |
--------------------------------------------------------------------------------
/src/kopf/models/alias_filter.js:
--------------------------------------------------------------------------------
1 | function AliasFilter(index, alias) {
2 |
3 | this.index = index;
4 | this.alias = alias;
5 |
6 | this.clone = function() {
7 | return new AliasFilter(this.index, this.alias);
8 | };
9 |
10 | this.getSorting = function() {
11 | return undefined;
12 | };
13 |
14 | this.equals = function(other) {
15 | return (other !== null &&
16 | this.index == other.index &&
17 | this.alias == other.alias);
18 | };
19 |
20 | this.isBlank = function() {
21 | return !notEmpty(this.index) && !notEmpty(this.alias);
22 | };
23 |
24 | this.matches = function(indexAlias) {
25 | if (this.isBlank()) {
26 | return true;
27 | } else {
28 | var matches = true;
29 | if (notEmpty(this.index)) {
30 | matches = indexAlias.index.indexOf(this.index) != -1;
31 | }
32 | if (matches && notEmpty(this.alias)) {
33 | matches = false;
34 | var aliases = indexAlias.aliases;
35 | for (var i = 0; !matches && i < aliases.length; i++) {
36 | var alias = aliases[i];
37 | matches = alias.alias.indexOf(this.alias) != -1;
38 | }
39 | }
40 | return matches;
41 | }
42 | };
43 |
44 | }
45 |
--------------------------------------------------------------------------------
/tests/jasmine/alerts.tests.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | describe('AlertsController', function() {
4 | var scope, createController;
5 |
6 | beforeEach(angular.mock.module('kopf'));
7 |
8 | beforeEach(angular.mock.inject(function($rootScope, $controller, $injector) {
9 | this.scope = $rootScope.$new();
10 | this.AlertService = $injector.get('AlertService');
11 | this.createController = function() {
12 | return $controller('AlertsController', {$scope: this.scope},
13 | this.AlertService);
14 | };
15 | this._controller = this.createController();
16 | }));
17 |
18 | it('init : values are set', function() {
19 | expect(this.scope.alerts).toEqual([]);
20 | });
21 |
22 | it('updates alerts when alert service changes', function() {
23 | expect(this.scope.alerts.length).toEqual(0);
24 | this.AlertService.info("test");
25 | this.scope.$digest();
26 | expect(this.scope.alerts.length).toEqual(1);
27 | });
28 |
29 | it('removes an alert when remove method is called', function() {
30 | spyOn(this.AlertService, 'remove').andReturn(true);
31 | this.scope.remove('hello');
32 | expect(this.AlertService.remove).toHaveBeenCalledWith('hello');
33 | });
34 |
35 | });
36 |
37 |
--------------------------------------------------------------------------------
/src/kopf/elastic/version.js:
--------------------------------------------------------------------------------
1 | function Version(version) {
2 | var checkVersion = new RegExp('(\\d)\\.(\\d)\\.(\\d)\\.*');
3 | var major;
4 | var minor;
5 | var patch;
6 | var value = version;
7 | var valid = false;
8 |
9 | if (checkVersion.test(value)) {
10 | valid = true;
11 | var parts = checkVersion.exec(version);
12 | major = parseInt(parts[1]);
13 | minor = parseInt(parts[2]);
14 | patch = parseInt(parts[3]);
15 | }
16 |
17 | this.isValid = function() {
18 | return valid;
19 | };
20 |
21 | this.getMajor = function() {
22 | return major;
23 | };
24 |
25 | this.getMinor = function() {
26 | return minor;
27 | };
28 |
29 | this.getPatch = function() {
30 | return patch;
31 | };
32 |
33 | this.getValue = function() {
34 | return value;
35 | };
36 |
37 | this.isGreater = function(other) {
38 | var higherMajor = major > other.getMajor();
39 | var higherMinor = major == other.getMajor() && minor > other.getMinor();
40 | var higherPatch = (
41 | major == other.getMajor() &&
42 | minor == other.getMinor() &&
43 | patch >= other.getPatch()
44 | );
45 | return (higherMajor || higherMinor || higherPatch);
46 | };
47 |
48 | }
49 |
--------------------------------------------------------------------------------
/src/kopf/services/host_history.js:
--------------------------------------------------------------------------------
1 | kopf.factory('HostHistoryService', function() {
2 |
3 | this.getHostHistory = function() {
4 | var history = localStorage.getItem('kopfHostHistory');
5 | history = isDefined(history) ? history : '[]';
6 | return JSON.parse(history);
7 | };
8 |
9 | this.addToHistory = function(connection) {
10 | var host = connection.host.toLowerCase();
11 | var username = connection.username;
12 | var password = connection.password;
13 | if (username && password) {
14 | host = host.replace(/^(https|http):\/\//gi, function addAuth(prefix) {
15 | return prefix + username + ':' + password + '@';
16 | });
17 | }
18 | var entry = {host: host};
19 | var history = this.getHostHistory();
20 | for (var i = 0; i < history.length; i++) {
21 | if (history[i].host === host) {
22 | history.splice(i, 1);
23 | break;
24 | }
25 | }
26 | history.splice(0, 0, entry);
27 | if (history.length > 10) {
28 | history.length = 10;
29 | }
30 | localStorage.setItem('kopfHostHistory', JSON.stringify(history));
31 | };
32 |
33 | this.clearHistory = function() {
34 | localStorage.removeItem('kopfHostHistory');
35 | };
36 |
37 | return this;
38 |
39 | });
40 |
--------------------------------------------------------------------------------
/src/kopf/elastic/cluster_settings.js:
--------------------------------------------------------------------------------
1 | function ClusterSettings(settings) {
2 | // FIXME: 0.90/1.0 check
3 | var valid = [
4 | // cluster
5 | 'cluster.blocks.read_only',
6 | 'indices.ttl.interval',
7 | 'indices.cache.filter.size',
8 | 'discovery.zen.minimum_master_nodes',
9 | // recovery
10 | 'indices.recovery.concurrent_streams',
11 | 'indices.recovery.compress',
12 | 'indices.recovery.file_chunk_size',
13 | 'indices.recovery.translog_ops',
14 | 'indices.recovery.translog_size',
15 | 'indices.recovery.max_bytes_per_sec',
16 | // routing
17 | 'cluster.routing.allocation.node_initial_primaries_recoveries',
18 | 'cluster.routing.allocation.cluster_concurrent_rebalance',
19 | 'cluster.routing.allocation.awareness.attributes',
20 | 'cluster.routing.allocation.node_concurrent_recoveries',
21 | 'cluster.routing.allocation.disable_allocation',
22 | 'cluster.routing.allocation.disable_replica_allocation'
23 | ];
24 | var instance = this;
25 | ['persistent', 'transient'].forEach(function(type) {
26 | instance[type] = {};
27 | var currentSettings = settings[type];
28 | valid.forEach(function(setting) {
29 | instance[type][setting] = getProperty(currentSettings, setting);
30 | });
31 | });
32 | }
33 |
--------------------------------------------------------------------------------
/src/kopf/directives/sort_table.js:
--------------------------------------------------------------------------------
1 | kopf.directive('ngSortBy',
2 | function() {
3 |
4 | function updateSortingIcon(scope, elem, attrs) {
5 | var sorts = scope.sortBy === attrs.property;
6 | var sortIcon = elem.find('i');
7 | sortIcon.removeClass('fa-sort-asc fa-sort-desc');
8 | if (sorts) {
9 | if (scope.reverse) {
10 | sortIcon.addClass('fa-sort-desc');
11 | } else {
12 | sortIcon.addClass('fa-sort-asc');
13 | }
14 | }
15 | }
16 |
17 | function link(scope, elem, attrs) {
18 | scope.$watch(
19 | function() {
20 | return scope.sortBy;
21 | },
22 | function() {
23 | updateSortingIcon(scope, elem, attrs);
24 | });
25 |
26 | scope.$watch(
27 | function() {
28 | return scope.reverse;
29 | },
30 | function() {
31 | updateSortingIcon(scope, elem, attrs);
32 | }
33 | );
34 | }
35 |
36 | return {
37 | link: link,
38 | template: function(elem, attrs) {
39 | return '' + attrs.text +
41 | '';
42 | }
43 | };
44 | }
45 | );
46 |
--------------------------------------------------------------------------------
/src/kopf/controllers/hotthreads.js:
--------------------------------------------------------------------------------
1 | kopf.controller('HotThreadsController', ['$scope', 'ElasticService',
2 | 'AlertService',
3 | function($scope, ElasticService, AlertService) {
4 |
5 | $scope.node = undefined;
6 |
7 | $scope.nodes = [];
8 |
9 | $scope.type = 'cpu';
10 |
11 | $scope.types = ['cpu', 'wait', 'block'];
12 |
13 | $scope.interval = '500ms';
14 |
15 | $scope.threads = 3;
16 |
17 | $scope.ignoreIdleThreads = true;
18 |
19 | $scope.nodesHotThreads = undefined;
20 |
21 | $scope.execute = function() {
22 | ElasticService.getHotThreads($scope.node, $scope.type, $scope.threads,
23 | $scope.interval, $scope.ignoreIdleThreads,
24 | function(result) {
25 | $scope.nodesHotThreads = result;
26 | },
27 | function(error) {
28 | AlertService.error('Error while fetching hot threads', error);
29 | $scope.nodesHotThreads = undefined;
30 | }
31 | );
32 | };
33 |
34 | $scope.$watch(
35 | function() {
36 | return ElasticService.cluster;
37 | },
38 | function(current, previous) {
39 | $scope.nodes = ElasticService.getNodes();
40 | },
41 | true
42 | );
43 |
44 | $scope.initializeController = function() {
45 | $scope.nodes = ElasticService.getNodes();
46 | };
47 |
48 | }
49 |
50 | ]);
51 |
--------------------------------------------------------------------------------
/src/kopf/models/ace_editor.js:
--------------------------------------------------------------------------------
1 | function AceEditor(target) {
2 | // ace editor
3 | ace.config.set('basePath', 'dist/');
4 | this.editor = ace.edit(target);
5 | this.editor.setFontSize('10px');
6 | this.editor.setTheme('ace/theme/kopf');
7 | this.editor.getSession().setMode('ace/mode/json');
8 | this.editor.setOptions({
9 | fontFamily: 'Monaco, Menlo, Consolas, "Courier New", monospace',
10 | fontSize: '12px',
11 | fontWeight: '400'
12 | });
13 |
14 | // validation error
15 | this.error = null;
16 |
17 | // sets value and moves cursor to beggining
18 | this.setValue = function(value) {
19 | this.editor.setValue(value, 1);
20 | this.editor.gotoLine(0, 0, false);
21 | };
22 |
23 | this.getValue = function() {
24 | return this.editor.getValue();
25 | };
26 |
27 | // formats the json content
28 | this.format = function() {
29 | var content = this.editor.getValue();
30 | try {
31 | if (isDefined(content) && content.trim().length > 0) {
32 | this.error = null;
33 | content = JSON.stringify(JSON.parse(content), undefined, 2);
34 | this.editor.setValue(content, 0);
35 | this.editor.gotoLine(0, 0, false);
36 | }
37 | } catch (error) {
38 | this.error = error.toString();
39 | }
40 | return content;
41 | };
42 |
43 | this.hasContent = function() {
44 | return this.editor.getValue().trim().length > 0;
45 | };
46 | }
47 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "elasticsearch-kopf",
3 | "version": "2.0.1",
4 | "description": "kopf - simple web administration tool for ElasticSearch",
5 | "main": "index.html",
6 | "scripts": {
7 | "test": "echo \"Error: no test specified\" && exit 1"
8 | },
9 | "repository": {
10 | "type": "git",
11 | "url": "git://github.com/lmenezes/elasticsearch-kopf.git"
12 | },
13 | "author": "Leonardo Menezes",
14 | "license": "MIT",
15 | "bugs": {
16 | "url": "https://github.com/lmenezes/elasticsearch-kopf/issues"
17 | },
18 | "devDependencies": {
19 | "grunt": "^0.4.5",
20 | "grunt-contrib-clean": "~0.5.0",
21 | "grunt-contrib-concat": "~0.3.0",
22 | "grunt-contrib-connect": "^0.5.0",
23 | "grunt-contrib-copy": "~0.4.1",
24 | "grunt-contrib-jshint": "~0.8.0",
25 | "grunt-contrib-qunit": "~0.4.0",
26 | "grunt-contrib-watch": "~0.5.3",
27 | "grunt-jscs": "^1.0.0",
28 | "grunt-karma": "~0.6.2",
29 | "karma": "~0.10.9",
30 | "karma-chrome-launcher": "~0.1.2",
31 | "karma-coffee-preprocessor": "~0.1.2",
32 | "karma-firefox-launcher": "~0.1.3",
33 | "karma-html2js-preprocessor": "~0.1.0",
34 | "karma-jasmine": "~0.1.5",
35 | "karma-phantomjs-launcher": "~0.1.2",
36 | "karma-requirejs": "~0.2.1",
37 | "karma-script-launcher": "~0.1.0",
38 | "requirejs": "~2.1.10"
39 | },
40 | "directories": {
41 | "test": "tests"
42 | },
43 | "dependencies": {
44 | }
45 | }
46 |
--------------------------------------------------------------------------------
/src/kopf/elastic/broken_cluster.js:
--------------------------------------------------------------------------------
1 | function BrokenCluster(health, state, nodesStats, settings, nodes) {
2 |
3 | this.status = health.status;
4 | this.initializing_shards = health.initializing_shards;
5 | this.active_primary_shards = health.active_primary_shards;
6 | this.active_shards = health.active_shards;
7 | this.relocating_shards = health.relocating_shards;
8 | this.unassigned_shards = health.unassigned_shards;
9 | this.number_of_nodes = health.number_of_nodes;
10 | this.number_of_data_nodes = health.number_of_data_nodes;
11 | this.timed_out = health.timed_out;
12 | this.shards = this.active_shards + this.relocating_shards +
13 | this.unassigned_shards + this.initializing_shards;
14 | this.fetched_at = getTimeString(new Date());
15 |
16 | this.name = state.cluster_name;
17 | this.master_node = state.master_node;
18 |
19 | this.settings = settings;
20 |
21 | var totalSize = 0;
22 |
23 | this.nodes = Object.keys(nodes.nodes).map(function(nodeId) {
24 | var nodeStats = nodesStats.nodes[nodeId];
25 | var nodeInfo = nodes.nodes[nodeId];
26 | var node = new Node(nodeId, nodeStats, nodeInfo);
27 | if (nodeId === state.master_node) {
28 | node.setCurrentMaster();
29 | }
30 | return node;
31 | });
32 |
33 | this.getNodes = function() {
34 | return this.nodes;
35 | };
36 |
37 | this.total_size = readablizeBytes(totalSize);
38 | this.total_size_in_bytes = totalSize;
39 | this.indices = [];
40 | }
41 |
--------------------------------------------------------------------------------
/src/kopf/models/node_filter.js:
--------------------------------------------------------------------------------
1 | function NodeFilter(name, data, master, client, timestamp) {
2 | this.name = name;
3 | this.data = data;
4 | this.master = master;
5 | this.client = client;
6 | this.timestamp = timestamp;
7 |
8 | this.clone = function() {
9 | return new NodeFilter(this.name, this.data, this.master, this.client);
10 | };
11 |
12 | this.getSorting = function() {
13 | return undefined;
14 | };
15 |
16 | this.equals = function(other) {
17 | return (
18 | other !== null &&
19 | this.name == other.name &&
20 | this.data == other.data &&
21 | this.master == other.master &&
22 | this.client == other.client &&
23 | this.timestamp == other.timestamp
24 | );
25 | };
26 |
27 | this.isBlank = function() {
28 | return !notEmpty(this.name) && (this.data && this.master && this.client);
29 | };
30 |
31 | this.matches = function(node) {
32 | if (this.isBlank()) {
33 | return true;
34 | } else {
35 | return this.matchesName(node.name) && this.matchesType(node);
36 | }
37 | };
38 |
39 | this.matchesType = function(node) {
40 | return (
41 | node.data && this.data ||
42 | node.master && this.master ||
43 | node.client && this.client
44 | );
45 | };
46 |
47 | this.matchesName = function(name) {
48 | if (notEmpty(this.name)) {
49 | return name.toLowerCase().indexOf(this.name.toLowerCase()) != -1;
50 | } else {
51 | return true;
52 | }
53 | };
54 |
55 | }
56 |
--------------------------------------------------------------------------------
/tests/jasmine/services/debug.tests.js:
--------------------------------------------------------------------------------
1 | "use strict";
2 |
3 | describe("DebugService", function() {
4 |
5 | var service, filter;
6 |
7 | beforeEach(module("kopf"));
8 |
9 | beforeEach(function() {
10 | module('kopf');
11 |
12 | module(function($provide) {
13 | $provide.value('$filter',
14 | function() {
15 | return function() {
16 | return 'prefix> ';
17 | }
18 | }
19 | );
20 | });
21 | });
22 |
23 | beforeEach(inject(function($injector) {
24 | service = $injector.get('DebugService');
25 | filter = $injector.get('$filter');
26 | filter.date = function() {
27 | return 'xxx';
28 | };
29 | }));
30 |
31 | it("should correctly add a formatted message to the messages list", function() {
32 | service.debug("hello");
33 | expect(service.getMessages().length).toEqual(1);
34 | expect(service.getMessages()).toEqual(['prefix> hello']);
35 | });
36 |
37 | it("should limit message to 100", function() {
38 | for (var i = 0; i < 1003; i++) {
39 | service.debug('message ' + i);
40 | }
41 | expect(service.getMessages().length).toEqual(1000);
42 | expect(service.getMessages()[999]).toEqual('prefix> message 1002');
43 |
44 | });
45 |
46 | it("adding a message should change internal updatedAt value", function() {
47 | expect(service.getUpdatedAt()).toEqual(0);
48 | service.debug('message');
49 | expect(service.getUpdatedAt()).not.toEqual(0);
50 | });
51 |
52 | });
53 |
--------------------------------------------------------------------------------
/_site/partials/cluster_overview/filters.html:
--------------------------------------------------------------------------------
1 |
35 |
40 |
--------------------------------------------------------------------------------
/src/kopf/elastic/percolator.js:
--------------------------------------------------------------------------------
1 | function PercolateQuery(queryInfo) {
2 | this.index = queryInfo._index;
3 | this.id = queryInfo._id;
4 | this.source = queryInfo._source;
5 | this.filter = {};
6 |
7 | this.sourceAsJSON = function() {
8 | try {
9 | return JSON.stringify(this.source, undefined, 2);
10 | } catch (error) {
11 |
12 | }
13 | };
14 |
15 | this.equals = function(other) {
16 | return (other instanceof PercolateQuery &&
17 | this.index == other.index &&
18 | this.id == other.id &&
19 | this.source == other.source);
20 | };
21 | }
22 |
23 | function PercolatorsPage(from, size, total, percolators) {
24 | this.from = from;
25 | this.size = size;
26 | this.total = total;
27 | this.percolators = percolators;
28 |
29 | this.hasNextPage = function() {
30 | return from + size < total;
31 | };
32 |
33 | this.hasPreviousPage = function() {
34 | return from > 0;
35 | };
36 |
37 | this.firstResult = function() {
38 | return total > 0 ? from + 1 : 0;
39 | };
40 |
41 | this.lastResult = function() {
42 | return this.hasNextPage() ? from + size : total;
43 | };
44 |
45 | this.nextOffset = function() {
46 | return this.hasNextPage() ? from + size : from;
47 | };
48 |
49 | this.previousOffset = function() {
50 | return this.hasPreviousPage() ? from - size : from;
51 | };
52 |
53 | this.getPage = function() {
54 | return percolators;
55 | };
56 |
57 | this.total = function() {
58 | return total;
59 | };
60 | }
61 |
--------------------------------------------------------------------------------
/_site/partials/snapshot/azure_repository.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
--------------------------------------------------------------------------------
/_site/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
--------------------------------------------------------------------------------
/_site/partials/aliases/alias_details.html:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/_site/partials/snapshot/fs_repository.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
--------------------------------------------------------------------------------
/src/kopf/css/navbar.css:
--------------------------------------------------------------------------------
1 | .dropdown-menu {
2 | margin-top: 0px;
3 | margin-left: 25px;
4 | }
5 |
6 | .navbar-nav {
7 | margin-top: 0px;
8 | font-size: 13px;
9 | }
10 |
11 | .navbar-collapse {
12 | border-top: 0px;
13 | }
14 |
15 | .navbar-toggle {
16 | padding: 6px 9px;
17 | margin-right: 25px;
18 | height: 22px;
19 | width: 30px;
20 | margin-top: 4px;
21 | margin-bottom: 4px;
22 | }
23 | .navbar-toggle .icon-bar {
24 | width: 10px;
25 | height: 1px;
26 | }
27 | .navbar-fixed-top {
28 | margin-right: 0px !important;
29 | }
30 | .navbar {
31 | height: 30px !important;
32 | min-height: 30px !important;
33 | }
34 | .navbar-logo {
35 | font-size: 38px;
36 | font-variant: small-caps;
37 | line-height: 32px !important;
38 | padding-top: 0px !important;
39 | padding-bottom: 0px !important;
40 | font-weight: 100;
41 | vertical-align: middle;
42 | }
43 | .navbar-logo-action {
44 | font-size: 24px;
45 | font-weight: 100;
46 | vertical-align: middle;
47 | font-variant: normal;
48 | }
49 | .navbar-menu-section {
50 | vertical-align: middle;
51 | padding-top: 4px;
52 | padding-bottom: 4px;
53 | width: 220px !important;
54 | }
55 | .navbar-nav > li {
56 | height: 30px !important;
57 | }
58 | .navbar-nav > li > a {
59 | height: 30px !important;
60 | line-height: 30px;
61 | padding-top: 0px;
62 | padding-bottom:0px;
63 | padding-left: 13px;
64 | padding-right: 13px;
65 | }
66 | .nav-pills > li > a {
67 | font-weight: 400;
68 | font-size: 13px;
69 | }
70 | .navbar-app-settings {
71 | width: auto;
72 | padding-top: 6px;
73 | padding-left: 10px;
74 | padding-right: 10px;
75 | }
76 | .navbar-app-setting {
77 | line-height: 30px;
78 | font-weight: 300;
79 | font-size: 12px;
80 | }
81 |
--------------------------------------------------------------------------------
/src/kopf/controllers/nodes.js:
--------------------------------------------------------------------------------
1 | kopf.controller('NodesController', ['$scope', 'ConfirmDialogService',
2 | 'AlertService', 'ElasticService', 'AppState',
3 | function($scope, ConfirmDialogService, AlertService, ElasticService,
4 | AppState) {
5 |
6 | $scope.sortBy = 'name';
7 | $scope.reverse = false;
8 |
9 | $scope.setSortBy = function(field) {
10 | if ($scope.sortBy === field) {
11 | $scope.reverse = !$scope.reverse;
12 | }
13 | $scope.sortBy = field;
14 | };
15 |
16 | $scope.filter = AppState.getProperty(
17 | 'NodesController',
18 | 'filter',
19 | new NodeFilter('', true, true, true, 0)
20 | );
21 |
22 | $scope.nodes = [];
23 |
24 | $scope.$watch('filter',
25 | function(newValue, oldValue) {
26 | $scope.refresh();
27 | },
28 | true);
29 |
30 | $scope.$watch(
31 | function() {
32 | return ElasticService.cluster;
33 | },
34 | function(newValue, oldValue) {
35 | $scope.refresh();
36 | }
37 | );
38 |
39 | $scope.refresh = function() {
40 | var nodes = ElasticService.getNodes();
41 | $scope.nodes = nodes.filter(function(node) {
42 | return $scope.filter.matches(node);
43 | });
44 | };
45 |
46 | $scope.showNodeStats = function(nodeId) {
47 | ElasticService.getNodeStats(nodeId,
48 | function(nodeStats) {
49 | $scope.displayInfo('stats for ' + nodeStats.name, nodeStats.stats);
50 | },
51 | function(error) {
52 | AlertService.error('Error while loading node stats', error);
53 | }
54 | );
55 | };
56 |
57 | }
58 |
59 | ]);
60 |
--------------------------------------------------------------------------------
/src/kopf/services/explain.js:
--------------------------------------------------------------------------------
1 | kopf.factory('ExplainService', ['$TreeDnDConvert',
2 | function($TreeDnDConvert) {
3 | function containsString(value, searched) {
4 | return value.indexOf(searched) >= 0;
5 | }
6 | this.isExplainPath = function(path) {
7 | return path &&
8 | (containsString(path, '_explain') ||
9 | containsString(path, '?explain') ||
10 | containsString(path, 'explain=true'));
11 | };
12 | /**
13 | * Normalize Get document by id and Document search responses.
14 | * Build explanation tree for TreeDnd directive.
15 | */
16 | this.normalizeExplainResponse = function(response) {
17 | var lHits;
18 | if (response.hits) {
19 | // Explain query
20 | lHits = response.hits.hits;
21 | // Remove hits from main response
22 | delete response.hits.hits;
23 | } else {
24 | // Explain document
25 | lHits = [response];
26 | }
27 | lHits.forEach(function(lHit) {
28 | // Sometimes ._explanation, .sometimes explanation, let's normalize it
29 | if (lHit.explanation) {
30 | var lExplanation = lHit.explanation;
31 | delete response.explanation;
32 | response._explanation = lExplanation;
33 | }
34 | lHit.documentId = lHit._index + '/' + lHit._type + '/' + lHit._id;
35 | if (lHit._explanation) {
36 | if (!lHit._score) {
37 | lHit._score = lHit._explanation.value;
38 | }
39 | lHit.explanationTreeData =
40 | $TreeDnDConvert.tree2tree([lHit._explanation], 'details');
41 | }
42 | });
43 | return lHits;
44 | };
45 |
46 | return this;
47 | }]);
48 |
--------------------------------------------------------------------------------
/src/kopf/util.js:
--------------------------------------------------------------------------------
1 | function readablizeBytes(bytes) {
2 | if (bytes > 0) {
3 | var s = ['b', 'KB', 'MB', 'GB', 'TB', 'PB'];
4 | var e = Math.floor(Math.log(bytes) / Math.log(1024));
5 | return (bytes / Math.pow(1024, e)).toFixed(2) + s[e];
6 | } else {
7 | return 0;
8 | }
9 | }
10 |
11 | // Gets the value of a nested property from an object if it exists.
12 | // Otherwise returns the default_value given.
13 | // Example: get the value of object[a][b][c][d]
14 | // where property_path is [a,b,c,d]
15 | function getProperty(object, propertyPath, defaultValue) {
16 | if (isDefined(object)) {
17 | if (isDefined(object[propertyPath])) {
18 | return object[propertyPath];
19 | }
20 | var pathParts = propertyPath.split('.'); // path as nested properties
21 | for (var i = 0; i < pathParts.length && isDefined(object); i++) {
22 | object = object[pathParts[i]];
23 | }
24 | }
25 | return isDefined(object) ? object : defaultValue;
26 | }
27 |
28 | // Checks if value is both non null and undefined
29 | function isDefined(value) {
30 | return value !== null && typeof value != 'undefined';
31 | }
32 |
33 | // Checks if the String representation of value is a non empty string
34 | // string.trim().length is grater than 0
35 | function notEmpty(value) {
36 | return isDefined(value) && value.toString().trim().length > 0;
37 | }
38 |
39 | function isNumber(value) {
40 | var exp = /\d+/;
41 | return exp.test(value);
42 | }
43 |
44 | // Returns the given date as a String formatted as hh:MM:ss
45 | function getTimeString(date) {
46 | var hh = ('0' + date.getHours()).slice(-2);
47 | var mm = ('0' + date.getMinutes()).slice(-2);
48 | var ss = ('0' + date.getSeconds()).slice(-2);
49 | return hh + ':' + mm + ':' + ss;
50 | }
51 |
--------------------------------------------------------------------------------
/docker/nginx.conf.tpl:
--------------------------------------------------------------------------------
1 | daemon off;
2 | user www-data;
3 | worker_processes 4;
4 | pid /run/nginx.pid;
5 |
6 | events {
7 | worker_connections 1024;
8 | }
9 |
10 | http {
11 | sendfile on;
12 | tcp_nopush on;
13 | tcp_nodelay on;
14 | keepalive_timeout 65;
15 | types_hash_max_size 2048;
16 |
17 | include /etc/nginx/mime.types;
18 | default_type application/octet-stream;
19 |
20 | access_log /dev/stdout;
21 | error_log /dev/stderr;
22 |
23 | upstream es {
24 | {% for server in KOPF_ES_SERVERS.split(",") %}
25 | server {{ server }};
26 | {% endfor %}
27 | }
28 |
29 | {% if KOPF_SSL_CERT is defined %}
30 | server {
31 | listen 80;
32 | server_name {{ KOPF_SERVER_NAME }};
33 | return 301 https://{{ KOPF_SERVER_NAME }}$request_uri;
34 | }
35 | {% endif %}
36 |
37 | server {
38 | {% if KOPF_SSL_CERT is defined %}
39 | listen 443 ssl;
40 |
41 | ssl_protocols TLSv1 TLSv1.1 TLSv1.2;
42 | ssl_certificate {{ KOPF_SSL_CERT }};
43 | ssl_certificate_key {{ KOPF_SSL_KEY }};
44 | {% else %}
45 | listen 80;
46 | {% endif %}
47 |
48 | server_name {{ KOPF_SERVER_NAME }};
49 |
50 | satisfy any;
51 |
52 | {% if KOPF_BASIC_AUTH_LOGIN is defined %}
53 | auth_basic "Access restricted";
54 | auth_basic_user_file /etc/nginx/kopf.htpasswd;
55 | {% endif %}
56 |
57 | {% if KOPF_NGINX_INCLUDE_FILE is defined %}
58 | include {{ KOPF_NGINX_INCLUDE_FILE }};
59 | {% endif %}
60 |
61 | # suppress passing basic auth to upstreams
62 | proxy_set_header Authorization "";
63 |
64 | # everybody loves caching bugs after upgrade
65 | expires -1;
66 |
67 | location / {
68 | root /kopf/_site;
69 | }
70 |
71 | location /es/ {
72 | rewrite ^/es/(.*)$ /$1 break;
73 | proxy_pass http://es;
74 | }
75 | }
76 | }
77 |
--------------------------------------------------------------------------------
/src/lib/jsontree/jsontree.min.js:
--------------------------------------------------------------------------------
1 | /*! json-tree - v0.2.0 - 2015-06-23 */
2 | var JSONTree=function(){var a={"&":"&","<":"<",">":">",'"':""","'":"'","/":"/"},b=0,c=0;this.create=function(a,b){return c+=1,p(f(a,0,!1),{"class":"jstValue"})};var d=function(b){return b.replace(/[&<>'"]/g,function(b){return a[b]})},e=function(){return c+"_"+b++},f=function(a,b,c){if(null===a)return l(c?b:0);var d=typeof a;switch(d){case"boolean":return k(a,c?b:0);case"number":return j(a,c?b:0);case"string":return i(a,c?b:0);default:return a instanceof Array?h(a,b,c):g(a,b,c)}},g=function(a,b,c){var d=e(),f=Object.keys(a).map(function(c){return m(c,a[c],b+1,!0)}).join(o()),g=[r("{",c?b:0,d),p(f,{id:d}),s("}",b)].join("\n");return p(g,{})},h=function(a,b,c){var d=e(),g=a.map(function(a){return f(a,b+1,!0)}).join(o()),h=[r("[",c?b:0,d),p(g,{id:d}),s("]",b)].join("\n");return h},i=function(a,b){return p(t(n(d(a)),b),{"class":"jstStr"})},j=function(a,b){return p(t(a,b),{"class":"jstNum"})},k=function(a,b){return p(t(a,b),{"class":"jstBool"})},l=function(a){return p(t("null",a),{"class":"jstNull"})},m=function(a,b,c){var e=t(n(d(a))+": ",c),g=p(f(b,c,!1),{});return p(e+g,{"class":"jstProperty"})},n=function(a){return'"'+a+'"'},o=function(){return p(",\n",{"class":"jstComma"})},p=function(a,b){return q("span",b,a)},q=function(a,b,c){return"<"+a+Object.keys(b).map(function(a){return" "+a+'="'+b[a]+'"'}).join("")+">"+c+""+a+">"},r=function(a,b,c){return p(t(a,b),{"class":"jstBracket"})+p("",{"class":"jstFold",onclick:"JSONTree.toggle('"+c+"')"})};this.toggle=function(a){var b=document.getElementById(a),c=b.parentNode,d=b.previousElementSibling;""===b.className?(b.className="jstHiddenBlock",c.className="jstFolded",d.className="jstExpand"):(b.className="",c.className="",d.className="jstFold")};var s=function(a,b){return p(t(a,b),{})},t=function(a,b){return Array(2*b+1).join(" ")+a};return this}();
--------------------------------------------------------------------------------
/tests/models/url_autocomplete.js:
--------------------------------------------------------------------------------
1 |
2 | var mappings = {
3 | getIndices: function() {
4 | return ['foo', 'bar', 'qux'];
5 | },
6 | getTypes: function(index) {
7 | return {
8 | foo: ['foobar'],
9 | bar: ['baz', 'qux'],
10 | qux: ['bar']
11 | }[index];
12 | }
13 | };
14 |
15 | test("Autocomplete first part of path", function() {
16 | var suggest = new URLAutocomplete(mappings);
17 | deepEqual(suggest.getAlternatives('f'), [ "_msearch", "_search", "_suggest", "bar", "foo", "qux" ], 'First part suggest');
18 | });
19 |
20 | test("Autocomplete second part of path", function() {
21 | var suggest = new URLAutocomplete(mappings);
22 | deepEqual(suggest.getAlternatives('foo/'), [ "foo/_msearch", "foo/_search", "foo/_suggest", "foo/foobar" ], 'Second prt suggest foo/');
23 | deepEqual(suggest.getAlternatives('foo/f'), [ "foo/_msearch", "foo/_search", "foo/_suggest", "foo/foobar" ], 'Second part suggest foo/f');
24 | deepEqual(suggest.getAlternatives('_search/'), [ "_search/exists", "_search/template" ], 'Second part suggest _search/');
25 | });
26 |
27 | test("Autocomplete third part of path", function() {
28 | var suggest = new URLAutocomplete(mappings);
29 | deepEqual(suggest.getAlternatives('foo/foobar/'), [ "foo/foobar/_msearch", "foo/foobar/_search" ], 'Third part suggest foo/foobar/');
30 | deepEqual(suggest.getAlternatives('foo/_search/'), [ "foo/_search/exists", "foo/_search/template" ], 'Third part suggest foo/_search/');
31 | });
32 |
33 | test("Autocomplete fourth part of path", function() {
34 | var suggest = new URLAutocomplete(mappings);
35 | deepEqual(suggest.getAlternatives('bar/baz/_search/'), [ "bar/baz/_search/exists", "bar/baz/_search/template" ], 'Third part suggest bar/baz/_search');
36 | deepEqual(suggest.getAlternatives('qux/bar/_msearch/'), [ "qux/bar/_msearch/template" ], 'Third part suggest qux/bar/_msearch');
37 | });
--------------------------------------------------------------------------------
/src/kopf/controllers/index_settings.js:
--------------------------------------------------------------------------------
1 | kopf.controller('IndexSettingsController', ['$scope', '$location',
2 | 'AlertService', 'ElasticService',
3 | function($scope, $location, AlertService, ElasticService) {
4 |
5 | $scope.index = null;
6 | $scope.settings = null;
7 | $scope.editable_settings = null;
8 |
9 | $scope.save = function() {
10 | var index = $scope.index;
11 | var settings = $scope.settings;
12 | var newSettings = {};
13 | var editableSettings = $scope.editable_settings;
14 | // TODO: could move that to editable_index_settings model
15 | editableSettings.valid_settings.forEach(function(setting) {
16 | if (notEmpty(editableSettings[setting])) {
17 | newSettings[setting] = editableSettings[setting];
18 | }
19 | });
20 | ElasticService.updateIndexSettings(index,
21 | JSON.stringify(newSettings, undefined, ''),
22 | function(response) {
23 | AlertService.success('Index settings were successfully updated',
24 | response);
25 | ElasticService.refresh();
26 | },
27 | function(error) {
28 | AlertService.error('Error while updating index settings', error);
29 | }
30 | );
31 | };
32 |
33 | $scope.initializeController = function() {
34 | var index = $location.search().index;
35 | ElasticService.getIndexMetadata(index,
36 | function(metadata) {
37 | $scope.index = index;
38 | $scope.settings = metadata.settings;
39 | $scope.editable_settings = new EditableIndexSettings(
40 | $scope.settings
41 | );
42 | },
43 | function(error) {
44 | AlertService.error('Error while loading index settings for [' +
45 | index + ']',
46 | error);
47 | }
48 | );
49 | };
50 |
51 | }
52 | ]);
53 |
--------------------------------------------------------------------------------
/_site/partials/cluster_overview/index_body.html:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/_site/partials/cluster_settings.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
10 |
11 |
12 |
13 |
43 |
44 |
45 |
46 |
--------------------------------------------------------------------------------
/src/kopf/controllers/benchmark.js:
--------------------------------------------------------------------------------
1 | kopf.controller('BenchmarkController', ['$scope', '$location', '$timeout',
2 | 'AlertService', 'ElasticService',
3 | function($scope, $location, $timeout, AlertService, ElasticService) {
4 |
5 | $scope.bench = new Benchmark();
6 | $scope.competitor = new Competitor();
7 | $scope.indices = [];
8 | $scope.types = [];
9 |
10 | $scope.initializeController = function() {
11 | $scope.indices = ElasticService.getIndices();
12 | };
13 |
14 | $scope.addCompetitor = function() {
15 | if (notEmpty($scope.competitor.name)) {
16 | this.bench.addCompetitor($scope.competitor);
17 | $scope.competitor = new Competitor();
18 | } else {
19 | AlertService.error('Competitor needs a name');
20 | }
21 | };
22 |
23 | $scope.removeCompetitor = function(index) {
24 | $scope.bench.competitors.splice(index, 1);
25 | };
26 |
27 | $scope.editCompetitor = function(index) {
28 | var edit = $scope.bench.competitors.splice(index, 1);
29 | $scope.competitor = edit[0];
30 | };
31 |
32 | $scope.runBenchmark = function() {
33 | $('#benchmark-result').html('');
34 | try {
35 | var json = $scope.bench.toJson();
36 | ElasticService.executeBenchmark(json,
37 | function(response) {
38 | $scope.result = JSONTree.create(response);
39 | $('#benchmark-result').html($scope.result);
40 | },
41 | function(error, status) {
42 | if (status == 503) {
43 | AlertService.info('No available nodes for benchmarking. ' +
44 | 'At least one node must be started with ' +
45 | '\'--node.bench true\' option.');
46 | } else {
47 | AlertService.error(error.error);
48 | }
49 | }
50 | );
51 | } catch (error) {
52 | AlertService.error(error);
53 | }
54 | };
55 |
56 | }
57 | ]);
58 |
--------------------------------------------------------------------------------
/src/kopf/elastic/node.js:
--------------------------------------------------------------------------------
1 | function Node(nodeId, nodeStats, nodeInfo) {
2 | this.id = nodeId;
3 | this.name = nodeInfo.name;
4 | this.elasticVersion = nodeInfo.version;
5 | this.jvmVersion = nodeInfo.jvm.version;
6 | this.availableProcessors = nodeInfo.os.available_processors;
7 | this.transportAddress = nodeInfo.transport_address;
8 | this.host = nodeInfo.host;
9 |
10 | var attributes = getProperty(nodeInfo, 'attributes', {});
11 | var master = attributes.master === 'false' ? false : true;
12 | var data = attributes.data === 'false' ? false : true;
13 | var client = attributes.client === 'true' ? true : false;
14 | this.master = master && !client;
15 | this.data = data && !client;
16 | this.client = client || !master && !data;
17 | this.current_master = false;
18 |
19 | this.stats = nodeStats;
20 | this.uptime = nodeStats.jvm.uptime_in_millis;
21 |
22 | this.heap_used = readablizeBytes(getProperty(this.stats,
23 | 'jvm.mem.heap_used_in_bytes'));
24 |
25 | this.heap_committed = readablizeBytes(getProperty(this.stats,
26 | 'jvm.mem.heap_committed_in_bytes'));
27 |
28 | this.heap_used_percent = getProperty(this.stats, 'jvm.mem.heap_used_percent');
29 |
30 | this.heap_max = readablizeBytes(getProperty(this.stats,
31 | 'jvm.mem.heap_max_in_bytes'));
32 |
33 | this.disk_total_in_bytes = getProperty(this.stats, 'fs.total.total_in_bytes');
34 | this.disk_free_in_bytes = getProperty(this.stats, 'fs.total.free_in_bytes');
35 | var diskUsedInBytes = (this.disk_total_in_bytes - this.disk_free_in_bytes);
36 | var usedRatio = (diskUsedInBytes / this.disk_total_in_bytes);
37 | this.disk_used_percent = Math.round(100 * usedRatio);
38 |
39 | this.cpu = getProperty(this.stats, 'process.cpu.percent');
40 |
41 | this.load_average = getProperty(this.stats, 'os.load_average');
42 |
43 | this.setCurrentMaster = function() {
44 | this.current_master = true;
45 | };
46 |
47 | this.equals = function(node) {
48 | return node.id === this.id;
49 | };
50 |
51 | }
52 |
--------------------------------------------------------------------------------
/src/kopf/elastic/index.js:
--------------------------------------------------------------------------------
1 | function Index(indexName, clusterState, indexStats, aliases) {
2 | this.name = indexName;
3 | this.shards = null;
4 | this.metadata = {};
5 | this.state = 'close';
6 | this.num_of_shards = 0;
7 | this.num_of_replicas = 0;
8 | this.aliases = [];
9 | if (isDefined(aliases)) {
10 | var indexAliases = aliases.aliases;
11 | if (isDefined(indexAliases)) {
12 | this.aliases = Object.keys(aliases.aliases);
13 | }
14 | }
15 |
16 | if (isDefined(clusterState)) {
17 | var routing = getProperty(clusterState, 'routing_table.indices');
18 | this.state = 'open';
19 | if (isDefined(routing)) {
20 | var shards = Object.keys(routing[indexName].shards);
21 | this.num_of_shards = shards.length;
22 | var shardMap = routing[indexName].shards;
23 | this.num_of_replicas = shardMap[0].length - 1;
24 | }
25 | }
26 | this.num_docs = getProperty(indexStats, 'primaries.docs.count', 0);
27 | this.deleted_docs = getProperty(indexStats, 'primaries.docs.deleted', 0);
28 | this.size_in_bytes = getProperty(indexStats,
29 | 'primaries.store.size_in_bytes', 0);
30 | this.total_size_in_bytes = getProperty(indexStats,
31 | 'total.store.size_in_bytes', 0);
32 |
33 | this.unassigned = [];
34 | this.unhealthy = false;
35 |
36 | if (isDefined(clusterState) && isDefined(clusterState.routing_table)) {
37 | var instance = this;
38 | var shardsMap = clusterState.routing_table.indices[this.name].shards;
39 | Object.keys(shardsMap).forEach(function(shardNum) {
40 | shardsMap[shardNum].forEach(function(shard) {
41 | if (shard.state != 'STARTED') {
42 | instance.unhealthy = true;
43 | }
44 | });
45 | });
46 | }
47 |
48 | this.special = this.name.indexOf('.') === 0 || this.name.indexOf('_') === 0;
49 |
50 | this.equals = function(index) {
51 | return index !== null && index.name == this.name;
52 | };
53 |
54 | this.closed = this.state === 'close';
55 |
56 | this.open = this.state === 'open';
57 |
58 | }
59 |
--------------------------------------------------------------------------------
/_site/partials/create_index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
33 |
34 |
35 |
36 |
37 |
43 |
44 |
45 |
46 |
47 | back
48 |
49 |
50 |
51 |
52 |
53 |
--------------------------------------------------------------------------------
/_site/partials/index_settings.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
settings for {{index}}
6 |
7 |
8 |
9 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 | back
34 |
35 |
36 |
37 |
38 |
39 |
--------------------------------------------------------------------------------
/src/kopf/elastic/editable_index_settings.js:
--------------------------------------------------------------------------------
1 | function EditableIndexSettings(settings) {
2 | // FIXME: 0.90/1.0 check
3 | this.valid_settings = [
4 | // blocks
5 | 'index.blocks.read_only',
6 | 'index.blocks.read',
7 | 'index.blocks.write',
8 | 'index.blocks.metadata',
9 | // cache
10 | 'index.cache.filter.max_size',
11 | 'index.cache.filter.expire',
12 | // index
13 | 'index.number_of_replicas',
14 | 'index.index_concurrency',
15 | 'index.warmer.enabled',
16 | 'index.refresh_interval',
17 | 'index.term_index_divisor',
18 | 'index.ttl.disable_purge',
19 | 'index.fail_on_merge_failure',
20 | 'index.gc_deletes',
21 | 'index.codec',
22 | 'index.compound_on_flush',
23 | 'index.term_index_interval',
24 | 'index.auto_expand_replicas',
25 | 'index.recovery.initial_shards',
26 | 'index.compound_format',
27 | // routing
28 | 'index.routing.allocation.disable_allocation',
29 | 'index.routing.allocation.disable_new_allocation',
30 | 'index.routing.allocation.disable_replica_allocation',
31 | 'index.routing.allocation.total_shards_per_node',
32 | // slowlog
33 | 'index.search.slowlog.threshold.query.warn',
34 | 'index.search.slowlog.threshold.query.info',
35 | 'index.search.slowlog.threshold.query.debug',
36 | 'index.search.slowlog.threshold.query.trace',
37 | 'index.search.slowlog.threshold.fetch.warn',
38 | 'index.search.slowlog.threshold.fetch.info',
39 | 'index.search.slowlog.threshold.fetch.debug',
40 | 'index.search.slowlog.threshold.fetch.trace',
41 | 'index.indexing.slowlog.threshold.index.warn',
42 | 'index.indexing.slowlog.threshold.index.info',
43 | 'index.indexing.slowlog.threshold.index.debug',
44 | 'index.indexing.slowlog.threshold.index.trace',
45 | // translog
46 | 'index.translog.flush_threshold_ops',
47 | 'index.translog.flush_threshold_size',
48 | 'index.translog.flush_threshold_period',
49 | 'index.translog.disable_flush',
50 | 'index.translog.fs.type'
51 | ];
52 | var instance = this;
53 | this.valid_settings.forEach(function(setting) {
54 | instance[setting] = getProperty(settings, setting);
55 | });
56 | }
57 |
--------------------------------------------------------------------------------
/_site/dist/theme-kopf.js:
--------------------------------------------------------------------------------
1 | define("ace/theme/kopf",["require","exports","module","ace/lib/dom"],function(e,t,n){t.isDark=!1,t.cssClass="ace-kopf",t.cssText='/* CSS slightly modified from github theme. */.ace-kopf .ace_gutter {background: #e8e8e8;color: #AAA;}.ace-kopf {background: #fff;color: #666;}.ace-kopf .ace_keyword {font-weight: bold;}.ace-kopf .ace_string {color: #2DB669;}.ace-kopf .ace_variable.ace_class {color: teal;}.ace-kopf .ace_constant.ace_numeric {color: #B627B6;}.ace-kopf .ace_constant.ace_buildin {color: #0086B3;}.ace-kopf .ace_support.ace_function {color: #0086B3;}.ace-kopf .ace_comment {color: #998;font-style: italic;}.ace-kopf .ace_variable.ace_language {color: #0086B3;}.ace-kopf .ace_paren {font-weight: bold;}.ace-kopf .ace_boolean {font-weight: bold; color: #2525CC;}.ace-kopf .ace_string.ace_regexp {color: #009926;font-weight: normal;}.ace-kopf .ace_variable.ace_instance {color: teal;}.ace-kopf .ace_constant.ace_language {font-weight: bold;}.ace-kopf .ace_cursor {border-left: 2px solid black;}.ace-kopf .ace_overwrite-cursors .ace_cursor {border-left: 0px;border-bottom: 1px solid black;}.ace-kopf .ace_marker-layer .ace_active-line {background: rgb(255, 255, 204);}.ace-kopf .ace_marker-layer .ace_selection {background: rgb(181, 213, 255);}.ace-kopf.ace_multiselect .ace_selection.ace_start {box-shadow: 0 0 3px 0px white;border-radius: 2px;}/* bold keywords cause cursor issues for some fonts *//* this disables bold style for editor and keeps for static highlighter */.ace-kopf.ace_nobold .ace_line > span {font-weight: normal !important;}.ace-kopf .ace_marker-layer .ace_step {background: rgb(252, 255, 0);}.ace-kopf .ace_marker-layer .ace_stack {background: rgb(164, 229, 101);}.ace-kopf .ace_marker-layer .ace_bracket {margin: -1px 0 0 -1px;border: 1px solid rgb(192, 192, 192);}.ace-kopf .ace_gutter-active-line {background-color : rgba(0, 0, 0, 0.07);}.ace-kopf .ace_marker-layer .ace_selected-word {background: rgb(250, 250, 255);border: 1px solid rgb(200, 200, 250);}.ace-kopf .ace_print-margin {width: 1px;background: #e8e8e8;}.ace-kopf .ace_indent-guide {background: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAACCAYAAACZgbYnAAAAE0lEQVQImWP4////f4bLly//BwAmVgd1/w11/gAAAABJRU5ErkJggg==") right repeat-y;}';var r=e("../lib/dom");r.importCssString(t.cssText,t.cssClass)})
--------------------------------------------------------------------------------
/src/kopf/theme-kopf.js:
--------------------------------------------------------------------------------
1 | define("ace/theme/kopf",["require","exports","module","ace/lib/dom"],function(e,t,n){t.isDark=!1,t.cssClass="ace-kopf",t.cssText='/* CSS slightly modified from github theme. */.ace-kopf .ace_gutter {background: #e8e8e8;color: #AAA;}.ace-kopf {background: #fff;color: #666;}.ace-kopf .ace_keyword {font-weight: bold;}.ace-kopf .ace_string {color: #2DB669;}.ace-kopf .ace_variable.ace_class {color: teal;}.ace-kopf .ace_constant.ace_numeric {color: #B627B6;}.ace-kopf .ace_constant.ace_buildin {color: #0086B3;}.ace-kopf .ace_support.ace_function {color: #0086B3;}.ace-kopf .ace_comment {color: #998;font-style: italic;}.ace-kopf .ace_variable.ace_language {color: #0086B3;}.ace-kopf .ace_paren {font-weight: bold;}.ace-kopf .ace_boolean {font-weight: bold; color: #2525CC;}.ace-kopf .ace_string.ace_regexp {color: #009926;font-weight: normal;}.ace-kopf .ace_variable.ace_instance {color: teal;}.ace-kopf .ace_constant.ace_language {font-weight: bold;}.ace-kopf .ace_cursor {border-left: 2px solid black;}.ace-kopf .ace_overwrite-cursors .ace_cursor {border-left: 0px;border-bottom: 1px solid black;}.ace-kopf .ace_marker-layer .ace_active-line {background: rgb(255, 255, 204);}.ace-kopf .ace_marker-layer .ace_selection {background: rgb(181, 213, 255);}.ace-kopf.ace_multiselect .ace_selection.ace_start {box-shadow: 0 0 3px 0px white;border-radius: 2px;}/* bold keywords cause cursor issues for some fonts *//* this disables bold style for editor and keeps for static highlighter */.ace-kopf.ace_nobold .ace_line > span {font-weight: normal !important;}.ace-kopf .ace_marker-layer .ace_step {background: rgb(252, 255, 0);}.ace-kopf .ace_marker-layer .ace_stack {background: rgb(164, 229, 101);}.ace-kopf .ace_marker-layer .ace_bracket {margin: -1px 0 0 -1px;border: 1px solid rgb(192, 192, 192);}.ace-kopf .ace_gutter-active-line {background-color : rgba(0, 0, 0, 0.07);}.ace-kopf .ace_marker-layer .ace_selected-word {background: rgb(250, 250, 255);border: 1px solid rgb(200, 200, 250);}.ace-kopf .ace_print-margin {width: 1px;background: #e8e8e8;}.ace-kopf .ace_indent-guide {background: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAACCAYAAACZgbYnAAAAE0lEQVQImWP4////f4bLly//BwAmVgd1/w11/gAAAABJRU5ErkJggg==") right repeat-y;}';var r=e("../lib/dom");r.importCssString(t.cssText,t.cssClass)})
--------------------------------------------------------------------------------
/docker/README.md:
--------------------------------------------------------------------------------
1 | # Kopf in docker
2 |
3 | Tagged docker images for kopf, `lmenezes/elasticsearch-kopf` on docker hub.
4 |
5 | ## Usage
6 |
7 | Use `docker run` as you always do. You need to publish port `80`
8 | (and `443` if you use ssl) in order to have access to kopf.
9 |
10 | Container should have access to elasticsearch. You don't
11 | need to expose elasticsearch to end users of kopf.
12 |
13 | It is strongly recommended to use https and basic auth
14 | if you don't want to get hacked.
15 |
16 | ### Env variables.
17 |
18 | * `KOPF_SERVER_NAME` server name for your grafana, for example `kopf.example.com`
19 | * `KOPF_ES_SERVERS` elasticsearch servers in `host:port[,host:port]` format
20 | * `KOPF_ES_ROOT_PATH` elasticsearch root path
21 | * `KOPF_SSL_CERT` path to ssl `.crt` file, enables http-to-https redirect, should be bind-mounted
22 | * `KOPF_SSL_KEY` path to ssl `.key` file, should be bind-mounted
23 | * `KOPF_BASIC_AUTH_LOGIN` basic auth login, if needed
24 | * `KOPF_BASIC_AUTH_PASSWORD` hashed basic auth password, if needed
25 | * `KOPF_NGINX_INCLUDE_FILE` file to include into main server of nginx (place allowed ips here)
26 | * `KOPF_WITH_CREDENTIALS` set the external setting with_credentials. Default: false
27 | * `KOPF_THEME` set the theme in external settings. Default: dark
28 | * `KOPF_REFRESH_RATE` set the external setting refresh_rate. Default: 5000
29 |
30 | ### Example
31 |
32 | #### pure docker run
33 |
34 | Running kopf with elasticsearch on `es.dev:9200`,
35 | exposing it on `kopf.dev` with ip address `10.10.10.10`:
36 |
37 | ```
38 | docker run -d -p 10.10.10.10:80:80 -e KOPF_SERVER_NAME=grafana.dev \
39 | -e KOPF_ES_SERVERS=es.dev:9200 --name kopf lmenezes/elasticsearch-kopf
40 | ```
41 | #### fig
42 |
43 | An easy way to orchestrate a local docker run is fig
44 | Install fig by fireing up ```pip install fig```.
45 | After create a fig file and off you go.
46 | ```
47 | $ cat << EOF > fig.yml
48 | kopf:
49 | image: lmenezes/elasticsearch-kopf
50 | ports:
51 | - 8080:80
52 | environment:
53 | - KOPF_SERVER_NAME=dockerhost
54 | - KOPF_ES_SERVERS=172.17.42.1:9200
55 | EOF
56 | $ fig up -d
57 | Creating docker_kopf_1...
58 | $
59 | ```
60 | This docker container will connect to an ES instance running on the DOCKER_HOST, which exposes 9200.
61 |
--------------------------------------------------------------------------------
/src/kopf/elastic/cluster_changes.js:
--------------------------------------------------------------------------------
1 | function ClusterChanges() {
2 |
3 | this.nodeJoins = null;
4 | this.nodeLeaves = null;
5 | this.indicesCreated = null;
6 | this.indicesDeleted = null;
7 |
8 | this.docDelta = 0;
9 | this.dataDelta = 0;
10 |
11 | this.setDocDelta = function(delta) {
12 | this.docDelta = delta;
13 | };
14 |
15 | this.getDocDelta = function() {
16 | return this.docDelta;
17 | };
18 |
19 | this.absDocDelta = function() {
20 | return Math.abs(this.docDelta);
21 | };
22 |
23 | this.absDataDelta = function() {
24 | return readablizeBytes(Math.abs(this.dataDelta));
25 | };
26 |
27 | this.getDataDelta = function() {
28 | return this.dataDelta;
29 | };
30 |
31 | this.setDataDelta = function(delta) {
32 | this.dataDelta = delta;
33 | };
34 |
35 | this.hasChanges = function() {
36 | return (
37 | isDefined(this.nodeJoins) ||
38 | isDefined(this.nodeLeaves) ||
39 | isDefined(this.indicesCreated) ||
40 | isDefined(this.indicesDeleted)
41 | );
42 | };
43 |
44 | this.addJoiningNode = function(node) {
45 | this.changes = true;
46 | if (!isDefined(this.nodeJoins)) {
47 | this.nodeJoins = [];
48 | }
49 | this.nodeJoins.push(node);
50 | };
51 |
52 | this.addLeavingNode = function(node) {
53 | this.changes = true;
54 | if (!isDefined(this.nodeLeaves)) {
55 | this.nodeLeaves = [];
56 | }
57 | this.nodeLeaves.push(node);
58 | };
59 |
60 | this.hasJoins = function() {
61 | return isDefined(this.nodeJoins);
62 | };
63 |
64 | this.hasLeaves = function() {
65 | return isDefined(this.nodeLeaves);
66 | };
67 |
68 | this.hasCreatedIndices = function() {
69 | return isDefined(this.indicesCreated);
70 | };
71 |
72 | this.hasDeletedIndices = function() {
73 | return isDefined(this.indicesDeleted);
74 | };
75 |
76 | this.addCreatedIndex = function(index) {
77 | if (!isDefined(this.indicesCreated)) {
78 | this.indicesCreated = [];
79 | }
80 | this.indicesCreated.push(index);
81 | };
82 |
83 | this.addDeletedIndex = function(index) {
84 | if (!isDefined(this.indicesDeleted)) {
85 | this.indicesDeleted = [];
86 | }
87 | this.indicesDeleted.push(index);
88 | };
89 |
90 | }
91 |
--------------------------------------------------------------------------------
/tests/all.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | QUnit Example
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
--------------------------------------------------------------------------------
/tests/karma.config.js:
--------------------------------------------------------------------------------
1 | // Karma configuration
2 | // Generated on Sat Feb 01 2014 16:02:02 GMT-0500 (EST)
3 |
4 | module.exports = function(config) {
5 | config.set({
6 |
7 | // base path, that will be used to resolve files and exclude
8 | basePath: '',
9 |
10 | // frameworks to use
11 | frameworks: ['jasmine'],
12 |
13 | // list of files / patterns to load in the browser
14 | files: [
15 | '../src/lib/angularjs/angular.min.js',
16 | '../src/lib/angularjs/angular-route.min.js',
17 | '../src/lib/angularjs/angular-animate.min.js',
18 | '../src/lib/jquery/jquery-1.10.2.min.js',
19 | '../src/lib/typeahead/typeahead.js',
20 | '../src/lib/ace/*.js',
21 | '../src/lib/jsontree/*.js',
22 | '../src/lib/angular-tree-dnd/*.js',
23 | '../src/kopf/*.js',
24 | '../src/kopf/**/*.js',
25 | 'angular-mocks.js',
26 | 'jasmine/**/*.tests.js'
27 | ],
28 |
29 | // list of files to exclude
30 | exclude: [
31 |
32 | ],
33 |
34 | // test results reporter to use
35 | // possible values: 'dots', 'progress', 'junit', 'growl', 'coverage'
36 | reporters: ['progress'],
37 |
38 |
39 | // web server port
40 | port: 9876,
41 |
42 | // enable / disable colors in the output (reporters and logs)
43 | colors: true,
44 |
45 | // level of logging
46 | // possible values: config.LOG_DISABLE || config.LOG_ERROR || config.LOG_WARN || config.LOG_INFO || config.LOG_DEBUG
47 | logLevel: config.LOG_DEBUG,
48 |
49 | // enable / disable watching file and executing tests whenever any file changes
50 | autoWatch: true,
51 |
52 | // Start these browsers, currently available:
53 | // - Chrome
54 | // - ChromeCanary
55 | // - Firefox
56 | // - Opera (has to be installed with `npm install karma-opera-launcher`)
57 | // - Safari (only Mac; has to be installed with `npm install karma-safari-launcher`)
58 | // - PhantomJS
59 | // - IE (only Windows; has to be installed with `npm install karma-ie-launcher`)
60 | browsers: ['PhantomJS'],
61 |
62 | // If browser does not capture in given timeout [ms], kill it
63 | captureTimeout: 60000,
64 |
65 | // Continuous Integration mode
66 | // if true, it capture browsers, run tests and exit
67 | singleRun: false
68 | });
69 | };
70 |
--------------------------------------------------------------------------------
/_site/partials/index_settings/routing.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
--------------------------------------------------------------------------------
/src/kopf/services/page.js:
--------------------------------------------------------------------------------
1 | kopf.factory('PageService', ['ElasticService', 'DebugService', '$rootScope',
2 | '$document', function(ElasticService, DebugService, $rootScope, $document) {
3 |
4 | var instance = this;
5 |
6 | this.clusterStatus = undefined;
7 | this.clusterName = undefined;
8 |
9 | this.link = $document[0].querySelector('link[rel~=\'icon\']');
10 |
11 | if (this.link) {
12 | var faviconUrl = this.link.href;
13 | var img = $document[0].createElement('img');
14 | img.src = faviconUrl;
15 | }
16 |
17 | $rootScope.$watch(
18 | function() {
19 | return ElasticService.cluster;
20 | },
21 | function(cluster, oldValue) {
22 | instance.setFavIconColor(cluster ? cluster.status : undefined);
23 | instance.setPageTitle(cluster ? cluster.name : undefined);
24 | }
25 | );
26 |
27 | /**
28 | * Updates page title if name is different than clusterName
29 | *
30 | * @param {string} name - cluster name
31 | */
32 | this.setPageTitle = function(name) {
33 | if (name !== this.clusterName) {
34 | if (name) {
35 | $rootScope.title = 'kopf[' + name + ']';
36 | } else {
37 | $rootScope.title = 'kopf - no connection';
38 | }
39 | this.clusterName = name;
40 | }
41 | };
42 |
43 | this.setFavIconColor = function(status) {
44 | if (this.link && this.clusterStatus !== status) {
45 | this.clusterStatus = status;
46 | try {
47 | var colors = {green: '#468847', yellow: '#c09853', red: '#B94A48'};
48 | var color = status ? colors[status] : '#333';
49 | var canvas = $document[0].createElement('canvas');
50 | canvas.width = 32;
51 | canvas.height = 32;
52 | var context = canvas.getContext('2d');
53 | context.drawImage(img, 0, 0);
54 | context.globalCompositeOperation = 'source-in';
55 | context.fillStyle = color;
56 | context.fillRect(0, 0, 32, 32);
57 | this.link.type = 'image/x-icon';
58 | this.link.href = canvas.toDataURL();
59 | } catch (exception) {
60 | DebugService.debug('Error while changing favicon', exception);
61 | }
62 | }
63 | };
64 |
65 | return this;
66 |
67 | }]);
68 |
--------------------------------------------------------------------------------
/_site/partials/index_settings/blocks.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
48 |
49 |
50 |
--------------------------------------------------------------------------------
/_site/partials/snapshot/hdfs_repository.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
35 |
49 |
--------------------------------------------------------------------------------
/_site/partials/cat.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
13 |
14 |
15 |
29 |
30 |
31 |
32 |
33 |
36 |
37 |
38 |
39 | |
40 | {{value}}
41 | |
42 |
43 |
44 |
45 |
46 | no data available
47 |
48 |
49 |
50 |
51 |
52 |
53 |
54 |
55 |
56 |
57 |
--------------------------------------------------------------------------------
/src/kopf/models/paginator.js:
--------------------------------------------------------------------------------
1 | function Paginator(page, pageSize, collection, filter) {
2 |
3 | this.filter = filter;
4 |
5 | this.page = page;
6 |
7 | this.pageSize = pageSize;
8 |
9 | this.$collection = isDefined(collection) ? collection : [];
10 |
11 | this.nextPage = function() {
12 | this.page += 1;
13 | };
14 |
15 | this.previousPage = function() {
16 | this.page -= 1;
17 | };
18 |
19 | this.setPageSize = function(newSize) {
20 | this.pageSize = newSize;
21 | };
22 |
23 | this.getPageSize = function() {
24 | return this.pageSize;
25 | };
26 |
27 | this.getCurrentPage = function() {
28 | return this.page;
29 | };
30 |
31 | this.getPage = function() {
32 | var results = this.getResults();
33 | var total = results.length;
34 |
35 | var first = total > 0 ? ((this.page - 1) * this.pageSize) + 1 : 0;
36 | while (total < first) {
37 | this.previousPage();
38 | first = (this.page - 1) * this.pageSize + 1;
39 | }
40 | var lastPage = this.page * this.pageSize > total;
41 | var last = lastPage ? total : this.page * this.pageSize;
42 |
43 | var elements = total > 0 ? results.slice(first - 1, last) : [];
44 |
45 | var next = this.pageSize * this.page < total;
46 | var previous = this.page > 1;
47 | while (elements.length < this.pageSize) {
48 | elements.push(null);
49 | }
50 | return new Page(elements, total, first, last, next, previous);
51 | };
52 |
53 | this.setCollection = function(collection) {
54 | if (this.filter.getSorting()) {
55 | this.$collection = collection.sort(this.filter.getSorting());
56 | } else {
57 | this.$collection = collection;
58 | }
59 | };
60 |
61 | this.getResults = function() {
62 | var filter = this.filter;
63 | var collection = this.$collection;
64 | if (filter.isBlank()) {
65 | return collection;
66 | } else {
67 | var filtered = [];
68 | collection.forEach(function(item) {
69 | if (filter.matches(item)) {
70 | filtered.push(item);
71 | }
72 | });
73 | return filtered;
74 | }
75 | };
76 |
77 | this.getCollection = function() {
78 | return this.$collection;
79 | };
80 |
81 | }
82 |
83 | function Page(elements, total, first, last, next, previous) {
84 | this.elements = elements;
85 | this.total = total;
86 | this.first = first;
87 | this.last = last;
88 | this.next = next;
89 | this.previous = previous;
90 | }
91 |
--------------------------------------------------------------------------------
/_site/partials/index_settings/translog.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
--------------------------------------------------------------------------------
/src/kopf/services/alerts.js:
--------------------------------------------------------------------------------
1 | var Alert = function(message, response, level, _class, icon) {
2 | var currentDate = new Date();
3 | this.message = message;
4 | this.response = response;
5 | this.level = level;
6 | this.class = _class;
7 | this.icon = icon;
8 | this.timestamp = getTimeString(currentDate);
9 | this.id = 'alert_box_' + currentDate.getTime();
10 |
11 | this.hasResponse = function() {
12 | return isDefined(this.response);
13 | };
14 |
15 | this.getResponse = function() {
16 | if (isDefined(this.response)) {
17 | return JSON.stringify(this.response, undefined, 2);
18 | }
19 | };
20 | };
21 |
22 | kopf.factory('AlertService', function() {
23 | this.maxAlerts = 3;
24 |
25 | this.alerts = [];
26 |
27 | // removes ALL alerts
28 | this.clear = function() {
29 | this.alerts.length = 0;
30 | };
31 |
32 | // remove a particular alert message
33 | this.remove = function(id) {
34 | $('#' + id).fadeTo(1000, 0).slideUp(200, function() {
35 | $(this).remove();
36 | });
37 | this.alerts = this.alerts.filter(function(a) {
38 | return id != a.id;
39 | });
40 | };
41 |
42 | // creates an error alert
43 | this.error = function(msg, resp, timeout) {
44 | timeout = isDefined(timeout) ? timeout : 7500;
45 | var alert = new Alert(msg, resp, 'error', 'alert-danger', 'fa fa-warning');
46 | return this.addAlert(alert, timeout);
47 | };
48 |
49 | // creates an info alert
50 | this.info = function(msg, resp, timeout) {
51 | timeout = isDefined(timeout) ? timeout : 2500;
52 | var alert = new Alert(msg, resp, 'info', 'alert-info', 'fa fa-info');
53 | return this.addAlert(alert, timeout);
54 | };
55 |
56 | // creates success alert
57 | this.success = function(msg, resp, timeout) {
58 | timeout = isDefined(timeout) ? timeout : 2500;
59 | var alert = new Alert(msg, resp, 'success', 'alert-success', 'fa fa-check');
60 | return this.addAlert(alert, timeout);
61 | };
62 |
63 | // creates a warn alert
64 | this.warn = function(msg, resp, timeout) {
65 | timeout = isDefined(timeout) ? timeout : 5000;
66 | var alert = new Alert(msg, resp, 'warn', 'alert-warning', 'fa fa-info');
67 | return this.addAlert(alert, timeout);
68 | };
69 |
70 | this.addAlert = function(alert, timeout) {
71 | this.alerts.unshift(alert);
72 | var service = this;
73 | setTimeout(function() {
74 | service.remove(alert.id);
75 | }, timeout);
76 | if (this.alerts.length >= this.maxAlerts) {
77 | this.alerts.length = 3;
78 | }
79 | return alert.id;
80 | };
81 |
82 | return this;
83 | });
84 |
--------------------------------------------------------------------------------
/src/lib/csv/csv.js:
--------------------------------------------------------------------------------
1 | // adapted from konlone's in-browser json to csv converter
2 | // https://github.com/konklone/json
3 | var csv = (function($) {
4 |
5 | function getParam(name) {
6 | name = name.replace(/[\[]/, "\\[").replace(/[\]]/, "\\]");
7 | var regex = new RegExp("[\\?&]" + name + "=([^]*)"),
8 | results = regex.exec(location.search);
9 | return results == null ? "" : decodeURIComponent(results[1].replace(/\+/g, " "));
10 | }
11 |
12 | // adapted from csvkit's recursive JSON flattening mechanism:
13 | // https://github.com/onyxfish/csvkit/blob/61b9c208b7665c20e9a8e95ba6eee811d04705f0/csvkit/convert/js.py#L15-L34
14 |
15 | // depends on jquery and jquery-csv (for now)
16 |
17 | function parse_object(obj, path) {
18 | if (path == undefined)
19 | path = "";
20 |
21 | var type = $.type(obj);
22 | var scalar = (type == "number" || type == "string" || type == "boolean" || type == "null");
23 |
24 | if (type == "array" || type == "object") {
25 | var d = {};
26 | for (var i in obj) {
27 |
28 | var newD = parse_object(obj[i], path + i + "/");
29 | $.extend(d, newD);
30 | }
31 |
32 | return d;
33 | }
34 |
35 | else if (scalar) {
36 | var d = {};
37 | var endPath = path.substr(0, path.length-1);
38 | d[endPath] = obj;
39 | return d;
40 | }
41 |
42 | // ?
43 | else return {};
44 | }
45 |
46 |
47 | // otherwise, just find the first one
48 | function arrayFrom(json) {
49 | var queue = [], next = json;
50 | while (next !== undefined) {
51 | if ($.type(next) == "array")
52 | return next;
53 | if ($.type(next) == "object") {
54 | for (var key in next)
55 | queue.push(next[key]);
56 | }
57 | next = queue.shift();
58 | }
59 | // none found, consider the whole object a row
60 | return [json];
61 | }
62 |
63 | // todo: add graceful error handling
64 | function jsonFrom(input) {
65 | var string = $.trim(input);
66 | if (!string) return;
67 | return JSON.parse(string);
68 | }
69 |
70 | this.doCSV = function(json) {
71 | var inArray = arrayFrom(json);
72 | var outArray = [];
73 | for (var row in inArray)
74 | outArray[outArray.length] = parse_object(inArray[row]);
75 | var csv = $.csv.fromObjects(outArray);
76 | return csv;
77 | }
78 |
79 | return this
80 | })(window.jQuery);
81 |
--------------------------------------------------------------------------------
/src/kopf/controllers/create_index.js:
--------------------------------------------------------------------------------
1 | kopf.controller('CreateIndexController', ['$scope', 'AlertService',
2 | 'ElasticService', 'AceEditorService',
3 | function($scope, AlertService, ElasticService, AceEditorService) {
4 |
5 | $scope.source_index = null;
6 | $scope.shards = '';
7 | $scope.replicas = '';
8 | $scope.name = '';
9 | $scope.indices = [];
10 |
11 | $scope.initializeController = function() {
12 | $('#create_index_option a').tab('show');
13 | $scope.prepareCreateIndex();
14 | };
15 |
16 | $scope.updateEditor = function() {
17 | ElasticService.getIndexMetadata($scope.source_index,
18 | function(meta) {
19 | var body = {settings: meta.settings, mappings: meta.mappings};
20 | $scope.editor.setValue(JSON.stringify(body, null, 2));
21 | },
22 | function(error) {
23 | AlertService.error('Error while loading index settings', error);
24 | }
25 | );
26 | };
27 |
28 | $scope.createIndex = function() {
29 | if ($scope.name.trim().length === 0) {
30 | AlertService.error('You must specify a valid index name');
31 | } else {
32 | var bodyString = $scope.editor.format();
33 | if (isDefined($scope.editor.error)) {
34 | AlertService.error('Invalid JSON: ' + $scope.editor.error);
35 | } else {
36 | var body = JSON.parse(bodyString);
37 | if (Object.keys(body).length === 0) {
38 | body = {settings: {index: {}}};
39 | if ($scope.shards.trim().length > 0) {
40 | body.settings.index.number_of_shards = $scope.shards;
41 | }
42 | if ($scope.replicas.trim().length > 0) {
43 | body.settings.index.number_of_replicas = $scope.replicas;
44 | }
45 | bodyString = JSON.stringify(body);
46 | }
47 | ElasticService.createIndex($scope.name, bodyString,
48 | function(response) {
49 | ElasticService.refresh();
50 | },
51 | function(error) {
52 | AlertService.error('Error while creating index', error);
53 | }
54 | );
55 | }
56 | }
57 | };
58 |
59 | $scope.prepareCreateIndex = function() {
60 | if (!isDefined($scope.editor)) {
61 | $scope.editor = AceEditorService.init('index-settings-editor');
62 | }
63 | $scope.indices = ElasticService.getIndices();
64 | $scope.source_index = null;
65 | $scope.editor.setValue('{}');
66 | $scope.shards = '';
67 | $scope.name = '';
68 | $scope.replicas = '';
69 | };
70 | }
71 | ]);
72 |
--------------------------------------------------------------------------------
/src/kopf/controllers/navbar.js:
--------------------------------------------------------------------------------
1 | kopf.controller('NavbarController', ['$scope', '$location',
2 | 'ExternalSettingsService', 'ElasticService', 'AlertService',
3 | 'HostHistoryService',
4 | function($scope, $location, ExternalSettingsService, ElasticService,
5 | AlertService, HostHistoryService) {
6 |
7 | $scope.new_refresh = '' + ExternalSettingsService.getRefreshRate();
8 | $scope.theme = ExternalSettingsService.getTheme();
9 | $scope.new_host = '';
10 | $scope.current_host = ElasticService.getHost();
11 | $scope.host_history = HostHistoryService.getHostHistory();
12 |
13 | $scope.clusterStatus = undefined;
14 | $scope.clusterName = undefined;
15 | $scope.fetchedAt = undefined;
16 |
17 | $scope.$watch(
18 | function() {
19 | return ElasticService.getHost();
20 | },
21 | function(newValue, oldValue) {
22 | $scope.current_host = ElasticService.getHost();
23 | }
24 | );
25 |
26 | $scope.$watch(
27 | function() {
28 | return ElasticService.cluster;
29 | },
30 | function(newValue, oldValue) {
31 | if (isDefined(ElasticService.cluster)) {
32 | $scope.clusterStatus = ElasticService.cluster.status;
33 | $scope.clusterName = ElasticService.cluster.name;
34 | $scope.fetchedAt = ElasticService.cluster.fetched_at;
35 | $scope.clientName = ElasticService.cluster.clientName;
36 | } else {
37 | $scope.clusterStatus = undefined;
38 | $scope.clusterName = undefined;
39 | $scope.fetchedAt = undefined;
40 | $scope.clientName = undefined;
41 | }
42 | }
43 | );
44 |
45 | $scope.handleConnectToHost = function(event) {
46 | if (event.keyCode == 13 && notEmpty($scope.new_host)) {
47 | $scope.connectToHost($scope.new_host);
48 | }
49 | };
50 |
51 | $scope.connectToHost = function(host) {
52 | try {
53 | ElasticService.connect(host);
54 | HostHistoryService.addToHistory(ElasticService.connection);
55 | $scope.host_history = HostHistoryService.getHostHistory();
56 | } catch (error) {
57 | AlertService.error('Error while connecting to new target host', error);
58 | } finally {
59 | $scope.current_host = ElasticService.connection.host;
60 | ElasticService.refresh();
61 | }
62 | };
63 |
64 | $scope.changeRefresh = function() {
65 | ExternalSettingsService.setRefreshRate($scope.new_refresh);
66 | };
67 |
68 | $scope.changeTheme = function() {
69 | ExternalSettingsService.setTheme($scope.theme);
70 | };
71 |
72 | }
73 | ]);
74 |
--------------------------------------------------------------------------------
/src/kopf/controllers/global.js:
--------------------------------------------------------------------------------
1 | kopf.controller('GlobalController', ['$scope', '$location', '$sce', '$window',
2 | 'AlertService', 'ElasticService', 'ExternalSettingsService', 'PageService',
3 | function($scope, $location, $sce, $window, AlertService, ElasticService,
4 | ExternalSettingsService, PageService) {
5 |
6 | $scope.version = '2.0.1';
7 |
8 | $scope.modal = new ModalControls();
9 |
10 | $scope.$watch(
11 | function() {
12 | return ElasticService.cluster;
13 | },
14 | function(newValue, oldValue) {
15 | var version = ElasticService.getVersion();
16 | if (version && version.isValid()) {
17 | var major = version.getMajor();
18 | if (major != parseInt($scope.version.charAt(0))) {
19 | AlertService.warn(
20 | 'This version of kopf is not compatible with your ES version',
21 | 'Upgrading to newest supported version is recommended'
22 | );
23 | }
24 | }
25 | }
26 | );
27 |
28 | $scope.getTheme = function() {
29 | return ExternalSettingsService.getTheme();
30 | };
31 |
32 | $scope.readParameter = function(name) {
33 | var regExp = new RegExp('[\\?&]' + name + '=([^]*)');
34 | var results = regExp.exec($window.location.href);
35 | return isDefined(results) ? results[1] : null;
36 | };
37 |
38 | $scope.connect = function() {
39 | try {
40 | var host = 'http://localhost:9200'; // default
41 | if ($location.host() !== '') { // not opening from fs
42 | var location = $scope.readParameter('location');
43 | var url = $location.absUrl();
44 | if (isDefined(location) ||
45 | isDefined(location = ExternalSettingsService.getElasticsearchHost())) {
46 | host = location;
47 | } else if (url.indexOf('/_plugin/kopf') > -1) {
48 | host = url.substring(0, url.indexOf('/_plugin/kopf'));
49 | } else {
50 | host = $location.protocol() + '://' + $location.host() +
51 | ':' + $location.port();
52 | }
53 | }
54 | ElasticService.connect(host);
55 | } catch (error) {
56 | AlertService.error(error.message, error.body);
57 | }
58 | };
59 |
60 | $scope.connect();
61 |
62 | ElasticService.refresh();
63 |
64 | $scope.hasConnection = function() {
65 | return isDefined(ElasticService.cluster);
66 | };
67 |
68 | $scope.displayInfo = function(title, info) {
69 | $scope.modal.title = title;
70 | $scope.modal.info = $sce.trustAsHtml(JSONTree.create(info));
71 | $('#modal_info').modal({show: true, backdrop: true});
72 | };
73 |
74 | }
75 | ]);
76 |
--------------------------------------------------------------------------------
/src/kopf/elastic/index_metadata.js:
--------------------------------------------------------------------------------
1 | function IndexMetadata(index, metadata) {
2 | this.index = index;
3 | this.mappings = metadata.mappings;
4 | this.settings = metadata.settings;
5 |
6 | this.getTypes = function() {
7 | return Object.keys(this.mappings).sort(function(a, b) {
8 | return a.localeCompare(b);
9 | });
10 | };
11 |
12 | this.getAnalyzers = function() {
13 | var analyzers = Object.keys(getProperty(this.settings,
14 | 'index.analysis.analyzer', {}));
15 | if (analyzers.length === 0) {
16 | Object.keys(this.settings).forEach(function(setting) {
17 | if (setting.indexOf('index.analysis.analyzer') === 0) {
18 | var analyzer = setting.substring('index.analysis.analyzer.'.length);
19 | analyzer = analyzer.substring(0, analyzer.indexOf('.'));
20 | if ($.inArray(analyzer, analyzers) == -1) {
21 | analyzers.push(analyzer);
22 | }
23 | }
24 | });
25 | }
26 | return analyzers.sort(function(a, b) {
27 | return a.localeCompare(b);
28 | });
29 | };
30 |
31 | function isAnalyzable(type) {
32 | var analyzableTypes = ['float', 'double', 'byte', 'short', 'integer',
33 | 'long', 'nested', 'object'
34 | ];
35 | return analyzableTypes.indexOf(type) == -1;
36 | }
37 |
38 | this.getFields = function(type) {
39 | var fields = [];
40 | if (isDefined(this.mappings[type])) {
41 | fields = this.getProperties('', this.mappings[type].properties);
42 | }
43 | return fields.sort(function(a, b) {
44 | return a.localeCompare(b);
45 | });
46 | };
47 |
48 | this.getProperties = function(parent, fields) {
49 | var prefix = parent !== '' ? parent + '.' : '';
50 | var validFields = [];
51 | for (var field in fields) {
52 | // multi field
53 | if (isDefined(fields[field].fields)) {
54 | var addPrefix = fields[field].path != 'just_name';
55 | var multiPrefix = addPrefix ? prefix + field : prefix;
56 | var multiProps = this.getProperties(multiPrefix, fields[field].fields);
57 | validFields = validFields.concat(multiProps);
58 | }
59 | // nested and object types
60 | var nestedType = fields[field].type == 'nested';
61 | var objectType = fields[field].type == 'object';
62 | if (nestedType || objectType || !isDefined(fields[field].type)) {
63 | var nestedProperties = this.getProperties(prefix + field,
64 | fields[field].properties);
65 | validFields = validFields.concat(nestedProperties);
66 | }
67 | // normal fields
68 | if (isDefined(fields[field].type) && isAnalyzable(fields[field].type)) {
69 | validFields.push(prefix + field);
70 | }
71 | }
72 | return validFields;
73 | };
74 | }
75 |
--------------------------------------------------------------------------------
/src/kopf/elastic/repository.js:
--------------------------------------------------------------------------------
1 | function Repository(name, info) {
2 | this.name = name;
3 | this.type = info.type;
4 | this.settings = info.settings;
5 |
6 | this.asJson = function() {
7 | var json = {type: this.type};
8 | if (this.type === 'fs') {
9 | var fsSettings = ['location', 'chunk_size', 'max_restore_bytes_per_sec',
10 | 'max_snapshot_bytes_per_sec', 'compress'];
11 | json.settings = this.getSettings(fsSettings);
12 | }
13 | if (this.type === 'url') {
14 | var urlSettings = ['url'];
15 | json.settings = this.getSettings(urlSettings);
16 | }
17 | if (this.type === 's3') {
18 | var s3Settings = ['region', 'bucket', 'base_path', 'access_key',
19 | 'secret_key', 'chunk_size', 'max_retries', 'compress',
20 | 'server_side_encryption'
21 | ];
22 | json.settings = this.getSettings(s3Settings);
23 | }
24 | if (this.type === 'hdfs') {
25 | var hdfsSettings = ['uri', 'path', 'load_defaults', 'conf_location',
26 | 'concurrent_streams', 'compress', 'chunk_size'];
27 | json.settings = this.getSettings(hdfsSettings);
28 | }
29 | if (this.type === 'azure') {
30 | var azureSettings = ['container', 'base_path', 'concurrent_streams',
31 | 'chunk_size', 'compress'];
32 | json.settings = this.getSettings(azureSettings);
33 | }
34 | return JSON.stringify(json);
35 | };
36 |
37 | this.validate = function() {
38 | if (!notEmpty(this.name)) {
39 | throw 'Repository name is required';
40 | }
41 | if (!notEmpty(this.type)) {
42 | throw 'Repository type is required';
43 | }
44 | if (this.type === 'fs') {
45 | var fsRequired = ['location'];
46 | this.validateSettings(fsRequired);
47 | }
48 | if (this.type === 'url') {
49 | var urlRequired = ['url'];
50 | this.validateSettings(urlRequired);
51 | }
52 | if (this.type === 's3') {
53 | var s3Required = ['bucket'];
54 | this.validateSettings(s3Required);
55 | }
56 | if (this.type === 'hdfs') {
57 | var hdfsRequired = ['path'];
58 | this.validateSettings(hdfsRequired);
59 | }
60 | };
61 |
62 | this.validateSettings = function(required) {
63 | var repository = this;
64 | required.forEach(function(setting) {
65 | if (!notEmpty(repository.settings[setting])) {
66 | var type = repository.type;
67 | throw(setting + ' is required for snapshot of type ' + type);
68 | }
69 | });
70 | };
71 |
72 | this.getSettings = function(availableSettings) {
73 | var settings = {};
74 | var currentSettings = this.settings;
75 | availableSettings.forEach(function(setting) {
76 | if (notEmpty(currentSettings[setting])) {
77 | settings[setting] = currentSettings[setting];
78 | }
79 | });
80 | return settings;
81 | };
82 | }
83 |
--------------------------------------------------------------------------------
/_site/partials/cluster_settings/cluster.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
43 |
44 |
--------------------------------------------------------------------------------
/src/kopf/models/index_filter.js:
--------------------------------------------------------------------------------
1 | function IndexFilter(name, closed, special, healthy, asc, timestamp) {
2 | this.name = name;
3 | this.closed = closed;
4 | this.special = special;
5 | this.healthy = healthy;
6 | this.sort = 'name';
7 | this.asc = asc;
8 | this.timestamp = timestamp;
9 |
10 | this.getSorting = function() {
11 | var asc = this.asc;
12 | switch (this.sort) {
13 | case 'name':
14 | return function(a, b) {
15 | if (asc) {
16 | return a.name.localeCompare(b.name);
17 | } else {
18 | return b.name.localeCompare(a.name);
19 | }
20 | };
21 | default:
22 | return undefined;
23 | }
24 | };
25 |
26 | this.clone = function() {
27 | return new IndexFilter(
28 | this.name,
29 | this.closed,
30 | this.special,
31 | this.healthy,
32 | this.asc,
33 | this.timestamp
34 | );
35 | };
36 |
37 | this.equals = function(other) {
38 | return (
39 | other !== null &&
40 | this.name === other.name &&
41 | this.closed === other.closed &&
42 | this.special === other.special &&
43 | this.healthy === other.healthy &&
44 | this.asc === other.asc &&
45 | this.timestamp === other.timestamp
46 | );
47 | };
48 |
49 | this.isBlank = function() {
50 | return (
51 | !notEmpty(this.name) &&
52 | this.closed &&
53 | this.special &&
54 | this.healthy &&
55 | this.asc
56 | );
57 | };
58 |
59 | this.matches = function(index) {
60 | var matches = true;
61 | if (!this.special && index.special) {
62 | matches = false;
63 | }
64 | if (!this.closed && index.closed) {
65 | matches = false;
66 | }
67 | // Hide healthy == show unhealthy only
68 | if (!this.healthy && !index.unhealthy) {
69 | matches = false;
70 | }
71 | if (matches && notEmpty(this.name)) {
72 | try {
73 | var regExp = new RegExp(this.name.trim(), 'i');
74 | matches = regExp.test(index.name);
75 | if (!matches) {
76 | for (var idx = 0; idx < index.aliases.length; idx++) {
77 | if ((matches = regExp.test(index.aliases[idx]))) {
78 | break;
79 | }
80 | }
81 | }
82 | }
83 | catch (err) { // if not valid regexp, still try normal matching
84 | matches = index.name.indexOf(this.name.toLowerCase()) != -1;
85 | if (!matches) {
86 | for (var idx = 0; idx < index.aliases.length; idx++) {
87 | var alias = index.aliases[idx].toLowerCase();
88 | matches = true;
89 | if ((matches = (alias.indexOf(this.name.toLowerCase()) != -1))) {
90 | break;
91 | }
92 | }
93 | }
94 | }
95 | }
96 | return matches;
97 | };
98 |
99 | }
100 |
--------------------------------------------------------------------------------
/tests/jasmine/cat.tests.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | describe('CatController', function() {
4 | var scope, createController;
5 |
6 | beforeEach(angular.mock.module('kopf'));
7 |
8 | beforeEach(angular.mock.inject(function($rootScope, $controller, $injector) {
9 | this.scope = $rootScope.$new();
10 | this.AlertService = $injector.get('AlertService');
11 | this.ElasticService = $injector.get('ElasticService');
12 | this.createController = function() {
13 | return $controller('CatController', {$scope: this.scope},
14 | this.ElasticService, this.AlertService);
15 | };
16 | this._controller = this.createController();
17 | }));
18 |
19 | it('initial values are set', function() {
20 | expect(this.scope.api).toEqual('');
21 | expect(this.scope.result).toEqual(undefined);
22 | expect(this.scope.apis).toEqual([
23 | 'aliases',
24 | //'allocation',
25 | 'count',
26 | //'fielddata',
27 | //'health',
28 | //'indices',
29 | 'master',
30 | //'nodes',
31 | //'pending_tasks',
32 | 'plugins',
33 | 'recovery',
34 | //'thread_pool',
35 | //'shards',
36 | //'segments'
37 | ]);
38 | });
39 |
40 | it('validates api is selected', function() {
41 | this.scope.api = '';
42 | spyOn(this.AlertService, 'error').andReturn(true);
43 | this.scope.execute();
44 | expect(this.AlertService.error).toHaveBeenCalledWith('You must select an API');
45 | });
46 |
47 | it('execute a successful cat request', function() {
48 | this.scope.api = 'shards';
49 | this.ElasticService.executeCatRequest = function(api, success, error) {
50 | success(new CatResult('header\nvalue\n'));
51 | };
52 | spyOn(this.ElasticService, 'executeCatRequest').andCallThrough();
53 | this.scope.execute();
54 | expect(this.ElasticService.executeCatRequest).toHaveBeenCalledWith(
55 | 'shards',
56 | jasmine.any(Function),
57 | jasmine.any(Function)
58 | );
59 | expect(this.scope.result.columns).toEqual(['header']);
60 | expect(this.scope.result.lines).toEqual([["value"]]);
61 | });
62 |
63 | it('alerts error while executing request', function() {
64 | this.scope.api = 'shards';
65 | this.ElasticService.executeCatRequest = function(api, success, error) {
66 | error('some weird unknown reason');
67 | };
68 | spyOn(this.ElasticService, 'executeCatRequest').andCallThrough();
69 | spyOn(this.AlertService, 'error').andReturn(true);
70 | this.scope.execute();
71 | expect(this.ElasticService.executeCatRequest).toHaveBeenCalledWith(
72 | 'shards',
73 | jasmine.any(Function),
74 | jasmine.any(Function)
75 | );
76 | expect(this.scope.result).toEqual(undefined);
77 | expect(this.AlertService.error).toHaveBeenCalledWith(
78 | 'Error while fetching data',
79 | 'some weird unknown reason'
80 | );
81 | });
82 |
83 | });
84 |
--------------------------------------------------------------------------------
/src/kopf/elastic/alias.js:
--------------------------------------------------------------------------------
1 | function IndexAliases(index, aliases) {
2 | this.index = index;
3 | this.aliases = aliases;
4 |
5 | this.clone = function() {
6 | var cloned = new IndexAliases(this.index, []);
7 | cloned.aliases = this.aliases.map(function(alias) {
8 | return alias.clone();
9 | });
10 | return cloned;
11 | };
12 | }
13 |
14 | IndexAliases.diff = function(original, modified) {
15 | var differences = [];
16 | modified.forEach(function(ia) {
17 | var isNew = true;
18 | original.forEach(function(origIA) {
19 | if (ia.index == origIA.index) {
20 | isNew = false;
21 | ia.aliases.forEach(function(alias) {
22 | var originalAliases = origIA.aliases.filter(function(originalAlias) {
23 | return alias.equals(originalAlias);
24 | });
25 | if (originalAliases.length === 0) {
26 | differences.push(alias);
27 | }
28 | });
29 | }
30 | });
31 | if (isNew) {
32 | ia.aliases.forEach(function(alias) {
33 | differences.push(alias);
34 | });
35 | }
36 | });
37 | return differences;
38 | };
39 |
40 | function Alias(alias, index, filter, indexRouting, searchRouting) {
41 | this.alias = isDefined(alias) ? alias.toLowerCase() : '';
42 | this.index = isDefined(index) ? index.toLowerCase() : '';
43 | this.filter = isDefined(filter) ? filter : '';
44 | this.index_routing = isDefined(indexRouting) ? indexRouting : '';
45 | this.search_routing = isDefined(searchRouting) ? searchRouting : '';
46 |
47 | this.validate = function() {
48 | if (!notEmpty(this.alias)) {
49 | throw 'Alias must have a non empty name';
50 | }
51 | if (!notEmpty(this.index)) {
52 | throw 'Alias must have a valid index name';
53 | }
54 | };
55 |
56 | this.equals = function(otherAlias) {
57 | var equal =
58 | (this.alias === otherAlias.alias) &&
59 | (this.index === otherAlias.index) &&
60 | (this.filter === otherAlias.filter) &&
61 | (this.index_routing === otherAlias.index_routing) &&
62 | (this.search_routing === otherAlias.search_routing);
63 | return equal;
64 | };
65 |
66 | this.info = function() {
67 | var info = {};
68 | info.index = this.index;
69 | info.alias = this.alias;
70 |
71 | if (isDefined(this.filter)) {
72 | if (typeof this.filter == 'string' && notEmpty(this.filter)) {
73 | info.filter = JSON.parse(this.filter);
74 | } else {
75 | info.filter = this.filter;
76 | }
77 | }
78 | if (notEmpty(this.index_routing)) {
79 | info.index_routing = this.index_routing;
80 | }
81 | if (notEmpty(this.search_routing)) {
82 | info.search_routing = this.search_routing;
83 | }
84 | return info;
85 | };
86 |
87 | this.clone = function() {
88 | return new Alias(this.alias, this.index, this.filter, this.index_routing,
89 | this.search_routing);
90 | };
91 | }
92 |
--------------------------------------------------------------------------------
/_site/partials/cluster_stats.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | {{cluster.number_of_nodes | number:0}}
6 | nodes
7 |
8 |
9 |
10 |
11 |
12 |
13 | {{cluster.total_indices | number:0}}
14 | indices
15 |
16 |
17 |
18 |
19 |
20 |
21 | {{cluster.shards | number:0}}
22 | shards
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 | {{cluster.num_docs | number:0}}
31 | docs
32 |
33 |
34 |
35 |
36 | {{cluster.changes.absDocDelta() | number:0}}
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 | {{cluster.total_size_in_bytes | bytes}}
46 |
47 |
48 |
49 |
50 | {{cluster.changes.absDataDelta()}}
51 |
52 |
53 |
54 |
55 |
56 |
61 |
--------------------------------------------------------------------------------
/src/lib/angular-tree-dnd/ng-tree-dnd.css:
--------------------------------------------------------------------------------
1 | .tree-dnd-animate-enter,
2 | .tree-dnd-node.ng-enter {
3 | -webkit-transition: 0 linear all;
4 | -moz-transition: 0 linear all;
5 | -ms-transition: 0 linear all;
6 | -o-transition: 0 linear all;
7 | transition: 0 linear all;
8 | position: relative;
9 | display: block;
10 | opacity: 0;
11 | max-height: 0;
12 | }
13 |
14 | .tree-dnd-animate-enter.tree-dnd-animate-enter-active,
15 | .tree-dnd-node.ng-enter-active {
16 | opacity: 1;
17 | max-height: 30px;
18 | }
19 |
20 | .tree-dnd-animate-leave,
21 | .tree-dnd-node.ng-leave {
22 | -webkit-transition: 0 linear all;
23 | -moz-transition: 0 linear all;
24 | -ms-transition: 0 linear all;
25 | -o-transition: 0 linear all;
26 | transition: 0 linear all;
27 | position: relative;
28 | display: block;
29 | height: 30px;
30 | max-height: 30px;
31 | opacity: 1;
32 | }
33 |
34 |
35 | .tree-dnd-animate-leave.tree-dnd-animate-leave-active,
36 | .tree-dnd-node.ng-leave-active {
37 | height: 0;
38 | max-height: 0;
39 | opacity: 0;
40 | }
41 |
42 | .tree-dnd-node > td {
43 | position: relative;
44 | }
45 |
46 | .tree-dnd-animate.ng-enter {
47 | }
48 |
49 | .tree-dnd .tree-icon,
50 | .tree-label {
51 | cursor: pointer;
52 | }
53 |
54 | .tree-dnd > thead > tr > td, .tree-dnd > tbody > tr > td, .tree-dnd > tfoot > tr > td {
55 | vertical-align: middle !important;
56 | }
57 |
58 | .tree-dnd-empty {
59 | border: 2px dashed #72da67 !important;
60 | min-height: 100px;
61 | background-color: #e5e5e5;
62 | }
63 |
64 | .indented {
65 | position: relative;
66 | }
67 |
68 | .tree-dnd-hidden {
69 | display: none;
70 | }
71 |
72 | .tree-dnd-placeholder {
73 | position: relative;
74 | margin: 0px;
75 | padding: 0px;
76 | min-height: 20px;
77 | line-height: 20px;
78 | }
79 |
80 | .tree-dnd-handle {
81 | cursor: pointer;
82 | text-decoration: none;
83 | -webkit-box-sizing: border-box;
84 | -moz-box-sizing: border-box;
85 | box-sizing: border-box;
86 | min-height: 20px;
87 | line-height: 20px;
88 | }
89 |
90 | .tree-dnd-drag {
91 | position: absolute;
92 | pointer-events: none;
93 | z-index: 999;
94 | opacity: .7;
95 | background-color: #cdffdc;
96 | border: 2px dashed #385736;
97 | }
98 |
99 | .tree-dnd-status {
100 | position: absolute;
101 | background-color: #ffffff;
102 | border: 1px solid #92e5d3;
103 | padding: 1px 4px;
104 | opacity: .85;
105 | }
106 |
107 | .tree-dnd-nodes {
108 | margin: 0px;
109 | padding: 0px;
110 | list-style: none;
111 | }
112 |
113 | .tree-dnd-nodes .tree-dnd-node {
114 | border-radius: 0px !important;
115 | }
116 |
117 | .tree-dnd-nodes .tree-dnd-nodes {
118 | padding-left: 20px;
119 | }
120 |
121 | .tree-dnd-node, .tree-dnd-placeholder {
122 | /*margin: 0px;*/
123 | /*padding: 0px;*/
124 | min-height: 20px;
125 | line-height: 20px;
126 | }
127 |
--------------------------------------------------------------------------------
/_site/partials/cluster_settings/recovery.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
61 |
62 |
65 |
--------------------------------------------------------------------------------
/tests/jasmine/services/explain.tests.js:
--------------------------------------------------------------------------------
1 | "use strict";
2 |
3 | describe('ExplainService', function() {
4 | var service;
5 | beforeEach(module('kopf'));
6 |
7 | beforeEach(inject(function($injector) {
8 | service = $injector.get('ExplainService');
9 | expect(service).not.toBeUndefined();
10 | }));
11 |
12 | it('should predict whether path will return explanation', function() {
13 | expect(service.isExplainPath('/_search')).toEqual(false);
14 | expect(service.isExplainPath('/_search?explain=true')).toEqual(true);
15 | expect(service.isExplainPath('/_search?pretty=true&explain=true')).toEqual(true);
16 | expect(service.isExplainPath('/x/y/z/_explain')).toEqual(true);
17 | });
18 |
19 | it('should normalize search response', function() {
20 | var response = {
21 | hits: {
22 | hits: [
23 | {
24 | _index:'x', _type: 'y', _id:'1',
25 | _score: 1.0,
26 | _source: {},
27 | _explanation: {
28 | description: 'desc 1', value: 1.0,
29 | details: [
30 | {
31 | description: 'desc 1.1', value: 0.5
32 | },
33 | {
34 | description: 'desc 1.2', value: 0.5
35 | }
36 | ]
37 | }
38 | },
39 | {
40 | _index:'x', _type: 'y', _id:'2',
41 | _score: 0.5,
42 | _source: {},
43 | _explanation: {
44 | description: 'desc 2', value: 0.5,
45 | details: [
46 | {
47 | description: 'desc 2.1', value: 0.5
48 | }
49 | ]
50 | }
51 | }
52 | ]
53 | }
54 | }
55 | var normalizedResponse = service.normalizeExplainResponse(response);
56 | expect(normalizedResponse[0].documentId).toEqual('x/y/1');
57 | expect(normalizedResponse[0]._score).toEqual(1.0);
58 | expect(normalizedResponse[0].explanationTreeData).not.toBeUndefined();
59 | expect(normalizedResponse[1].documentId).toEqual('x/y/2');
60 | expect(normalizedResponse[1]._score).toEqual(0.5);
61 | expect(normalizedResponse[1].explanationTreeData).not.toBeUndefined();
62 | });
63 |
64 | it('should normalize explain document response', function() {
65 | var response = {
66 | _index:'x', _type: 'y', _id:'1',
67 | _source: {},
68 | explanation: {
69 | description: 'desc 1', value: 1.0,
70 | details: [
71 | {
72 | description: 'desc 1.1', value: 0.5
73 | },
74 | {
75 | description: 'desc 1.2', value: 0.5
76 | }
77 | ]
78 | }
79 | };
80 | var normalizedResponse = service.normalizeExplainResponse(response);
81 | expect(normalizedResponse[0].documentId).toEqual('x/y/1');
82 | expect(normalizedResponse[0]._score).toEqual(1.0);
83 | expect(normalizedResponse[0]._explanation).not.toBeUndefined();
84 | expect(normalizedResponse[0].explanationTreeData).not.toBeUndefined();
85 | });
86 |
87 | });
88 |
--------------------------------------------------------------------------------
/_site/partials/snapshot/s3_repository.html:
--------------------------------------------------------------------------------
1 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
47 |
--------------------------------------------------------------------------------
/src/kopf/kopf.js:
--------------------------------------------------------------------------------
1 | var kopf = angular.module('kopf', ['ngRoute', 'ntt.TreeDnD', 'ngAnimate',
2 | 'ui.bootstrap']);
3 |
4 | // manages behavior of confirmation dialog
5 | kopf.factory('ConfirmDialogService', function() {
6 | this.header = 'Default Header';
7 | this.body = 'Default Body';
8 | this.cancel_text = 'cancel';
9 | this.confirm_text = 'confirm';
10 |
11 | this.confirm = function() {
12 | // when created, does nothing
13 | };
14 |
15 | this.close = function() {
16 | // when created, does nothing
17 | };
18 |
19 | this.open = function(header, body, action, confirmCallback, closeCallback) {
20 | this.header = header;
21 | this.body = body;
22 | this.action = action;
23 | this.confirm = confirmCallback;
24 | this.close = closeCallback;
25 | };
26 |
27 | return this;
28 | });
29 |
30 | kopf.config(function($routeProvider, $locationProvider) {
31 | $locationProvider.hashPrefix('!');
32 | $routeProvider.
33 | when('/cluster', {
34 | templateUrl: 'partials/cluster_overview.html',
35 | controller: 'ClusterOverviewController'
36 | }).
37 | when('/nodes', {
38 | templateUrl: 'partials/nodes/nodes.html',
39 | controller: 'NodesController'
40 | }).
41 | when('/rest', {
42 | templateUrl: 'partials/rest_client.html',
43 | controller: 'RestController'
44 | }).
45 | when('/aliases', {
46 | templateUrl: 'partials/aliases.html',
47 | controller: 'AliasesController'
48 | }).
49 | when('/analysis', {
50 | templateUrl: 'partials/analysis.html',
51 | controller: 'AnalysisController'
52 | }).
53 | when('/percolator', {
54 | templateUrl: 'partials/percolator.html',
55 | controller: 'PercolatorController'
56 | }).
57 | when('/warmers', {
58 | templateUrl: 'partials/warmers.html',
59 | controller: 'WarmersController'
60 | }).
61 | when('/snapshot', {
62 | templateUrl: 'partials/snapshot.html',
63 | controller: 'SnapshotController'
64 | }).
65 | when('/createIndex', {
66 | templateUrl: 'partials/create_index.html',
67 | controller: 'CreateIndexController'
68 | }).
69 | when('/clusterHealth', {
70 | templateUrl: 'partials/cluster_health.html',
71 | controller: 'ClusterHealthController'
72 | }).
73 | when('/clusterSettings', {
74 | templateUrl: 'partials/cluster_settings.html',
75 | controller: 'ClusterSettingsController'
76 | }).
77 | when('/indexSettings', {
78 | templateUrl: 'partials/index_settings.html',
79 | controller: 'IndexSettingsController'
80 | }).
81 | when('/indexTemplates', {
82 | templateUrl: 'partials/index_templates.html',
83 | controller: 'IndexTemplatesController'
84 | }).
85 | when('/cat', {
86 | templateUrl: 'partials/cat.html',
87 | controller: 'CatController'
88 | }).
89 | when('/hotthreads', {
90 | templateUrl: 'partials/hotthreads.html',
91 | controller: 'HotThreadsController'
92 | }).
93 | otherwise({redirectTo: '/cluster'});
94 | });
95 |
--------------------------------------------------------------------------------
/tests/models/node_filter.js:
--------------------------------------------------------------------------------
1 | test("Blank node filter", function() {
2 | var filter = new NodeFilter("", true, true, true, 0);
3 | var node = { name: 'a', master: true, client: false, data: true };
4 | ok(filter.matches(node), "Matches any node if filter is blank");
5 | })
6 |
7 | test("Only master node filter x non master node", function() {
8 | var filter = new NodeFilter("", false, true, false, 0);
9 | var node = { name: 'a', master: false, client: false, data: true };
10 | ok(!filter.matches(node),"Doesnt match a non master node");
11 | })
12 |
13 | test("Only master node filter x master node", function() {
14 | var filter = new NodeFilter("", false, true, false, 0);
15 | var node = { name: 'a', master: true, client: false, data: true };
16 | ok(filter.matches(node),"Matches a master node");
17 | })
18 |
19 | test("Only data node filter x non data node", function() {
20 | var filter = new NodeFilter("", true, false, false, 0);
21 | var node = { name: 'a', master: false, client: true, data: false };
22 | ok(!filter.matches(node),"Doesnt match a non data node");
23 | })
24 |
25 | test("Only data node filter x data node", function() {
26 | var filter = new NodeFilter("", true, false, false, 0);
27 | var node = { name: 'a', master: false, client: false, data: true };
28 | ok(filter.matches(node),"Match a data node");
29 | })
30 |
31 | test("Only client node filter x non client node", function() {
32 | var filter = new NodeFilter("", false, false, true, 0);
33 | var node = { name: 'a', master: false, client: false, data: true };
34 | ok(!filter.matches(node), "Doesnt match a non client node");
35 | })
36 |
37 | test("Only client node filter x client node", function() {
38 | var filter = new NodeFilter("", false, false, true, 0);
39 | var node = { name: 'a', master: false, client: true, data: false };
40 | ok(filter.matches(node),"Match a client node");
41 | })
42 |
43 | test("Master or client node filter x data node", function() {
44 | var filter = new NodeFilter("", false, true, true, 0);
45 | var node = { name: 'a', master: false, client: false, data: true };
46 | ok(!filter.matches(node), "Doesnt match a non master/client node");
47 | })
48 |
49 | test("Master or client node filter x client node x master node", function() {
50 | var filter = new NodeFilter("", false, true, true, 0);
51 | var node = { name: 'a', master: false, client: true, data: false };
52 | var node2 = { name: 'a', master: true, client: false, data: false };
53 | ok(filter.matches(node),"Match a client node");
54 | ok(filter.matches(node2),"Match a master node");
55 | })
56 |
57 | test("node filter with name x non matching name", function() {
58 | var filter = new NodeFilter("moli", true, true, true, 0);
59 | var node = { name: 'milo_id', master: true, client: false, data: true };
60 | ok(!filter.matches(node),"Doesnt match if name is not a substring");
61 | })
62 |
63 | test("node filter with name x matching name", function() {
64 | var filter = new NodeFilter("moli", true, true, true, 0);
65 | var node = { name: 'moliware', master: true, client: false, data: true };
66 | ok(filter.matches(node),"Matches if name is not a substring");
67 | })
--------------------------------------------------------------------------------
/_site/partials/snapshot/repositories_table.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 | {{validation_error}}
30 |
33 |
34 |
35 |
36 |
37 |
38 |
42 |
43 | |
44 | {{r.name}}
45 |
46 |
47 |
48 |
49 |
50 |
51 | |
52 | {{r.type}} |
53 |
54 |
55 |
56 |
57 |
58 |
59 |
60 |
61 |
--------------------------------------------------------------------------------
/tests/jasmine/services/page.tests.js:
--------------------------------------------------------------------------------
1 | "use strict";
2 |
3 | describe("PageService", function() {
4 |
5 | var PageService, ElasticService, $scope;
6 |
7 | beforeEach(module("kopf"));
8 |
9 | beforeEach(function() {
10 | module('kopf');
11 |
12 | module(function($provide) {
13 | $provide.value('ElasticService', {
14 | cluster: {}
15 | });
16 | });
17 | });
18 |
19 | beforeEach(angular.mock.inject(function($rootScope, $injector) {
20 | ElasticService = $injector.get('ElasticService');
21 | PageService = $injector.get('PageService');
22 | PageService.link = '';
23 | $scope = $rootScope;
24 | }));
25 |
26 | it("should watch for cluster changes and update title and favicon",
27 | function() {
28 | spyOn(PageService, 'setFavIconColor').andReturn(true);
29 | spyOn(PageService, 'setPageTitle').andReturn(true);
30 | ElasticService.cluster = {name: 'super cluster', status: 'green'};
31 | $scope.$digest();
32 | expect(PageService.setFavIconColor).toHaveBeenCalledWith('green');
33 | expect(PageService.setPageTitle).toHaveBeenCalledWith('super cluster');
34 | });
35 |
36 | it("should watch for disconnect with cluster and update title and favicon",
37 | function() {
38 | spyOn(PageService, 'setFavIconColor').andReturn(true);
39 | spyOn(PageService, 'setPageTitle').andReturn(true);
40 | ElasticService.cluster = undefined;
41 | $scope.$digest();
42 | expect(PageService.setFavIconColor).toHaveBeenCalledWith(undefined);
43 | expect(PageService.setPageTitle).toHaveBeenCalledWith(undefined);
44 | });
45 |
46 | it("should change title and favicon if changed",
47 | function() {
48 | spyOn(PageService, 'setFavIconColor').andCallThrough(true);
49 | spyOn(PageService, 'setPageTitle').andCallThrough(true);
50 | PageService.link = {};
51 | ElasticService.cluster = {name: 'name1', status: 'status1'};
52 | $scope.$digest();
53 | expect(PageService.clusterName).toEqual('name1');
54 | expect(PageService.clusterStatus).toEqual('status1');
55 | ElasticService.cluster = {name: 'name2', status: 'status2'};
56 | $scope.$digest();
57 | expect(PageService.clusterName).toEqual('name2');
58 | expect(PageService.clusterStatus).toEqual('status2');
59 | ElasticService.cluster = {name: 'name2', status: 'status2', other: {}};
60 | $scope.$digest();
61 | expect(PageService.clusterName).toEqual('name2');
62 | expect(PageService.clusterStatus).toEqual('status2');
63 | });
64 |
65 | it("should preserver title and favicon if not changed",
66 | function() {
67 | spyOn(PageService, 'setFavIconColor').andCallThrough(true);
68 | spyOn(PageService, 'setPageTitle').andCallThrough(true);
69 | PageService.link = {};
70 | ElasticService.cluster = {name: 'name1', status: 'status1'};
71 | $scope.$digest();
72 | expect(PageService.clusterName).toEqual('name1');
73 | expect(PageService.clusterStatus).toEqual('status1');
74 | ElasticService.cluster = {name: 'name1', status: 'status1'};
75 | $scope.$digest();
76 | expect(PageService.clusterName).toEqual('name1');
77 | expect(PageService.clusterStatus).toEqual('status1');
78 | });
79 |
80 | });
81 |
--------------------------------------------------------------------------------
/tests/jasmine/host_history.tests.js:
--------------------------------------------------------------------------------
1 | "use strict";
2 |
3 | describe("Host history service", function() {
4 | var service;
5 |
6 | beforeEach(angular.mock.module('kopf'));
7 |
8 | beforeEach(angular.mock.inject(function($rootScope, $injector) {
9 | service = $injector.get('HostHistoryService');
10 | service.clearHistory();
11 | }));
12 |
13 | it("Successfully adds a new element to history", function() {
14 | var history = service.getHostHistory();
15 | expect(history).toEqual([]);
16 | service.addToHistory(new ESConnection("http://localhost"));
17 | history = service.getHostHistory();
18 | expect(history).toEqual([{host: 'http://localhost'}]);
19 | });
20 |
21 | it("Successfully adds a new authenticated element to history", function() {
22 | var history = service.getHostHistory();
23 | expect(history).toEqual([]);
24 | service.addToHistory(new ESConnection("http://usr:pwd@localhost"));
25 | history = service.getHostHistory();
26 | expect(history).toEqual([{host: 'http://usr:pwd@localhost'}]);
27 | });
28 |
29 | it("Only adds an element once", function() {
30 | var history = service.getHostHistory();
31 | expect(history).toEqual([]);
32 | service.addToHistory(new ESConnection("http://localhost"));
33 | history = service.getHostHistory();
34 | expect(history).toEqual([{host: 'http://localhost'}]);
35 | service.addToHistory(new ESConnection("http://localhost"));
36 | history = service.getHostHistory();
37 | expect(history).toEqual([{host: 'http://localhost'}]);
38 | });
39 |
40 | it("should move an alredy present entry to the top when added again",
41 | function() {
42 | var history = service.getHostHistory();
43 | expect(history).toEqual([]);
44 | service.addToHistory(new ESConnection("http://localhost1"));
45 | history = service.getHostHistory();
46 | expect(history).toEqual([{host: 'http://localhost1'}]);
47 | service.addToHistory(new ESConnection("http://localhost2"));
48 | history = service.getHostHistory();
49 | expect(history).toEqual([{host: 'http://localhost2'},
50 | {host: 'http://localhost1'}]);
51 | service.addToHistory(new ESConnection("http://localhost1"));
52 | history = service.getHostHistory();
53 | expect(history).toEqual([{host: 'http://localhost1'},
54 | {host: 'http://localhost2'}]);
55 | });
56 |
57 | it("should limit history to 10 elements", function() {
58 | var history = service.getHostHistory();
59 | expect(history).toEqual([]);
60 | service.addToHistory(new ESConnection("http://localhost1"));
61 | service.addToHistory(new ESConnection("http://localhost2"));
62 | service.addToHistory(new ESConnection("http://localhost3"));
63 | service.addToHistory(new ESConnection("http://localhost4"));
64 | service.addToHistory(new ESConnection("http://localhost5"));
65 | service.addToHistory(new ESConnection("http://localhost6"));
66 | service.addToHistory(new ESConnection("http://localhost7"));
67 | service.addToHistory(new ESConnection("http://localhost8"));
68 | service.addToHistory(new ESConnection("http://localhost9"));
69 | service.addToHistory(new ESConnection("http://localhost10"));
70 | service.addToHistory(new ESConnection("http://localhost11"));
71 | history = service.getHostHistory();
72 | expect(history.length).toEqual(10);
73 | });
74 |
75 | });
76 |
--------------------------------------------------------------------------------
/tests/jasmine/services/external_settings.tests.js:
--------------------------------------------------------------------------------
1 | "use strict";
2 |
3 | describe("ExternalSettingsService", function() {
4 |
5 | var service;
6 |
7 | beforeEach(module("kopf"));
8 |
9 | beforeEach(function() {
10 | var store = {};
11 | spyOn(localStorage, 'getItem').andCallFake(function(key) {
12 | return store[key];
13 | });
14 | spyOn(localStorage, 'setItem').andCallFake(function(key, value) {
15 | return store[key] = value + '';
16 | });
17 | spyOn(localStorage, 'clear').andCallFake(function() {
18 | store = {};
19 | });
20 | });
21 |
22 | beforeEach(inject(function($injector) {
23 | service = $injector.get('ExternalSettingsService');
24 | this.DebugService = $injector.get('DebugService');
25 | }));
26 |
27 | it("should correctly save/retrieve settings to/from localstorage",
28 | function() {
29 | var settings = {refresh_rate: 'doesntmatter', theme: 'whatever'};
30 | service.settings = settings;
31 | service.saveSettings();
32 | expect(localStorage.setItem).toHaveBeenCalledWith('kopfSettings',
33 | JSON.stringify(settings));
34 | expect(service.loadLocalSettings()).toEqual(settings);
35 | });
36 |
37 | it("should correctly save/retrieve ONLY updatable settings",
38 | function() {
39 | var settings = {refresh_rate: 'doesntmatter', theme: 'whatever', other: 'a'};
40 | var expectedSettings = {refresh_rate: 'doesntmatter', theme: 'whatever'};
41 | service.settings = settings;
42 | service.saveSettings();
43 | expect(localStorage.setItem).toHaveBeenCalledWith('kopfSettings',
44 | JSON.stringify(expectedSettings));
45 | expect(service.loadLocalSettings()).toEqual(expectedSettings);
46 | });
47 |
48 | it("should handle invalid sotred content",
49 | function() {
50 | localStorage.setItem('kopfSettings', "invalid json");
51 | spyOn(this.DebugService, 'debug').andReturn(true);
52 | var settings = service.loadLocalSettings();
53 | expect(localStorage.getItem).toHaveBeenCalledWith('kopfSettings');
54 | expect(this.DebugService.debug).toHaveBeenCalled();
55 | expect(settings).toEqual({});
56 | });
57 |
58 | it("should handle invalid sotred content",
59 | function() {
60 | localStorage.setItem('kopfSettings', "invalid json");
61 | spyOn(this.DebugService, 'debug').andReturn(true);
62 | var settings = service.loadLocalSettings();
63 | expect(localStorage.getItem).toHaveBeenCalledWith('kopfSettings');
64 | expect(this.DebugService.debug).toHaveBeenCalled();
65 | expect(settings).toEqual({});
66 | });
67 |
68 | it("should correctly update the updatable settings",
69 | function() {
70 | var settings = {refresh_rate: '1', theme: 'whatever'};
71 | service.settings = {};
72 | service.updateSettings(settings);
73 | expect(service.settings).toEqual(settings);
74 | });
75 |
76 | it("should update ONLY the updatable settings",
77 | function() {
78 | var settings = {
79 | refresh_rate: '1',
80 | theme: 'whatever',
81 | elasticsearch_root_path: 'blah blah',
82 | with_credentials: 'nono'
83 | };
84 | service.settings = {};
85 | service.updateSettings(settings);
86 | expect(service.settings).toEqual({refresh_rate: '1', theme: 'whatever'});
87 | });
88 |
89 | });
90 |
--------------------------------------------------------------------------------
/src/kopf/controllers/warmers.js:
--------------------------------------------------------------------------------
1 | kopf.controller('WarmersController', [
2 | '$scope', 'ConfirmDialogService', 'AlertService', 'AceEditorService',
3 | 'ElasticService',
4 | function($scope, ConfirmDialogService, AlertService, AceEditorService,
5 | ElasticService) {
6 | $scope.editor = undefined;
7 | $scope.indices = [];
8 | $scope.index = null;
9 | $scope.paginator = new Paginator(1, 10, [], new WarmerFilter(''));
10 | $scope.page = $scope.paginator.getPage();
11 |
12 | $scope.warmer = new Warmer('', '', {types: [], source: {}});
13 |
14 | $scope.warmers = [];
15 |
16 | $scope.$watch(
17 | function() {
18 | return ElasticService.cluster;
19 | },
20 | function(filter, previous) {
21 | $scope.indices = ElasticService.getIndices();
22 | },
23 | true
24 | );
25 |
26 | $scope.$watch('paginator', function(filter, previous) {
27 | $scope.page = $scope.paginator.getPage();
28 | }, true);
29 |
30 | $scope.initEditor = function() {
31 | if (!angular.isDefined($scope.editor)) {
32 | $scope.editor = AceEditorService.init('warmer-editor');
33 | }
34 | };
35 |
36 | $scope.createWarmer = function() {
37 | if ($scope.editor.hasContent()) {
38 | $scope.editor.format();
39 | if (!isDefined($scope.editor.error)) {
40 | $scope.warmer.source = $scope.editor.getValue();
41 | ElasticService.registerWarmer($scope.warmer,
42 | function(response) {
43 | $scope.loadIndexWarmers();
44 | AlertService.success('Warmer successfully created', response);
45 | },
46 | function(error) {
47 | AlertService.error('Request returned invalid JSON', error);
48 | }
49 | );
50 | }
51 | } else {
52 | AlertService.error('Warmer query can\'t be empty');
53 | }
54 | };
55 |
56 | $scope.deleteWarmer = function(warmer) {
57 | ConfirmDialogService.open(
58 | 'are you sure you want to delete warmer ' + warmer.id + '?',
59 | warmer.source,
60 | 'Delete',
61 | function() {
62 | ElasticService.deleteWarmer(warmer, // FIXME: better send name + id
63 | function(response) {
64 | AlertService.success('Warmer successfully deleted', response);
65 | $scope.loadIndexWarmers();
66 | },
67 | function(error) {
68 | AlertService.error('Error while deleting warmer', error);
69 | }
70 | );
71 | }
72 | );
73 | };
74 |
75 | $scope.loadIndexWarmers = function() {
76 | if (isDefined($scope.index)) {
77 | ElasticService.getIndexWarmers($scope.index, '',
78 | function(warmers) {
79 | $scope.paginator.setCollection(warmers);
80 | $scope.page = $scope.paginator.getPage();
81 | },
82 | function(error) {
83 | $scope.paginator.setCollection([]);
84 | $scope.page = $scope.paginator.getPage();
85 | AlertService.error('Error while fetching warmers', error);
86 | }
87 | );
88 | } else {
89 | $scope.paginator.setCollection([]);
90 | $scope.page = $scope.paginator.getPage();
91 | }
92 | };
93 |
94 | $scope.initializeController = function() {
95 | $scope.indices = ElasticService.getIndices();
96 | $scope.initEditor();
97 | };
98 |
99 | }
100 | ]);
101 |
--------------------------------------------------------------------------------