├── .drone.sec ├── .drone.yml ├── .gitignore ├── .travis.yml ├── Dockerfile ├── Makefile ├── README.md ├── consul.cfg ├── html ├── consul │ ├── .gitkeep │ ├── index.html │ └── static │ │ ├── application.min.js │ │ ├── base.css │ │ ├── base.css.map │ │ ├── bootstrap.min.css │ │ ├── consul-logo.png │ │ ├── favicon.png │ │ └── loading-cylon-purple.svg ├── index.html ├── mesos │ ├── browse.html │ ├── css │ │ ├── bootstrap-3.0.3.min.css │ │ └── mesos.css │ ├── directives │ │ ├── pagination.html │ │ ├── tableHeader.html │ │ └── timestamp.html │ ├── fonts │ │ ├── glyphicons-halflings-regular.eot │ │ ├── glyphicons-halflings-regular.svg │ │ ├── glyphicons-halflings-regular.ttf │ │ └── glyphicons-halflings-regular.woff │ ├── framework.html │ ├── frameworks.html │ ├── home.html │ ├── ico │ │ └── favicon.ico │ ├── img │ │ └── loading.gif │ ├── index.html │ ├── js │ │ ├── angular-1.2.3.js │ │ ├── angular-1.2.3.min.js │ │ ├── angular-route-1.2.3.js │ │ ├── angular-route-1.2.3.min.js │ │ ├── app.js │ │ ├── controllers.js │ │ ├── jquery-1.7.1.js │ │ ├── jquery-1.7.1.min.js │ │ ├── jquery.pailer.js │ │ ├── relative-date.js │ │ ├── services.js │ │ ├── ui-bootstrap-tpls-0.9.0.js │ │ ├── ui-bootstrap-tpls-0.9.0.min.js │ │ ├── underscore-1.4.3.js │ │ ├── underscore-1.4.3.min.js │ │ ├── zeroclipboard-1.1.7.js │ │ └── zeroclipboard-1.1.7.min.js │ ├── obj │ │ └── zeroclipboard-1.1.7.swf │ ├── offers.html │ ├── pailer.html │ ├── slave.html │ ├── slave_executor.html │ ├── slave_framework.html │ └── slaves.html ├── traefik │ └── dashboard │ │ ├── index.html │ │ ├── scripts │ │ ├── app-34497b2210.js │ │ └── vendor-b67094242f.js │ │ └── styles │ │ ├── app-8448ca8e46.css │ │ └── vendor-bfe0181a5e.css └── ui │ ├── appcache.appcache │ ├── images │ ├── bg.jpg │ ├── bg@2x.jpg │ ├── favicon.ico │ └── logos │ │ ├── chronos.png │ │ ├── chronos@2x.png │ │ ├── consul.png │ │ ├── consul@2x.png │ │ ├── elasticsearch.png │ │ ├── elasticsearch@2x.png │ │ ├── kibana.png │ │ ├── kibana@2x.png │ │ ├── kubernetes.png │ │ ├── kubernetes@2x.png │ │ ├── marathon.png │ │ ├── marathon@2x.png │ │ ├── mesos.png │ │ ├── mesos@2x.png │ │ ├── traefik.png │ │ └── traefik@2x.png │ ├── index.html │ ├── index.html.gz │ ├── scripts │ ├── lib.js │ ├── lib.js.gz │ ├── main.js │ └── main.js.gz │ ├── signature │ └── styles │ ├── app.css │ └── app.css.gz ├── nginx.tmpl ├── screenshot.png └── services.json.tmpl /.drone.sec: -------------------------------------------------------------------------------- 1 | eyJhbGciOiJSU0EtT0FFUCIsImVuYyI6IkExMjhHQ00ifQ.WBAxA-J8o7qRRJymfkuRoZt8Nd-4ZB0xR-n4smS5YL6_12_nEalnrNcHoXo5O18EpHukhDMo5mEBoFEhxiVKqvTQyxzFK38Oa0XrFR21IijBXGgXvChpRpUYsLlBAIg3u4R7rfpDSPtBHNGrCd9Ou9Dz26ZnGh9maSY5YtCZ534YOnAnsUgEr_zVJHGlqY2S-g0_4iZsChFfo6uEbaIyww7DIe83Es8lY_bI0fU7AT1QcAXvLcODvJGzPlbPKnw6AULO_hVEm1E2br_TmkjTNSYO9PO_CXYu5WzpcL1lGjsebNkakYq-_yYF_sFPEtjs6YAq9e8LmmPASPKr2pNA1g.MkujUp2AiK5up-X_.z5DaR5nZ-iHlqQ_qzFgarcXLjSA8D4SoDsvj_sQyxxasqGfGKrg98SbJe7LKvKvJsavrOCPb_YK1SG4mGdf1vnOBIinzTLO6Cx-L4jznYDbCgV3TfPvOLYC2XxamRQFZD2tOwV1pE72aCw-ZVmH_zHzHCm8N68EWcbqQD3oiUUKOYv8n_xsiXkiChyiq0KZx2pOM7I_jZq58FAL9kTjGqYtjAlSha6gH5yi5QIfT4gPSR-q6CgnXc57YJA.nPXFX_Izl_sKIsh-JwlsDQ -------------------------------------------------------------------------------- /.drone.yml: -------------------------------------------------------------------------------- 1 | build: 2 | image: plugins/drone-git 3 | commands: 4 | - true 5 | publish: 6 | docker: 7 | username: $$DOCKER_USERNAME 8 | password: $$DOCKER_PASSWORD 9 | email: $$DOCKER_EMAIL 10 | repo: ciscocloud/nginx-mantlui 11 | tag: dev 12 | when: 13 | branch: master 14 | docker: 15 | username: $$DOCKER_USERNAME 16 | password: $$DOCKER_PASSWORD 17 | email: $$DOCKER_EMAIL 18 | repo: ciscocloud/nginx-mantlui 19 | tag: 20 | - $$TAG 21 | - latest 22 | when: 23 | event: tag -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /secrets.yml 2 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | services: 2 | - docker 3 | 4 | script: 5 | - make build 6 | 7 | before_deploy: 8 | - docker login -e="$DOCKER_EMAIL" -u="$DOCKER_USERNAME" -p="$DOCKER_PASSWORD" 9 | 10 | deploy: 11 | - provider: script 12 | skip_cleanup: true 13 | script: 14 | - make pushedge 15 | on: 16 | branch: master 17 | - provider: script 18 | skip_cleanup: true 19 | script: 20 | - make push 21 | on: 22 | tags: true 23 | -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | FROM ciscocloud/nginx-consul:1.1 2 | MAINTAINER Ryan Eschinger 3 | 4 | RUN mkdir -p /usr/share/nginx 5 | COPY html /usr/share/nginx/html 6 | 7 | COPY nginx.tmpl /consul-template/templates/ 8 | COPY services.json.tmpl /consul-template/templates/ 9 | COPY consul.cfg /consul-template/config.d/ 10 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | .PHONY: build tag push rmi clean 2 | 3 | DOCKER = docker 4 | REPO = ciscocloud 5 | NAME = nginx-mantlui 6 | VERSION = 0.7.2 7 | 8 | build: 9 | find . -name ".DS_Store" -depth -exec rm {} \; 10 | $(DOCKER) build -t $(NAME) . 11 | 12 | tag: build 13 | $(DOCKER) tag $(NAME) $(REPO)/$(NAME):latest 14 | $(DOCKER) tag $(NAME) $(REPO)/$(NAME):$(VERSION) 15 | 16 | push: tag 17 | $(DOCKER) push $(REPO)/$(NAME):latest 18 | $(DOCKER) push $(REPO)/$(NAME):$(VERSION) 19 | 20 | pushedge: build 21 | docker tag $(NAME) $(REPO)/$(NAME):edge 22 | docker push $(REPO)/$(NAME):edge 23 | 24 | rmi: 25 | $(DOCKER) rmi $(NAME) $(REPO)/$(NAME):latest $(REPO)/$(NAME):$(VERSION) || true 26 | 27 | clean: rmi 28 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # nginx-mantlui 2 | 3 | Docker image derived from [nginx-consul](nginx-consul) that includes a simple user interface to [Mantl](http://mantl.io/) components. 4 | 5 | ![Screenshot](screenshot.png) 6 | 7 | ## Setup 8 | 9 | This image is integrated tightly with Mantl clusters and is not likely to be useful outside of one (except, perhaps, as an example of using nginx-consul). In Mantl, it is configured to run on all control nodes and, based on Consul service discovery, consolidates various control UIs and APIs into a single place. 10 | 11 | ## User Interfaces 12 | 13 | ### Mesos UI (/mesos) 14 | 15 | The 0.26 Mesos UI is included in the nginx-mantlui container. The following source files have been modified: 16 | 17 | * html/mesos/browse.html 18 | * html/mesos/index.html 19 | * html/mesos/js/app.js 20 | * html/mesos/js/controllers.js 21 | * html/mesos/js/services.js 22 | * html/mesos/pailer.html 23 | 24 | The changes are minor &mdash they are just small changes to URLs so that the UI works properly through the proxy. 25 | 26 | The proxy ensures that you are always connected to the UI on the leading Mesos master. This functionality depends on [mesos-consul](https://github.com/CiscoCloud/mesos-consul). Also, every Mesos agent is proxied so that the agent APIs are reachable. 27 | 28 | ### Marathon UI and API (/marathon) 29 | 30 | The nginx configuration just proxies requests to the underlying Marathon UI and API. 31 | 32 | ### Consul UI (/consul) 33 | 34 | The 0.5.2 Consul UI is included in the nginx-mantlui container. The only update is to the `html/consul/application.min.js` file and it is to adjust the path to properly communicate to the Consul API through the proxy. The update was made in [this commit](https://github.com/CiscoCloud/nginx-mantlui/commit/5ab35f3819fb81a1bbb9120d258e1b42dfbbd207) with the following command: 35 | 36 | ```shell 37 | find . -type f -exec sed -i '' 's/\/v1/\/consul\/v1/g' {} + 38 | ``` 39 | 40 | ### Chronos UI (/chronos) 41 | 42 | The nginx configuration just proxies requests to the underlying Marathon UI and API. 43 | 44 | ### Mantl API (/api) 45 | 46 | The nginx configuration just proxies requests to [mantl-api](https://github.com/CiscoCloud/mantl-api) running in the cluster. 47 | 48 | ### Traefik (/traefik) 49 | 50 | The Traefik UI is embedded in the Traefik binary running on edge nodes. We need to override a few files to change the API urls that the Traefik UI depends on. These files are: 51 | 52 | * html/traefik/dashboard/index.html 53 | * html/traefik/dashboard/scripts/app-*.js 54 | * html/traefik/dashboard/scripts/vendor-*.js 55 | 56 | Because Traefik script file names include a content hash, we must rebuild them with our changes. It is important that the script files match the version of Traefik that is running in Mantl. 57 | 58 | #### Building Traefik Assets 59 | 60 | 1. Clone the Traefik repository 61 | 62 | ```shell 63 | git clone git@github.com:emilevauge/traefik.git 64 | cd traefik 65 | ``` 66 | 67 | 2. Checkout the tag of the correct Traefik release. For example: 68 | 69 | ```shell 70 | git checkout -b r475 v1.0.0-beta.475 71 | ``` 72 | 73 | 3. Update the webui API paths. 74 | 75 | At the time of this writing, the changes look like this: 76 | 77 | ```diff 78 | diff --git a/webui/src/app/core/health.resource.js b/webui/src/app/core/health.resource.js 79 | index a765462..f50b001 100644 80 | --- a/webui/src/app/core/health.resource.js 81 | +++ b/webui/src/app/core/health.resource.js 82 | @@ -7,7 +7,7 @@ 83 | 84 | /** @ngInject */ 85 | function Health($resource) { 86 | - return $resource('/health'); 87 | + return $resource('/traefik/health'); 88 | } 89 | 90 | })(); 91 | diff --git a/webui/src/app/core/providers.resource.js b/webui/src/app/core/providers.resource.js 92 | index c363fd2..dcf4a60 100644 93 | --- a/webui/src/app/core/providers.resource.js 94 | +++ b/webui/src/app/core/providers.resource.js 95 | @@ -7,7 +7,7 @@ 96 | 97 | /** @ngInject */ 98 | function Providers($resource) { 99 | - return $resource('/api/providers'); 100 | + return $resource('/traefik/api/providers'); 101 | } 102 | 103 | })(); 104 | 105 | ``` 106 | 107 | 4. Generate Docker image 108 | 109 | ```shell 110 | make build 111 | ``` 112 | 113 | 5. Generate the Web UI 114 | 115 | ```shell 116 | make generate-webui 117 | ``` 118 | 119 | 6. Copy the script files from `./static` to the nginx-mantlui project. For example: 120 | 121 | ```shell 122 | cp static/index.html ../nginx-mantlui/html/traefik/dashboard/ 123 | cp static/scripts/app-34497b2210.js ../nginx-mantlui/html/traefik/dashboard/scripts 124 | cp static/scripts/vendor-b67094242f.js ../nginx-mantlui/html/traefik/dashboard/scripts 125 | ``` 126 | 127 | You will have to adjust the file names to use the content hash that is appropriate for the release you are building. 128 | 129 | 7. Delete any old script files from traefik scripts directory (if applicable): 130 | 131 | ```shell 132 | git rm html/traefik/dashboard/scripts/app-a08ab8d76c.js 133 | git rm html/traefik/dashboard/scripts/vendor-3ce5552a6a.js 134 | ``` 135 | -------------------------------------------------------------------------------- /consul.cfg: -------------------------------------------------------------------------------- 1 | template { 2 | source = "/consul-template/templates/nginx.tmpl" 3 | destination = "/etc/nginx/nginx.conf" 4 | command = "/scripts/nginx-run.sh || true" 5 | } 6 | 7 | template { 8 | source = "/consul-template/templates/services.json.tmpl" 9 | destination = "/usr/share/nginx/html/_internal/services.json" 10 | } -------------------------------------------------------------------------------- /html/consul/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mantl/nginx-mantlui/174479309553be933bd1e978c706b94739f90dd1/html/consul/.gitkeep -------------------------------------------------------------------------------- /html/consul/static/base.css.map: -------------------------------------------------------------------------------- 1 | { 2 | "version": 3, 3 | "mappings": "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", 4 | "sources": ["../styles/_type.scss","../styles/_variables.scss","../styles/_mixins.scss","../styles/_panels.scss","../styles/_nav.scss","../styles/_buttons.scss","../styles/_lists.scss","../styles/_forms.scss","../styles/_notifications.scss","../styles/base.scss"], 5 | "names": [], 6 | "file": "base.css" 7 | } 8 | -------------------------------------------------------------------------------- /html/consul/static/consul-logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mantl/nginx-mantlui/174479309553be933bd1e978c706b94739f90dd1/html/consul/static/consul-logo.png -------------------------------------------------------------------------------- /html/consul/static/favicon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mantl/nginx-mantlui/174479309553be933bd1e978c706b94739f90dd1/html/consul/static/favicon.png -------------------------------------------------------------------------------- /html/consul/static/loading-cylon-purple.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /html/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Mantl - Redirecting 5 | 6 | 7 | 8 | 9 | Redirecting to UI. If you are not automatically 10 | redirected, follow this link. 11 | 12 | 13 | -------------------------------------------------------------------------------- /html/mesos/browse.html: -------------------------------------------------------------------------------- 1 | 13 | 14 | 22 | 23 |
24 | 25 | {{alert_message}} 26 |
27 | 28 |
29 |
30 |
31 |
32 | No files in this directory. 33 |
34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 69 | 77 | 78 | 79 |
modenlinkuidgidsizemtime
{{file.mode}}{{file.nlink}}{{file.uid}}{{file.gid}}{{file.size | dataSize}}{{file.mtime * 1000 | unixDate}} 56 | 57 | 58 | 59 | {{basename(file.path)}} 60 | 61 | 62 | 63 | 64 | 65 | {{basename(file.path)}} 66 | 67 | 68 | 70 | 72 | 75 | 76 |
80 |
81 |
82 |
83 | -------------------------------------------------------------------------------- /html/mesos/css/mesos.css: -------------------------------------------------------------------------------- 1 | /* 2 | * BOOTSTRAP OVERRIDES 3 | */ 4 | 5 | .table thead th { 6 | vertical-align: top; 7 | } 8 | 9 | .breadcrumb > li + li:before { 10 | /* Default content includes a space after the slash, which spreads breadcrumbs 11 | * out too much. Remove the trailing space here. 12 | */ 13 | content: "/"; 14 | } 15 | 16 | /* 17 | * /BOOTSTRAP OVERRIDES 18 | */ 19 | 20 | 21 | body { 22 | /* Add space for fixed position navbar. */ 23 | padding-top: 60px; 24 | } 25 | 26 | @media (max-width: 979px) { 27 | body { 28 | /* Mobile devices 979px and narrower use a statically positioned navbar */ 29 | /* and therefore need no padding for the . */ 30 | /* */ 31 | /* @see http://twitter.github.io/bootstrap/scaffolding.html#responsive */ 32 | padding-top: 0; 33 | } 34 | } 35 | 36 | dl.inline dt, dl.inline dd { 37 | float: left; 38 | } 39 | 40 | dl.inline dd { 41 | margin-left: 5px; 42 | } 43 | 44 | dl.inline dd + dt, dl.inline dd + dd { 45 | clear: left; 46 | } 47 | 48 | dl.inline dd + dd { 49 | float: none; 50 | } 51 | 52 | dl.inline dt { 53 | font-weight: bold 54 | } 55 | 56 | th.descending:after { 57 | padding-left: 5px; 58 | content: "▼"; 59 | } 60 | 61 | th.ascending:after { 62 | padding-left: 5px; 63 | content: "▲"; 64 | } 65 | 66 | th.unselected:after { 67 | padding-left: 5px; 68 | content: " "; 69 | } 70 | 71 | time { 72 | border-bottom: 1px #999 dashed; 73 | } 74 | 75 | time:hover { 76 | cursor: pointer; 77 | } 78 | 79 | .inline .btn-toggle, 80 | .table-condensed .btn-toggle { 81 | margin-bottom: -2px; 82 | margin-top: -5px; 83 | visibility: hidden; 84 | } 85 | 86 | .inline .zeroclipboard-is-hover, 87 | .table-condensed .zeroclipboard-is-hover, 88 | .flash dd:hover .btn-toggle, 89 | .flash td:hover .btn-toggle { 90 | visibility: visible; 91 | } 92 | 93 | .zeroclipboard-is-hover { 94 | background-color: #e6e6e6; 95 | background-position: 0 -15px; 96 | } 97 | 98 | .zeroclipboard-is-active { 99 | background-image: none; 100 | } 101 | 102 | .badge-type { 103 | -moz-border-radius: 3px; 104 | -webkit-border-radius: 3px; 105 | border-radius: 3px; 106 | font-weight: normal; 107 | padding-left: 4px; 108 | padding-right: 4px; 109 | text-shadow: none; 110 | } 111 | 112 | a.badge-type:hover { 113 | text-decoration: underline; 114 | } 115 | 116 | .table .text-right { 117 | text-align: right; 118 | } 119 | 120 | .well .inline:first-child { 121 | margin-top: 0; 122 | } 123 | 124 | .well .inline:last-child, 125 | .well .table:last-child { 126 | margin-bottom: 0; 127 | } 128 | 129 | /* Force pointer cursor for anchors generated by Angular UI's pagination 130 | * directive. The links are generated without "href" attributes, which means 131 | * they don't match "a:link" and won't get the pointer cursor by default. 132 | */ 133 | .pagination a { 134 | cursor: pointer; 135 | } 136 | 137 | .input-group-header { 138 | margin-top: 20px; 139 | } 140 | 141 | .input-group-addon { 142 | -webkit-transition: background-color 0.25s, color 0.25s; 143 | transition: background-color 0.25s, color 0.25s; 144 | } 145 | 146 | .input-group-addon-success { 147 | background-color: #EFFFF0; 148 | color: #3C763D; 149 | } 150 | 151 | .input-group-inner-right { 152 | position: absolute; 153 | right: 1px; 154 | padding: 4px 10px; 155 | top: 1px; 156 | } 157 | 158 | .btn-clear { 159 | background: transparent; 160 | } 161 | 162 | .btn-clear:active { 163 | -webkit-box-shadow: none; 164 | box-shadow: none; 165 | } 166 | -------------------------------------------------------------------------------- /html/mesos/directives/pagination.html: -------------------------------------------------------------------------------- 1 | 7 | -------------------------------------------------------------------------------- /html/mesos/directives/tableHeader.html: -------------------------------------------------------------------------------- 1 |
2 |
3 |

{{headerTitle}}

4 |
5 |
6 |
8 | 10 | 11 | 12 | 14 | 18 |
19 |
20 |
21 | -------------------------------------------------------------------------------- /html/mesos/directives/timestamp.html: -------------------------------------------------------------------------------- 1 | 3 | 5 | 6 | -------------------------------------------------------------------------------- /html/mesos/fonts/glyphicons-halflings-regular.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mantl/nginx-mantlui/174479309553be933bd1e978c706b94739f90dd1/html/mesos/fonts/glyphicons-halflings-regular.eot -------------------------------------------------------------------------------- /html/mesos/fonts/glyphicons-halflings-regular.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mantl/nginx-mantlui/174479309553be933bd1e978c706b94739f90dd1/html/mesos/fonts/glyphicons-halflings-regular.ttf -------------------------------------------------------------------------------- /html/mesos/fonts/glyphicons-halflings-regular.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mantl/nginx-mantlui/174479309553be933bd1e978c706b94739f90dd1/html/mesos/fonts/glyphicons-halflings-regular.woff -------------------------------------------------------------------------------- /html/mesos/framework.html: -------------------------------------------------------------------------------- 1 | 10 | 11 |
12 | 13 | {{alert_message}} 14 |
15 | 16 |
17 |
18 |
19 |
20 |
Name:
21 |
{{framework.name}}
22 |
Web UI:
23 |
{{framework.webui_url}}
24 |
User:
25 |
{{framework.user}}
26 |
Registered:
27 |
28 | 29 |
30 |
Re-registered:
31 |
-
32 |
33 | 34 |
35 |
Active tasks:
36 |
{{framework.tasks.length | number}}
37 |
CPUs:
38 |
{{framework.resources.cpus | number}}
39 |
Mem:
40 |
{{framework.resources.mem * (1024 * 1024) | dataSize}}
41 |
42 |
43 |
44 | 45 |
46 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 65 | 66 | 67 | 70 | 78 | 86 | 87 | 88 |
IDNameStateStartedHost
61 | 62 | {{task.id}} 63 | 64 | {{task.name}}{{task.state | truncateMesosState}} 68 | 69 | 71 | 72 | {{slaves[task.slave_id].hostname}} 73 | 74 | 75 | Slave offline 76 | 77 | 79 | 80 | Sandbox 81 | 82 | 83 | Slave offline 84 | 85 |
89 | 90 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 111 | 114 | 123 | 131 | 132 | 133 |
IDNameStateStartedStoppedHost
{{task.id}}{{task.name}}{{task.state | truncateMesosState}} 109 | 110 | 112 | 113 | 115 | 117 | {{slaves[task.slave_id].hostname}} 118 | 119 | 120 | Slave offline 121 | 122 | 124 | 125 | Sandbox 126 | 127 | 128 | Slave offline 129 | 130 |
134 |
135 |
136 | -------------------------------------------------------------------------------- /html/mesos/frameworks.html: -------------------------------------------------------------------------------- 1 | 7 | 8 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 37 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 50 | 51 | 54 | 55 | 56 |
IDHostUserNameActive TasksCPUsMemMax ShareRegisteredRe-Registered
27 | 28 | {{(framework.id | truncateMesosID) || framework.name}} 29 | 36 | 38 | {{framework.hostname}} 39 | {{framework.hostname}} 40 | {{framework.user}}{{framework.name}}{{framework.tasks.length}}{{framework.resources.cpus | number}}{{framework.resources.mem * (1024 * 1024) | dataSize}}{{framework.max_share * 100 | number}}% 48 | 49 | - 52 | 53 |
57 | 58 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 83 | 84 | 85 | 86 | 89 | 92 | 93 | 94 |
IDHostUserNameRegisteredUnregistered
73 | 74 | {{framework.id | truncateMesosID}} 75 | 82 | {{framework.hostname}}{{framework.user}}{{framework.name}} 87 | 88 | 90 | 91 |
95 | -------------------------------------------------------------------------------- /html/mesos/home.html: -------------------------------------------------------------------------------- 1 | 7 | 8 |
9 |
10 |
11 |
12 |
Cluster:
13 |
14 | {{state.cluster}} 15 | 16 | (Unnamed) 17 | 20 | 21 |
22 |
Server:
23 |
{{state.pid.split("@")[1]}}
24 |
Version:
25 |
{{state.version}}
26 |
Built:
27 |
28 | 29 | by {{state.build_user}} 30 | 31 |
32 |
Started:
33 |
34 | 35 |
36 |
Elected:
37 |
38 | 39 |
40 |
41 | 42 |

LOG

43 | 44 |

Slaves

45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 |
Activated{{activated_slaves | number}}
Deactivated{{deactivated_slaves | number}}
57 | 58 |

Tasks

59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 |
Staged{{staged_tasks | number}}
Started{{started_tasks | number}}
Finished{{finished_tasks | number}}
Killed{{killed_tasks | number}}
Failed{{failed_tasks | number}}
Lost{{lost_tasks | number}}
88 | 89 |

Resources

90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | 110 | 111 | 112 | 113 | 114 | 115 | 116 | 117 | 118 | 119 | 120 |
CPUsMem
Total{{total_cpus | decimalFloat}}{{total_mem * (1024 * 1024) | dataSize}}
Used{{used_cpus | decimalFloat}}{{used_mem * (1024 * 1024) | dataSize}}
Offered{{offered_cpus | decimalFloat}}{{offered_mem * (1024 * 1024) | dataSize}}
Idle{{idle_cpus | decimalFloat}}{{idle_mem * (1024 * 1024) | dataSize}}
121 |
122 |
123 | 124 |
125 | 127 | 128 | 129 | 130 | 131 | 132 | 133 | 134 | 135 | 136 | 137 | 138 | 139 | 140 | 141 | 142 | 147 | 148 | 149 | 152 | 160 | 168 | 169 | 170 |
IDNameStateStartedHost
No active tasks.
143 | 144 | {{task.id}} 145 | 146 | {{task.name}}{{task.state | truncateMesosState}} 150 | 151 | 153 | 154 | {{slaves[task.slave_id].hostname}} 155 | 156 | 157 | Slave offline 158 | 159 | 161 | 162 | Sandbox 163 | 164 | 165 | Slave offline 166 | 167 |
171 | 172 | 174 | 175 | 176 | 177 | 178 | 179 | 180 | 181 | 182 | 183 | 184 | 185 | 186 | 187 | 188 | 189 | 190 | 191 | 192 | 193 | 196 | 199 | 208 | 216 | 217 | 218 |
IDNameStateStartedStoppedHost
No completed tasks.
{{task.id}}{{task.name}}{{task.state | truncateMesosState}} 194 | 195 | 197 | 198 | 200 | 202 | {{slaves[task.slave_id].hostname}} 203 | 204 | 205 | Slave offline 206 | 207 | 209 | 210 | Sandbox 211 | 212 | 213 | Slave offline 214 | 215 |
219 |
220 |
221 | -------------------------------------------------------------------------------- /html/mesos/ico/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mantl/nginx-mantlui/174479309553be933bd1e978c706b94739f90dd1/html/mesos/ico/favicon.ico -------------------------------------------------------------------------------- /html/mesos/img/loading.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mantl/nginx-mantlui/174479309553be933bd1e978c706b94739f90dd1/html/mesos/img/loading.gif -------------------------------------------------------------------------------- /html/mesos/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Mesos 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 |
18 | 19 |
20 | 21 |
22 | 55 | 56 |
57 |
58 | No master is currently leading ... 59 |
60 |
61 | 62 | This master is not the leader, redirecting in {{redirect / 1000}} seconds ... 63 | go now 64 |
65 | 66 |
67 |

68 | {{ alert.title }} 69 |

70 | {{ alert.message }} 71 |
    72 |
  • 73 | {{ bullet }} 74 |
  • 75 |
76 |
77 | 78 |
79 |
80 |
81 | 82 | 95 | 96 | 110 | 111 | 112 | 113 | 114 | 115 | 116 | 117 | 118 | 119 | 120 | 121 | 122 | 123 | 124 | -------------------------------------------------------------------------------- /html/mesos/js/angular-route-1.2.3.min.js: -------------------------------------------------------------------------------- 1 | /* 2 | AngularJS v1.2.3 3 | (c) 2010-2014 Google, Inc. http://angularjs.org 4 | License: MIT 5 | */ 6 | (function(t,c,A){'use strict';function x(r,m,d,b,h){return{restrict:"ECA",terminal:!0,priority:400,transclude:"element",link:function(l,z,k,B,w){function v(){g&&(g.$destroy(),g=null);q&&(h.leave(q),q=null)}function u(){var a=r.current&&r.current.locals,e=a&&a.$template;if(e){var y=l.$new(),s=w(y,c.noop);s.html(e);h.enter(s,null,q||z,function(){!c.isDefined(n)||n&&!l.$eval(n)||m()});v();var e=d(s.contents()),f=r.current;g=f.scope=y;q=s;f.controller&&(a.$scope=g,a=b(f.controller,a),f.controllerAs&& 7 | (g[f.controllerAs]=a),s.data("$ngControllerController",a),s.children().data("$ngControllerController",a));e(g);g.$emit("$viewContentLoaded");g.$eval(p)}else v()}var g,q,n=k.autoscroll,p=k.onload||"";l.$on("$routeChangeSuccess",u);u()}}}t=c.module("ngRoute",["ng"]).provider("$route",function(){function r(b,h){return c.extend(new (c.extend(function(){},{prototype:b})),h)}function m(b,c){var l=c.caseInsensitiveMatch,d={originalPath:b,regexp:b},k=d.keys=[];b=b.replace(/([().])/g,"\\$1").replace(/(\/)?:(\w+)([\?|\*])?/g, 8 | function(b,c,h,d){b="?"===d?d:null;d="*"===d?d:null;k.push({name:h,optional:!!b});c=c||"";return""+(b?"":c)+"(?:"+(b?c:"")+(d&&"(.+?)"||"([^/]+)")+(b||"")+")"+(b||"")}).replace(/([\/$\*])/g,"\\$1");d.regexp=RegExp("^"+b+"$",l?"i":"");return d}var d={};this.when=function(b,h){d[b]=c.extend({reloadOnSearch:!0},h,b&&m(b,h));if(b){var l="/"==b[b.length-1]?b.substr(0,b.length-1):b+"/";d[l]=c.extend({redirectTo:b},m(l,h))}return this};this.otherwise=function(b){this.when(null,b);return this};this.$get= 9 | ["$rootScope","$location","$routeParams","$q","$injector","$http","$templateCache","$sce",function(b,h,l,m,k,t,w,v){function u(){var a=g(),e=p.current;if(a&&e&&a.$$route===e.$$route&&c.equals(a.pathParams,e.pathParams)&&!a.reloadOnSearch&&!n)e.params=a.params,c.copy(e.params,l),b.$broadcast("$routeUpdate",e);else if(a||e)n=!1,b.$broadcast("$routeChangeStart",a,e),(p.current=a)&&a.redirectTo&&(c.isString(a.redirectTo)?h.path(q(a.redirectTo,a.params)).search(a.params).replace():h.url(a.redirectTo(a.pathParams, 10 | h.path(),h.search())).replace()),m.when(a).then(function(){if(a){var b=c.extend({},a.resolve),e,f;c.forEach(b,function(a,e){b[e]=c.isString(a)?k.get(a):k.invoke(a)});c.isDefined(e=a.template)?c.isFunction(e)&&(e=e(a.params)):c.isDefined(f=a.templateUrl)&&(c.isFunction(f)&&(f=f(a.params)),f=v.getTrustedResourceUrl(f),c.isDefined(f)&&(a.loadedTemplateUrl=f,e=t.get(f,{cache:w}).then(function(a){return a.data})));c.isDefined(e)&&(b.$template=e);return m.all(b)}}).then(function(d){a==p.current&&(a&&(a.locals= 11 | d,c.copy(a.params,l)),b.$broadcast("$routeChangeSuccess",a,e))},function(c){a==p.current&&b.$broadcast("$routeChangeError",a,e,c)})}function g(){var a,b;c.forEach(d,function(d,l){var f;if(f=!b){var g=h.path();f=d.keys;var m={};if(d.regexp)if(g=d.regexp.exec(g)){for(var k=1,q=g.length;k 3) { 58 | return '…' + truncatedIdParts.splice(3, 3).join('-'); 59 | } else { 60 | return id; 61 | } 62 | } else { 63 | return ''; 64 | } 65 | }; 66 | }) 67 | .filter('truncateMesosState', function() { 68 | return function(state) { 69 | // Remove the "TASK_" prefix. 70 | return state.substring(5); 71 | }; 72 | }) 73 | .filter('isoDate', function($filter) { 74 | return function(date) { 75 | var i = parseInt(date, 10); 76 | if (_.isNaN(i)) { return '' }; 77 | return $filter('date')(i, 'yyyy-MM-ddTH:mm:ssZ'); 78 | }; 79 | }) 80 | .filter('relativeDate', function() { 81 | return function(date, refDate) { 82 | var i = parseInt(date, 10); 83 | if (_.isNaN(i)) { return '' }; 84 | return relativeDate(i, refDate); 85 | }; 86 | }) 87 | .filter('slice', function() { 88 | return function(array, begin, end) { 89 | if (_.isArray(array)) { 90 | return array.slice(begin, end); 91 | } 92 | }; 93 | }) 94 | .filter('unixDate', function($filter) { 95 | return function(date) { 96 | if ((new Date(date)).getFullYear() == (new Date()).getFullYear()) { 97 | return $filter('date')(date, 'MMM dd HH:mm'); 98 | } else { 99 | return $filter('date')(date, 'MMM dd yyyy'); 100 | } 101 | }; 102 | }) 103 | .filter('dataSize', function() { 104 | var BYTES_PER_KB = Math.pow(2, 10); 105 | var BYTES_PER_MB = Math.pow(2, 20); 106 | var BYTES_PER_GB = Math.pow(2, 30); 107 | 108 | return function(bytes) { 109 | if (bytes == null || isNaN(bytes)) { 110 | return ''; 111 | } else if (bytes < BYTES_PER_KB) { 112 | return bytes.toFixed() + ' B'; 113 | } else if (bytes < BYTES_PER_MB) { 114 | return (bytes / BYTES_PER_KB).toFixed() + ' KB'; 115 | } else if (bytes < BYTES_PER_GB) { 116 | return (bytes / BYTES_PER_MB).toFixed() + ' MB'; 117 | } else { 118 | return (bytes / BYTES_PER_GB).toFixed(1) + ' GB'; 119 | } 120 | }; 121 | }) 122 | // Defines the 'clipboard' directive, which integrates copying to the user's 123 | // clipboard with an Adobe Flash object via the ZeroClipboard library. 124 | // 125 | // Text to be copied on click is specified with the 'data-clipboard-text' 126 | // attribute. 127 | // 128 | // The 'mouseenter' and 'mouseleave' events from the Flash object are exposed 129 | // to the directive's element via the 'clipboardhover' event. There is no 130 | // differentiation between enter/leave; they are both called 'clipboardhover'. 131 | // 132 | // Example: 133 | // 134 | // 137 | // 138 | // See: http://zeroclipboard.github.io/ZeroClipboard/ 139 | .directive('clipboard', [function() { 140 | return { 141 | restrict: 'A', 142 | scope: true, 143 | template: '', 144 | 145 | link: function(scope, element, attrs) { 146 | var clip = new ZeroClipboard(element[0]); 147 | 148 | clip.on('mouseover', function() { 149 | angular.element(this).triggerHandler('clipboardhover'); 150 | }); 151 | 152 | clip.on('mouseout', function() { 153 | // TODO(ssorallen): Why is 'scope' incorrect here? It has to be 154 | // retrieved from the element explicitly to be correct. 155 | var elScope = angular.element(this).scope(); 156 | 157 | // Restore tooltip content to its original value if it was changed by 158 | // this Clipboard instance. 159 | if (elScope && elScope.tt_content_orig) { 160 | elScope.tt_content = elScope.tt_content_orig; 161 | delete elScope.tt_content_orig; 162 | } 163 | 164 | angular.element(this).triggerHandler('clipboardhover'); 165 | }); 166 | 167 | clip.on('complete', function() { 168 | // TODO(ssorallen): Why is 'scope' incorrect here? It has to be 169 | // retrieved from the element explicitly to be correct. 170 | var elScope = angular.element(this).scope(); 171 | 172 | if (elScope) { 173 | // Store the tooltip's original content so it can be restored when 174 | // the tooltip is hidden. 175 | elScope.tt_content_orig = elScope.tt_content; 176 | 177 | // Angular UI's Tooltip sets content on the element's scope in a 178 | // variable named 'tt_content'. The Tooltip has no public interface, 179 | // so set the value directly here to change the value of the tooltip 180 | // when content is successfully copied. 181 | elScope.tt_content = 'copied!'; 182 | elScope.$apply(); 183 | } 184 | }); 185 | 186 | clip.on('load', function() { 187 | // The 'load' event fires only if the Flash file loads successfully. 188 | // The copy buttons will only display if the class 'flash' exists 189 | // on an ancestor. 190 | // 191 | // Browsers with no flash support will not append the 'flash' class 192 | // and will therefore not see the copy buttons. 193 | angular.element('html').addClass('flash'); 194 | }); 195 | } 196 | }; 197 | }]) 198 | .directive('mTimestamp', [ '$rootScope', function($rootScope) { 199 | return { 200 | restrict: 'E', 201 | transclude: true, 202 | scope: { 203 | value: '@' 204 | }, 205 | link: function($scope, element, attrs) { 206 | $scope.longDate = JSON.parse( 207 | localStorage.getItem('longDate') || false); 208 | 209 | $scope.$on('mTimestamp.toggle', function() { 210 | $scope.longDate = !$scope.longDate; 211 | }); 212 | 213 | $scope.toggle = function() { 214 | localStorage.setItem('longDate', !$scope.longDate); 215 | $rootScope.$broadcast('mTimestamp.toggle'); 216 | }; 217 | }, 218 | templateUrl: 'directives/timestamp.html' 219 | } 220 | }]) 221 | .directive('mPagination', function() { 222 | return { templateUrl: 'directives/pagination.html' } 223 | }) 224 | .directive('mTableHeader', function() { 225 | return { templateUrl: 'directives/tableHeader.html' } 226 | }) 227 | .directive('mTable', ['$compile', '$filter', function($compile, $filter) { 228 | /* This directive does not have a template. The DOM doesn't like 229 | * having partially defined tables and so they don't work well with 230 | * directives and templates. Because of this, the sub-elements that this 231 | * includes are their own directive/templates and it adds them via. DOM 232 | * manipulation here. 233 | */ 234 | return { 235 | scope: true, 236 | link: function(scope, element, attrs) { 237 | var defaultOrder = true; 238 | 239 | _.extend(scope, { 240 | originalData: [], 241 | columnKey: '', 242 | sortOrder: defaultOrder, 243 | pgNum: 1, 244 | pageLength: 50, 245 | filterTerm: '', 246 | headerTitle: attrs.title 247 | }) 248 | // --- 249 | 250 | // --- Allow sorting by column based on the data-key attr 251 | var th = element.find('th'); 252 | th.attr('ng-click', 'sortColumn($event)'); 253 | $compile(th)(scope); 254 | 255 | var setSorting = function(el) { 256 | var key = el.attr('data-key'); 257 | 258 | if (scope.columnKey === key) { 259 | scope.sortOrder = !scope.sortOrder; 260 | } 261 | else { scope.sortOrder = defaultOrder } 262 | 263 | scope.columnKey = key; 264 | 265 | th.removeClass('descending ascending'); 266 | el.addClass(scope.sortOrder ? 'descending' : 'ascending'); 267 | }; 268 | 269 | var defaultSortColumn = function() { 270 | var el = element.find('[data-sort]'); 271 | if (el.length === 0) { 272 | el = element.find('th:first'); 273 | } 274 | return el; 275 | }; 276 | 277 | scope.sortColumn = function(ev) { 278 | setSorting(angular.element(ev.target)); 279 | }; 280 | 281 | setSorting(defaultSortColumn()); 282 | // --- 283 | 284 | scope.$watch(attrs.tableContent, function(data) { 285 | if (!data) { scope.originalData = []; return }; 286 | if (angular.isObject(data)) { data = _.values(data) } 287 | 288 | scope.originalData = data; 289 | }); 290 | 291 | var setTableData = function() { 292 | scope.filteredData = $filter('filter')(scope.originalData, scope.filterTerm) 293 | scope.$data = $filter('orderBy')( 294 | scope.filteredData, 295 | scope.columnKey, 296 | scope.sortOrder).slice( 297 | (scope.pgNum - 1) * scope.pageLength, 298 | scope.pgNum * scope.pageLength); 299 | }; 300 | 301 | // Reset the page number for each new filtering. 302 | scope.$watch('filterTerm', function() { scope.pgNum = 1; }); 303 | 304 | _.each(['originalData', 'columnKey', 'sortOrder', 'pgNum', 'filterTerm'], 305 | function(k) { scope.$watch(k, setTableData); }); 306 | 307 | // --- Pagination controls 308 | var el = angular.element('
'); 309 | $compile(el)(scope); 310 | element.after(el); 311 | // --- 312 | 313 | // --- Filtering 314 | var el = angular.element('
'); 315 | $compile(el)(scope); 316 | element.before(el); 317 | // --- 318 | } 319 | }; 320 | }]); 321 | })(); 322 | -------------------------------------------------------------------------------- /html/mesos/js/controllers.js: -------------------------------------------------------------------------------- 1 | (function() { 2 | 'use strict'; 3 | 4 | var mesosApp = angular.module('mesos'); 5 | 6 | function getMasterUrl() { 7 | return '/mesos'; 8 | } 9 | 10 | function getSlaveUrl(slaveId) { 11 | return '/mesos/slave/' + slaveId; 12 | } 13 | 14 | function hasSelectedText() { 15 | if (window.getSelection) { // All browsers except IE before version 9. 16 | var range = window.getSelection(); 17 | return range.toString().length > 0; 18 | } 19 | return false; 20 | } 21 | 22 | // Invokes the pailer for the specified host and path using the 23 | // specified window_title. 24 | function pailer(host, path, window_title) { 25 | var url = host + '/files/read?path=' + path; 26 | var pailer = 27 | window.open('pailer.html', url, 'width=580px, height=700px'); 28 | 29 | // Need to use window.onload instead of document.ready to make 30 | // sure the title doesn't get overwritten. 31 | pailer.onload = function() { 32 | pailer.document.title = window_title + ' (' + host + ')'; 33 | }; 34 | } 35 | 36 | 37 | function updateInterval(num_slaves) { 38 | // TODO(bmahler): Increasing the update interval for large clusters 39 | // is done purely to mitigate webui performance issues. Ideally we can 40 | // keep a consistently fast rate for updating statistical information. 41 | // For the full system state updates, it may make sense to break 42 | // it up using pagination and/or splitting the endpoint. 43 | if (num_slaves < 500) { 44 | return 10000; 45 | } else if (num_slaves < 1000) { 46 | return 20000; 47 | } else if (num_slaves < 5000) { 48 | return 60000; 49 | } else if (num_slaves < 10000) { 50 | return 120000; 51 | } else if (num_slaves < 15000) { 52 | return 240000; 53 | } else if (num_slaves < 20000) { 54 | return 480000; 55 | } else { 56 | return 960000; 57 | } 58 | } 59 | 60 | 61 | // Update the outermost scope with the new state. 62 | function updateState($scope, $timeout, data) { 63 | // Don't do anything if the data hasn't changed. 64 | if ($scope.data == data) { 65 | return true; // Continue polling. 66 | } 67 | 68 | $scope.state = JSON.parse(data); 69 | 70 | // A cluster is named if the state returns a non-empty string name. 71 | // Track whether this cluster is named in a Boolean for display purposes. 72 | $scope.clusterNamed = !!$scope.state.cluster; 73 | 74 | // Check for selected text, and allow up to 20 seconds to pass before 75 | // potentially wiping the user highlighted text. 76 | // TODO(bmahler): This is to avoid the annoying loss of highlighting when 77 | // the tables update. Once we can have tighter granularity control on the 78 | // angular.js dynamic table updates, we should remove this hack. 79 | $scope.time_since_update += $scope.delay; 80 | 81 | if (hasSelectedText() && $scope.time_since_update < 20000) { 82 | return true; 83 | } 84 | 85 | $scope.data = data; 86 | 87 | // Pass this pollTime to all relativeDate calls to make them all relative to 88 | // the same moment in time. 89 | // 90 | // If relativeDate is called without a reference time, it instantiates a new 91 | // Date to be the reference. Since there can be hundreds of dates on a given 92 | // page, they would all be relative to slightly different moments in time. 93 | $scope.pollTime = new Date(); 94 | 95 | // Update the maps. 96 | $scope.slaves = {}; 97 | $scope.frameworks = {}; 98 | $scope.offers = {}; 99 | $scope.completed_frameworks = {}; 100 | $scope.active_tasks = []; 101 | $scope.completed_tasks = []; 102 | 103 | // Update the stats. 104 | $scope.cluster = $scope.state.cluster; 105 | $scope.total_cpus = 0; 106 | $scope.total_mem = 0; 107 | $scope.used_cpus = 0; 108 | $scope.used_mem = 0; 109 | $scope.offered_cpus = 0; 110 | $scope.offered_mem = 0; 111 | 112 | $scope.activated_slaves = $scope.state.activated_slaves; 113 | $scope.deactivated_slaves = $scope.state.deactivated_slaves; 114 | 115 | _.each($scope.state.slaves, function(slave) { 116 | $scope.slaves[slave.id] = slave; 117 | $scope.total_cpus += slave.resources.cpus; 118 | $scope.total_mem += slave.resources.mem; 119 | }); 120 | 121 | var setTaskMetadata = function(task) { 122 | if (!task.executor_id) { 123 | task.executor_id = task.id; 124 | } 125 | if (task.statuses.length > 0) { 126 | task.start_time = task.statuses[0].timestamp * 1000; 127 | task.finish_time = 128 | task.statuses[task.statuses.length - 1].timestamp * 1000; 129 | } 130 | }; 131 | 132 | _.each($scope.state.frameworks, function(framework) { 133 | $scope.frameworks[framework.id] = framework; 134 | 135 | _.each(framework.offers, function(offer) { 136 | $scope.offers[offer.id] = offer; 137 | $scope.offered_cpus += offer.resources.cpus; 138 | $scope.offered_mem += offer.resources.mem; 139 | offer.framework_name = $scope.frameworks[offer.framework_id].name; 140 | offer.hostname = $scope.slaves[offer.slave_id].hostname; 141 | }); 142 | 143 | $scope.used_cpus += framework.resources.cpus; 144 | $scope.used_mem += framework.resources.mem; 145 | 146 | framework.cpus_share = 0; 147 | if ($scope.total_cpus > 0) { 148 | framework.cpus_share = framework.resources.cpus / $scope.total_cpus; 149 | } 150 | 151 | framework.mem_share = 0; 152 | if ($scope.total_mem > 0) { 153 | framework.mem_share = framework.resources.mem / $scope.total_mem; 154 | } 155 | 156 | framework.max_share = Math.max(framework.cpus_share, framework.mem_share); 157 | 158 | // If the executor ID is empty, this is a command executor with an 159 | // internal executor ID generated from the task ID. 160 | // TODO(brenden): Remove this once 161 | // https://issues.apache.org/jira/browse/MESOS-527 is fixed. 162 | _.each(framework.tasks, setTaskMetadata); 163 | _.each(framework.completed_tasks, setTaskMetadata); 164 | 165 | $scope.active_tasks = $scope.active_tasks.concat(framework.tasks); 166 | $scope.completed_tasks = 167 | $scope.completed_tasks.concat(framework.completed_tasks); 168 | }); 169 | 170 | _.each($scope.state.completed_frameworks, function(framework) { 171 | $scope.completed_frameworks[framework.id] = framework; 172 | 173 | _.each(framework.completed_tasks, setTaskMetadata); 174 | }); 175 | 176 | $scope.used_cpus -= $scope.offered_cpus; 177 | $scope.used_mem -= $scope.offered_mem; 178 | 179 | $scope.idle_cpus = $scope.total_cpus - ($scope.offered_cpus + $scope.used_cpus); 180 | $scope.idle_mem = $scope.total_mem - ($scope.offered_mem + $scope.used_mem); 181 | 182 | $scope.time_since_update = 0; 183 | $scope.$broadcast('state_updated'); 184 | 185 | return true; // Continue polling. 186 | } 187 | 188 | // Add a filter to convert small float number to decimal string 189 | mesosApp.filter('decimalFloat', function() { 190 | return function(num) { 191 | return parseFloat(num.toFixed(4)).toString(); 192 | } 193 | }); 194 | 195 | // Update the outermost scope with the metrics/snapshot endpoint. 196 | function updateMetrics($scope, $timeout, data) { 197 | var metrics = JSON.parse(data); 198 | $scope.staged_tasks = metrics['master/tasks_staging']; 199 | $scope.started_tasks = metrics['master/tasks_starting']; 200 | $scope.finished_tasks = metrics['master/tasks_finished']; 201 | $scope.killed_tasks = metrics['master/tasks_killed']; 202 | $scope.failed_tasks = metrics['master/tasks_failed']; 203 | $scope.lost_tasks = metrics['master/tasks_lost']; 204 | 205 | return true; // Continue polling. 206 | } 207 | 208 | 209 | // Main controller that can be used to handle "global" events. E.g.,: 210 | // $scope.$on('$afterRouteChange', function() { ...; }); 211 | // 212 | // In addition, the MainCntl encapsulates the "view", allowing the 213 | // active controller/view to easily access anything in scope (e.g., 214 | // the state). 215 | mesosApp.controller('MainCntl', [ 216 | '$scope', '$http', '$location', '$timeout', '$modal', 217 | function($scope, $http, $location, $timeout, $modal) { 218 | $scope.doneLoading = true; 219 | 220 | // Adding bindings into scope so that they can be used from within 221 | // AngularJS expressions. 222 | $scope._ = _; 223 | $scope.stringify = JSON.stringify; 224 | $scope.encodeURIComponent = encodeURIComponent; 225 | $scope.basename = function(path) { 226 | // This is only a basic version of basename that handles the cases we care 227 | // about, rather than duplicating unix basename functionality perfectly. 228 | if (path === '/') { 229 | return path; // Handle '/'. 230 | } 231 | 232 | // Strip a trailing '/' if present. 233 | if (path.length > 0 && path.lastIndexOf('/') === (path.length - 1)) { 234 | path = path.substr(0, path.length - 1); 235 | } 236 | return path.substr(path.lastIndexOf('/') + 1); 237 | }; 238 | 239 | $scope.$location = $location; 240 | $scope.delay = 2000; 241 | $scope.retry = 0; 242 | $scope.time_since_update = 0; 243 | $scope.isErrorModalOpen = false; 244 | 245 | // Ordered Array of path => activeTab mappings. On successful route changes, 246 | // the `pathRegexp` values are matched against the current route. The first 247 | // match will be used to set the active navbar tab. 248 | var NAVBAR_PATHS = [ 249 | { 250 | pathRegexp: /^\/slaves/, 251 | tab: 'slaves' 252 | }, 253 | { 254 | pathRegexp: /^\/frameworks/, 255 | tab: 'frameworks' 256 | }, 257 | { 258 | pathRegexp: /^\/offers/, 259 | tab: 'offers' 260 | } 261 | ]; 262 | 263 | // Set the active tab on route changes according to NAVBAR_PATHS. 264 | $scope.$on('$routeChangeSuccess', function(event, current) { 265 | var path = current.$$route.originalPath; 266 | 267 | // Use _.some so the loop can exit on the first `pathRegexp` match. 268 | var matched = _.some(NAVBAR_PATHS, function(nav) { 269 | if (path.match(nav.pathRegexp)) { 270 | $scope.navbarActiveTab = nav.tab; 271 | return true; 272 | } 273 | }); 274 | 275 | if (!matched) $scope.navbarActiveTab = null; 276 | }); 277 | 278 | var popupErrorModal = function() { 279 | if ($scope.delay >= 128000) { 280 | $scope.delay = 2000; 281 | } else { 282 | $scope.delay = $scope.delay * 2; 283 | } 284 | 285 | $scope.isErrorModalOpen = true; 286 | 287 | var errorModal = $modal.open({ 288 | controller: function($scope, $modalInstance, scope) { 289 | // Give the modal reference to the root scope so it can access the 290 | // `retry` variable. It needs to be passed by reference, not by 291 | // value, since its value is changed outside the scope of the 292 | // modal. 293 | $scope.rootScope = scope; 294 | }, 295 | resolve: { 296 | scope: function() { return $scope; } 297 | }, 298 | templateUrl: "template/dialog/masterGone.html" 299 | }); 300 | 301 | // Make it such that everytime we hide the error-modal, we stop the 302 | // countdown and restart the polling. 303 | errorModal.result.then(function() { 304 | $scope.isErrorModalOpen = false; 305 | 306 | if ($scope.countdown != null) { 307 | if ($timeout.cancel($scope.countdown)) { 308 | // Restart since they cancelled the countdown. 309 | $scope.delay = 2000; 310 | } 311 | } 312 | 313 | // Start polling again, but do it asynchronously (and wait at 314 | // least a second because otherwise the error-modal won't get 315 | // properly shown). 316 | $timeout(pollState, 1000); 317 | $timeout(pollMetrics, 1000); 318 | }); 319 | 320 | $scope.retry = $scope.delay; 321 | var countdown = function() { 322 | if ($scope.retry === 0) { 323 | errorModal.close(); 324 | } else { 325 | $scope.retry = $scope.retry - 1000; 326 | $scope.countdown = $timeout(countdown, 1000); 327 | } 328 | }; 329 | countdown(); 330 | }; 331 | 332 | var pollState = function() { 333 | $http.get('master/state.json', 334 | {transformResponse: function(data) { return data; }}) 335 | .success(function(data) { 336 | if (updateState($scope, $timeout, data)) { 337 | $scope.delay = updateInterval(_.size($scope.slaves)); 338 | $timeout(pollState, $scope.delay); 339 | } 340 | }) 341 | .error(function() { 342 | if ($scope.isErrorModalOpen === false) { 343 | popupErrorModal(); 344 | } 345 | }); 346 | }; 347 | 348 | var pollMetrics = function() { 349 | $http.get('metrics/snapshot', 350 | {transformResponse: function(data) { return data; }}) 351 | .success(function(data) { 352 | if (updateMetrics($scope, $timeout, data)) { 353 | $scope.delay = updateInterval(_.size($scope.slaves)); 354 | $timeout(pollMetrics, $scope.delay); 355 | } 356 | }) 357 | .error(function() { 358 | if ($scope.isErrorModalOpen === false) { 359 | popupErrorModal(); 360 | } 361 | }); 362 | }; 363 | 364 | pollState(); 365 | pollMetrics(); 366 | }]); 367 | 368 | 369 | mesosApp.controller('HomeCtrl', function($dialog, $scope) { 370 | $scope.log = function($event) { 371 | if (!$scope.state.external_log_file && !$scope.state.log_dir) { 372 | $dialog.messageBox( 373 | 'Logging to a file is not enabled', 374 | "Set the 'external_log_file' or 'log_dir' option if you wish to access the logs.", 375 | [{label: 'Continue'}] 376 | ).open(); 377 | } else { 378 | pailer( 379 | getMasterUrl(), 380 | '/master/log', 381 | 'Mesos Master'); 382 | } 383 | }; 384 | }); 385 | 386 | mesosApp.controller('FrameworksCtrl', function() {}); 387 | 388 | mesosApp.controller('OffersCtrl', function() {}); 389 | 390 | mesosApp.controller('FrameworkCtrl', function($scope, $routeParams) { 391 | var update = function() { 392 | if ($routeParams.id in $scope.completed_frameworks) { 393 | $scope.framework = $scope.completed_frameworks[$routeParams.id]; 394 | $scope.alert_message = 'This framework has terminated!'; 395 | $('#alert').show(); 396 | $('#framework').show(); 397 | } else if ($routeParams.id in $scope.frameworks) { 398 | $scope.framework = $scope.frameworks[$routeParams.id]; 399 | $('#framework').show(); 400 | } else { 401 | $scope.alert_message = 'No framework found with ID: ' + $routeParams.id; 402 | $('#alert').show(); 403 | } 404 | }; 405 | 406 | if ($scope.state) { 407 | update(); 408 | } 409 | 410 | var removeListener = $scope.$on('state_updated', update); 411 | $scope.$on('$routeChangeStart', removeListener); 412 | }); 413 | 414 | 415 | mesosApp.controller('SlavesCtrl', function() {}); 416 | 417 | 418 | mesosApp.controller('SlaveCtrl', [ 419 | '$dialog', '$scope', '$routeParams', '$http', '$q', '$timeout', 'top', 420 | function($dialog, $scope, $routeParams, $http, $q, $timeout, $top) { 421 | $scope.slave_id = $routeParams.slave_id; 422 | 423 | var update = function() { 424 | if (!($routeParams.slave_id in $scope.slaves)) { 425 | $scope.alert_message = 'No slave found with ID: ' + $routeParams.slave_id; 426 | $('#alert').show(); 427 | return; 428 | } 429 | 430 | var pid = $scope.slaves[$routeParams.slave_id].pid; 431 | var id = pid.substring(0, pid.indexOf('@')); 432 | var host = getSlaveUrl($routeParams.slave_id); 433 | 434 | $scope.log = function($event) { 435 | if (!$scope.state.external_log_file && !$scope.state.log_dir) { 436 | $dialog.messageBox( 437 | 'Logging to a file is not enabled', 438 | "Set the 'external_log_file' or 'log_dir' option if you wish to access the logs.", 439 | [{label: 'Continue'}] 440 | ).open(); 441 | } else { 442 | pailer(host, '/slave/log', 'Mesos Slave'); 443 | } 444 | }; 445 | 446 | // Set up polling for the monitor if this is the first update. 447 | if (!$top.started()) { 448 | $top.start(host, $scope); 449 | } 450 | 451 | $http.jsonp(getSlaveUrl($routeParams.slave_id) + '/' + id + '/state?jsonp=JSON_CALLBACK') 452 | .success(function (response) { 453 | $scope.state = response; 454 | 455 | $scope.slave = {}; 456 | $scope.slave.frameworks = {}; 457 | $scope.slave.completed_frameworks = {}; 458 | 459 | // Computes framework stats by setting new attributes on the 'framework' 460 | // object. 461 | function computeFrameworkStats(framework) { 462 | framework.num_tasks = 0; 463 | framework.cpus = 0; 464 | framework.mem = 0; 465 | 466 | _.each(framework.executors, function(executor) { 467 | framework.num_tasks += _.size(executor.tasks); 468 | framework.cpus += executor.resources.cpus; 469 | framework.mem += executor.resources.mem; 470 | }); 471 | } 472 | 473 | // Compute framework stats and update slave's mappings of those 474 | // frameworks. 475 | _.each($scope.state.frameworks, function(framework) { 476 | $scope.slave.frameworks[framework.id] = framework; 477 | computeFrameworkStats(framework); 478 | }); 479 | 480 | _.each($scope.state.completed_frameworks, function(framework) { 481 | $scope.slave.completed_frameworks[framework.id] = framework; 482 | computeFrameworkStats(framework); 483 | }); 484 | 485 | $('#slave').show(); 486 | }) 487 | .error(function(reason) { 488 | $scope.alert_message = 'Failed to get slave usage / state: ' + reason; 489 | $('#alert').show(); 490 | }); 491 | 492 | $http.jsonp('//' + host + '/metrics/snapshot?jsonp=JSON_CALLBACK') 493 | .success(function (response) { 494 | if (!$scope.state) { 495 | $scope.state = {}; 496 | } 497 | 498 | $scope.state.staged_tasks = response['slave/tasks_staging']; 499 | $scope.state.started_tasks = response['slave/tasks_starting']; 500 | $scope.state.finished_tasks = response['slave/tasks_finished']; 501 | $scope.state.killed_tasks = response['slave/tasks_killed']; 502 | $scope.state.failed_tasks = response['slave/tasks_failed']; 503 | $scope.state.lost_tasks = response['slave/tasks_lost']; 504 | }) 505 | .error(function(reason) { 506 | $scope.alert_message = 'Failed to get slave metrics: ' + reason; 507 | $('#alert').show(); 508 | }); 509 | }; 510 | 511 | if ($scope.state) { 512 | update(); 513 | } 514 | 515 | var removeListener = $scope.$on('state_updated', update); 516 | $scope.$on('$routeChangeStart', removeListener); 517 | }]); 518 | 519 | 520 | mesosApp.controller('SlaveFrameworkCtrl', [ 521 | '$scope', '$routeParams', '$http', '$q', '$timeout', 'top', 522 | function($scope, $routeParams, $http, $q, $timeout, $top) { 523 | $scope.slave_id = $routeParams.slave_id; 524 | $scope.framework_id = $routeParams.framework_id; 525 | 526 | var update = function() { 527 | if (!($routeParams.slave_id in $scope.slaves)) { 528 | $scope.alert_message = 'No slave found with ID: ' + $routeParams.slave_id; 529 | $('#alert').show(); 530 | return; 531 | } 532 | 533 | var pid = $scope.slaves[$routeParams.slave_id].pid; 534 | var id = pid.substring(0, pid.indexOf('@')); 535 | var host = getSlaveUrl($routeParams.slave_id); 536 | 537 | // Set up polling for the monitor if this is the first update. 538 | if (!$top.started()) { 539 | $top.start(host, $scope); 540 | } 541 | 542 | $http.jsonp(host + '/' + id + '/state?jsonp=JSON_CALLBACK') 543 | .success(function (response) { 544 | $scope.state = response; 545 | 546 | $scope.slave = {}; 547 | 548 | function matchFramework(framework) { 549 | return $scope.framework_id === framework.id; 550 | } 551 | 552 | // Find the framework; it's either active or completed. 553 | $scope.framework = 554 | _.find($scope.state.frameworks, matchFramework) || 555 | _.find($scope.state.completed_frameworks, matchFramework); 556 | 557 | if (!$scope.framework) { 558 | $scope.alert_message = 'No framework found with ID: ' + $routeParams.framework_id; 559 | $('#alert').show(); 560 | return; 561 | } 562 | 563 | // Compute the framework stats. 564 | $scope.framework.num_tasks = 0; 565 | $scope.framework.cpus = 0; 566 | $scope.framework.mem = 0; 567 | 568 | _.each($scope.framework.executors, function(executor) { 569 | $scope.framework.num_tasks += _.size(executor.tasks); 570 | $scope.framework.cpus += executor.resources.cpus; 571 | $scope.framework.mem += executor.resources.mem; 572 | }); 573 | 574 | $('#slave').show(); 575 | }) 576 | .error(function (reason) { 577 | $scope.alert_message = 'Failed to get slave usage / state: ' + reason; 578 | $('#alert').show(); 579 | }); 580 | }; 581 | 582 | if ($scope.state) { 583 | update(); 584 | } 585 | 586 | var removeListener = $scope.$on('state_updated', update); 587 | $scope.$on('$routeChangeStart', removeListener); 588 | }]); 589 | 590 | 591 | mesosApp.controller('SlaveExecutorCtrl', [ 592 | '$scope', '$routeParams', '$http', '$q', '$timeout', 'top', 593 | function($scope, $routeParams, $http, $q, $timeout, $top) { 594 | $scope.slave_id = $routeParams.slave_id; 595 | $scope.framework_id = $routeParams.framework_id; 596 | $scope.executor_id = $routeParams.executor_id; 597 | 598 | var update = function() { 599 | if (!($routeParams.slave_id in $scope.slaves)) { 600 | $scope.alert_message = 'No slave found with ID: ' + $routeParams.slave_id; 601 | $('#alert').show(); 602 | return; 603 | } 604 | 605 | var pid = $scope.slaves[$routeParams.slave_id].pid; 606 | var id = pid.substring(0, pid.indexOf('@')); 607 | var host = getSlaveUrl($routeParams.slave_id); 608 | 609 | // Set up polling for the monitor if this is the first update. 610 | if (!$top.started()) { 611 | $top.start(host, $scope); 612 | } 613 | 614 | $http.jsonp(host + '/' + id + '/state?jsonp=JSON_CALLBACK') 615 | .success(function (response) { 616 | $scope.state = response; 617 | 618 | $scope.slave = {}; 619 | 620 | function matchFramework(framework) { 621 | return $scope.framework_id === framework.id; 622 | } 623 | 624 | // Find the framework; it's either active or completed. 625 | $scope.framework = 626 | _.find($scope.state.frameworks, matchFramework) || 627 | _.find($scope.state.completed_frameworks, matchFramework); 628 | 629 | if (!$scope.framework) { 630 | $scope.alert_message = 'No framework found with ID: ' + $routeParams.framework_id; 631 | $('#alert').show(); 632 | return; 633 | } 634 | 635 | function matchExecutor(executor) { 636 | return $scope.executor_id === executor.id; 637 | } 638 | 639 | // Look for the executor; it's either active or completed. 640 | $scope.executor = 641 | _.find($scope.framework.executors, matchExecutor) || 642 | _.find($scope.framework.completed_executors, matchExecutor); 643 | 644 | if (!$scope.executor) { 645 | $scope.alert_message = 'No executor found with ID: ' + $routeParams.executor_id; 646 | $('#alert').show(); 647 | return; 648 | } 649 | 650 | $('#slave').show(); 651 | }) 652 | .error(function (reason) { 653 | $scope.alert_message = 'Failed to get slave usage / state: ' + reason; 654 | $('#alert').show(); 655 | }); 656 | }; 657 | 658 | if ($scope.state) { 659 | update(); 660 | } 661 | 662 | var removeListener = $scope.$on('state_updated', update); 663 | $scope.$on('$routeChangeStart', removeListener); 664 | }]); 665 | 666 | 667 | // Reroutes a request like 668 | // '/mesos/slave/:slave_id/frameworks/:framework_id/executors/:executor_id/browse' 669 | // to the executor's sandbox. This requires a second request because the 670 | // directory to browse is known by the slave but not by the master. Request 671 | // the directory from the slave, and then redirect to it. 672 | // 673 | // TODO(ssorallen): Add `executor.directory` to the master's state endpoint 674 | // output so this controller of rerouting is no longer necessary. 675 | mesosApp.controller('SlaveExecutorRerouterCtrl', 676 | function($alert, $http, $location, $routeParams, $scope, $window) { 677 | 678 | function goBack(flashMessageOrOptions) { 679 | if (flashMessageOrOptions) { 680 | $alert.danger(flashMessageOrOptions); 681 | } 682 | 683 | if ($window.history.length > 1) { 684 | // If the browser has something in its history, just go back. 685 | $window.history.back(); 686 | } else { 687 | // Otherwise navigate to the framework page, which is likely the 688 | // previous page anyway. 689 | $location.path('/frameworks/' + $routeParams.framework_id).replace(); 690 | } 691 | } 692 | 693 | // When navigating directly to this page, e.g. pasting the URL into the 694 | // browser, the previous page is not a page in Mesos. In that case, navigate 695 | // home. 696 | if (!$scope.slaves) { 697 | $alert.danger({ 698 | message: "Navigate to the slave's sandbox via the Mesos UI.", 699 | title: "Failed to find slaves." 700 | }); 701 | return $location.path('/').replace(); 702 | } 703 | 704 | var slave = $scope.slaves[$routeParams.slave_id]; 705 | 706 | // If the slave doesn't exist, send the user back. 707 | if (!slave) { 708 | return goBack("Slave with ID '" + $routeParams.slave_id + "' does not exist."); 709 | } 710 | 711 | var pid = slave.pid; 712 | var id = pid.substring(0, pid.indexOf('@')); 713 | var host = getSlaveUrl($routeParams.slave_id); 714 | 715 | // Request slave details to get access to the route executor's "directory" 716 | // to navigate directly to the executor's sandbox. 717 | $http.jsonp(host + '/' + id + '/state?jsonp=JSON_CALLBACK') 718 | .success(function(response) { 719 | 720 | function matchFramework(framework) { 721 | return $routeParams.framework_id === framework.id; 722 | } 723 | 724 | var framework = 725 | _.find(response.frameworks, matchFramework) || 726 | _.find(response.completed_frameworks, matchFramework); 727 | 728 | if (!framework) { 729 | return goBack( 730 | "Framework with ID '" + $routeParams.framework_id + 731 | "' does not exist on slave with ID '" + $routeParams.slave_id + 732 | "'." 733 | ); 734 | } 735 | 736 | function matchExecutor(executor) { 737 | return $routeParams.executor_id === executor.id; 738 | } 739 | 740 | var executor = 741 | _.find(framework.executors, matchExecutor) || 742 | _.find(framework.completed_executors, matchExecutor); 743 | 744 | if (!executor) { 745 | return goBack( 746 | "Executor with ID '" + $routeParams.executor_id + 747 | "' does not exist on slave with ID '" + $routeParams.slave_id + 748 | "'." 749 | ); 750 | } 751 | 752 | // Navigate to a path like '/mesos/slave/:id/browse?path=%2Ftmp%2F', the 753 | // recognized "browse" endpoint for a slave. 754 | $location.path('slaves/' + $routeParams.slave_id + '/browse') 755 | .search({path: executor.directory}) 756 | .replace(); 757 | }) 758 | .error(function(response) { 759 | $alert.danger({ 760 | bullets: [ 761 | "The slave is not accessible from your network", 762 | "The slave timed out or went offline" 763 | ], 764 | message: "Potential reasons:", 765 | title: "Failed to connect to slave '" + $routeParams.slave_id + 766 | "' on '" + host + "'." 767 | }); 768 | 769 | // Is the slave dead? Navigate home since returning to the slave might 770 | // end up in an endless loop. 771 | $location.path('/').replace(); 772 | }); 773 | }); 774 | 775 | 776 | mesosApp.controller('BrowseCtrl', function($scope, $routeParams, $http) { 777 | var update = function() { 778 | if ($routeParams.slave_id in $scope.slaves && $routeParams.path) { 779 | $scope.slave_id = $routeParams.slave_id; 780 | $scope.path = $routeParams.path; 781 | 782 | var url = getSlaveUrl($scope.slave_id) + '/files/browse.json?jsonp=JSON_CALLBACK'; 783 | 784 | $scope.slave_host = getSlaveUrl($scope.slave_id); 785 | 786 | $scope.pail = function($event, path) { 787 | pailer(getSlaveUrl($scope.slave_id), path, decodeURIComponent(path)); 788 | }; 789 | 790 | // TODO(bmahler): Try to get the error code / body in the error callback. 791 | // This wasn't working with the current version of angular. 792 | $http.jsonp(url, {params: {path: $routeParams.path}}) 793 | .success(function(data) { 794 | $scope.listing = data; 795 | $('#listing').show(); 796 | }) 797 | .error(function() { 798 | $scope.alert_message = 'Error browsing path: ' + $routeParams.path; 799 | $('#alert').show(); 800 | }); 801 | } else { 802 | if (!($routeParams.slave_id in $scope.slaves)) { 803 | $scope.alert_message = 'No slave found with ID: ' + $routeParams.slave_id; 804 | } else { 805 | $scope.alert_message = 'Missing "path" request parameter.'; 806 | } 807 | $('#alert').show(); 808 | } 809 | }; 810 | 811 | if ($scope.state) { 812 | update(); 813 | } 814 | 815 | var removeListener = $scope.$on('state_updated', update); 816 | $scope.$on('$routeChangeStart', removeListener); 817 | }); 818 | })(); 819 | -------------------------------------------------------------------------------- /html/mesos/js/jquery.pailer.js: -------------------------------------------------------------------------------- 1 | // A jQuery plugin for PAging and taILing data (i.e., a 2 | // 'PAILer'). Paging occurs when scrolling reaches the "top" and 3 | // tailing occurs when scrolling has reached the "bottom". 4 | 5 | // A 'read' function must be provided for reading the data (in 6 | // bytes). This function should expect an "options" object with the 7 | // fields 'offset' and 'length' set for reading the data. The result 8 | // from of the function should be a "promise" like value which has a 9 | // 'success' and 'error' callback which each take a function. An 10 | // object with at least two fields defined ('offset' and 'data') is 11 | // expected on success. The length of 'data' may be smaller than the 12 | // amount requested. If the offset requested is greater than the 13 | // available offset, the result should be an object with the 'offset' 14 | // field set to the available offset (i.e., the total length of the 15 | // data) with an empty 'data' field. 16 | 17 | // The plugin prepends, appends, and updates the "html" component of 18 | // the elements specified in the jQuery selector (e.g., doing 19 | // $('#data').pailer(...) means that data will be updated within 20 | // $('#data') via $('#data').prepend(...) and $('#data').append(...) 21 | // and $('#data').html(...) calls). 22 | 23 | // An indicator paragraph element (i.e.,

) can be specified that 24 | // the plugin will write text to describing any status/errors that 25 | // have been encountered. 26 | 27 | // Data will automagically get truncated at some specified length, 28 | // configurable via the 'truncate-length' option. Likewise, the amount 29 | // of data paged in at a time can be configured via the 'page-size' 30 | // option. 31 | 32 | // Example: 33 | // HTML: 34 | //

35 | // 36 | //
37 | //

38 | //
39 | // Javascript: 40 | // $('#data').pailer({ 41 | // 'read': function(options) { 42 | // var settings = $.extend({ 43 | // 'offset': -1, 44 | // 'length': -1 45 | // }, options); 46 | // var url = '/url/for/data' 47 | // + '?offset=' + settings.offset 48 | // + '&length=' + settings.length; 49 | // return $.getJSON(url); 50 | // }, 51 | // 'indicator': $('#indicator') 52 | // }); 53 | 54 | (function($) { 55 | // Helper for escaping html, based on _.escape from underscore.js. 56 | function escapeHTML(string) { 57 | if (string == null) { 58 | return ''; 59 | } 60 | 61 | var escapes = { 62 | '&': '&', 63 | '<': '<', 64 | '>': '>', 65 | '"': '"', 66 | "'": ''', 67 | '/': '/' 68 | }; 69 | var regex = new RegExp('[' + Object.keys(escapes).join('') + ']', 'g'); 70 | 71 | return ('' + string).replace(regex, function (match) { 72 | return escapes[match]; 73 | }); 74 | } 75 | 76 | function Pailer(read, element, indicator, page_size, truncate_length) { 77 | var this_ = this; 78 | 79 | this_.read = read; 80 | this_.element = element; 81 | this_.indicator = indicator; 82 | this_.initialized = false; 83 | this_.start = -1; 84 | this_.end = -1; 85 | this_.paging = false; 86 | this_.tailing = true; 87 | 88 | page_size || $.error('Expecting page_size to be defined'); 89 | truncate_length || $.error('Expecting truncate_length to be defined'); 90 | 91 | this_.page_size = page_size; 92 | this_.truncate_length = truncate_length; 93 | 94 | this_.element.css('overflow', 'auto'); 95 | 96 | this_.element.scroll(function () { 97 | var scrollTop = this_.element.scrollTop(); 98 | var height = this_.element.height(); 99 | var scrollHeight = this_.element[0].scrollHeight; 100 | 101 | if (scrollTop === 0) { 102 | this_.page(); 103 | } else if (scrollTop + height >= scrollHeight) { 104 | if (!this_.tailing) { 105 | this_.tailing = true; 106 | this_.tail(); 107 | } 108 | } else { 109 | this_.tailing = false; 110 | } 111 | }); 112 | } 113 | 114 | 115 | Pailer.prototype.initialize = function() { 116 | var this_ = this; 117 | 118 | // Set an indicator while we load the data. 119 | this_.indicate('(LOADING)'); 120 | 121 | this_.read({'offset': -1}) 122 | .success(function(data) { 123 | this_.indicate(''); 124 | 125 | // Get the last page of data. 126 | if (data.offset > this_.page_size) { 127 | this_.start = this_.end = data.offset - this_.page_size; 128 | } else { 129 | this_.start = this_.end = 0; 130 | } 131 | 132 | this_.initialized = true; 133 | this_.element.html(''); 134 | setTimeout(function() { this_.tail(); }, 0); 135 | }) 136 | .error(function() { 137 | this_.indicate('(FAILED TO INITIALIZE ... RETRYING)'); 138 | setTimeout(function() { 139 | this_.indicate(''); 140 | this_.initialize(); 141 | }, 1000); 142 | }); 143 | }; 144 | 145 | 146 | Pailer.prototype.page = function() { 147 | var this_ = this; 148 | 149 | if (!this_.initialized) { 150 | return; 151 | } 152 | 153 | if (this_.paging) { 154 | return; 155 | } 156 | 157 | this_.paging = true; 158 | this_.indicate('(PAGING)'); 159 | 160 | if (this_.start === 0) { 161 | this_.paging = false; 162 | this_.indicate('(AT BEGINNING OF FILE)'); 163 | setTimeout(function() { this_.indicate(''); }, 1000); 164 | return; 165 | } 166 | 167 | var offset = this_.start - this_.page_size; 168 | var length = this_.page_size; 169 | 170 | if (offset < 0) { 171 | offset = 0; 172 | length = this_.start; 173 | } 174 | 175 | // Buffer the data in case what gets read is less than 'length'. 176 | var buffer = ''; 177 | 178 | var read = function(offset, length) { 179 | this_.read({'offset': offset, 'length': length}) 180 | .success(function(data) { 181 | if (data.data.length < length) { 182 | buffer += data.data; 183 | read(offset + data.data.length, length - data.data.length); 184 | } else if (data.data.length > 0) { 185 | this_.indicate('(PAGED)'); 186 | setTimeout(function() { this_.indicate(''); }, 1000); 187 | 188 | // Prepend buffer onto data. 189 | data.offset -= buffer.length; 190 | data.data = buffer + data.data; 191 | 192 | // Truncate to the first newline (unless this is the beginning). 193 | if (data.offset !== 0) { 194 | var index = data.data.indexOf('\n') + 1; 195 | data.offset += index; 196 | data.data = data.data.substring(index); 197 | } 198 | 199 | this_.start = data.offset; 200 | 201 | var scrollTop = this_.element.scrollTop(); 202 | var scrollHeight = this_.element[0].scrollHeight; 203 | 204 | this_.element.prepend(escapeHTML(data.data)); 205 | 206 | scrollTop += this_.element[0].scrollHeight - scrollHeight; 207 | this_.element.scrollTop(scrollTop); 208 | 209 | this_.paging = false; 210 | } 211 | }) 212 | .error(function() { 213 | this_.indicate('(FAILED TO PAGE ... RETRYING)'); 214 | setTimeout(function() { 215 | this_.indicate(''); 216 | this_.page(); 217 | }, 1000); 218 | }); 219 | }; 220 | 221 | read(offset, length); 222 | }; 223 | 224 | 225 | Pailer.prototype.tail = function() { 226 | var this_ = this; 227 | 228 | if (!this_.initialized) { 229 | return; 230 | } 231 | 232 | this_.read({'offset': this_.end, 'length': this_.truncate_length}) 233 | .success(function(data) { 234 | var scrollTop = this_.element.scrollTop(); 235 | var height = this_.element.height(); 236 | var scrollHeight = this_.element[0].scrollHeight; 237 | 238 | // Check if we are still at the bottom (since this event might 239 | // have fired before the scroll event has been dispatched). 240 | if (scrollTop + height < scrollHeight) { 241 | this_.tailing = false; 242 | return; 243 | } 244 | 245 | if (data.data.length > 0) { 246 | // Truncate to the first newline if this is the first time 247 | // (and we aren't reading from the beginning of the log). 248 | if (this_.start === this_.end && data.offset !== 0) { 249 | var index = data.data.indexOf('\n') + 1; 250 | data.offset += index; 251 | data.data = data.data.substring(index); 252 | this_.start = data.offset; // Adjust the actual start too! 253 | } 254 | 255 | this_.end = data.offset + data.data.length; 256 | 257 | this_.element.append(escapeHTML(data.data)); 258 | 259 | scrollTop += this_.element[0].scrollHeight - scrollHeight; 260 | this_.element.scrollTop(scrollTop); 261 | 262 | // Also, only if we're at the bottom, truncate data so that we 263 | // don't consume too much memory. TODO(benh): Only do 264 | // truncations if we've been at the bottom for a while. 265 | this_.truncate(); 266 | } 267 | 268 | // Tail immediately if we got as much data as requested (since 269 | // this probably means we've waited around a while). The 270 | // alternative here would be to not get data in chunks, but the 271 | // potential issue here is that we might end up requesting GB of 272 | // log data at a time ... the right solution here might be to do 273 | // a request to determine the new ending offset and then request 274 | // the proper length. 275 | if (data.data.length === this_.truncate_length) { 276 | setTimeout(function() { this_.tail(); }, 0); 277 | } else { 278 | setTimeout(function() { this_.tail(); }, 1000); 279 | } 280 | }) 281 | .error(function() { 282 | this_.indicate('(FAILED TO TAIL ... RETRYING)'); 283 | this_.initialized = false; 284 | setTimeout(function() { 285 | this_.indicate(''); 286 | this_.initialize(); 287 | }, 1000); 288 | }); 289 | }; 290 | 291 | 292 | Pailer.prototype.indicate = function(text) { 293 | var this_ = this; 294 | 295 | if (this_.indicator) { 296 | this_.indicator.text(text); 297 | } 298 | }; 299 | 300 | 301 | Pailer.prototype.truncate = function() { 302 | var this_ = this; 303 | 304 | var length = this_.element.html().length; 305 | if (length >= this_.truncate_length) { 306 | var index = length - this_.truncate_length; 307 | this_.start = this_.end - this_.truncate_length; 308 | this_.element.html(this_.element.html().substring(index)); 309 | } 310 | }; 311 | 312 | $.fn.pailer = function(options) { 313 | var settings = $.extend({ 314 | read: function() { 315 | return { 316 | success: function() {}, 317 | error: function(f) { f(); } 318 | }; 319 | }, 320 | 'page_size': 8 * 4096, // 8 "pages". 321 | 'truncate_length': 50000 322 | }, options); 323 | 324 | this.each(function() { 325 | var pailer = $.data(this, 'pailer'); 326 | if (!pailer) { 327 | pailer = new Pailer(settings.read, 328 | $(this), 329 | settings.indicator, 330 | settings.page_size, 331 | settings.truncate_length); 332 | $.data(this, 'pailer', pailer); 333 | pailer.initialize(); 334 | } 335 | }); 336 | }; 337 | 338 | })(jQuery); 339 | -------------------------------------------------------------------------------- /html/mesos/js/relative-date.js: -------------------------------------------------------------------------------- 1 | // Retrieved from: 2 | // https://github.com/azer/relative-date/blob/master/lib/relative-date.js. 3 | 4 | var relativeDate = (function(undefined){ 5 | 6 | var SECOND = 1000, 7 | MINUTE = 60 * SECOND, 8 | HOUR = 60 * MINUTE, 9 | DAY = 24 * HOUR, 10 | WEEK = 7 * DAY, 11 | YEAR = DAY * 365, 12 | MONTH = YEAR / 12; 13 | 14 | var formats = [ 15 | [ 0.7 * MINUTE, 'just now' ], 16 | [ 1.5 * MINUTE, 'a minute ago' ], 17 | [ 60 * MINUTE, 'minutes ago', MINUTE ], 18 | [ 1.5 * HOUR, 'an hour ago' ], 19 | [ DAY, 'hours ago', HOUR ], 20 | [ 2 * DAY, 'yesterday' ], 21 | [ 7 * DAY, 'days ago', DAY ], 22 | [ 1.5 * WEEK, 'a week ago'], 23 | [ MONTH, 'weeks ago', WEEK ], 24 | [ 1.5 * MONTH, 'a month ago' ], 25 | [ YEAR, 'months ago', MONTH ], 26 | [ 1.5 * YEAR, 'a year ago' ], 27 | [ Number.MAX_VALUE, 'years ago', YEAR ] 28 | ]; 29 | 30 | function relativeDate(input,reference){ 31 | !reference && ( reference = (new Date).getTime() ); 32 | reference instanceof Date && ( reference = reference.getTime() ); 33 | input instanceof Date && ( input = input.getTime() ); 34 | 35 | var delta = reference - input, 36 | format, i, len; 37 | 38 | for(i = -1, len=formats.length; ++i < len; ){ 39 | format = formats[i]; 40 | if(delta < format[0]){ 41 | return format[2] == undefined ? format[1] : Math.round(delta/format[2]) + ' ' + format[1]; 42 | } 43 | }; 44 | } 45 | 46 | return relativeDate; 47 | 48 | })(); 49 | -------------------------------------------------------------------------------- /html/mesos/js/services.js: -------------------------------------------------------------------------------- 1 | (function() { 2 | 'use strict'; 3 | 4 | var mesosServices = angular.module('mesos.services', []); 5 | 6 | mesosServices.service('$alert', ['$rootScope', function($rootScope) { 7 | // Types taken from Bootstraps v3's "Alerts"[1] so the type can be used 8 | // as the class name. 9 | // 10 | // [1] http://getbootstrap.com/components/#alerts 11 | var TYPE_DANGER = 'danger'; 12 | var TYPE_INFO = 'info'; 13 | var TYPE_SUCCESS = 'success'; 14 | var TYPE_WARNING = 'warning'; 15 | 16 | var nextId = 1; 17 | 18 | var nextAlerts = []; 19 | var currentAlerts = $rootScope.currentAlerts = []; 20 | 21 | // Creates an alert to be rendered on the next page view. 22 | // 23 | // messageOrOptions - Either a String or an Object that will be used to 24 | // render an alert on the next view. If a String, it will be the 25 | // message in the alert. If an Object, "title" will be bolded, "message" 26 | // will be normal font weight, and "bullets" will be rendered as a list. 27 | function alert(type, messageOrOptions) { 28 | var alertObject; 29 | 30 | if (angular.isObject(messageOrOptions)) { 31 | alertObject = angular.copy(messageOrOptions); 32 | alertObject.type = type; 33 | } else { 34 | alertObject = { 35 | message: messageOrOptions, 36 | type: type 37 | }; 38 | } 39 | 40 | alertObject.id = nextId; 41 | nextId += 1; 42 | return nextAlerts.push(alertObject); 43 | } 44 | 45 | this.danger = function(messageOrOptions) { 46 | return alert(TYPE_DANGER, messageOrOptions); 47 | }; 48 | this.info = function(messageOrOptions) { 49 | return alert(TYPE_INFO, messageOrOptions); 50 | }; 51 | this.success = function(messageOrOptions) { 52 | return alert(TYPE_SUCCESS, messageOrOptions); 53 | }; 54 | this.warning = function(messageOrOptions) { 55 | return alert(TYPE_WARNING, messageOrOptions); 56 | }; 57 | 58 | // Rotate alerts each time the user navigates. 59 | $rootScope.$on('$locationChangeSuccess', function() { 60 | if (nextAlerts.length > 0) { 61 | // If there are alerts to be shown next, they become the current alerts. 62 | currentAlerts = $rootScope.currentAlerts = nextAlerts; 63 | nextAlerts = []; 64 | } else if (currentAlerts.length > 0) { 65 | // If there are no next alerts, the current alerts still need to expire 66 | // if there are any so they won't display again. 67 | currentAlerts = $rootScope.currentAlerts = []; 68 | } 69 | }); 70 | }]); 71 | 72 | var uiModalDialog = angular.module('ui.bootstrap.dialog', ['ui.bootstrap']); 73 | uiModalDialog 74 | .factory('$dialog', ['$rootScope', '$modal', function ($rootScope, $modal) { 75 | 76 | var prompt = function(title, message, buttons) { 77 | 78 | if (typeof buttons === 'undefined') { 79 | buttons = [ 80 | {result:'cancel', label: 'Cancel'}, 81 | {result:'yes', label: 'Yes', cssClass: 'btn-primary'} 82 | ]; 83 | } 84 | 85 | var ModalCtrl = function($scope, $modalInstance) { 86 | $scope.title = title; 87 | $scope.message = message; 88 | $scope.buttons = buttons; 89 | }; 90 | 91 | return $modal.open({ 92 | templateUrl: 'template/dialog/message.html', 93 | controller: ModalCtrl 94 | }).result; 95 | }; 96 | 97 | return { 98 | prompt: prompt, 99 | messageBox: function(title, message, buttons) { 100 | return { 101 | open: function() { 102 | return prompt(title, message, buttons); 103 | } 104 | }; 105 | } 106 | }; 107 | }]); 108 | 109 | function Statistics() { 110 | this.cpus_user_time_secs = 0.0; 111 | this.cpus_user_usage = 0.0; 112 | this.cpus_system_time_secs = 0.0; 113 | this.cpus_system_usage = 0.0; 114 | this.cpus_limit = 0.0; 115 | this.cpus_total_usage = 0.0; 116 | this.mem_rss_bytes = 0.0; 117 | this.mem_limit_bytes = 0.0; 118 | } 119 | 120 | Statistics.prototype.add = function(statistics) { 121 | this.cpus_user_time_secs += statistics.cpus_user_time_secs; 122 | this.cpus_system_time_secs += statistics.cpus_system_time_secs; 123 | this.cpus_total_usage += statistics.cpus_total_usage; 124 | this.cpus_limit += statistics.cpus_limit; 125 | this.mem_rss_bytes += statistics.mem_rss_bytes; 126 | this.mem_limit_bytes += statistics.mem_limit_bytes; 127 | 128 | // Set instead of add the timestamp since this is an instantaneous view of 129 | // CPU usage since the last poll. 130 | this.timestamp = statistics.timestamp; 131 | }; 132 | 133 | Statistics.prototype.diffUsage = function(statistics) { 134 | this.cpus_user_usage = 135 | (this.cpus_user_time_secs - statistics.cpus_user_time_secs) / 136 | (this.timestamp - statistics.timestamp); 137 | this.cpus_system_usage = 138 | (this.cpus_system_time_secs - statistics.cpus_system_time_secs) / 139 | (this.timestamp - statistics.timestamp); 140 | this.cpus_total_usage = this.cpus_user_usage + this.cpus_system_usage; 141 | }; 142 | 143 | Statistics.parseJSON = function(json) { 144 | var statistics = new Statistics(); 145 | statistics.add(json); 146 | return statistics; 147 | }; 148 | 149 | // Top is an abstraction for polling a slave's monitoring endpoint to 150 | // periodically update the monitoring data. It also computes CPU usage. 151 | // This places the following data in scope.monitor: 152 | // 153 | // $scope.monitor = { 154 | // "statistics": , 155 | // "frameworks": { 156 | // : { 157 | // "statistics": , 158 | // "executors": { 159 | // : { 160 | // "executor_id": , 161 | // "framework_id": , 162 | // "executor_name: , 163 | // "source": , 164 | // "statistics": , 165 | // } 166 | // } 167 | // } 168 | // } 169 | // } 170 | // 171 | // To obtain slave statistics: 172 | // $scope.monitor.statistics 173 | // 174 | // To obtain a framework's statistics: 175 | // $scope.monitor.frameworks[].statistics 176 | // 177 | // To obtain an executor's statistics: 178 | // $scope.monitor.frameworks[].executors[].statistics 179 | // 180 | // In the above, is the following object: 181 | // 182 | // { 183 | // cpus_user_time_secs: value, 184 | // cpus_user_usage: value, // Once computed. 185 | // cpus_system_time_secs: value, 186 | // cpus_system_usage: value, // Once computed. 187 | // mem_limit_bytes: value, 188 | // mem_rss_bytes: value, 189 | // } 190 | // 191 | // TODO(bmahler): The complexity of the monitor object is mostly in place 192 | // until we have path-params on the monitoring endpoint to request 193 | // statistics for the slave, or for a specific framework / executor. 194 | // 195 | // Arguments: 196 | // http: $http service from Angular. 197 | // timeout: $timeout service from Angular. 198 | function Top($http, $timeout) { 199 | this.http = $http; 200 | this.timeout = $timeout; 201 | } 202 | 203 | Top.prototype.poll = function() { 204 | this.http.jsonp(this.endpoint) 205 | 206 | // Success! Parse the response. 207 | .success(angular.bind(this, this.parseResponse)) 208 | 209 | // Do not continue polling on error. 210 | .error(angular.noop); 211 | }; 212 | 213 | Top.prototype.parseResponse = function(response) { 214 | var that = this; 215 | var monitor = { 216 | frameworks: {}, 217 | statistics: new Statistics() 218 | }; 219 | 220 | response.forEach(function(executor) { 221 | var executor_id = executor.executor_id; 222 | var framework_id = executor.framework_id; 223 | var current = executor.statistics = 224 | Statistics.parseJSON(executor.statistics); 225 | 226 | current.cpus_user_usage = 0.0; 227 | current.cpus_system_usage = 0.0; 228 | 229 | // Compute CPU usage if possible. 230 | if (that.scope.monitor && 231 | that.scope.monitor.frameworks[framework_id] && 232 | that.scope.monitor.frameworks[framework_id].executors[executor_id]) { 233 | var previous = that.scope.monitor.frameworks[framework_id].executors[executor_id].statistics; 234 | current.diffUsage(previous); 235 | } 236 | 237 | // Index the data. 238 | if (!monitor.frameworks[executor.framework_id]) { 239 | monitor.frameworks[executor.framework_id] = { 240 | executors: {}, 241 | statistics: new Statistics() 242 | }; 243 | } 244 | 245 | // Aggregate these statistics into the slave and framework statistics. 246 | monitor.statistics.add(current); 247 | monitor.frameworks[executor.framework_id].statistics.add(current); 248 | monitor.frameworks[executor.framework_id].executors[executor.executor_id] = { 249 | statistics: current 250 | }; 251 | }); 252 | 253 | if (this.scope.monitor) { 254 | // Continue polling. 255 | this.polling = this.timeout(angular.bind(this, this.poll), 3000); 256 | } else { 257 | // Try to compute initial CPU usage more quickly than 3 seconds. 258 | this.polling = this.timeout(angular.bind(this, this.poll), 500); 259 | } 260 | 261 | // Update the monitoring data. 262 | this.scope.monitor = monitor; 263 | }; 264 | 265 | // Arguments: 266 | // host: host of slave. 267 | // scope: $scope service from Angular. 268 | Top.prototype.start = function(host, scope) { 269 | if (this.started()) { 270 | // TODO(bmahler): Consider logging a warning here. 271 | return; 272 | } 273 | 274 | this.endpoint = host + '/monitor/statistics?jsonp=JSON_CALLBACK'; 275 | this.scope = scope; 276 | 277 | // Initial poll is immediate. 278 | this.polling = this.timeout(angular.bind(this, this.poll), 0); 279 | 280 | // Stop when we leave the page. 281 | scope.$on('$routeChangeStart', angular.bind(this, this.stop)); 282 | }; 283 | 284 | Top.prototype.started = function() { 285 | return this.polling != null; 286 | }; 287 | 288 | Top.prototype.stop = function() { 289 | this.timeout.cancel(this.polling); 290 | this.polling = null; 291 | }; 292 | 293 | mesosServices.service('top', ['$http', '$timeout', Top]); 294 | })(); 295 | -------------------------------------------------------------------------------- /html/mesos/js/underscore-1.4.3.min.js: -------------------------------------------------------------------------------- 1 | (function(){var n=this,t=n._,r={},e=Array.prototype,u=Object.prototype,i=Function.prototype,a=e.push,o=e.slice,c=e.concat,l=u.toString,f=u.hasOwnProperty,s=e.forEach,p=e.map,v=e.reduce,h=e.reduceRight,g=e.filter,d=e.every,m=e.some,y=e.indexOf,b=e.lastIndexOf,x=Array.isArray,_=Object.keys,j=i.bind,w=function(n){return n instanceof w?n:this instanceof w?(this._wrapped=n,void 0):new w(n)};"undefined"!=typeof exports?("undefined"!=typeof module&&module.exports&&(exports=module.exports=w),exports._=w):n._=w,w.VERSION="1.4.3";var A=w.each=w.forEach=function(n,t,e){if(null!=n)if(s&&n.forEach===s)n.forEach(t,e);else if(n.length===+n.length){for(var u=0,i=n.length;i>u;u++)if(t.call(e,n[u],u,n)===r)return}else for(var a in n)if(w.has(n,a)&&t.call(e,n[a],a,n)===r)return};w.map=w.collect=function(n,t,r){var e=[];return null==n?e:p&&n.map===p?n.map(t,r):(A(n,function(n,u,i){e[e.length]=t.call(r,n,u,i)}),e)};var O="Reduce of empty array with no initial value";w.reduce=w.foldl=w.inject=function(n,t,r,e){var u=arguments.length>2;if(null==n&&(n=[]),v&&n.reduce===v)return e&&(t=w.bind(t,e)),u?n.reduce(t,r):n.reduce(t);if(A(n,function(n,i,a){u?r=t.call(e,r,n,i,a):(r=n,u=!0)}),!u)throw new TypeError(O);return r},w.reduceRight=w.foldr=function(n,t,r,e){var u=arguments.length>2;if(null==n&&(n=[]),h&&n.reduceRight===h)return e&&(t=w.bind(t,e)),u?n.reduceRight(t,r):n.reduceRight(t);var i=n.length;if(i!==+i){var a=w.keys(n);i=a.length}if(A(n,function(o,c,l){c=a?a[--i]:--i,u?r=t.call(e,r,n[c],c,l):(r=n[c],u=!0)}),!u)throw new TypeError(O);return r},w.find=w.detect=function(n,t,r){var e;return E(n,function(n,u,i){return t.call(r,n,u,i)?(e=n,!0):void 0}),e},w.filter=w.select=function(n,t,r){var e=[];return null==n?e:g&&n.filter===g?n.filter(t,r):(A(n,function(n,u,i){t.call(r,n,u,i)&&(e[e.length]=n)}),e)},w.reject=function(n,t,r){return w.filter(n,function(n,e,u){return!t.call(r,n,e,u)},r)},w.every=w.all=function(n,t,e){t||(t=w.identity);var u=!0;return null==n?u:d&&n.every===d?n.every(t,e):(A(n,function(n,i,a){return(u=u&&t.call(e,n,i,a))?void 0:r}),!!u)};var E=w.some=w.any=function(n,t,e){t||(t=w.identity);var u=!1;return null==n?u:m&&n.some===m?n.some(t,e):(A(n,function(n,i,a){return u||(u=t.call(e,n,i,a))?r:void 0}),!!u)};w.contains=w.include=function(n,t){return null==n?!1:y&&n.indexOf===y?-1!=n.indexOf(t):E(n,function(n){return n===t})},w.invoke=function(n,t){var r=o.call(arguments,2);return w.map(n,function(n){return(w.isFunction(t)?t:n[t]).apply(n,r)})},w.pluck=function(n,t){return w.map(n,function(n){return n[t]})},w.where=function(n,t){return w.isEmpty(t)?[]:w.filter(n,function(n){for(var r in t)if(t[r]!==n[r])return!1;return!0})},w.max=function(n,t,r){if(!t&&w.isArray(n)&&n[0]===+n[0]&&65535>n.length)return Math.max.apply(Math,n);if(!t&&w.isEmpty(n))return-1/0;var e={computed:-1/0,value:-1/0};return A(n,function(n,u,i){var a=t?t.call(r,n,u,i):n;a>=e.computed&&(e={value:n,computed:a})}),e.value},w.min=function(n,t,r){if(!t&&w.isArray(n)&&n[0]===+n[0]&&65535>n.length)return Math.min.apply(Math,n);if(!t&&w.isEmpty(n))return 1/0;var e={computed:1/0,value:1/0};return A(n,function(n,u,i){var a=t?t.call(r,n,u,i):n;e.computed>a&&(e={value:n,computed:a})}),e.value},w.shuffle=function(n){var t,r=0,e=[];return A(n,function(n){t=w.random(r++),e[r-1]=e[t],e[t]=n}),e};var F=function(n){return w.isFunction(n)?n:function(t){return t[n]}};w.sortBy=function(n,t,r){var e=F(t);return w.pluck(w.map(n,function(n,t,u){return{value:n,index:t,criteria:e.call(r,n,t,u)}}).sort(function(n,t){var r=n.criteria,e=t.criteria;if(r!==e){if(r>e||void 0===r)return 1;if(e>r||void 0===e)return-1}return n.indexi;){var o=i+a>>>1;u>r.call(e,n[o])?i=o+1:a=o}return i},w.toArray=function(n){return n?w.isArray(n)?o.call(n):n.length===+n.length?w.map(n,w.identity):w.values(n):[]},w.size=function(n){return null==n?0:n.length===+n.length?n.length:w.keys(n).length},w.first=w.head=w.take=function(n,t,r){return null==n?void 0:null==t||r?n[0]:o.call(n,0,t)},w.initial=function(n,t,r){return o.call(n,0,n.length-(null==t||r?1:t))},w.last=function(n,t,r){return null==n?void 0:null==t||r?n[n.length-1]:o.call(n,Math.max(n.length-t,0))},w.rest=w.tail=w.drop=function(n,t,r){return o.call(n,null==t||r?1:t)},w.compact=function(n){return w.filter(n,w.identity)};var R=function(n,t,r){return A(n,function(n){w.isArray(n)?t?a.apply(r,n):R(n,t,r):r.push(n)}),r};w.flatten=function(n,t){return R(n,t,[])},w.without=function(n){return w.difference(n,o.call(arguments,1))},w.uniq=w.unique=function(n,t,r,e){w.isFunction(t)&&(e=r,r=t,t=!1);var u=r?w.map(n,r,e):n,i=[],a=[];return A(u,function(r,e){(t?e&&a[a.length-1]===r:w.contains(a,r))||(a.push(r),i.push(n[e]))}),i},w.union=function(){return w.uniq(c.apply(e,arguments))},w.intersection=function(n){var t=o.call(arguments,1);return w.filter(w.uniq(n),function(n){return w.every(t,function(t){return w.indexOf(t,n)>=0})})},w.difference=function(n){var t=c.apply(e,o.call(arguments,1));return w.filter(n,function(n){return!w.contains(t,n)})},w.zip=function(){for(var n=o.call(arguments),t=w.max(w.pluck(n,"length")),r=Array(t),e=0;t>e;e++)r[e]=w.pluck(n,""+e);return r},w.object=function(n,t){if(null==n)return{};for(var r={},e=0,u=n.length;u>e;e++)t?r[n[e]]=t[e]:r[n[e][0]]=n[e][1];return r},w.indexOf=function(n,t,r){if(null==n)return-1;var e=0,u=n.length;if(r){if("number"!=typeof r)return e=w.sortedIndex(n,t),n[e]===t?e:-1;e=0>r?Math.max(0,u+r):r}if(y&&n.indexOf===y)return n.indexOf(t,r);for(;u>e;e++)if(n[e]===t)return e;return-1},w.lastIndexOf=function(n,t,r){if(null==n)return-1;var e=null!=r;if(b&&n.lastIndexOf===b)return e?n.lastIndexOf(t,r):n.lastIndexOf(t);for(var u=e?r:n.length;u--;)if(n[u]===t)return u;return-1},w.range=function(n,t,r){1>=arguments.length&&(t=n||0,n=0),r=arguments[2]||1;for(var e=Math.max(Math.ceil((t-n)/r),0),u=0,i=Array(e);e>u;)i[u++]=n,n+=r;return i};var I=function(){};w.bind=function(n,t){var r,e;if(n.bind===j&&j)return j.apply(n,o.call(arguments,1));if(!w.isFunction(n))throw new TypeError;return r=o.call(arguments,2),e=function(){if(!(this instanceof e))return n.apply(t,r.concat(o.call(arguments)));I.prototype=n.prototype;var u=new I;I.prototype=null;var i=n.apply(u,r.concat(o.call(arguments)));return Object(i)===i?i:u}},w.bindAll=function(n){var t=o.call(arguments,1);return 0==t.length&&(t=w.functions(n)),A(t,function(t){n[t]=w.bind(n[t],n)}),n},w.memoize=function(n,t){var r={};return t||(t=w.identity),function(){var e=t.apply(this,arguments);return w.has(r,e)?r[e]:r[e]=n.apply(this,arguments)}},w.delay=function(n,t){var r=o.call(arguments,2);return setTimeout(function(){return n.apply(null,r)},t)},w.defer=function(n){return w.delay.apply(w,[n,1].concat(o.call(arguments,1)))},w.throttle=function(n,t){var r,e,u,i,a=0,o=function(){a=new Date,u=null,i=n.apply(r,e)};return function(){var c=new Date,l=t-(c-a);return r=this,e=arguments,0>=l?(clearTimeout(u),u=null,a=c,i=n.apply(r,e)):u||(u=setTimeout(o,l)),i}},w.debounce=function(n,t,r){var e,u;return function(){var i=this,a=arguments,o=function(){e=null,r||(u=n.apply(i,a))},c=r&&!e;return clearTimeout(e),e=setTimeout(o,t),c&&(u=n.apply(i,a)),u}},w.once=function(n){var t,r=!1;return function(){return r?t:(r=!0,t=n.apply(this,arguments),n=null,t)}},w.wrap=function(n,t){return function(){var r=[n];return a.apply(r,arguments),t.apply(this,r)}},w.compose=function(){var n=arguments;return function(){for(var t=arguments,r=n.length-1;r>=0;r--)t=[n[r].apply(this,t)];return t[0]}},w.after=function(n,t){return 0>=n?t():function(){return 1>--n?t.apply(this,arguments):void 0}},w.keys=_||function(n){if(n!==Object(n))throw new TypeError("Invalid object");var t=[];for(var r in n)w.has(n,r)&&(t[t.length]=r);return t},w.values=function(n){var t=[];for(var r in n)w.has(n,r)&&t.push(n[r]);return t},w.pairs=function(n){var t=[];for(var r in n)w.has(n,r)&&t.push([r,n[r]]);return t},w.invert=function(n){var t={};for(var r in n)w.has(n,r)&&(t[n[r]]=r);return t},w.functions=w.methods=function(n){var t=[];for(var r in n)w.isFunction(n[r])&&t.push(r);return t.sort()},w.extend=function(n){return A(o.call(arguments,1),function(t){if(t)for(var r in t)n[r]=t[r]}),n},w.pick=function(n){var t={},r=c.apply(e,o.call(arguments,1));return A(r,function(r){r in n&&(t[r]=n[r])}),t},w.omit=function(n){var t={},r=c.apply(e,o.call(arguments,1));for(var u in n)w.contains(r,u)||(t[u]=n[u]);return t},w.defaults=function(n){return A(o.call(arguments,1),function(t){if(t)for(var r in t)null==n[r]&&(n[r]=t[r])}),n},w.clone=function(n){return w.isObject(n)?w.isArray(n)?n.slice():w.extend({},n):n},w.tap=function(n,t){return t(n),n};var S=function(n,t,r,e){if(n===t)return 0!==n||1/n==1/t;if(null==n||null==t)return n===t;n instanceof w&&(n=n._wrapped),t instanceof w&&(t=t._wrapped);var u=l.call(n);if(u!=l.call(t))return!1;switch(u){case"[object String]":return n==t+"";case"[object Number]":return n!=+n?t!=+t:0==n?1/n==1/t:n==+t;case"[object Date]":case"[object Boolean]":return+n==+t;case"[object RegExp]":return n.source==t.source&&n.global==t.global&&n.multiline==t.multiline&&n.ignoreCase==t.ignoreCase}if("object"!=typeof n||"object"!=typeof t)return!1;for(var i=r.length;i--;)if(r[i]==n)return e[i]==t;r.push(n),e.push(t);var a=0,o=!0;if("[object Array]"==u){if(a=n.length,o=a==t.length)for(;a--&&(o=S(n[a],t[a],r,e)););}else{var c=n.constructor,f=t.constructor;if(c!==f&&!(w.isFunction(c)&&c instanceof c&&w.isFunction(f)&&f instanceof f))return!1;for(var s in n)if(w.has(n,s)&&(a++,!(o=w.has(t,s)&&S(n[s],t[s],r,e))))break;if(o){for(s in t)if(w.has(t,s)&&!a--)break;o=!a}}return r.pop(),e.pop(),o};w.isEqual=function(n,t){return S(n,t,[],[])},w.isEmpty=function(n){if(null==n)return!0;if(w.isArray(n)||w.isString(n))return 0===n.length;for(var t in n)if(w.has(n,t))return!1;return!0},w.isElement=function(n){return!(!n||1!==n.nodeType)},w.isArray=x||function(n){return"[object Array]"==l.call(n)},w.isObject=function(n){return n===Object(n)},A(["Arguments","Function","String","Number","Date","RegExp"],function(n){w["is"+n]=function(t){return l.call(t)=="[object "+n+"]"}}),w.isArguments(arguments)||(w.isArguments=function(n){return!(!n||!w.has(n,"callee"))}),w.isFunction=function(n){return"function"==typeof n},w.isFinite=function(n){return isFinite(n)&&!isNaN(parseFloat(n))},w.isNaN=function(n){return w.isNumber(n)&&n!=+n},w.isBoolean=function(n){return n===!0||n===!1||"[object Boolean]"==l.call(n)},w.isNull=function(n){return null===n},w.isUndefined=function(n){return void 0===n},w.has=function(n,t){return f.call(n,t)},w.noConflict=function(){return n._=t,this},w.identity=function(n){return n},w.times=function(n,t,r){for(var e=Array(n),u=0;n>u;u++)e[u]=t.call(r,u);return e},w.random=function(n,t){return null==t&&(t=n,n=0),n+(0|Math.random()*(t-n+1))};var T={escape:{"&":"&","<":"<",">":">",'"':""","'":"'","/":"/"}};T.unescape=w.invert(T.escape);var M={escape:RegExp("["+w.keys(T.escape).join("")+"]","g"),unescape:RegExp("("+w.keys(T.unescape).join("|")+")","g")};w.each(["escape","unescape"],function(n){w[n]=function(t){return null==t?"":(""+t).replace(M[n],function(t){return T[n][t]})}}),w.result=function(n,t){if(null==n)return null;var r=n[t];return w.isFunction(r)?r.call(n):r},w.mixin=function(n){A(w.functions(n),function(t){var r=w[t]=n[t];w.prototype[t]=function(){var n=[this._wrapped];return a.apply(n,arguments),z.call(this,r.apply(w,n))}})};var N=0;w.uniqueId=function(n){var t=""+ ++N;return n?n+t:t},w.templateSettings={evaluate:/<%([\s\S]+?)%>/g,interpolate:/<%=([\s\S]+?)%>/g,escape:/<%-([\s\S]+?)%>/g};var q=/(.)^/,B={"'":"'","\\":"\\","\r":"r","\n":"n"," ":"t","\u2028":"u2028","\u2029":"u2029"},D=/\\|'|\r|\n|\t|\u2028|\u2029/g;w.template=function(n,t,r){r=w.defaults({},r,w.templateSettings);var e=RegExp([(r.escape||q).source,(r.interpolate||q).source,(r.evaluate||q).source].join("|")+"|$","g"),u=0,i="__p+='";n.replace(e,function(t,r,e,a,o){return i+=n.slice(u,o).replace(D,function(n){return"\\"+B[n]}),r&&(i+="'+\n((__t=("+r+"))==null?'':_.escape(__t))+\n'"),e&&(i+="'+\n((__t=("+e+"))==null?'':__t)+\n'"),a&&(i+="';\n"+a+"\n__p+='"),u=o+t.length,t}),i+="';\n",r.variable||(i="with(obj||{}){\n"+i+"}\n"),i="var __t,__p='',__j=Array.prototype.join,print=function(){__p+=__j.call(arguments,'');};\n"+i+"return __p;\n";try{var a=Function(r.variable||"obj","_",i)}catch(o){throw o.source=i,o}if(t)return a(t,w);var c=function(n){return a.call(this,n,w)};return c.source="function("+(r.variable||"obj")+"){\n"+i+"}",c},w.chain=function(n){return w(n).chain()};var z=function(n){return this._chain?w(n).chain():n};w.mixin(w),A(["pop","push","reverse","shift","sort","splice","unshift"],function(n){var t=e[n];w.prototype[n]=function(){var r=this._wrapped;return t.apply(r,arguments),"shift"!=n&&"splice"!=n||0!==r.length||delete r[0],z.call(this,r)}}),A(["concat","join","slice"],function(n){var t=e[n];w.prototype[n]=function(){return z.call(this,t.apply(this._wrapped,arguments))}}),w.extend(w.prototype,{chain:function(){return this._chain=!0,this},value:function(){return this._wrapped}})}).call(this); -------------------------------------------------------------------------------- /html/mesos/js/zeroclipboard-1.1.7.js: -------------------------------------------------------------------------------- 1 | /*! 2 | * zeroclipboard 3 | * The ZeroClipboard library provides an easy way to copy text to the clipboard using an invisible Adobe Flash movie, and a JavaScript interface. 4 | * Copyright 2013 Jon Rohan, James M. Greene, . 5 | * Released under the MIT license 6 | * http://zeroclipboard.github.io/ZeroClipboard/ 7 | * v1.2.0-beta.2 8 | */(function() { 9 | "use strict"; 10 | var _camelizeCssPropName = function() { 11 | var matcherRegex = /\-([a-z])/g, replacerFn = function(match, group) { 12 | return group.toUpperCase(); 13 | }; 14 | return function(prop) { 15 | return prop.replace(matcherRegex, replacerFn); 16 | }; 17 | }(); 18 | var _getStyle = function(el, prop) { 19 | var value, camelProp, tagName, possiblePointers, i, len; 20 | if (window.getComputedStyle) { 21 | value = window.getComputedStyle(el, null).getPropertyValue(prop); 22 | } else { 23 | camelProp = _camelizeCssPropName(prop); 24 | if (el.currentStyle) { 25 | value = el.currentStyle[camelProp]; 26 | } else { 27 | value = el.style[camelProp]; 28 | } 29 | } 30 | if (value === "auto" && prop === "cursor") { 31 | tagName = el.tagName.toLowerCase(); 32 | possiblePointers = [ "a" ]; 33 | for (i = 0, len = possiblePointers.length; i < len; i++) { 34 | if (tagName === possiblePointers[i]) { 35 | return "pointer"; 36 | } 37 | } 38 | } 39 | return value; 40 | }; 41 | var _elementMouseOver = function(event) { 42 | if (!ZeroClipboard.prototype._singleton) return; 43 | if (!event) { 44 | event = window.event; 45 | } 46 | var target; 47 | if (this !== window) { 48 | target = this; 49 | } else if (event.target) { 50 | target = event.target; 51 | } else if (event.srcElement) { 52 | target = event.srcElement; 53 | } 54 | ZeroClipboard.prototype._singleton.setCurrent(target); 55 | }; 56 | var _addEventHandler = function(element, method, func) { 57 | if (element.addEventListener) { 58 | element.addEventListener(method, func, false); 59 | } else if (element.attachEvent) { 60 | element.attachEvent("on" + method, func); 61 | } 62 | }; 63 | var _removeEventHandler = function(element, method, func) { 64 | if (element.removeEventListener) { 65 | element.removeEventListener(method, func, false); 66 | } else if (element.detachEvent) { 67 | element.detachEvent("on" + method, func); 68 | } 69 | }; 70 | var _addClass = function(element, value) { 71 | if (element.addClass) { 72 | element.addClass(value); 73 | return element; 74 | } 75 | if (value && typeof value === "string") { 76 | var classNames = (value || "").split(/\s+/); 77 | if (element.nodeType === 1) { 78 | if (!element.className) { 79 | element.className = value; 80 | } else { 81 | var className = " " + element.className + " ", setClass = element.className; 82 | for (var c = 0, cl = classNames.length; c < cl; c++) { 83 | if (className.indexOf(" " + classNames[c] + " ") < 0) { 84 | setClass += " " + classNames[c]; 85 | } 86 | } 87 | element.className = setClass.replace(/^\s+|\s+$/g, ""); 88 | } 89 | } 90 | } 91 | return element; 92 | }; 93 | var _removeClass = function(element, value) { 94 | if (element.removeClass) { 95 | element.removeClass(value); 96 | return element; 97 | } 98 | if (value && typeof value === "string" || value === undefined) { 99 | var classNames = (value || "").split(/\s+/); 100 | if (element.nodeType === 1 && element.className) { 101 | if (value) { 102 | var className = (" " + element.className + " ").replace(/[\n\t]/g, " "); 103 | for (var c = 0, cl = classNames.length; c < cl; c++) { 104 | className = className.replace(" " + classNames[c] + " ", " "); 105 | } 106 | element.className = className.replace(/^\s+|\s+$/g, ""); 107 | } else { 108 | element.className = ""; 109 | } 110 | } 111 | } 112 | return element; 113 | }; 114 | var _getZoomFactor = function() { 115 | var rect, physicalWidth, logicalWidth, zoomFactor = 1; 116 | if (typeof document.body.getBoundingClientRect === "function") { 117 | rect = document.body.getBoundingClientRect(); 118 | physicalWidth = rect.right - rect.left; 119 | logicalWidth = document.body.offsetWidth; 120 | zoomFactor = Math.round(physicalWidth / logicalWidth * 100) / 100; 121 | } 122 | return zoomFactor; 123 | }; 124 | var _getDOMObjectPosition = function(obj) { 125 | var info = { 126 | left: 0, 127 | top: 0, 128 | width: 0, 129 | height: 0, 130 | zIndex: 999999999 131 | }; 132 | var zi = _getStyle(obj, "z-index"); 133 | if (zi && zi !== "auto") { 134 | info.zIndex = parseInt(zi, 10); 135 | } 136 | if (typeof obj.getBoundingClientRect === "function") { 137 | var rect = obj.getBoundingClientRect(); 138 | var pageXOffset, pageYOffset, zoomFactor; 139 | if ("pageXOffset" in window && "pageYOffset" in window) { 140 | pageXOffset = window.pageXOffset; 141 | pageYOffset = window.pageYOffset; 142 | } else { 143 | zoomFactor = _getZoomFactor(); 144 | pageXOffset = Math.round(document.documentElement.scrollLeft / zoomFactor); 145 | pageYOffset = Math.round(document.documentElement.scrollTop / zoomFactor); 146 | } 147 | var leftBorderWidth = document.documentElement.clientLeft || 0; 148 | var topBorderWidth = document.documentElement.clientTop || 0; 149 | info.left = rect.left + pageXOffset - leftBorderWidth; 150 | info.top = rect.top + pageYOffset - topBorderWidth; 151 | info.width = rect.width; 152 | info.height = rect.height; 153 | } 154 | return info; 155 | }; 156 | var _noCache = function(path) { 157 | var client = ZeroClipboard.prototype._singleton; 158 | if (client.options.useNoCache) { 159 | return (path.indexOf("?") >= 0 ? "&nocache=" : "?nocache=") + (new Date).getTime(); 160 | } else { 161 | return ""; 162 | } 163 | }; 164 | var _vars = function(options) { 165 | var str = []; 166 | if (options.trustedDomains) { 167 | var domains; 168 | if (typeof options.trustedDomains === "string" && options.trustedDomains) { 169 | domains = [ options.trustedDomains ]; 170 | } else if ("length" in options.trustedDomains) { 171 | domains = options.trustedDomains; 172 | } 173 | str.push("trustedDomain=" + encodeURIComponent(domains.join(","))); 174 | } 175 | if (typeof options.amdModuleId === "string" && options.amdModuleId) { 176 | str.push("amdModuleId=" + encodeURIComponent(options.amdModuleId)); 177 | } 178 | if (typeof options.cjsModuleId === "string" && options.cjsModuleId) { 179 | str.push("cjsModuleId=" + encodeURIComponent(options.cjsModuleId)); 180 | } 181 | return str.join("&"); 182 | }; 183 | var _inArray = function(elem, array) { 184 | if (array.indexOf) { 185 | return array.indexOf(elem); 186 | } 187 | for (var i = 0, length = array.length; i < length; i++) { 188 | if (array[i] === elem) { 189 | return i; 190 | } 191 | } 192 | return -1; 193 | }; 194 | var _prepGlue = function(elements) { 195 | if (typeof elements === "string") throw new TypeError("ZeroClipboard doesn't accept query strings."); 196 | if (!elements.length) return [ elements ]; 197 | return elements; 198 | }; 199 | var _dispatchCallback = function(func, element, instance, args, async) { 200 | if (async) { 201 | window.setTimeout(function() { 202 | func.call(element, instance, args); 203 | }, 0); 204 | } else { 205 | func.call(element, instance, args); 206 | } 207 | }; 208 | var ZeroClipboard = function(elements, options) { 209 | if (elements) (ZeroClipboard.prototype._singleton || this).glue(elements); 210 | if (ZeroClipboard.prototype._singleton) return ZeroClipboard.prototype._singleton; 211 | ZeroClipboard.prototype._singleton = this; 212 | this.options = {}; 213 | for (var kd in _defaults) this.options[kd] = _defaults[kd]; 214 | for (var ko in options) this.options[ko] = options[ko]; 215 | this.handlers = {}; 216 | if (ZeroClipboard.detectFlashSupport()) _bridge(); 217 | }; 218 | var currentElement, gluedElements = []; 219 | ZeroClipboard.prototype.setCurrent = function(element) { 220 | currentElement = element; 221 | this.reposition(); 222 | if (element.getAttribute("title")) { 223 | this.setTitle(element.getAttribute("title")); 224 | } 225 | this.setHandCursor(_getStyle(element, "cursor") === "pointer"); 226 | }; 227 | ZeroClipboard.prototype.setText = function(newText) { 228 | if (newText && newText !== "") { 229 | this.options.text = newText; 230 | if (this.ready()) this.flashBridge.setText(newText); 231 | } 232 | }; 233 | ZeroClipboard.prototype.setTitle = function(newTitle) { 234 | if (newTitle && newTitle !== "") this.htmlBridge.setAttribute("title", newTitle); 235 | }; 236 | ZeroClipboard.prototype.setSize = function(width, height) { 237 | if (this.ready()) this.flashBridge.setSize(width, height); 238 | }; 239 | ZeroClipboard.prototype.setHandCursor = function(enabled) { 240 | if (this.ready()) this.flashBridge.setHandCursor(enabled); 241 | }; 242 | ZeroClipboard.version = "1.2.0-beta.2"; 243 | var _defaults = { 244 | moviePath: "ZeroClipboard.swf", 245 | trustedDomains: null, 246 | text: null, 247 | hoverClass: "zeroclipboard-is-hover", 248 | activeClass: "zeroclipboard-is-active", 249 | allowScriptAccess: "sameDomain", 250 | useNoCache: true 251 | }; 252 | ZeroClipboard.setDefaults = function(options) { 253 | for (var ko in options) _defaults[ko] = options[ko]; 254 | }; 255 | ZeroClipboard.destroy = function() { 256 | ZeroClipboard.prototype._singleton.unglue(gluedElements); 257 | var bridge = ZeroClipboard.prototype._singleton.htmlBridge; 258 | bridge.parentNode.removeChild(bridge); 259 | delete ZeroClipboard.prototype._singleton; 260 | }; 261 | ZeroClipboard.detectFlashSupport = function() { 262 | var hasFlash = false; 263 | if (typeof ActiveXObject === "function") { 264 | try { 265 | if (new ActiveXObject("ShockwaveFlash.ShockwaveFlash")) { 266 | hasFlash = true; 267 | } 268 | } catch (error) {} 269 | } 270 | if (!hasFlash && navigator.mimeTypes["application/x-shockwave-flash"]) { 271 | hasFlash = true; 272 | } 273 | return hasFlash; 274 | }; 275 | var _amdModuleId = null; 276 | var _cjsModuleId = null; 277 | var _bridge = function() { 278 | var client = ZeroClipboard.prototype._singleton; 279 | var container = document.getElementById("global-zeroclipboard-html-bridge"); 280 | if (!container) { 281 | var opts = {}; 282 | for (var ko in client.options) opts[ko] = client.options[ko]; 283 | opts.amdModuleId = _amdModuleId; 284 | opts.cjsModuleId = _cjsModuleId; 285 | var flashvars = _vars(opts); 286 | var html = ' '; 287 | container = document.createElement("div"); 288 | container.id = "global-zeroclipboard-html-bridge"; 289 | container.setAttribute("class", "global-zeroclipboard-container"); 290 | container.setAttribute("data-clipboard-ready", false); 291 | container.style.position = "absolute"; 292 | container.style.left = "-9999px"; 293 | container.style.top = "-9999px"; 294 | container.style.width = "15px"; 295 | container.style.height = "15px"; 296 | container.style.zIndex = "9999"; 297 | container.innerHTML = html; 298 | document.body.appendChild(container); 299 | } 300 | client.htmlBridge = container; 301 | client.flashBridge = document["global-zeroclipboard-flash-bridge"] || container.children[0].lastElementChild; 302 | }; 303 | ZeroClipboard.prototype.resetBridge = function() { 304 | this.htmlBridge.style.left = "-9999px"; 305 | this.htmlBridge.style.top = "-9999px"; 306 | this.htmlBridge.removeAttribute("title"); 307 | this.htmlBridge.removeAttribute("data-clipboard-text"); 308 | _removeClass(currentElement, this.options.activeClass); 309 | currentElement = null; 310 | this.options.text = null; 311 | }; 312 | ZeroClipboard.prototype.ready = function() { 313 | var ready = this.htmlBridge.getAttribute("data-clipboard-ready"); 314 | return ready === "true" || ready === true; 315 | }; 316 | ZeroClipboard.prototype.reposition = function() { 317 | if (!currentElement) return false; 318 | var pos = _getDOMObjectPosition(currentElement); 319 | this.htmlBridge.style.top = pos.top + "px"; 320 | this.htmlBridge.style.left = pos.left + "px"; 321 | this.htmlBridge.style.width = pos.width + "px"; 322 | this.htmlBridge.style.height = pos.height + "px"; 323 | this.htmlBridge.style.zIndex = pos.zIndex + 1; 324 | this.setSize(pos.width, pos.height); 325 | }; 326 | ZeroClipboard.dispatch = function(eventName, args) { 327 | ZeroClipboard.prototype._singleton.receiveEvent(eventName, args); 328 | }; 329 | ZeroClipboard.prototype.on = function(eventName, func) { 330 | var events = eventName.toString().split(/\s/g); 331 | for (var i = 0; i < events.length; i++) { 332 | eventName = events[i].toLowerCase().replace(/^on/, ""); 333 | if (!this.handlers[eventName]) this.handlers[eventName] = func; 334 | } 335 | if (this.handlers.noflash && !ZeroClipboard.detectFlashSupport()) { 336 | this.receiveEvent("onNoFlash", null); 337 | } 338 | }; 339 | ZeroClipboard.prototype.addEventListener = ZeroClipboard.prototype.on; 340 | ZeroClipboard.prototype.off = function(eventName, func) { 341 | var events = eventName.toString().split(/\s/g); 342 | for (var i = 0; i < events.length; i++) { 343 | eventName = events[i].toLowerCase().replace(/^on/, ""); 344 | for (var event in this.handlers) { 345 | if (event === eventName && this.handlers[event] === func) { 346 | delete this.handlers[event]; 347 | } 348 | } 349 | } 350 | }; 351 | ZeroClipboard.prototype.removeEventListener = ZeroClipboard.prototype.off; 352 | ZeroClipboard.prototype.receiveEvent = function(eventName, args) { 353 | eventName = eventName.toString().toLowerCase().replace(/^on/, ""); 354 | var element = currentElement; 355 | var performCallbackAsync = true; 356 | switch (eventName) { 357 | case "load": 358 | if (args && parseFloat(args.flashVersion.replace(",", ".").replace(/[^0-9\.]/gi, "")) < 10) { 359 | this.receiveEvent("onWrongFlash", { 360 | flashVersion: args.flashVersion 361 | }); 362 | return; 363 | } 364 | this.htmlBridge.setAttribute("data-clipboard-ready", true); 365 | break; 366 | case "mouseover": 367 | _addClass(element, this.options.hoverClass); 368 | break; 369 | case "mouseout": 370 | _removeClass(element, this.options.hoverClass); 371 | this.resetBridge(); 372 | break; 373 | case "mousedown": 374 | _addClass(element, this.options.activeClass); 375 | break; 376 | case "mouseup": 377 | _removeClass(element, this.options.activeClass); 378 | break; 379 | case "datarequested": 380 | var targetId = element.getAttribute("data-clipboard-target"), targetEl = !targetId ? null : document.getElementById(targetId); 381 | if (targetEl) { 382 | var textContent = targetEl.value || targetEl.textContent || targetEl.innerText; 383 | if (textContent) this.setText(textContent); 384 | } else { 385 | var defaultText = element.getAttribute("data-clipboard-text"); 386 | if (defaultText) this.setText(defaultText); 387 | } 388 | performCallbackAsync = false; 389 | break; 390 | case "complete": 391 | this.options.text = null; 392 | break; 393 | } 394 | if (this.handlers[eventName]) { 395 | var func = this.handlers[eventName]; 396 | if (typeof func === "string" && typeof window[func] === "function") { 397 | func = window[func]; 398 | } 399 | if (typeof func === "function") { 400 | _dispatchCallback(func, element, this, args, performCallbackAsync); 401 | } 402 | } 403 | }; 404 | ZeroClipboard.prototype.glue = function(elements) { 405 | elements = _prepGlue(elements); 406 | for (var i = 0; i < elements.length; i++) { 407 | if (_inArray(elements[i], gluedElements) == -1) { 408 | gluedElements.push(elements[i]); 409 | _addEventHandler(elements[i], "mouseover", _elementMouseOver); 410 | } 411 | } 412 | }; 413 | ZeroClipboard.prototype.unglue = function(elements) { 414 | elements = _prepGlue(elements); 415 | for (var i = 0; i < elements.length; i++) { 416 | _removeEventHandler(elements[i], "mouseover", _elementMouseOver); 417 | var arrayIndex = _inArray(elements[i], gluedElements); 418 | if (arrayIndex != -1) gluedElements.splice(arrayIndex, 1); 419 | } 420 | }; 421 | if (typeof define === "function" && define.amd) { 422 | define([ "require", "exports", "module" ], function(require, exports, module) { 423 | _amdModuleId = module && module.id || null; 424 | return ZeroClipboard; 425 | }); 426 | } else if (typeof module !== "undefined" && module) { 427 | _cjsModuleId = module.id || null; 428 | module.exports = ZeroClipboard; 429 | } else { 430 | window.ZeroClipboard = ZeroClipboard; 431 | } 432 | })(); -------------------------------------------------------------------------------- /html/mesos/js/zeroclipboard-1.1.7.min.js: -------------------------------------------------------------------------------- 1 | /*! 2 | * zeroclipboard 3 | * The Zero Clipboard library provides an easy way to copy text to the clipboard using an invisible Adobe Flash movie, and a JavaScript interface. 4 | * Copyright 2012 Jon Rohan, James M. Greene, . 5 | * Released under the MIT license 6 | * http://jonrohan.github.com/ZeroClipboard/ 7 | * v1.1.7 8 | */(function(){"use strict";var a=function(a,b){var c=a.style[b];a.currentStyle?c=a.currentStyle[b]:window.getComputedStyle&&(c=document.defaultView.getComputedStyle(a,null).getPropertyValue(b));if(c=="auto"&&b=="cursor"){var d=["a"];for(var e=0;e=0?"&":"?")+"nocache="+(new Date).getTime()},i=function(a){var b=[];return a.trustedDomains&&(typeof a.trustedDomains=="string"?b.push("trustedDomain="+a.trustedDomains):b.push("trustedDomain="+a.trustedDomains.join(","))),b.join("&")},j=function(a,b){if(b.indexOf)return b.indexOf(a);for(var c=0,d=b.length;c ';b=document.createElement("div"),b.id="global-zeroclipboard-html-bridge",b.setAttribute("class","global-zeroclipboard-container"),b.setAttribute("data-clipboard-ready",!1),b.style.position="absolute",b.style.left="-9999px",b.style.top="-9999px",b.style.width="15px",b.style.height="15px",b.style.zIndex="9999",b.innerHTML=c,document.body.appendChild(b)}a.htmlBridge=b,a.flashBridge=document["global-zeroclipboard-flash-bridge"]||b.children[0].lastElementChild};l.prototype.resetBridge=function(){this.htmlBridge.style.left="-9999px",this.htmlBridge.style.top="-9999px",this.htmlBridge.removeAttribute("title"),this.htmlBridge.removeAttribute("data-clipboard-text"),f(m,this.options.activeClass),m=null,this.options.text=null},l.prototype.ready=function(){var a=this.htmlBridge.getAttribute("data-clipboard-ready");return a==="true"||a===!0},l.prototype.reposition=function(){if(!m)return!1;var a=g(m);this.htmlBridge.style.top=a.top+"px",this.htmlBridge.style.left=a.left+"px",this.htmlBridge.style.width=a.width+"px",this.htmlBridge.style.height=a.height+"px",this.htmlBridge.style.zIndex=a.zIndex+1,this.setSize(a.width,a.height)},l.dispatch=function(a,b){l.prototype._singleton.receiveEvent(a,b)},l.prototype.on=function(a,b){var c=a.toString().split(/\s/g);for(var d=0;d 2 |
  • 3 | Master 4 |
  • 5 |
  • 6 | Offers 7 |
  • 8 | 9 | 10 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 26 | 31 | 36 | 37 | 38 | 39 | 40 |
    IDFrameworkHostCPUsMem
    24 | {{offer.id | truncateMesosID}} 25 | 27 | 28 | {{offer.framework_name}} 29 | 30 | 32 | 33 | {{offer.hostname}} 34 | 35 | {{offer.resources.cpus | number}}{{offer.resources.mem * (1024 * 1024) | dataSize}}
    41 | -------------------------------------------------------------------------------- /html/mesos/pailer.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 21 | 22 | 23 | 24 |
    
    25 |     
    26 | 27 | 28 | 29 | 30 | 31 | 65 | 66 | 67 | -------------------------------------------------------------------------------- /html/mesos/slave.html: -------------------------------------------------------------------------------- 1 | 10 | 11 |
    12 | 13 | {{alert_message}} 14 |
    15 | 16 |
    17 |
    18 |
    19 |
    20 |
    Cluster:
    21 |
    22 | {{cluster}} 23 | 24 | (Unnamed) 25 | 28 | 29 |
    30 |
    Slave:
    31 |
    {{state.hostname}}
    32 |
    Version:
    33 |
    {{state.version}}
    34 |
    Built:
    35 |
    36 | 37 |
    38 |
    Started:
    39 |
    40 | 41 |
    42 |
    Master:
    43 |
    {{state.master_hostname}}
    44 |
    45 |

    LOG

    46 | 47 |

    Tasks

    48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 |
    Staged{{state.staged_tasks | number}}
    Started{{state.started_tasks | number}}
    Finished{{state.finished_tasks | number}}
    Killed{{state.killed_tasks | number}}
    Failed{{state.failed_tasks | number}}
    Lost{{state.lost_tasks | number}}
    76 | 77 |

    Resources

    78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 98 | 99 | 100 | 101 | 102 | 105 | 106 | 107 |
    UsedAllocated
    CPUs{{monitor.statistics.cpus_total_usage | number}}{{state.resources.cpus | number}}
    Memory{{monitor.statistics.mem_rss_bytes | dataSize}} 96 | {{state.resources.mem * (1024 * 1024) | dataSize}} 97 |
    Disk{{0 | dataSize}} 103 | {{state.resources.disk * (1024 * 1024) | dataSize}} 104 |
    108 |
    109 | 110 |
    111 |
    112 | 114 | 115 | 116 | 117 | 118 | 119 | 120 | 121 | 122 | 123 | 124 | 125 | 126 | 137 | 138 | 139 | 140 | 141 | 142 | 143 | 144 |
    IDUserNameActive TasksCPUs (Used / Allocated)Mem (Used / Allocated)
    127 | 128 | {{(framework.id | truncateMesosID) || framework.name}} 129 | 136 | {{framework.user}}{{framework.name}}{{framework.num_tasks | number}}{{monitor.frameworks[framework.id].statistics.cpus_user_usage + monitor.frameworks[framework.id].statistics.cpus_system_usage | number}} / {{framework.cpus | number}}{{monitor.frameworks[framework.id].statistics.mem_rss_bytes | dataSize}} / {{framework.mem * (1024 * 1024) | dataSize}}
    145 | 146 | 148 | 149 | 150 | 151 | 152 | 153 | 154 | 155 | 156 | 157 | 158 | 159 | 160 | 171 | 172 | 173 | 174 | 175 | 176 | 177 | 178 |
    IDUserNameActive TasksCPUsMem
    161 | 162 | {{completed_framework.id | truncateMesosID}} 163 | 170 | {{completed_framework.user}}{{completed_framework.name}}{{completed_framework.num_tasks | number}}{{completed_framework.cpus | number}}{{completed_framework.mem * (1024 * 1024) | dataSize}}
    179 |
    180 |
    181 | -------------------------------------------------------------------------------- /html/mesos/slave_executor.html: -------------------------------------------------------------------------------- 1 | 18 | 19 |
    20 | 21 | {{alert_message}} 22 |
    23 | 24 |
    25 |
    26 |
    27 |
    28 |
    Executor Name:
    29 |
    {{executor.name}}
    30 |
    Executor Source:
    31 |
    {{executor.source}}
    32 |
    33 | 34 |
    35 |
    Cluster:
    36 |
    37 | {{cluster}} 38 | 39 | (Unnamed) 40 | 43 | 44 |
    45 |
    Master:
    46 |
    {{state.master_hostname}}
    47 |
    48 | 49 |
    50 |
    Active Tasks:
    51 |
    {{executor.tasks.length | number}}
    52 |
    53 | 54 |

    Resources

    55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 69 | 70 | 71 | 72 | 73 | 76 | 79 | 80 | 81 | 82 | 83 | 86 | 87 | 88 |
    UsedAllocated
    CPUs 67 | {{monitor.frameworks[framework.id].executors[executor.id].statistics.cpus_total_usage | number}} 68 | {{executor.resources.cpus | number}}
    Mem 74 | {{monitor.frameworks[framework.id].executors[executor.id].statistics.mem_rss_bytes | dataSize}} 75 | 77 | {{executor.resources.mem * (1024 * 1024) | dataSize}} 78 |
    Disk{{0 | dataSize}} 84 | {{(executor.resources.disk || 0) * (1024 * 1024) | dataSize}} 85 |
    89 |
    90 |
    91 | 92 |
    93 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | 110 | 111 |
    IDNameCPUsMem
    {{queued_task.id}}{{queued_task.name}}{{queued_task.resources.cpus | number}}{{queued_task.resources.mem * (1024 * 1024) | dataSize}}
    112 | 113 | 115 | 116 | 117 | 118 | 119 | 120 | 121 | 122 | 123 | 124 | 125 | 126 | 127 | 128 | 129 | 130 | 131 | 132 | 138 | 139 | 140 |
    IDNameStateCPUs (allocated)Mem (allocated)
    {{task.id}}{{task.name}}{{task.state}}{{task.resources.cpus | number}}{{task.resources.mem * (1024 * 1024) | dataSize}} 133 | 135 | Sandbox 136 | 137 |
    141 | 142 | 144 | 145 | 146 | 147 | 148 | 149 | 150 | 151 | 152 | 153 | 154 | 155 | 156 | 157 | 158 | 159 | 160 | 161 | 167 | 168 | 169 |
    IDNameStateCPUs (allocated)Mem (allocated)
    {{completed_task.id}}{{completed_task.name}}{{completed_task.state}}{{completed_task.resources.cpus | number}}{{completed_task.resources.mem * (1024 * 1024) | dataSize}} 162 | 164 | Sandbox 165 | 166 |
    170 |
    171 | 172 |
    173 | -------------------------------------------------------------------------------- /html/mesos/slave_framework.html: -------------------------------------------------------------------------------- 1 | 14 | 15 |
    16 | 17 | {{alert_message}} 18 |
    19 | 20 |
    21 |
    22 |
    23 |
    24 |
    Name:
    {{framework.name}}
    25 |
    Master:
    {{state.master_hostname}}
    26 |
    27 | 28 |
    29 |
    Active Tasks:
    30 |
    {{framework.num_tasks | number}}
    31 |
    32 | 33 |

    Resources

    34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 48 | 49 | 50 | 51 | 52 | 55 | 58 | 59 | 60 | 61 | 62 | 65 | 66 | 67 |
    UsedAllocated
    CPUs 46 | {{monitor.frameworks[framework.id].statistics.cpus_total_usage | number}} 47 | {{framework.cpus | number}}
    Memory 53 | {{monitor.frameworks[framework.id].statistics.mem_rss_bytes | dataSize}} 54 | 56 | {{framework.mem * (1024 * 1024) | dataSize}} 57 |
    Disk- 63 | {{(framework.disk || 0) * (1024 * 1024) | dataSize}} 64 |
    68 |
    69 |
    70 | 71 |
    72 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 93 | 94 | 95 | 96 | 97 | 99 | 101 | 107 | 108 | 109 |
    IDNameSourceActive TasksQueued TasksCPUs (Used / Allocated)Mem (Used / Allocated)
    89 | 90 | {{executor.id}} 91 | 92 | {{executor.name}}{{executor.source}}{{executor.tasks.length | number}}{{executor.queued_tasks.length | number}}{{monitor.frameworks[framework.id].executors[executor.id].statistics.cpus_user_usage + monitor.frameworks[framework.id].executors[executor.id].statistics.cpus_system_usage | number}} / 98 | {{executor.resources.cpus | number}}{{monitor.frameworks[framework.id].executors[executor.id].statistics.mem_rss_bytes | dataSize}} / 100 | {{executor.resources.mem * (1024 * 1024) | dataSize}} 102 | 104 | Sandbox 105 | 106 |
    110 | 111 | 113 | 114 | 115 | 116 | 117 | 118 | 119 | 120 | 121 | 122 | 123 | 128 | 129 | 130 | 136 | 137 | 138 |
    IDNameSourceSandbox
    124 | 125 | {{completed_executor.id}} 126 | 127 | {{completed_executor.name}}{{completed_executor.source}} 131 | 133 | browse 134 | 135 |
    139 |
    140 | 141 |
    142 | -------------------------------------------------------------------------------- /html/mesos/slaves.html: -------------------------------------------------------------------------------- 1 | 9 | 10 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 35 | 36 | 37 | 38 | 39 | 43 | 46 | 47 |
    IDHostCPUsMemDiskRegisteredRe-Registered
    25 | {{slave.id | truncateMesosID}} 26 | 34 | {{slave.hostname}}{{slave.resources.cpus | number}}{{slave.resources.mem * (1024 * 1024) | dataSize}}{{slave.resources.disk * (1024 * 1024) | dataSize}} 40 | 41 | 42 | 44 | 45 |
    48 | -------------------------------------------------------------------------------- /html/traefik/dashboard/index.html: -------------------------------------------------------------------------------- 1 | Træfɪk -------------------------------------------------------------------------------- /html/traefik/dashboard/scripts/app-34497b2210.js: -------------------------------------------------------------------------------- 1 | !function(){"use strict";angular.module("traefik.section.providers.frontend-monitor",[])}(),function(){"use strict";function e(){return{restrict:"EA",templateUrl:"app/sections/providers/frontend-monitor/frontend-monitor.html",controller:t,controllerAs:"frontendCtrl",bindToController:!0,scope:{frontend:"=",frontendId:"="}}}function t(){}angular.module("traefik.section.providers.frontend-monitor").directive("frontendMonitor",e)}(),function(){"use strict";angular.module("traefik.section.providers.backend-monitor",[])}(),function(){"use strict";function e(){return{restrict:"EA",templateUrl:"app/sections/providers/backend-monitor/backend-monitor.html",controller:t,controllerAs:"backendCtrl",bindToController:!0,scope:{backend:"=",backendId:"="}}}function t(){}angular.module("traefik.section.providers.backend-monitor").directive("backendMonitor",e)}(),function(){"use strict";function e(e){e.state("provider",{url:"/",templateUrl:"app/sections/providers/providers.html",controller:"ProvidersController",controllerAs:"providersCtrl"})}e.$inject=["$stateProvider"],angular.module("traefik.section.providers",["traefik.core.provider","traefik.section.providers.backend-monitor","traefik.section.providers.frontend-monitor"]).config(e)}(),function(){"use strict";function e(e,t,a,r){var n=this;n.providers=r.get();var o=t(function(){r.get(function(e){n.providers=e},function(e){n.providers={},a.error(e)})},2e3);e.$on("$destroy",function(){t.cancel(o)})}e.$inject=["$scope","$interval","$log","Providers"],angular.module("traefik.section.providers").controller("ProvidersController",e)}(),function(){"use strict";function e(e){e.state("health",{url:"/health",templateUrl:"app/sections/health/health.html",controller:"HealthController",controllerAs:"healthCtrl"})}e.$inject=["$stateProvider"],angular.module("traefik.section.health",["traefik.core.health"]).config(e)}(),function(e){"use strict";function t(t,a,r,n){function o(e){d.graph.totalStatusCodeCount.data[0].values=[];for(var t in e)e.hasOwnProperty(t)&&d.graph.totalStatusCodeCount.data[0].values.push({label:t,value:e[t]});d.graph.totalStatusCodeCount.api?d.graph.totalStatusCodeCount.api.update():r.error("fail")}function i(e,t){var a={x:1e3*e,y:1e3*t};d.graph.averageResponseTime.data[0].values.push(a),d.graph.averageResponseTime.data[0].values.length>100&&d.graph.averageResponseTime.data[0].values.shift(),d.graph.averageResponseTime.api&&d.graph.averageResponseTime.api.update()}function s(e){i(e.unixtime,e.average_response_time_sec),o(e.total_status_code_count),d.health=e}function l(e){d.health={},r.error(e)}var d=this;d.graph={averageResponseTime:{},totalStatusCodeCount:{}},d.graph.totalStatusCodeCount.options={chart:{type:"discreteBarChart",height:200,margin:{top:20,right:20,bottom:40,left:55},x:function(e){return e.label},y:function(e){return e.value},showValues:!0,valueFormat:function(t){return e.format("d")(t)},transitionDuration:50,yAxis:{axisLabelDistance:30}},title:{enable:!0,text:"Total Status Code Count",css:{textAlign:"center"}}},d.graph.totalStatusCodeCount.data=[{key:"Total Status Code Count",values:[{label:"200",value:0}]}],d.graph.averageResponseTime.options={chart:{type:"lineChart",height:200,margin:{top:20,right:40,bottom:40,left:55},transitionDuration:50,x:function(e){return e.x},y:function(e){return e.y},useInteractiveGuideline:!0,xAxis:{tickFormat:function(t){return e.time.format("%X")(new Date(t))}},yAxis:{tickFormat:function(t){return e.format(",.1f")(t)}}},title:{enable:!0,text:"Average response time",css:{textAlign:"center"}}};var c={x:Date.now()-3e3,y:0};d.graph.averageResponseTime.data=[{values:[c],key:"Average response time (ms)",type:"line",color:"#2ca02c"}],n.get(s,l);var u=a(function(){n.get(s,l)},3e3);t.$on("$destroy",function(){a.cancel(u)})}t.$inject=["$scope","$interval","$log","Health"],angular.module("traefik.section.health").controller("HealthController",t)}(d3),function(){"use strict";angular.module("traefik.section",["ui.router","ui.bootstrap","nvd3","traefik.section.providers","traefik.section.health"])}(),function(){"use strict";function e(e){e.otherwise("/")}e.$inject=["$urlRouterProvider"],angular.module("traefik.section").config(e)}(),function(){"use strict";function e(e){return e("/traefik/api/providers")}e.$inject=["$resource"],angular.module("traefik.core.provider",["ngResource"]).factory("Providers",e)}(),function(){"use strict";function e(e){return e("/traefik/health")}e.$inject=["$resource"],angular.module("traefik.core.health",["ngResource"]).factory("Health",e)}(),function(){"use strict";angular.module("traefik",["ngAnimate","ngCookies","ngSanitize","ngMessages","ngAria","ngResource","ui.router","ui.bootstrap","traefik.section"])}(),function(){"use strict";function e(e){e.debug("runBlock end")}e.$inject=["$log"],angular.module("traefik").run(e)}(),function(){"use strict";angular.module("traefik").constant("moment",moment)}(),function(){"use strict";function e(e){e.debugEnabled(!0)}e.$inject=["$logProvider"],angular.module("traefik").config(e)}(),angular.module("traefik").run(["$templateCache",function(e){e.put("app/sections/health/health.html",'

    Health

    • Total response time :{{healthCtrl.health.total_response_time}}
    • PID :{{healthCtrl.health.pid}}
    • Uptime :{{healthCtrl.health.uptime}}
    • Total count :{{healthCtrl.health.total_count}}
    • Count :{{healthCtrl.health.count}}
    '),e.put("app/sections/providers/providers.html",'
    '),e.put("app/sections/providers/backend-monitor/backend-monitor.html",'
    {{backendCtrl.backendId}}
    ServerURLWeight
    {{serverId}}{{server.url}}{{server.weight}}
    '),e.put("app/sections/providers/frontend-monitor/frontend-monitor.html",'
    {{frontendCtrl.frontendId}}
    RouteRule
    {{routeId}}{{route.rule}}
    ')}]); 2 | //# sourceMappingURL=../maps/scripts/app-34497b2210.js.map 3 | -------------------------------------------------------------------------------- /html/ui/appcache.appcache: -------------------------------------------------------------------------------- 1 | CACHE MANIFEST 2 | # 09c1d8284511b8a3c1edbcfb4de5e405cd202e9b 3 | 4 | NETWORK: 5 | * 6 | 7 | FALLBACK: 8 | 9 | 10 | CACHE: 11 | ./images/bg.jpg 12 | ./images/bg@2x.jpg 13 | ./images/favicon.ico 14 | ./images/logos/chronos.png 15 | ./images/logos/chronos@2x.png 16 | ./images/logos/consul.png 17 | ./images/logos/consul@2x.png 18 | ./images/logos/elasticsearch.png 19 | ./images/logos/elasticsearch@2x.png 20 | ./images/logos/kibana.png 21 | ./images/logos/kibana@2x.png 22 | ./images/logos/kubernetes.png 23 | ./images/logos/kubernetes@2x.png 24 | ./images/logos/marathon.png 25 | ./images/logos/marathon@2x.png 26 | ./images/logos/mesos.png 27 | ./images/logos/mesos@2x.png 28 | ./images/logos/traefik.png 29 | ./images/logos/traefik@2x.png 30 | ./index.html 31 | ./scripts/lib.js 32 | ./scripts/lib.js.gz 33 | ./scripts/main.js 34 | ./scripts/main.js.gz 35 | ./styles/app.css 36 | ./styles/app.css.gz 37 | -------------------------------------------------------------------------------- /html/ui/images/bg.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mantl/nginx-mantlui/174479309553be933bd1e978c706b94739f90dd1/html/ui/images/bg.jpg -------------------------------------------------------------------------------- /html/ui/images/bg@2x.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mantl/nginx-mantlui/174479309553be933bd1e978c706b94739f90dd1/html/ui/images/bg@2x.jpg -------------------------------------------------------------------------------- /html/ui/images/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mantl/nginx-mantlui/174479309553be933bd1e978c706b94739f90dd1/html/ui/images/favicon.ico -------------------------------------------------------------------------------- /html/ui/images/logos/chronos.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mantl/nginx-mantlui/174479309553be933bd1e978c706b94739f90dd1/html/ui/images/logos/chronos.png -------------------------------------------------------------------------------- /html/ui/images/logos/chronos@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mantl/nginx-mantlui/174479309553be933bd1e978c706b94739f90dd1/html/ui/images/logos/chronos@2x.png -------------------------------------------------------------------------------- /html/ui/images/logos/consul.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mantl/nginx-mantlui/174479309553be933bd1e978c706b94739f90dd1/html/ui/images/logos/consul.png -------------------------------------------------------------------------------- /html/ui/images/logos/consul@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mantl/nginx-mantlui/174479309553be933bd1e978c706b94739f90dd1/html/ui/images/logos/consul@2x.png -------------------------------------------------------------------------------- /html/ui/images/logos/elasticsearch.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mantl/nginx-mantlui/174479309553be933bd1e978c706b94739f90dd1/html/ui/images/logos/elasticsearch.png -------------------------------------------------------------------------------- /html/ui/images/logos/elasticsearch@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mantl/nginx-mantlui/174479309553be933bd1e978c706b94739f90dd1/html/ui/images/logos/elasticsearch@2x.png -------------------------------------------------------------------------------- /html/ui/images/logos/kibana.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mantl/nginx-mantlui/174479309553be933bd1e978c706b94739f90dd1/html/ui/images/logos/kibana.png -------------------------------------------------------------------------------- /html/ui/images/logos/kibana@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mantl/nginx-mantlui/174479309553be933bd1e978c706b94739f90dd1/html/ui/images/logos/kibana@2x.png -------------------------------------------------------------------------------- /html/ui/images/logos/kubernetes.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mantl/nginx-mantlui/174479309553be933bd1e978c706b94739f90dd1/html/ui/images/logos/kubernetes.png -------------------------------------------------------------------------------- /html/ui/images/logos/kubernetes@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mantl/nginx-mantlui/174479309553be933bd1e978c706b94739f90dd1/html/ui/images/logos/kubernetes@2x.png -------------------------------------------------------------------------------- /html/ui/images/logos/marathon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mantl/nginx-mantlui/174479309553be933bd1e978c706b94739f90dd1/html/ui/images/logos/marathon.png -------------------------------------------------------------------------------- /html/ui/images/logos/marathon@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mantl/nginx-mantlui/174479309553be933bd1e978c706b94739f90dd1/html/ui/images/logos/marathon@2x.png -------------------------------------------------------------------------------- /html/ui/images/logos/mesos.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mantl/nginx-mantlui/174479309553be933bd1e978c706b94739f90dd1/html/ui/images/logos/mesos.png -------------------------------------------------------------------------------- /html/ui/images/logos/mesos@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mantl/nginx-mantlui/174479309553be933bd1e978c706b94739f90dd1/html/ui/images/logos/mesos@2x.png -------------------------------------------------------------------------------- /html/ui/images/logos/traefik.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mantl/nginx-mantlui/174479309553be933bd1e978c706b94739f90dd1/html/ui/images/logos/traefik.png -------------------------------------------------------------------------------- /html/ui/images/logos/traefik@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mantl/nginx-mantlui/174479309553be933bd1e978c706b94739f90dd1/html/ui/images/logos/traefik@2x.png -------------------------------------------------------------------------------- /html/ui/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Mantl 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | -------------------------------------------------------------------------------- /html/ui/index.html.gz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mantl/nginx-mantlui/174479309553be933bd1e978c706b94739f90dd1/html/ui/index.html.gz -------------------------------------------------------------------------------- /html/ui/scripts/lib.js: -------------------------------------------------------------------------------- 1 | !function(){"use strict";var n="undefined"==typeof window?global:window;if("function"!=typeof n.require){var r={},e={},t={},i={}.hasOwnProperty,o="components/",u=function(n,r){var e=0;r&&(0===r.indexOf(o)&&(e=o.length),r.indexOf("/",e)>0&&(r=r.substring(e,r.indexOf("/",e))));var i=t[n+"/index.js"]||t[r+"/deps/"+n+"/index.js"];return i?o+i.substring(0,i.length-".js".length):n},f=/^\.\.?(\/|$)/,l=function(n,r){for(var e,t=[],i=(f.test(r)?n+"/"+r:r).split("/"),o=0,u=i.length;u>o;o++)e=i[o],".."===e?t.pop():"."!==e&&""!==e&&t.push(e);return t.join("/")},s=function(n){return n.split("/").slice(0,-1).join("/")},c=function(r){return function(e){var t=l(s(r),e);return n.require(t,r)}},a=function(n,r){var t={id:n,exports:{}};return e[n]=t,r(t.exports,c(n),t),t.exports},p=function(n,t){var o=l(n,".");if(null==t&&(t="/"),o=u(n,t),i.call(e,o))return e[o].exports;if(i.call(r,o))return a(o,r[o]);var f=l(o,"./index");if(i.call(e,f))return e[f].exports;if(i.call(r,f))return a(f,r[f]);throw new Error('Cannot find module "'+n+'" from "'+t+'"')};p.alias=function(n,r){t[r]=n},p.register=p.define=function(n,e){if("object"==typeof n)for(var t in n)i.call(n,t)&&(r[t]=n[t]);else r[n]=e},p.list=function(){var n=[];for(var e in r)i.call(r,e)&&n.push(e);return n},p.brunch=!0,p._cache=e,n.require=p}}(),require.register("lib/index",function(n,r,e){e.exports=function(n){var r=Elm.fullscreen(n);return r}}); -------------------------------------------------------------------------------- /html/ui/scripts/lib.js.gz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mantl/nginx-mantlui/174479309553be933bd1e978c706b94739f90dd1/html/ui/scripts/lib.js.gz -------------------------------------------------------------------------------- /html/ui/scripts/main.js.gz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mantl/nginx-mantlui/174479309553be933bd1e978c706b94739f90dd1/html/ui/scripts/main.js.gz -------------------------------------------------------------------------------- /html/ui/signature: -------------------------------------------------------------------------------- 1 | 09c1d8284511b8a3c1edbcfb4de5e405cd202e9b -------------------------------------------------------------------------------- /html/ui/styles/app.css.gz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mantl/nginx-mantlui/174479309553be933bd1e978c706b94739f90dd1/html/ui/styles/app.css.gz -------------------------------------------------------------------------------- /nginx.tmpl: -------------------------------------------------------------------------------- 1 | worker_processes 1; 2 | error_log /dev/stderr; 3 | 4 | events { 5 | worker_connections 1024; 6 | } 7 | 8 | {{$SSL := or (key "config/mantlui/ssl") (or (env "MANTLUI_SSL") "false") | parseBool}} 9 | {{$AUTH := or (key "config/mantl/auth") (or (env "MANTLUI_AUTH") "false") | parseBool}} 10 | {{$consulSSL := or (key "config/mantl/consul_ssl") (or (env "CONSUL_SSL") "false") | parseBool}} 11 | 12 | http { 13 | include mime.types; 14 | default_type application/octet-stream; 15 | 16 | sendfile on; 17 | keepalive_timeout 65; 18 | access_log /dev/stdout; 19 | 20 | {{$mesos := service "leader.mesos" "any"}}{{with $mesos}} 21 | upstream mesos { {{range $mesos}} 22 | server {{.Address}}:{{.Port}};{{end}} 23 | } {{end}} 24 | 25 | {{$marathon := service "marathon" "any"}}{{with $marathon}} 26 | upstream marathon { {{range $marathon}} 27 | server {{.Address}}:{{.Port}};{{end}} 28 | } {{end}} 29 | 30 | {{$kubernetes_ui := service "nodeport" "any"}}{{with $kubernetes_ui}} 31 | upstream kubernetes_ui { {{range $kubernetes_ui}} 32 | server {{.Address}}:30000;{{end}} 33 | } {{end}} 34 | 35 | {{$kubernetes_api := service "kubernetes" "any"}}{{with $kubernetes_api}} 36 | upstream kubernetes_api { {{range $kubernetes_api}} 37 | server {{.Address}}:{{.Port}};{{end}} 38 | } {{end}} 39 | 40 | {{$chronos := service "chronos" "any"}}{{with $chronos}} 41 | upstream chronos { {{range $chronos}} 42 | server {{.Address}}:{{.Port}};{{end}} 43 | } {{end}} 44 | 45 | {{$consul := service "consul" "any"}}{{with $consul}} 46 | upstream consul { {{range $consul}} 47 | server {{.Address}}:8500;{{end}} 48 | } {{end}} 49 | 50 | {{$mantlapi := service "mantl-api" "any"}}{{with $mantlapi}} 51 | upstream mantlapi { {{range $mantlapi}} 52 | server {{.Address}}:{{.Port}};{{end}} 53 | } {{end}} 54 | 55 | {{$traefikAdmin := service "traefik-admin" "any"}}{{with $traefikAdmin}} 56 | upstream traefik-admin { {{range $traefikAdmin}} 57 | server {{.Address}}:{{.Port}};{{end}} 58 | } {{end}} 59 | 60 | {{$kibana := (or (service "kibana-mantl-task" "any") (service "kibana-mantl" "any") (service "kibana" "any"))}}{{with $kibana}} 61 | upstream kibana { {{range $kibana}} 62 | server {{.Address}}:{{.Port}};{{end}} 63 | } {{end}} 64 | 65 | {{$elasticsearch := (or (service "elasticsearch-mantl" "any") (service "elasticsearch" "any"))}}{{with $elasticsearch}} 66 | upstream elasticsearch { {{range $elasticsearch}} 67 | server {{.Address}}:{{.Port}};{{end}} 68 | } {{end}} 69 | 70 | {{$kafka_api := service "kafka-mantl" "any"}}{{with $kafka_api}} 71 | upstream kafka-api { {{range $kafka_api}} 72 | server {{.Address}}:{{.Port}};{{end}} 73 | } {{end}} 74 | 75 | {{if $SSL}} 76 | server { 77 | listen 80; 78 | return 301 https://$host$request_uri; 79 | } 80 | {{end}} 81 | 82 | server { 83 | listen {{if $SSL}}443 ssl{{else}}80{{end}}; 84 | 85 | {{if $SSL}} 86 | ssl_certificate {{or (env "CERTFILE") "/etc/nginx/ssl/nginx.cert"}}; 87 | ssl_certificate_key {{or (env "KEYFILE") "/etc/nginx/ssl/nginx.key"}}; 88 | 89 | ssl on; 90 | ssl_session_cache builtin:1000 shared:SSL:10m; 91 | ssl_protocols TLSv1 TLSv1.1 TLSv1.2; 92 | ssl_ciphers {{or (key "config/mantlui/ssl_ciphers") "HIGH:!aNULL:!eNULL:!EXPORT:!CAMELLIA:!DES:!MD5:!PSK:!RC4"}}; 93 | ssl_prefer_server_ciphers on; 94 | 95 | error_page 497 https://$host:$server_port$request_uri; 96 | {{end}} 97 | 98 | {{if $AUTH}} 99 | auth_basic on; 100 | auth_basic_user_file /etc/nginx/nginx-auth.conf; 101 | {{end}} 102 | 103 | location / { 104 | proxy_connect_timeout 600; 105 | proxy_send_timeout 600; 106 | proxy_read_timeout 600; 107 | send_timeout 600; 108 | 109 | root /usr/share/nginx/html; 110 | index index.html index.htm; 111 | try_files $uri $uri/ =404; 112 | } 113 | 114 | {{with $mesos}} 115 | location = /mesos { 116 | return 301 /mesos/; 117 | } 118 | 119 | location = /mesos/ { 120 | root /usr/share/nginx/html; 121 | } 122 | 123 | location /mesos { 124 | try_files $uri @mesos-upstream; 125 | root /usr/share/nginx/html; 126 | } 127 | 128 | location @mesos-upstream { 129 | proxy_set_header host $host; 130 | proxy_set_header x-real-ip $remote_addr; 131 | proxy_set_header x-forwarded-for $proxy_add_x_forwarded_for; 132 | proxy_set_header x-forwarded-proto $scheme; 133 | 134 | rewrite /mesos/(.+)$ /$1 break; 135 | proxy_pass http://mesos; 136 | } {{end}} 137 | 138 | {{range service "agent.mesos" "any"}} 139 | location ~ /mesos/slave/{{index (.ID | split ":") 2}}/(.*) { 140 | proxy_set_header Host $host; 141 | proxy_set_header X-Real-IP $remote_addr; 142 | proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; 143 | proxy_set_header X-Forwarded-Proto $scheme; 144 | proxy_pass http://{{.Address}}:{{.Port}}/$1$is_args$args; 145 | }{{end}} 146 | 147 | {{with $marathon}} 148 | location /marathon/ { 149 | proxy_set_header host $host; 150 | proxy_set_header x-real-ip $remote_addr; 151 | proxy_set_header x-forwarded-for $proxy_add_x_forwarded_for; 152 | proxy_set_header x-forwarded-proto $scheme; 153 | proxy_pass http://marathon/; 154 | } {{end}} 155 | 156 | {{with $kubernetes_ui}} 157 | location /kubernetes/ { 158 | proxy_set_header host $host; 159 | proxy_set_header x-real-ip $remote_addr; 160 | proxy_set_header x-forwarded-for $proxy_add_x_forwarded_for; 161 | proxy_set_header x-forwarded-proto $scheme; 162 | proxy_pass http://kubernetes_ui/; 163 | } {{end}} 164 | 165 | {{with $kubernetes_api}} 166 | location /kubeapi/ { 167 | proxy_set_header host $host; 168 | proxy_set_header x-real-ip $remote_addr; 169 | proxy_set_header x-forwarded-for $proxy_add_x_forwarded_for; 170 | proxy_set_header x-forwarded-proto $scheme; 171 | proxy_pass https://kubernetes_api/; 172 | } {{end}} 173 | 174 | {{with $chronos}} 175 | location /chronos/ { 176 | proxy_set_header host $host; 177 | proxy_set_header x-real-ip $remote_addr; 178 | proxy_set_header x-forwarded-for $proxy_add_x_forwarded_for; 179 | proxy_set_header x-forwarded-proto $scheme; 180 | proxy_pass http://chronos/; 181 | } {{end}} 182 | 183 | {{with $consul}} 184 | location ~ /consul/(v1\/.*) { 185 | proxy_set_header host $host; 186 | proxy_set_header x-real-ip $remote_addr; 187 | proxy_set_header x-forwarded-for $proxy_add_x_forwarded_for; 188 | proxy_set_header x-forwarded-proto $scheme; 189 | proxy_pass {{if $consulSSL}}https{{else}}http{{end}}://consul/$1$is_args$args; 190 | } {{end}} 191 | 192 | location /consul { 193 | root /usr/share/nginx/html; 194 | index index.html index.htm; 195 | try_files $uri $uri/ =404; 196 | } 197 | 198 | {{with $mantlapi}} 199 | location /api/ { 200 | proxy_set_header host $host; 201 | proxy_set_header x-real-ip $remote_addr; 202 | proxy_set_header x-forwarded-for $proxy_add_x_forwarded_for; 203 | proxy_set_header x-forwarded-proto $scheme; 204 | proxy_pass http://mantlapi/; 205 | } {{end}} 206 | 207 | {{with $traefikAdmin}} 208 | location = /traefik { 209 | return 301 /traefik/dashboard/; 210 | } 211 | 212 | location /traefik { 213 | try_files $uri $uri/ @traefik-upstream; 214 | index index.html; 215 | root /usr/share/nginx/html; 216 | } 217 | 218 | location @traefik-upstream { 219 | proxy_set_header host $host; 220 | proxy_set_header x-real-ip $remote_addr; 221 | proxy_set_header x-forwarded-for $proxy_add_x_forwarded_for; 222 | proxy_set_header x-forwarded-proto $scheme; 223 | 224 | rewrite /traefik/(.+)$ /$1 break; 225 | proxy_pass https://traefik-admin; 226 | } 227 | {{end}} 228 | 229 | {{with $elasticsearch}} 230 | location /elasticsearch/ { 231 | proxy_set_header host $host; 232 | proxy_set_header x-real-ip $remote_addr; 233 | proxy_set_header x-forwarded-for $proxy_add_x_forwarded_for; 234 | proxy_set_header x-forwarded-proto $scheme; 235 | proxy_pass http://elasticsearch/; 236 | } {{end}} 237 | 238 | {{with $kibana}} 239 | location /kibana { 240 | proxy_set_header host $host; 241 | proxy_set_header x-real-ip $remote_addr; 242 | proxy_set_header x-forwarded-for $proxy_add_x_forwarded_for; 243 | proxy_set_header x-forwarded-proto $scheme; 244 | rewrite /kibana/(.+)$ /$1 break; 245 | proxy_pass http://kibana/; 246 | } {{end}} 247 | 248 | {{with $kafka_api}} 249 | location /kafka/ { 250 | proxy_set_header host $host; 251 | proxy_set_header x-real-ip $remote_addr; 252 | proxy_set_header x-forwarded-for $proxy_add_x_forwarded_for; 253 | proxy_set_header x-forwarded-proto $scheme; 254 | proxy_pass http://kafka-api/; 255 | } {{end}} 256 | } 257 | } 258 | -------------------------------------------------------------------------------- /screenshot.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mantl/nginx-mantlui/174479309553be933bd1e978c706b94739f90dd1/screenshot.png -------------------------------------------------------------------------------- /services.json.tmpl: -------------------------------------------------------------------------------- 1 | [ 2 | {"name": "Mesos", "id": "mesos", "check": "mesos", "path": "/mesos"}, 3 | {"name": "Marathon", "id": "marathon", "check": "marathon", "path": "/marathon"}, 4 | {"name": "Kubernetes", "id": "kubernetes", "check": "kubernetes", "path": "/kubernetes"}, 5 | {"name": "Consul", "id": "consul", "check": "consul", "path": "/consul/"}{{with service "traefik" "any"}}, 6 | {"name": "Traefik", "id": "traefik", "check": "traefik-admin", "path": "/traefik"}{{end}}{{with service "chronos" "any"}}, 7 | {"name": "Chronos", "id": "chronos", "check": "chronos", "path": "/chronos"}{{end}}{{if service "kibana-mantl-task" "any"}}, 8 | {"name": "Kibana", "id": "kibana-mantl-task", "check": "kibana-mantl", "path": "/kibana"}{{else}}{{if service "kibana-mantl" "any"}}, 9 | {"name": "Kibana", "id": "kibana-mantl", "check": "kibana-mantl", "path": "/kibana"}{{else}}{{if service "kibana" "any"}}, 10 | {"name": "Kibana", "id": "kibana", "check": "kibana", "path": "/kibana"}{{end}}{{end}}{{end}}{{if service "elasticsearch-mantl" "any"}}, 11 | {"name": "Elasticsearch", "id": "elasticsearch-mantl", "check": "elasticsearch-mantl", "path": "/elasticsearch"}{{else}}{{if service "elasticsearch" "any"}}, 12 | {"name": "Elasticsearch", "id": "elasticsearch", "check": "elasticsearch", "path": "/elasticsearch"}{{end}}{{end}} 13 | ] 14 | --------------------------------------------------------------------------------