initial commit of chrome only of new replacement web ui

remove node modules

make new data file for web ui

initial commit of dashboard

switch back to non SSL request

move port splitting to common place; add to node resource location

Signed-off-by: Patrick Reilly <patrick@kismatic.io>

various path fixes

make svg path relative

work around missing mime type

Signed-off-by: Patrick Reilly <patrick@kismatic.io>

fix paths

fix karma path

remove bad protractor test
pull/6/head
Patrick Reilly 2015-04-19 04:45:14 -07:00
parent 27daa29753
commit 716d98c39e
182 changed files with 51954 additions and 2746 deletions

3
.gitignore vendored
View File

@ -50,3 +50,6 @@ network_closure.sh
.kubeconfig
.tags*
# Web UI
www/master/node_modules/

View File

@ -33,7 +33,7 @@ fi
DATAFILE=pkg/ui/datafile.go
TMP_DATAFILE=/tmp/datafile.go
go-bindata -nocompress -o $DATAFILE -prefix ${PWD} -pkg ui www/... third_party/swagger-ui/...
go-bindata -nocompress -o $DATAFILE -prefix ${PWD} -pkg ui www/app/... third_party/swagger-ui/...
cat hooks/boilerplate.go.txt > $TMP_DATAFILE
echo "// generated by hack/build-ui.sh; DO NOT EDIT

File diff suppressed because one or more lines are too long

121
www/README.md Normal file
View File

@ -0,0 +1,121 @@
### Install dependencies
We have two kinds of dependencies in this project: tools and angular framework code. The tools help
us manage and test the application.
* We get the tools we depend upon via `npm`, the [node package manager](https://www.npmjs.com/).
* We get the angular code via `bower`, a [client-side code package manager](http://bower.io/).
`npm` is configured to automatically run `bower install` and `gulp`. Before you run the application for the first time, simply run this command from the `www/master` directory:
```
npm install
```
To start the application, run this command from the `www/master` directory:
```
npm start
```
The `gulp` command will start a file watcher which will update the generated `app` code after any changes are saved. Note: gulp file watcher does not currently support adding or deleting files, this will require a restart of gulp). Two new directories will also be created in the project.
* `master/node_modules` - contains npm dependencies
* `master/bower_components` - contains the angular framework files and any custom dependencies
Bower components should be refernced in one of the `vendor.json` files below:
* `master/vendor.base.json` - 3rd party vendor javascript required to start the app. JS is compiled to `base.js` and loaded before `app.js`
* `master/vendor.json` - 3rd party vendor scripts to make the app work, usually lazy loaded. Can be js or css. Copied to `vendor/*`.
### Serving the app during development
The app can be served through `kubectl`, but for some types of review a local web server is convenient. One can be installed as follows:
```
sudo npm install -g http-server
```
The server can then be launched:
```
cd app
http-server -a localhost -p 8000
```
### Configuration
#### Configuration settings
A json file can be used by `gulp` to automatically create angular constants. This is useful for setting per environment variables such as api endpoints.
* ```www/master/shared/config/development.json``` or ```www/master/shared/config/production.json``` can be created from the ```www/master/shared/config/development.example.json``` file.
* ```development.example.json``` should be kept up to date with default values, since ```development.json``` is not under source control.
* Component configuration can be added to ```www/master/components/<component name>/config/development.json``` and it will be combined with the main app config files and compiled into the intermediary ```www/master/shared/config/generated-config.js``` file.
* All ```generated-config.js``` is compiled into ```app.js```
* Production config can be generated using ```gulp config --env production``` or ```gulp --env production```
* The generated angular constant is named ```ENV``` with the shared root and each component having their own child configuration. For example,
```
www/master
├── shared/config/development.json
└── components
├── dashboard/config/development.json
├── graph/config/development.json
└── my_component/config/development.json
```
produces ```www/master/shared/config/generated-config.js```:
```
angular.module('kubernetesApp.config', [])
.constant('ENV', {
'/': <www/master/shared/config/development.json>,
'dashboard': <www/master/components/dashboard/config/development.json>,
'graph': <www/master/components/graph/config/development.json>,
'my_component': <www/master/components/my_component/config/development.json>
});
```
#### Kubernetes server configuration
**RECOMMENDED**: By default the Kubernetes api server does not support CORS,
so the `kube-apiserver.service` must be started with
`--cors_allowed_origins=.*` or `--cors_allowed_origins=http://<your
host here>`
**HACKS**: If you don't want to/cannot restart the Kubernetes api server:
* Or you can start your browser with web security disabled. For
Chrome, you can [launch](http://www.chromium.org/developers/how-tos/run-chromium-with-flags) it with flag ```--disable-web-security```.
### Building a new visualizer or component
See [master/components/README.md](master/components/README.md).
### Testing
Currently kuberntes-ui includes both unit-testing (run via [Karma](http://karma-runner.github.io/0.12/index.html)) and
end-to-end testing (run via
[Protractor](http://angular.github.io/protractor/#/)).
#### Unittests via Karma
To run the existing Karma tests:
* Install the Karma CLI: `sudo npm install -g karma-cli` (it needs to
be installed globally, hence the `sudo` may be needed). Note that
the other Karma packages (such as `karma`, `karma-jasmine`, and
`karma-chrome-launcher` should be automatically installed when
running `npm start`).
* Go to the `www/master` directory, and run `karma start
karma.conf.js`. The Karma configuration is defined in `karma.config.js`. The console should show the test results.
To write new Karma tests:
* For testing each components, write test files (`*.spec.js`) under the
corresponding `www/master/components/**/test/modules/` directory.
* For testing the chrome and the framework, write test files
(*.spec.js) under the `www/master/test/modules/*` directory.
#### End-to-end testing via Protractor
To run the existing Protractor tests:
* Install the CLIs: `sudo npm install -g protractor`.
* Start the webdriver server: `sudo webdriver-manager start`
* Start the kubernetes-ui app (see instructions above), assuming
running at port 8000.
* Go to the `www/master/protractor` directory and run `protractor
conf.js`. The protractor configuration is in `conf.js`. The console
should show the test results.
To write new protractor tests, put the test files (`*.spec.js`) in the
corresponding `www/master/components/**/protractor/` directory.

1508
www/app/assets/css/app.css Normal file

File diff suppressed because it is too large Load Diff

Binary file not shown.

After

Width:  |  Height:  |  Size: 373 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 635 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.6 KiB

View File

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><path d="M12 4l-1.41 1.41L16.17 11H4v2h12.17l-5.58 5.59L12 20l8-8z"/></svg>

After

Width:  |  Height:  |  Size: 158 B

View File

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><path d="M12 2C6.47 2 2 6.47 2 12s4.47 10 10 10 10-4.47 10-10S17.53 2 12 2zm5 13.59L15.59 17 12 13.41 8.41 17 7 15.59 10.59 12 7 8.41 8.41 7 12 10.59 15.59 7 17 8.41 13.41 12 17 15.59z"/></svg>

After

Width:  |  Height:  |  Size: 276 B

View File

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><path d="M19 6.41L17.59 5 12 10.59 6.41 5 5 6.41 10.59 12 5 17.59 6.41 19 12 13.41 17.59 19 19 17.59 13.41 12z"/></svg>

After

Width:  |  Height:  |  Size: 202 B

View File

@ -0,0 +1,13 @@
<?xml version="1.0" encoding="iso-8859-1"?>
<!-- Generator: Adobe Illustrator 18.1.1, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
<svg version="1.1" id="Capa_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
viewBox="0 0 10 10" style="enable-background:new 0 0 10 10;" xml:space="preserve">
<g>
<path style="fill:#010002;" d="M9.5,4.5h-9C0.224,4.5,0,4.724,0,5s0.224,0.5,0.5,0.5h9C9.775,5.5,10,5.276,10,5
S9.775,4.5,9.5,4.5z"/>
<path style="fill:#010002;" d="M0.5,2.5h9C9.775,2.5,10,2.276,10,2S9.775,1.5,9.5,1.5h-9C0.224,1.5,0,1.724,0,2
S0.224,2.5,0.5,2.5z"/>
<path style="fill:#010002;" d="M9.5,7.5h-9C0.224,7.5,0,7.725,0,8s0.224,0.5,0.5,0.5h9C9.775,8.5,10,8.275,10,8
S9.775,7.5,9.5,7.5z"/>
</g>
</svg>

After

Width:  |  Height:  |  Size: 791 B

View File

@ -0,0 +1,23 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Generator: Adobe Illustrator 17.1.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
<svg version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px" width="24px"
height="24px" viewBox="0 0 24 24" enable-background="new 0 0 24 24" xml:space="preserve">
<g id="Header">
<g>
<rect x="-618" y="-2232" fill="none" width="1400" height="3600"/>
</g>
</g>
<g id="Label">
</g>
<g id="Icon">
<g>
<rect fill="none" width="24" height="24"/>
<path d="M3,18h18v-2H3V18z M3,13h18v-2H3V13z M3,6v2h18V6H3z" style="fill:#f3f3f3;"/>
</g>
</g>
<g id="Grid" display="none">
<g display="inline">
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 841 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 309 B

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 11 KiB

View File

2359
www/app/assets/js/app.js Normal file

File diff suppressed because it is too large Load Diff

26
www/app/assets/js/base.js Normal file

File diff suppressed because one or more lines are too long

View File

@ -0,0 +1,125 @@
Components
==========
A tab in the Kubernetes UI with its set of visualizations is referred to as a *component*. Components are separated from the UI chrome and base data providers to simplify the development of new visualizations. This document provides reference for creation and modification of components.
Each component has its own directory, which contains a manifest file, HTML views, Angular providers, CSS, Less and other assets. Below is the recommended directory structure for a component.
```
foo_component
├── config
├── css
├── img
├── js
│   └── modules
│   ├── controllers
│   ├── directives
│   └── services
├── less
├── pages
├── views
│   └── partials
└── manifest.json
```
###Manifest file
The JSON-formatted manifest file, named ```manifest.json```, is located at the root of a component. Based on the component directory name and the contents of the manifest, the Kubernetes UI automatically adds a tab to the chrome, a dependency on the component's AngularJS module to main AngularJS app and Angular routes for the component.
For example, consider a manifest file at ```master/components/foo_component/manifest.json```:
```
{
"routes": [
{
"url": "/",
"templateUrl": "/components/foo_component/pages/home.html"
},
{
"url": "/kittens",
"templateUrl": "/components/foo_component/pages/kittens.html",
"css": "/components/foo_component/css/kittens.css"
}
]
}
```
From the name of the component directory, the Kubernetes UI
* creates a tab called "Foo Component",
* adds Angular module ```kubernetesApp.components.fooComponent``` to the dependencies of ```kubernetesApp```, and
* defines Angular routes ```/foo_component/``` and ```/foo_component/kittens```.
Every tab links to ```/``` relative to its component, so it is important to always define a ```/``` route.
###Source files
In general, all files located in ```master/components/<component>``` are copied to ```app/components/<component>/``` on each gulp build. This includes (but is not limited to) HTML views, CSS and images. Exceptions to this copy are the ```config``` and ```less``` directories as well as all ```.js``` files.
The sections below describe how the exceptions are built into the UI.
####JavaScript
All JavaScript files located in the ```master/components/<component>/js``` are uglified and concatenated together with the rest of the UI's JavaScript. Once aggregated, the JavaScript file is minified and written to ```app/assets/js/app.js```.
####Configuration
Similar to the [UI-wide configuration](../../README.md#configuration), components can define different configuration for each environment. The gulp task creates the constant ```ENV``` under the ```kubernetesApp.config``` module for configuration, which is an object with a property for the root UI and each component.
For example, a configuration for the ```development``` environment specific to ```foo_component``` would be located at ```master/components/foo_component/config/development.json```. Such a component would access its ```development.json``` configuration verbatim at ```ENV.foo_component```:
```
angular.module('kubernetesApp.components.fooComponent', ['kubernetesApp.config'])
.provider('foo', ...)
.config(['fooProvider', 'ENV', function(fooProvider, ENV) {
// Configure fooProvider using ENV['foo_component'].
});
```
####Less
Like JavaScript, the component's Less files are built into one monolithic CSS file. All top-level Less files located at ```master/components/<component>/less/*.less``` are imported into the main UI's Less file. The result is then minified and copied to ```app/assets/css/app.css```.
Sub-directories of this path are watched for changes, but not directly imported. This is useful for defining common colors, mixins and other functions or variables used by the top-level Less files.
###Appendix
####Manifest schema
```
{
"$schema": "http://json-schema.org/draft-04/schema#",
"type": "object",
"properties": {
"description": {
"type": "string",
"description": "Very brief summary of the component. Use a README.md file for detailed descriptions."
},
"routes": {
"type": "array",
"description": "Angular routes for the component.",
"items": {
"type": "object",
"properties": {
"description": {
"type": "string",
"description": "Short description of the route."
},
"url": {
"type": "string",
"description": "Route location relative to '/<component>'."
},
"templateUrl": {
"type": "string",
"description": "Absolute location of the HTML template."
},
"css": {
"type": "string",
"description": "Absolute location of CSS to use with this route."
}
},
"required": ["url", "templateUrl"]
},
"minItems": 1
}
},
"required": ["routes"]
}
```
Content available under the [CC-By 3.0
license](http://creativecommons.org/licenses/by/3.0/)

View File

@ -0,0 +1 @@
Dashboard Component for Kubernetes WebUI

View File

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" width="18" height="18" viewBox="0 0 18 18"><path d="M5 8l4 4 4-4z"/></svg>

After

Width:  |  Height:  |  Size: 114 B

View File

@ -0,0 +1,4 @@
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24">
<path d="M7 10l5 5 5-5z"/>
<path d="M0 0h24v24h-24z" fill="none"/>
</svg>

After

Width:  |  Height:  |  Size: 166 B

View File

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" width="18" height="18" viewBox="0 0 18 18"><path d="M14.53 4.53l-1.06-1.06L9 7.94 4.53 3.47 3.47 4.53 7.94 9l-4.47 4.47 1.06 1.06L9 10.06l4.47 4.47 1.06-1.06L10.06 9z"/></svg>

After

Width:  |  Height:  |  Size: 215 B

View File

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><path d="M19 6.41L17.59 5 12 10.59 6.41 5 5 6.41 10.59 12 5 17.59 6.41 19 12 13.41 17.59 19 19 17.59 13.41 12z"/></svg>

After

Width:  |  Height:  |  Size: 202 B

View File

@ -0,0 +1,60 @@
{
"description": "The basic kubernetes ui dashboard... ",
"routes": [
{
"description": "Dashboard visualization.",
"url": "/",
"templateUrl": "components/dashboard/pages/home.html"
},
{
"description": "Pods",
"url": "/pods",
"templateUrl": "components/dashboard/views/listPods.html"
},
{
"description": "Pod Visualizer",
"url": "/visualpods",
"templateUrl": "components/dashboard/views/listPodsVisualizer.html"
},
{
"description": "Services",
"url": "/services",
"templateUrl": "components/dashboard/views/listServices.html"
},
{
"description": "Replication Controllers",
"url": "/replicationcontrollers",
"templateUrl": "components/dashboard/views/listReplicationControllers.html"
},
{
"description": "Events",
"url": "/events",
"templateUrl": "components/dashboard/views/listEvents.html"
},
{
"description": "Minions",
"url": "/minions",
"templateUrl": "components/dashboard/views/listMinions.html"
},
{
"description": "Replication Controller",
"url": "/replicationcontrollers/:replicationControllerId",
"templateUrl": "components/dashboard/views/replication.html"
},
{
"description": "Service",
"url": "/services/:serviceId",
"templateUrl": "components/dashboard/views/service.html"
},
{
"description": "Explore",
"url": "/groups/:grouping*?/selector/:selector*?",
"templateUrl": "components/dashboard/views/groups.html"
},
{
"description": "Pod",
"url": "/pods/:podId",
"templateUrl": "components/dashboard/views/pod.html"
}
]
}

View File

@ -0,0 +1 @@
<p></p>

View File

@ -0,0 +1,20 @@
<div layout-fill>
<md-toolbar md-scroll-shrink class="dashboard-subnav">
<div class="md-toolbar-tools">
<div layout="row" flex class="fill-height">
<div class="md-toolbar-item md-breadcrumb"></div>
<span style="display: inline-block;">Kubernetes</span>
<span flex></span>
<div class="md-toolbar-item md-tools" layout="row">
<div layout="column" class="selectSubPages">
<md-select ng-model="page" placeholder="Views" class="selectTitle">
<md-optgroup label="Dashboard">
<md-option id="{{subpage.id}}" ng-value="subpage.value" ng-repeat="subpage in subpages | filter: {category: 'dashboard' }">{{subpage.name}}</md-option>
</md-option-group>
</md-select>
</div>
</div>
</div>
</div>
</md-toolbar>
</div>

View File

@ -0,0 +1,7 @@
<div dashboard-header></div>
<div class="dashboard" ng-controller="DashboardCtrl" layout-fill>
<md-content>
<div ng-include="'components/dashboard/views/partials/cadvisor.html'">&gt;</div>
</md-content>
</div>
<div dashboard-footer></div>

View File

@ -0,0 +1 @@
describe("Kubernetes UI Dashboard",function(){it("should have all the expected components loaded",function(){browser.get("http://localhost:8000"),expect(browser.getTitle()).toEqual("Kubernetes UI");var e=element(by.id("tab_001"));expect(e).toBeDefined(),e.click(),expect(browser.getLocationAbsUrl()).toBe("/dashboard/");var t=element(by.model("page"));expect(t).toBeDefined()}),it("should have the subnav view",function(){browser.get("http://localhost:8000/"),expect(by.css(".dashboard-subnav")).toBeDefined();var e=element(by.css(".selectSubPages"));expect(e).toBeDefined(),e.click();var t=element(by.model("page"));expect(t).toBeDefined(),e.click(),t.click(),expect(element(by.id("groupsView"))).toBeDefined(),expect(element(by.id("podsView"))).toBeDefined(),expect(element(by.id("minionsView"))).toBeDefined(),expect(element(by.id("rcView"))).toBeDefined(),expect(element(by.id("servicesView"))).toBeDefined(),expect(element(by.id("eventsView"))).toBeDefined(),expect(element(by.id("cAdvisorView"))).toBeDefined()}),it("should have the cAdvisor view by default",function(){browser.get("http://localhost:8000/"),expect(browser.getTitle()).toEqual("Kubernetes UI"),expect(element.all(by.css(".dashboard")).count()).toBeGreaterThan(0),expect(element.all(by.css(".server-overview")).count()).toEqual(1),expect(element(by.repeater("minion in minions.items"))).toBeDefined();var e=element(by.css("svg"));expect(e).toBeDefined()}),it("should have the correct subviews",function(){browser.get("http://localhost:8000/");for(var e=["podsView","minionsView","rcView","servicesView","eventsView"],t=0;t<e.length;t++){var o=e[t],i=element(by.model("page"));i.click();var n=element(by.id(o));expect(n).toBeDefined(),n.click(),expect(browser.getTitle()).toEqual("Kubernetes UI"),expect(by.css(".dashboard-subnav")).toBeDefined(),expect(element(by.css(".selectSubPages"))).toBeDefined();var c=element(by.model("page"));expect(c).toBeDefined(),expect(element.all(by.css(".list-pods")).count()).toEqual(1),expect(element(by.repeater("h in headers"))).toBeDefined()}}),it("should have the correct groups view",function(){browser.get("http://localhost:8000/");var e=element(by.model("page"));e.click();var t=element(by.id("groupsView"));expect(t).toBeDefined(),t.click(),expect(browser.getTitle()).toEqual("Kubernetes UI"),expect(by.css(".dashboard-subnav")).toBeDefined(),expect(element(by.css(".selectSubPages"))).toBeDefined();var o=element(by.model("page"));expect(o).toBeDefined();var e=element(by.model("selectedGroupBy"));expect(e).toBeDefined(),e.click(),expect(element(by.repeater("g in groupByOptions"))).toBeDefined()})});

View File

@ -0,0 +1,34 @@
<div dashboard-header></div>
<div class="dashboard">
<div ng-controller="GroupCtrl" layout="column" class="body-wrapper groups">
<md-content>
<!-- page header -->
<div class="header" layout="row">
<div class="">Group by: </div>
<md-select placeholder="{{routeParams.grouping}}" class="select-group-by" ng-model="selectedGroupBy" ng-change="changeGroupBy()">
<md-option ng-value="g.value" ng-repeat="g in groupByOptions">{{g.name}}</md-option>
</md-select>
<div ng-if="selector" layout="row" class="selector-area">
<div class="filter-label">Filter:</div>
<div class="filter-text">{{selector}}</div>
<div class="filter-area" ng-if="selector && selector.length > 0">
<button ng-click="clearSelector(routeParams.grouping)" class="md-button cancel-button">
<md-icon md-svg-src="components/dashboard/img/icons/ic_close_18px.svg" class="cancel-icon" alt="Cancel"></md-icon>
</button>
</div>
</div>
</div>
<div class="group-item" ng-repeat="(groupName,group) in groups.items" ng-include="'components/dashboard/views/partials/groupBox.html'"></div>
</md-content>
</div>
</div>
<div dashboard-footer></div>

View File

@ -0,0 +1,7 @@
<div dashboard-header></div>
<div class="dashboard">
<div ng-controller="ListEventsCtrl" style="padding:25px;" class="list-pods">
<md-table headers="headers" content="content" sortable="sortable" filters="search" custom-class="custom" thumbs="thumbs" count="count"></md-table>
</div>
</div>
<div dashboard-footer></div>

View File

@ -0,0 +1,7 @@
<div dashboard-header></div>
<div class="dashboard">
<div ng-controller="ListMinionsCtrl" style="padding:25px;" class="list-pods">
<md-table headers="headers" content="content" sortable="sortable" filters="search" custom-class="custom" thumbs="thumbs" count="count"></md-table>
</div>
</div>
<div dashboard-footer></div>

View File

@ -0,0 +1,7 @@
<div dashboard-header></div>
<div class="dashboard">
<div ng-controller="ListPodsCtrl" style="padding:25px;" class="list-pods">
<md-table headers="headers" content="content" sortable="sortable" filters="search" custom-class="custom" thumbs="thumbs" count="count"></md-table>
</div>
</div>
<div dashboard-footer></div>

View File

@ -0,0 +1,59 @@
<div class="dashboard">
<div ng-controller="ListPodsCtrl" style="padding:25px;" class="list-pods">
<md-whiteframe layout-margin class="md-whiteframe-z2">
<md-toolbar class="">
<div class="md-toolbar-tools">
Pods
</div>
</md-toolbar>
<md-content>
<div class="pod-group">
<md-grid-list md-cols="6" md-row-height="1:1.5" md-gutter="8px" ng-repeat="(podName, groupPods) in groupedPods">
<md-grid-tile md-rowspan="1" md-colspan="2" class="gray">
<md-grid-tile-footer>
<div class="pod-title"><h2>{{podName}}</h2></div>
</md-grid-tile-footer>
</md-grid-tile>
<md-grid-tile class="purple" md-rowspan="1" md-colspan="1" ng-repeat="pod in groupPods" >
<div>
<div>
Containers:
<span ng-repeat="container in pod.desiredState.manifest.containers">
{{container.name}}
</span>
</div>
<div>
Images:
<span ng-repeat="container in pod.desiredState.manifest.containers">
{{container.image}}
</span>
</div>
<div>Internal IP: {{pod.currentState.podIP}}</div>
<div>
Labels:
<span ng-repeat="(label, value) in pod.labels">{{label}}={{value}}<span ng-show="!$last">, </span></span>
</div>
<div>Status: {{pod.currentState.status}}</div>
</div>
<md-grid-tile-footer>
<div class="pod-host">{{pod.currentState.host}}</div>
<div><a ng-href="/#/dashboard/pods/{{ pod.id }}"><h3>{{ pod.id }}</h3></a></div>
</md-grid-tile-footer>
</md-grid-tile>
</md-grid-list>
</div>
</md-content>
</md-whiteframe>
</div>
</div>

View File

@ -0,0 +1,17 @@
<div dashboard-header></div>
<div class="dashboard">
<div ng-controller="ListPodsCtrl" style="padding:25px;" class="list-pods">
<md-whiteframe layout-margin class="md-whiteframe-z2">
<md-content>
<div>
<md-button class="md-primary" ng-click="serverView = false">By Name</md-button> |
<md-button class="md-primary" ng-click="serverView = true">By Server</md-button>
<div style="float:right;"><md-button class="md-primary" ng-href="/#/dashboard/pods">View Pod Listing</md-button></div>
</div>
<div class="pod-group" ng-show="!serverView" ng-include="'components/dashboard/views/partials/podTilesByName.html'"></div>
<div class="pod-group" ng-show="serverView" ng-include="'components/dashboard/views/partials/podTilesByServer.html'"></div>
</md-content>
</md-whiteframe>
</div>
</div>
<div dashboard-footer></div>

View File

@ -0,0 +1,7 @@
<div dashboard-header></div>
<div class="dashboard">
<div ng-controller="ListReplicationControllersCtrl" style="padding:25px;" class="list-pods">
<md-table headers="headers" content="content" sortable="sortable" filters="search" custom-class="custom" thumbs="thumbs" count="count"></md-table>
</div>
</div>
<div dashboard-footer></div>

View File

@ -0,0 +1,7 @@
<div dashboard-header></div>
<div class="dashboard">
<div ng-controller="ListServicesCtrl" style="padding:25px;" class="list-pods">
<md-table headers="headers" content="content" sortable="sortable" filters="search" custom-class="custom" thumbs="thumbs" count="count"></md-table>
</div>
</div>
<div dashboard-footer></div>

View File

@ -0,0 +1,9 @@
<div class="dashboard" ng-controller="cAdvisorController">
<div class="server-overview">
<md-content layout="row" layout-wrap>
<div flex-sm="100" flex-md="50" flex-lg="33" flex-gt-lg="25" class="chart_area" ng-repeat="minion in minions.items">
<d3-minion-bar-gauge data="activeMinionDataById[minion.id]" class="concentric" graph-width="325" graph-height="325" thickness=18 />
</div>
</md-content>
</div>
</div>

View File

@ -0,0 +1,20 @@
<div>
<md-content>
<div class="server-overview" layout="column">
<!-- subheader -->
<div class="group-heading" layout="row">
<div class="label">{{routeParams.grouping | ucfirst}}: <span class="bold">{{ (groupName) || "blank" }}</span></div>
</div>
<!-- render group data -->
<div ng-include="'components/dashboard/views/partials/groupItem.html'"></div>
<div class="footer">
<!-- Alternate box inside a box -->
<div ng-if="group.kind == 'grouping'">
{{group.kind}}
<div ng-repeat="(groupName,group) in group.items" ng-include="'components/dashboard/views/partials/groupBox.html'"></div>
</div>
<md-divider></md-divider>
</div>
</div>
</md-content>
</div>

View File

@ -0,0 +1,42 @@
<div layout="row" ng-if="group.kind != 'grouping'">
<div>
<!-- Default box display -->
<div layout="row" class="group-item" ng-repeat="(groupType, data) in group | groupBy: 'labels.type'">
<!-- left image -->
<div class="icon-area">
<div class="group-icon" style="background-color: {{getGroupColor(groupType)}}"></div>
</div>
<!-- right area -->
<div class="group-main-area" layout="column">
<!-- type -->
<div class="subtype">
{{groupType | ucfirst}}s
</div>
<!-- links -->
<div layout="row" layout-wrap>
<div layout="row" ng-repeat="item in data">
<!-- title -->
<div ng-switch on='item.labels["type"]'>
<div class="group-name">
<a ng-switch-when='pod' ng-href="/#/dashboard/pods/{{ item.id }}">{{ item.id }}</a>
<a ng-switch-when='service' ng-href="/#/dashboard/services/{{ item.id }}">{{ item.id }}</a>
<a ng-switch-when='replicationController' ng-href="/#/dashboard/replicationcontrollers/{{ item.id }}">{{ item.id }}</a>
<div ng-switch-default>{{item.id}}</div>
</div>
</div>
<md-select ng-model="selectedFilter" ng-change="changeFilterBy(selectedFilter)" class="selectFilter">
<md-optgroup label="FILTER">
<md-option ng-value="'{{key}}={{value}}'" ng-repeat="(key, value) in item.labels">{{key}}: {{value}}</md-option>
</md-option-group>
</md-optgroup>
</md-select>
<!-- This is the official button design, but requires a custom dialog. Fix up after the demo. -->
<!-- <md-button ng-click="" class="filter-button" style="padding:0">
<md-icon md-svg-src="components/dashboard/img/icons/ic_arrow_drop_down_18px.svg" class="filter-icon" aria-label="" alt="Filter"></md-icon>
</md-button> -->
</div>
</div>
</div>
</div>
</div>
</div>

View File

@ -0,0 +1,34 @@
<md-grid-list class="list-color-{{$index + 1}}" md-cols="6" md-row-height="1:1" md-gutter="8px" ng-repeat="(podName, groupPods) in podsByName">
<md-grid-tile md-rowspan="2" md-colspan="2" class="colored">
<md-grid-tile-footer>
<div class="pod-title"><h2>{{podName}} overview</h2></div>
</md-grid-tile-footer>
</md-grid-tile>
<md-grid-tile class="colored {{podStatusClass(pod)}}" md-rowspan="1" md-colspan="1" ng-repeat="pod in groupPods" >
<md-grid-tile-header class="clear-bg">
<div layout="row">
<div class="labels"><span ng-repeat="(label, value) in otherLabels(pod.labels)">{{label}}: {{value}}<span ng-show="!$last">, </span></span></div>
<div flex="20" class="restarts" ng-show="getPodRestarts(pod) > 0">
<md-button class="md-fab restart-button">
{{getPodRestarts(pod)}}
</md-button>
</div>
</div>
</md-grid-tile-header>
<div class="inner-box">
<div ng-show="podStatusClass(pod)">Status: {{pod.currentState.status}}</div>
</div>
<md-grid-tile-footer>
<div class="pod-host">{{pod.currentState.host}}</div>
<div><a ng-href="/#/dashboard/pods/{{ pod.id }}"><h3>{{ pod.id }}</h3></a></div>
</md-grid-tile-footer>
</md-grid-tile>
</md-grid-list>

View File

@ -0,0 +1,34 @@
<md-grid-list class="" md-cols="6" md-row-height="1:1" md-gutter="8px" ng-repeat="(serverIp, groupPods) in podsByServer">
<md-grid-tile md-rowspan="2" md-colspan="2" class="gray">
<md-grid-tile-footer>
<div class="pod-title"><h2>{{serverIp}} overview</h2></div>
</md-grid-tile-footer>
</md-grid-tile>
<md-grid-tile class="color-{{podIndexFromName(pod)}} {{podStatusClass(pod)}}" md-rowspan="1" md-colspan="1" ng-repeat="pod in groupPods" >
<md-grid-tile-header class="clear-bg">
<div layout="row">
<div class="labels"><span ng-repeat="(label, value) in otherLabels(pod.labels)">{{label}}: {{value}}<span ng-show="!$last">, </span></span></div>
<div flex="20" class="restarts" ng-show="getPodRestarts(pod) > 0">
<md-button class="md-fab restart-button">
{{getPodRestarts(pod)}}
</md-button>
</div>
</div>
</md-grid-tile-header>
<div class="inner-box">
<div ng-show="podStatusClass(pod)">Status: {{pod.currentState.status}}</div>
</div>
<md-grid-tile-footer>
<div class="pod-host">{{pod.labels.name}}</div>
<div><a ng-href="/#/dashboard/pods/{{ pod.id }}"><h3>{{ pod.id }}</h3></a></div>
</md-grid-tile-footer>
</md-grid-tile>
</md-grid-list>

View File

@ -0,0 +1,97 @@
<div dashboard-header></div>
<div class="dashboard">
<div ng-controller="PodCtrl" layout="column" class="body-wrapper pod">
<div class="detail">
<div class="back">
<div class="nav-back">
<a ng-click="doTheBack()">BACK</a>
</div>
<!-- <md-button class="md-default-theme" ng-click="doTheBack">BACK</md-button> -->
</div>
<div class="heading">
<span class="label">Pod:</span>
<span>{{pod.id}}</span>
</div>
<table>
<tbody>
<tr>
<td class="name">Status</td>
<td class="value">
{{pod.currentState.status}} on <a ng-href="/#/dashboard/groups/host/selector/host={{pod.currentState.host}}">{{pod.currentState.host}}</a>
</td>
</tr>
<tr>
<td class="name">Created</td>
<td class="value">
{{pod.creationTimestamp | date:'medium'}}
</td>
</tr>
<tr>
<td class="name">Pod Networking</td>
<td class="value">
{{pod.currentState.podIP}}
<span ng-repeat="container in pod.desiredState.manifest.containers">
<span ng-repeat="port in container.ports">
: {{port.containerPort}}
</span>
</span>
</td>
</tr>
<tr>
<td class="name">Host Networking</td>
<td class="value">
{{pod.currentState.hostIP}}
<span ng-repeat="container in pod.desiredState.manifest.containers">
<span ng-repeat="port in container.ports">
:{{port.hostPort}}
</span>
</span>
</td>
</tr>
<tr>
<td class="name">Labels</td>
<td class="value">
<div ng-repeat="(label, value) in pod.labels">
{{label}}: {{value}}
</div>
</td>
</tr>
<tr>
<td class="name">Containers</td>
<td class="value">
<table class="containerTable">
<tr>
<td>Name</td>
<td>Image</td>
<td>Restarts</td>
</tr>
<tr ng-repeat="container in pod.desiredState.manifest.containers">
<td>{{container.name}}</td>
<td>{{container.image}}</td>
<td>{{pod.currentState.info[container.name].restartCount}}</td>
</tr>
</table>
</td>
</tr>
</tbody>
</table>
</div>
</div>
</div>
<div dashboard-footer></div>

View File

@ -0,0 +1,82 @@
<div dashboard-header></div>
<div class="dashboard">
<div ng-controller="ReplicationControllerCtrl" layout="column" class="body-wrapper">
<div class="detail">
<div class="back">
<md-button class="md-default-theme" ng-href="/#/dashboard/replicationcontrollers"> BACK</md-button>
</div>
<div class="heading">
<span class="label">Replication Controller: </span>
<span>{{replicationController.id}}</span>
</div>
<table>
<tbody>
<tr>
<td class="name">Created</td>
<td class="value">
{{replicationController.creationTimestamp | date:'medium'}}
</td>
</tr>
<tr>
<td class="name">Desired Replicas</td>
<td class="value">
{{replicationController.desiredState.replicas}}
</td>
</tr>
<tr>
<td class="name">Current Replicas</td>
<td class="value">
{{replicationController.currentState.replicas}}
</td>
</tr>
<tr>
<td class="name">Labels</td>
<td class="value">
<div ng-repeat="(label, value) in replicationController.labels">
{{label}}: {{value}}
</div>
</td>
</tr>
<tr>
<td class="name">Related Pods</td>
<td class="value">
<div ng-repeat="(label, value) in replicationController.desiredState.replicaSelector">
<a ng-href="/#/dashboard/groups/type/selector/{{label}}={{value}},type=pod">{{label}}: {{value}}</a>
</div>
</td>
</tr>
<tr>
<td class="name">Related Services</td>
<td class="value">
<div ng-repeat="(label, value) in replicationController.desiredState.replicaSelector">
<a ng-href="/#/dashboard/groups/type/selector/{{label}}={{value}},type=service">{{label}}: {{value}}</a>
</div>
</td>
</tr>
</tbody>
</table>
</div>
</div>
</div>
<div dashboard-footer></div>

View File

@ -0,0 +1,97 @@
<div dashboard-header></div>
<div class="dashboard">
<div ng-controller="ServiceCtrl" layout="column" class="body-wrapper service">
<div class="detail">
<div class="back">
<md-button class="md-default-theme" ng-href="/#/dashboard/services"> BACK</md-button>
</div>
<div class="heading">
<span class="label">Service: </span>
<span>{{service.id}}</span>
</div>
<table>
<tbody>
<tr>
<td class="name">Created</td>
<td class="value">
{{service.creationTimestamp | date:'medium'}}
</td>
</tr>
<tr>
<td class="name">Port</td>
<td class="value">
{{service.port}}
</td>
</tr>
<tr>
<td class="name">Container Port</td>
<td class="value">
{{service.containerPort}}
</td>
</tr>
<tr>
<td class="name">Portal IP</td>
<td class="value">
{{service.portalIP}}
</td>
</tr>
<tr>
<td class="name">Protocol</td>
<td class="value">
{{service.protocol}}
</td>
</tr>
<tr>
<td class="name">Session Affinity</td>
<td class="value">
{{service.sessionAffinity}}
</td>
</tr>
<tr>
<td class="name">Labels</td>
<td class="value">
<div ng-repeat="(label, value) in service.labels">
{{label}}: {{value}}
</div>
</td>
</tr>
<tr>
<td class="name">Related Pods</td>
<td class="value">
<div ng-repeat="(label, value) in service.selector">
<a ng-href="/#/dashboard/groups/type/selector/{{label}}={{value}},type=pod">{{label}}: {{value}}</a>
</div>
</td>
</tr>
<tr>
<td class="name">Related Replication Controllers</td>
<td class="value">
<div ng-repeat="(label, value) in service.selector">
<a ng-href="/#/dashboard/groups/type/selector/{{label}}={{value}},type=replicationController">{{label}}: {{value}}</a>
</div>
</td>
</tr>
</tbody>
</table>
</div>
</div>
</div>
<div dashboard-footer></div>

60
www/app/index.html Normal file
View File

@ -0,0 +1,60 @@
<!DOCTYPE html>
<html lang="en" ng-app="kubernetesApp">
<head>
<title>Kubernetes UI</title>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="description" content="">
<meta name="viewport" content="initial-scale=1, maximum-scale=1, user-scalable=no" />
<link rel="stylesheet" href="vendor/angular-material/angular-material.css">
<link rel="stylesheet" href="assets/css/app.css" >
<link rel="shortcut icon" href="assets/img/icons/favicon.png" type="image/vnd.microsoft.icon" />
</head>
<body layout="row">
<md-sidenav layout="column"
md-is-locked-open="shouldLockOpen()"
style="overflow: hidden; display: flex;"
class="site-sidenav md-sidenav-left md-whiteframe-z2"
md-component-id="left"
md-closed>
<md-toolbar>
<h1 class="md-toolbar-tools">
<a ng-href="/" layout="row" flex>
<span class="kubernetes-ui-logo"></span>
<div style="line-height:40px; text-indent: 15px;">Kubernetes</div>
</a>
</h1>
</md-toolbar>
<md-content flex>
<div kubernetes-ui-menu role="kubernetes-ui-menu"></div>
<div compile="sidenavLeft"></div>
</md-content>
</md-sidenav>
<div layout="column" layout-fill tabIndex="-1" role="main" flex>
<md-toolbar>
<div class="md-toolbar-tools">
<h1 class="md-toolbar-tools">
<a ng-href="/" layout="row" flex>
<span class="kubernetes-ui-logo"></span>
<div style="line-height:40px; text-indent: 15px;">Kubernetes</div>
</a>
</h1>
</div>
</md-toolbar>
<md-content md-scroll-y flex>
<md-whiteframe layout layout-align="center center">
<div ng-controller="TabCtrl" class="tabsDefaultTabs">
<md-tabs md-selected="0">
<md-tab ng-repeat="tab in tabs" md-on-select="switchTab($index)" label="{{tab.title}}">
<div class="demo-tab tab{{$index%4}}" layout="column" layout-fill></div>
</md-tab>
</md-tabs>
<div ng-view layout="column" layout-fill role="main"></div>
</div>
</md-whiteframe>
</md-content>
</div>
<script src="assets/js/base.js"></script>
<script src="assets/js/app.js"></script>
</body>
</html>

File diff suppressed because it is too large Load Diff

0
www/app/vendor/.gitkeep vendored Normal file
View File

View File

@ -0,0 +1 @@
.jh-key,.jh-root,.jh-root tr,.jh-type-array,.jh-type-object,.jh-value{-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box}.jh-key,.jh-value{margin:0;padding:.2em}.jh-value{border-left:1px solid #ddd}.jh-type-bool,.jh-type-number{font-weight:700;text-align:center;color:#5286BC}.jh-type-string{font-style:italic;color:#839B00}.jh-array-key{font-style:italic;font-size:small;text-align:center}.jh-array-key,.jh-object-key{color:#444;vertical-align:top}.jh-type-array>tbody>tr:nth-child(odd),.jh-type-object>tbody>tr:nth-child(odd){background-color:#f5f5f5}.jh-type-array>tbody>tr:nth-child(even),.jh-type-object>tbody>tr:nth-child(even){background-color:#fff}.jh-type-array,.jh-type-object{width:100%;border-collapse:collapse}.jh-root{border:1px solid #ccc;margin:.2em}th.jh-key{text-align:left}.jh-type-array>tbody>tr,.jh-type-object>tbody>tr{border:1px solid #ddd;border-bottom:none}.jh-type-array>tbody>tr:last-child,.jh-type-object>tbody>tr:last-child{border-bottom:1px solid #ddd}.jh-type-array>tbody>tr:hover,.jh-type-object>tbody>tr:hover{border:1px solid #F99927}.jh-empty{font-style:italic;color:#999;font-size:small}

File diff suppressed because one or more lines are too long

5
www/app/vendor/d3/d3.min.js vendored Normal file

File diff suppressed because one or more lines are too long

View File

@ -0,0 +1 @@
<span>The page you're looking for could not be found.</span>

View File

@ -0,0 +1,14 @@
<ul class="kubernetes-ui-menu">
<li ng-repeat="section in menu.sections" class="parent-list-item" ng-class="{'parentActive' : isSectionSelected(section)}">
<h2 class="menu-heading" ng-if="section.type === 'heading'" id="heading_{{ section.name | nospace }}">
{{section.name}}
</h2>
<menu-link section="section" ng-if="section.type === 'link'"></menu-link>
<menu-toggle section="section" ng-if="section.type === 'toggle'"></menu-toggle>
<ul ng-if="section.children" class="menu-nested-list">
<li ng-repeat="child in section.children" ng-class="{'childActive' : isSectionSelected(child)}">
<menu-toggle section="child"></menu-toggle>
</li>
</ul>
</li>
</ul>

View File

@ -0,0 +1,45 @@
<table class="md-table">
<thead>
<tr class="md-table-headers-row">
<th class="md-table-header" ng-repeat="h in headers">
<a href ng-if="handleSort(h.field)" ng-click="reverse=!reverse;order(h.field,reverse)">{{h.name}} <span class="md-table-caret" ng-show="reverse && h.field == predicate"><img src="https://google.github.io/material-design-icons/navigation/svg/ic_arrow_drop_up_24px.svg"></span><span class="md-table-caret" ng-show="!reverse && h.field == predicate"><img src="https://google.github.io/material-design-icons/navigation/svg/ic_arrow_drop_down_24px.svg"></span></a>
<span ng-if="!handleSort(h.field)">{{h.name}}</span>
</th>
<th class="md-table-header"></th>
</tr>
</thead>
<tbody>
<tr class="md-table-content-row" ng-repeat="c in content | filter:filters | startFrom:currentPage*count | limitTo: count">
<td ng-repeat="h in headers" ng-if="h.field == thumbs" class="md-table-thumbs">
<div ng-if="h.field == thumbs" style="background-image:url({{c.thumb}})"></div>
</td>
<td class="md-table-content" ng-click="go(c)" ng-repeat="h in headers" ng-class="customClass[h.field]" ng-if="h.field != thumbs">
{{c[h.field]}}
</td>
<td class="md-table-td-more">
<md-button aria-label="More">
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24">
<path d="M12 8c1.1 0 2-.9 2-2s-.9-2-2-2-2 .9-2 2 .9 2 2 2zm0 2c-1.1 0-2 .9-2 2s.9 2 2 2 2-.9 2-2-.9-2-2-2zm0 6c-1.1 0-2 .9-2 2s.9 2 2 2 2-.9 2-2-.9-2-2-2z"/>
</svg>
</md-button>
</td>
</tr>
</tbody>
</table>
<div class="md-table-footer" layout="row">
<span class="md-table-count-info">Rows count per page : <a href ng-click="goToPage(0); count=1">1</a>, <a href ng-click="goToPage(0); count=10">10</a>, <a href ng-click="goToPage(0); count=25">25</a>, <a href ng-click="goToPage(0); count=50">50</a>, <a href ng-click="goToPage(0); count=100">100</a> (current is <strong>{{count}}</strong>)</span>
<span flex></span>
<span ng-show="nbOfPages() > 1">
<md-button aria-label="Back" class="md-table-footer-item" ng-disabled="currentPage==0" ng-click="currentPage=currentPage-1">
<img src="//google.github.io/material-design-icons/hardware/svg/ic_keyboard_arrow_left_24px.svg">
</md-button>
<a href ng-repeat="i in getNumber(nbOfPages()) track by $index" >
<md-button aria-label="Next" class="md-primary md-table-footer-item" ng-click="goToPage($index)">
<span ng-class="{ 'md-table-active-page': currentPage==$index}">{{$index+1}}</span>
</md-button>
</a>
<md-button aria-label="Jump" class="md-table-footer-item" ng-disabled="currentPage==nbOfPages()-1" ng-click="currentPage=currentPage+1">
<img src="//google.github.io/material-design-icons/hardware/svg/ic_keyboard_arrow_right_24px.svg">
</md-button>
</span>
</div>

View File

@ -0,0 +1,14 @@
<md-button class="md-button-toggle"
ng-click="toggle()"
aria-controls="kubernetes-ui-menu-{{section.name | nospace}}"
flex layout="row"
aria-expanded="{{isOpen()}}">
{{section.name}}
<span aria-hidden="true" class="md-toggle-icon" ng-class="{'toggled' : isOpen()}"></span>
<span class="visually-hidden">Toggle {{isOpen()? 'expanded' : 'collapsed'}}</span>
</md-button>
<ul ng-show="isOpen()" id="kubernetes-ui-menu-{{section.name | nospace}}" class="menu-toggle-list">
<li ng-repeat="page in section.pages">
<menu-link section="page"></menu-link>
</li>
</ul>

View File

@ -1,62 +0,0 @@
<!--
Copyright 2014 Google Inc. All rights reserved.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-->
<div style="margin-top: 10px">
<div class="k8s-title-font k8s-box">
{{ groupName }}
<div ng-if="group.kind != 'grouping'">
<div ng-if="!settings.display || settings.display=='box'">
<div class="content k8s-item k8s-inline" ng-repeat="item in group">
<div class="k8s-title-font k8s-font-regular">
<div ng-switch on='item.labels["type"]'>
<a ng-switch-when='pod' href="#/pods/{{ item.id }}">{{ item.id }}</a>
<a ng-switch-when='service' href="#/services/{{ item.id }}">{{ item.id }}</a>
<a ng-switch-when='replicationController' href="#/replicationControllers/{{ item.id }}">{{ item.id }}</a>
<span ng-switch-default>{{item.id}}</span>
</div>
</div>
</div>
</div>
<div ng-if="settings.display=='list'">
<table style="width: 90%; padding: 10px">
<tr ng-repeat="item in group" ng-class-odd="'k8s-odd'" ng-class-even="'k8s-even'" valign="top">
<td class="k8s-cell">
<div ng-switch on='item.labels["type"]'>
<a ng-switch-when='pod' href="#/pods/{{ item.id }}">{{ item.id }}</a>
<a ng-switch-when='service' href="#/services/{{ item.id }}">{{ item.id }}</a>
<a ng-switch-when='replicationController' href="#/replicationControllers/{{ item.id }}">{{ item.id }}</a>
<span ng-switch-default>{{item.id}}</span>
</div>
</td>
<td class="k8s-cell">
<div ng-repeat='(key, value) in item.labels'>
<a href="#/groups/{{key}}/selector/{{controller.routeParams.selector}}">{{key}}</a> :
<a href="#/groups/{{controller.routeParams.grouping}}/selector/{{key}}={{value}}">{{value}}</a> </span>
</td>
</tr>
</table>
</div>
</div>
<div ng-if="group.kind == 'grouping'">
<div ng-repeat="(groupName,group) in group.items" ng-include="'box.ng'">
</div>
</div>
<div>
<a ng-click="settings={display:'box'}">box</a>
<a ng-click="settings={display:'list'}">list</a>
<a ng-click="controller.resetGroupLayout(this);">reset</a>
</div>
</div>

View File

@ -1,39 +0,0 @@
<!--
Copyright 2014 Google Inc. All rights reserved.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-->
<html ng-app="k8s">
<head>
<title>Kubernetes</title>
<link href='https://fonts.googleapis.com/css?family=Ubuntu%20Mono' rel='stylesheet' type='text/css'>
<link href='https://fonts.googleapis.com/css?family=Open+Sans' rel='stylesheet' type='text/css'>
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.25/angular.min.js"></script>
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.25/angular-route.min.js"></script>
<!--
For local/network free development.
<script src="/angular.min.js"></script>
<!-- -->
<script src="podcontroller.js"></script>
<link rel="stylesheet" href="k8s-style.css">
</head>
<body>
<div class="navbar">
<div class="navbar-logo">
<img src="logotext.svg" id="nav-logo-img" alt="logo">
</div>
</div>
<div ng-view></div>
</body>
</html>

View File

@ -1,108 +0,0 @@
.logo {
background: url('titlelogo.svg');
background-repeat: no-repeat;
height: 150px;
width: 600px;
}
.navbar-logo {
margin-left: 10px;
margin-top: 5px;
}
.title {
color: #fff;
font-size: 14pt;
height: 20px;
padding-top: 10px;
padding-left: 10px;
width: 1024px;
}
#nav-logo-img {
height: 42px;
}
.spread {
width: 1024px;
}
.content {
margin-left: 20px;
margin-top: 10px;
}
.bar {
background: #DFE7FA;
padding: 10px;
}
.navbar {
background-color: #447AE8;
border: 0px;
border-radius: 0;
}
.link {
color: #22F;
cursor: pointer;
text-decoration: underline;
}
.k8s-title-font {
font-family: 'Ubuntu Mono', arial, sans-serif;
font-size: 14pt;
}
.k8s-font-medium {
font-size: 18pt;
}
.k8s-font-regular {
font-size: 12pt;
}
.k8s-box {
background: #CCC;
border: 1px solid darkgray;
padding: 10px;
width: 90%;
margin-left: 5%;
}
.k8s-item {
background: #DDD;
border: 1px solid whitesmoke;
padding: 10px;
width: 200px;
}
.k8s-list {
background: #DDD;
border: 1px solid whitesmoke;
padding: 10px;
width: 90%;
}
.k8s-inline {
display: inline-block;
}
.k8s-button {
border: 1px solid black;
background: lightgray;
cursor: pointer;
padding: 2px 4px 2px 4px;
}
.k8s-odd {
background: #EEE;
}
.k8s-even {
background: #DDD;
}
.k8s-cell {
padding: 10px;
}

View File

@ -1,374 +0,0 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Created with Inkscape (http://www.inkscape.org/) -->
<svg
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
version="1.1"
width="562.78589"
height="134.62993"
id="svg2"
xml:space="preserve"><metadata
id="metadata8"><rdf:RDF><cc:Work
rdf:about=""><dc:format>image/svg+xml</dc:format><dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" /><dc:title></dc:title></cc:Work></rdf:RDF></metadata><defs
id="defs6" /><g
transform="matrix(1.25,0,0,-1.25,-169.2546,1414.8444)"
id="g10"><g
transform="matrix(0,0.18092,-0.18275,0,0,0)"
id="g12"><path
d="m 6196.6587,-1043.6173 -94.2902,-195.4939 -211.9113,-48.3046 -169.9617,135.2607 -0.025,216.9692 169.9297,135.2974 211.9254,-48.257 94.3336,-195.4718 z"
id="path14"
style="fill:none;stroke:#ffffff;stroke-width:118.52590179;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none" /></g><path
d="m 190.7198,1121.0876 35.725,-17.0586 8.8274,-38.3391 -24.7181,-30.7489 -39.6505,0 -24.7249,30.7434 8.8192,38.3412 35.7219,17.0665 z"
id="path16"
style="fill:#336ee5;fill-opacity:1;fill-rule:nonzero;stroke:none" /><g
transform="matrix(0,0.18092,-0.18275,0,0,0)"
id="g18"><path
d="m 6196.6587,-1043.6173 -94.2888,-195.4939 -211.9141,-48.3046 -169.9603,135.2593 -0.025,216.9723 169.9297,135.2942 211.9237,-48.2572 94.3353,-195.4701 z"
id="path20"
style="fill:none;stroke:#336ee5;stroke-width:74.74790192;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none" /></g><g
transform="scale(0.18275,0.18275)"
id="g22"><path
d="m 1013.0746,6022.3961 c 73.5242,16.6963 146.8298,-29.4129 163.7263,-102.9881 16.9013,-73.5693 -29.0033,-146.7459 -102.5258,-163.4409 -73.5273,-16.6903 -146.8343,29.4189 -163.7325,102.9867 -16.8967,73.5769 29.0047,146.7505 102.532,163.4423 z"
id="path24"
style="fill:none;stroke:#ffffff;stroke-width:30.78089905;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none" /></g><path
d="m 188.4186,1104.8074 2.7468,-0.01 0.1749,-20.1918 -4.0657,-0.038 1.144,20.236 z"
id="path26"
style="fill:#ffffff;fill-opacity:1;fill-rule:nonzero;stroke:none" /><g
transform="scale(0.17178,0.18275)"
id="g28"><path
d="m 1096.8024,6045.6095 15.9899,-0.036 1.0191,-110.4894 -23.6699,-0.2094 6.6609,110.7345 z"
id="path30"
style="fill:none;stroke:#ffffff;stroke-width:0.41159999;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none" /></g><path
d="m 193.0309,1104.8074 -2.7474,-0.01 -0.1703,-20.1918 4.0654,-0.037 -1.1477,20.2354 z"
id="path32"
style="fill:#ffffff;fill-opacity:1;fill-rule:nonzero;stroke:none" /><g
transform="scale(0.17178,0.18275)"
id="g34"><path
d="m 1123.6518,6045.6098 -15.993,-0.036 -0.991,-110.4894 23.6681,-0.2029 -6.6841,110.7283 z"
id="path36"
style="fill:none;stroke:#ffffff;stroke-width:0.41159999;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none" /></g><path
d="m 192.8625,1112.4315 c 0,-1.3119 -0.9576,-2.3758 -2.1382,-2.3758 -1.1806,0 -2.1379,1.0639 -2.1379,2.3752 0,1.3119 0.9564,2.3754 2.1379,2.3763 1.1806,0 2.1382,-1.0636 2.1382,-2.3757"
id="path38"
style="fill:#ffffff;fill-opacity:1;fill-rule:nonzero;stroke:none" /><g
transform="matrix(0.16447,2e-5,-2e-5,0.18275,0,0)"
id="g40"><path
d="m 1173.5053,6087.183 c -8e-4,-7.1804 -5.8238,-12.9997 -13.0019,-12.9988 -7.1785,8e-4 -12.998,5.8229 -12.9969,12.9971 0,7.1819 5.817,13.0011 13.0006,13.0031 7.1781,-6e-4 12.9994,-5.8229 12.9982,-13.0014 z"
id="path42"
style="fill:none;stroke:#ffffff;stroke-width:0.2744;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none" /></g><path
d="m 188.5873,1112.4629 c 5e-4,-0.1876 -0.009,-0.458 -0.003,-0.6389 0.0289,-0.7566 0.1945,-1.3368 0.294,-2.0344 0.1797,-1.4922 0.3311,-2.7289 0.2381,-3.8781 -0.0851,-0.5757 -0.4184,-0.8028 -0.6959,-1.0689 l 3.3721,-2.2315 -0.5069,9.8354 -2.6988,0.016 z"
id="path44"
style="fill:#ffffff;fill-opacity:1;fill-rule:nonzero;stroke:none" /><g
transform="matrix(0,-0.17178,0.18275,0,0,0)"
id="g46"><path
d="m -6476.0579,1031.9675 c 1.0925,0 2.6666,-0.048 3.7194,-0.014 4.4045,0.1568 7.7839,1.0641 11.8431,1.6087 8.6865,0.9819 15.8862,1.8102 22.5791,1.3028 3.3483,-0.4652 4.6701,-2.2896 6.2212,-3.8095 l 12.9884,18.4555 -57.257,-2.7751 -0.094,-14.7685 z"
id="path48"
style="fill:none;stroke:#ffffff;stroke-width:0.26840001;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none" /></g><path
d="m 192.8625,1112.4629 c -0.001,-0.1876 0.008,-0.458 0.002,-0.6389 -0.0289,-0.7566 -0.1942,-1.3368 -0.2934,-2.0344 -0.1797,-1.4922 -0.3311,-2.7289 -0.2384,-3.8781 0.0847,-0.5757 0.4184,-0.8028 0.6959,-1.0689 l -3.3724,-2.2315 0.5074,9.8354 2.6989,0.016 z"
id="path50"
style="fill:#ffffff;fill-opacity:1;fill-rule:nonzero;stroke:none" /><g
transform="matrix(0,-0.17178,0.18275,0,0,0)"
id="g52"><path
d="m -6476.0579,1055.3604 c 1.0925,0 2.6666,0.046 3.7194,0.011 4.4045,-0.1534 7.7839,-1.0624 11.8431,-1.6022 8.6865,-0.9867 15.8862,-1.815 22.5791,-1.3076 3.3483,0.4669 4.6701,2.291 6.2212,3.8081 l 12.9884,-18.4541 -57.257,2.7765 -0.094,14.7685 z"
id="path54"
style="fill:none;stroke:#ffffff;stroke-width:0.26840001;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none" /></g><g
transform="scale(0.18275,0.18275)"
id="g56"><path
d="m 1073.7275,5865.2637 -30.1062,-14.4303 -30.1,14.438 -7.4344,32.4422 20.8395,26.0065 33.4099,0 20.8321,-26.0175 -7.4409,-32.4374 z"
id="path58"
style="fill:none;stroke:#ffffff;stroke-width:30.34600067;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none" /></g><path
d="m 166.9153,1092.2468 1.7178,2.1436 15.8952,-12.4532 -2.5049,-3.2023 -15.1081,13.5119 z"
id="path60"
style="fill:#ffffff;fill-opacity:1;fill-rule:nonzero;stroke:none" /><g
transform="matrix(0.1071,0.1343,-0.14288,0.11394,0,0)"
id="g62"><path
d="m 5577.0313,3012.37 15.9908,-0.036 1.0134,-110.4917 -23.6665,-0.2083 6.6623,110.7357 z"
id="path64"
style="fill:none;stroke:#ffffff;stroke-width:0.41159999;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none" /></g><path
d="m 169.7905,1095.853 -1.7074,-2.1523 15.6805,-12.723 2.5636,3.1561 -16.5367,11.7192 z"
id="path66"
style="fill:#ffffff;fill-opacity:1;fill-rule:nonzero;stroke:none" /><g
transform="matrix(0.1071,0.1343,-0.14288,0.11394,0,0)"
id="g68"><path
d="m 5603.8799,3012.3729 -15.9928,-0.039 -0.9944,-110.4931 23.6693,-0.2001 -6.6821,110.7321 z"
id="path70"
style="fill:none;stroke:#ffffff;stroke-width:0.41159999;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none" /></g><path
d="m 163.7252,1100.4746 c 1.0258,-0.8183 1.2608,-2.2297 0.5247,-3.1529 -0.7368,-0.9233 -2.1651,-1.0089 -3.191,-0.1908 -1.0256,0.8181 -1.2606,2.2292 -0.5238,3.1524 0.7358,0.9235 2.1642,1.0086 3.1901,0.1913"
id="path72"
style="fill:#ffffff;fill-opacity:1;fill-rule:nonzero;stroke:none" /><g
transform="matrix(0.10253,0.1286,-0.14289,0.11392,0,0)"
id="g74"><path
d="m 5852.363,3053.3992 c 0,-7.181 -5.8216,-13.0011 -13.0009,-13.0005 -7.1815,0 -13.0025,5.8227 -13.0013,13.0025 0,7.1796 5.8198,12.9977 13.0013,12.9949 7.1799,0 12.998,-5.8198 13.0009,-12.9969 z"
id="path76"
style="fill:none;stroke:#ffffff;stroke-width:0.2744;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none" /></g><path
d="m 161.0351,1097.1516 c 0.1474,-0.1171 0.3518,-0.2931 0.4986,-0.4005 0.6089,-0.4496 1.1656,-0.6818 1.7734,-1.0387 1.2781,-0.7894 2.3397,-1.4428 3.1807,-2.232 0.3969,-0.4249 0.3662,-0.8268 0.4008,-1.2104 l 3.8472,1.2453 -8.0047,5.7356 -1.696,-2.0993 z"
id="path78"
style="fill:#ffffff;fill-opacity:1;fill-rule:nonzero;stroke:none" /><g
transform="matrix(0.1343,-0.1071,0.11394,0.14288,0,0)"
id="g80"><path
d="m -3249.2299,5243.3232 c 1.0919,-9e-4 2.6612,-0.054 3.7205,-0.014 4.403,0.1539 7.7794,1.0602 11.8417,1.6056 8.6817,0.9844 15.8837,1.8121 22.5768,1.3042 3.3486,-0.4641 4.6681,-2.2882 6.2175,-3.8109 l 12.9912,18.4518 -57.2536,-2.7726 -0.094,-14.7639 z"
id="path82"
style="fill:none;stroke:#ffffff;stroke-width:0.26840001;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none" /></g><path
d="m 163.6999,1100.4936 c 0.1472,-0.1176 0.3643,-0.278 0.5012,-0.3965 0.574,-0.4944 0.9247,-0.9851 1.4077,-1.4979 1.0548,-1.0709 1.9275,-1.9601 2.8842,-2.6039 0.5026,-0.2928 0.8876,-0.1737 1.2691,-0.1221 l -0.3586,-4.0275 -7.3732,6.5273 1.6696,2.1206 z"
id="path84"
style="fill:#ffffff;fill-opacity:1;fill-rule:nonzero;stroke:none" /><g
transform="matrix(0.1343,-0.1071,0.11394,0.14288,0,0)"
id="g86"><path
d="m -3249.2341,5266.7113 c 1.0978,-3e-4 2.6671,0.053 3.7204,0.013 4.4068,-0.1565 7.7837,-1.0596 11.8432,-1.6053 8.6876,-0.983 15.8876,-1.8099 22.5782,-1.2999 3.3503,0.462 4.6704,2.2847 6.2195,3.8072 l 12.9884,-18.4521 -57.2517,2.768 -0.098,14.7688 z"
id="path88"
style="fill:none;stroke:#ffffff;stroke-width:0.26840001;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none" /></g><path
d="m 163.3717,1067.5885 -0.6052,2.6796 19.6464,4.6636 0.9419,-3.9555 -19.9831,-3.3877 z"
id="path90"
style="fill:#ffffff;fill-opacity:1;fill-rule:nonzero;stroke:none" /><g
transform="matrix(0.03823,-0.16747,0.17816,0.04067,0,0)"
id="g92"><path
d="m -5847.3595,2171.5736 -15.992,0.034 -1.017,110.4899 23.669,0.2087 -6.66,-110.7329 z"
id="path94"
style="fill:none;stroke:#ffffff;stroke-width:0.41159999;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none" /></g><path
d="m 162.3444,1072.0845 0.6182,-2.6767 19.7238,4.3271 -0.8685,3.9719 -19.4735,-5.6223 z"
id="path96"
style="fill:#ffffff;fill-opacity:1;fill-rule:nonzero;stroke:none" /><g
transform="matrix(0.03823,-0.16747,0.17816,0.04067,0,0)"
id="g98"><path
d="m -5874.2073,2171.5679 15.9931,0.04 0.9924,110.4916 -23.6673,0.203 6.6818,-110.7349 z"
id="path100"
style="fill:none;stroke:#ffffff;stroke-width:0.41159999;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none" /></g><path
d="m 154.9497,1070.2236 c 1.2787,0.2928 2.529,-0.4039 2.7921,-1.5551 0.2625,-1.1509 -0.561,-2.3213 -1.8405,-2.6127 -1.2785,-0.2928 -2.5285,0.4039 -2.7919,1.5551 -0.2625,1.1509 0.5613,2.3207 1.8403,2.6127"
id="path102"
style="fill:#ffffff;fill-opacity:1;fill-rule:nonzero;stroke:none" /><g
transform="matrix(0.03662,-0.16034,0.17816,0.04069,0,0)"
id="g104"><path
d="m -6133.9467,2130.5761 c 0,7.1782 5.8161,12.9985 12.9991,12.9988 7.1756,-3e-4 12.9985,-5.8192 12.9951,-13.0005 0.01,-7.1753 -5.8189,-12.9966 -12.9988,-12.9988 -7.1773,5e-4 -12.9963,5.8218 -12.9954,13.0005 z"
id="path106"
style="fill:none;stroke:#ffffff;stroke-width:0.2744;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none" /></g><path
d="m 155.8706,1066.049 c 0.1829,0.042 0.4488,0.093 0.6234,0.14 0.7316,0.1962 1.2606,0.4867 1.9182,0.7387 1.4148,0.508 2.5869,0.9301 3.7281,1.0951 0.58,0.046 0.8745,-0.2282 1.1968,-0.4405 l 1.4247,3.7842 -9.4759,-2.6816 0.5847,-2.6359 z"
id="path108"
style="fill:#ffffff;fill-opacity:1;fill-rule:nonzero;stroke:none" /><g
transform="matrix(0.16747,0.03823,-0.04067,0.17816,0,0)"
id="g110"><path
d="m 2265.6285,5497.4356 c 1.0922,0 2.6666,-0.045 3.7177,-0.01 4.4093,0.1534 7.7862,1.0596 11.8448,1.603 8.6851,0.9845 15.8865,1.8117 22.5771,1.3023 3.3483,-0.4592 4.6676,-2.2825 6.2201,-3.8072 l 12.9895,18.4535 -57.2534,-2.7672 -0.096,-14.7744 z"
id="path112"
style="fill:none;stroke:#ffffff;stroke-width:0.26840001;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none" /></g><path
d="m 154.9191,1070.2165 c 0.1834,0.042 0.4447,0.1106 0.6224,0.1451 0.7447,0.1404 1.3471,0.1078 2.0489,0.1661 1.4947,0.1571 2.7338,0.2849 3.8339,0.6313 0.5428,0.2106 0.6894,0.5859 0.8867,0.9165 l 2.9262,-2.791 -9.701,-1.696 -0.6171,2.628 z"
id="path114"
style="fill:#ffffff;fill-opacity:1;fill-rule:nonzero;stroke:none" /><g
transform="matrix(0.16747,0.03823,-0.04067,0.17816,0,0)"
id="g116"><path
d="m 2265.6266,5520.8273 c 1.0955,0 2.6674,0.048 3.7193,0.017 4.4096,-0.1585 7.7859,-1.0659 11.8448,-1.6093 8.6865,-0.9822 15.8843,-1.809 22.5766,-1.3005 3.3536,0.4626 4.6684,2.287 6.2195,3.8092 l 12.9917,-18.4527 -57.2533,2.7683 -0.099,14.7682 z"
id="path118"
style="fill:none;stroke:#ffffff;stroke-width:0.26840001;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none" /></g><path
d="m 180.4031,1049.4196 -2.473,1.1973 8.6032,18.2685 3.6802,-1.73 -9.8104,-17.7358 z"
id="path120"
style="fill:#ffffff;fill-opacity:1;fill-rule:nonzero;stroke:none" /><g
transform="matrix(0.15477,-0.07453,0.07929,0.16465,0,0)"
id="g122"><path
d="m -1704.3131,5602.1797 -15.9959,0.035 -1.0171,110.4913 23.6718,0.2081 -6.6588,-110.7343 z"
id="path124"
style="fill:none;stroke:#ffffff;stroke-width:0.41159999;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none" /></g><path
d="m 176.2469,1051.4203 2.4784,-1.1858 8.9141,18.1179 -3.6474,1.7978 -7.7451,-18.7299 z"
id="path126"
style="fill:#ffffff;fill-opacity:1;fill-rule:nonzero;stroke:none" /><g
transform="matrix(0.15477,-0.07453,0.07929,0.16465,0,0)"
id="g128"><path
d="m -1731.1657,5602.1774 15.9936,0.038 0.9918,110.4882 -23.669,0.2044 6.6836,-110.7309 z"
id="path130"
style="fill:none;stroke:#ffffff;stroke-width:0.41159999;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none" /></g><path
d="m 173.0911,1044.4779 c 0.5695,1.1824 1.8927,1.7255 2.9568,1.2132 1.0642,-0.5122 1.4653,-1.8858 0.8964,-3.0676 -0.5687,-1.1826 -1.8933,-1.7252 -2.9569,-1.2132 -1.0644,0.5122 -1.4655,1.8856 -0.8963,3.0676"
id="path132"
style="fill:#ffffff;fill-opacity:1;fill-rule:nonzero;stroke:none" /><g
transform="matrix(0.14819,-0.07134,0.07926,0.16466,0,0)"
id="g134"><path
d="m -1806.2371,5560.6799 c 3e-4,7.1804 5.821,12.9988 13.0023,13.0011 7.1781,0 12.9974,-5.8221 12.9957,-12.9997 0.01,-7.1801 -5.821,-12.9991 -12.9974,-12.9994 -7.1822,-5e-4 -13.0014,5.819 -13.0006,12.998 z"
id="path136"
style="fill:none;stroke:#ffffff;stroke-width:0.2744;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none" /></g><path
d="m 176.9292,1042.5954 c 0.0817,0.1695 0.207,0.4085 0.2792,0.5749 0.3033,0.6934 0.4057,1.2883 0.6183,1.9596 0.4853,1.4227 0.8858,2.6025 1.468,3.5974 0.3266,0.4819 0.7249,0.542 1.0908,0.6614 l -2.0698,3.4733 -3.8107,-9.0808 2.4242,-1.1858 z"
id="path138"
style="fill:#ffffff;fill-opacity:1;fill-rule:nonzero;stroke:none" /><g
transform="matrix(0.07453,0.15477,-0.16465,0.07929,0,0)"
id="g140"><path
d="m 5915.209,1602.9547 c 1.0953,0 2.6649,-0.05 3.7202,-0.012 4.4056,0.1519 7.7811,1.0601 11.8409,1.605 8.6862,0.9844 15.8865,1.8127 22.5763,1.3028 3.3508,-0.4666 4.6717,-2.2873 6.222,-3.8084 l 12.9909,18.4516 -57.2547,-2.7709 -0.096,-14.7685 z"
id="path142"
style="fill:none;stroke:#ffffff;stroke-width:0.26840001;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none" /></g><path
d="m 173.0784,1044.4501 c 0.0813,0.169 0.1902,0.4165 0.2744,0.5766 0.354,0.6698 0.7554,1.12 1.1471,1.7059 0.8099,1.266 1.482,2.3145 1.8975,3.3905 0.1735,0.5553 -0.0286,0.904 -0.1644,1.2646 l 4.0071,0.5473 -4.7237,-8.6411 -2.438,1.1562 z"
id="path144"
style="fill:#ffffff;fill-opacity:1;fill-rule:nonzero;stroke:none" /><g
transform="matrix(0.07453,0.15477,-0.16465,0.07929,0,0)"
id="g146"><path
d="m 5915.2107,1626.3439 c 1.0917,0 2.6638,0.052 3.7171,0.016 4.4073,-0.1553 7.7822,-1.0655 11.8432,-1.6064 8.6876,-0.9845 15.8853,-1.8102 22.5771,-1.3051 3.3499,0.4646 4.6689,2.2873 6.22,3.8141 l 12.9887,-18.4575 -57.2528,2.7737 -0.093,14.7653 z"
id="path148"
style="fill:none;stroke:#ffffff;stroke-width:0.26840001;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none" /></g><path
d="m 205.2142,1051.4548 -2.4778,-1.1863 -8.9186,18.1171 3.6467,1.798 7.7497,-18.7288 z"
id="path150"
style="fill:#ffffff;fill-opacity:1;fill-rule:nonzero;stroke:none" /><g
transform="matrix(0.15477,0.07454,-0.0793,0.16465,0,0)"
id="g152"><path
d="m 3732.232,4696.5288 -15.992,0.035 -1.0145,110.4942 23.669,0.2069 -6.6625,-110.7357 z"
id="path154"
style="fill:none;stroke:#ffffff;stroke-width:0.41159999;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none" /></g><path
d="m 201.0589,1049.4533 2.4729,1.1988 -8.6082,18.2661 -3.6797,-1.7314 9.815,-17.7335 z"
id="path156"
style="fill:#ffffff;fill-opacity:1;fill-rule:nonzero;stroke:none" /><g
transform="matrix(0.15477,0.07454,-0.0793,0.16465,0,0)"
id="g158"><path
d="m 3705.3822,4696.5274 15.9982,0.038 0.9873,110.4936 -23.6687,0.201 6.6832,-110.7329 z"
id="path160"
style="fill:none;stroke:#ffffff;stroke-width:0.41159999;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none" /></g><path
d="m 204.5188,1042.6575 c -0.5697,1.1821 -0.1686,2.5563 0.8952,3.0685 1.0636,0.5122 2.3879,-0.03 2.9574,-1.2124 0.5689,-1.182 0.1684,-2.5554 -0.8952,-3.0682 -1.0644,-0.5125 -2.3876,0.031 -2.9574,1.2121"
id="path162"
style="fill:#ffffff;fill-opacity:1;fill-rule:nonzero;stroke:none" /><g
transform="matrix(0.14817,0.07138,-0.07932,0.16464,0,0)"
id="g164"><path
d="m 3871.7606,4654.3567 c -9e-4,7.1818 5.8221,13.0025 13.0017,13.0022 7.1767,0 12.9999,-5.8209 12.9991,-13.002 0,-7.1784 -5.8212,-12.9977 -13,-12.9996 -7.183,0 -12.9977,5.8215 -13.0008,12.9994 z"
id="path166"
style="fill:none;stroke:#ffffff;stroke-width:0.2744;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none" /></g><path
d="m 208.3836,1044.4844 c -0.0811,0.1698 -0.1896,0.4167 -0.2747,0.5775 -0.354,0.6698 -0.7554,1.1199 -1.1472,1.7056 -0.8095,1.2665 -1.4819,2.3142 -1.8978,3.3908 -0.1732,0.5553 0.029,0.9037 0.1636,1.2639 l -4.0059,0.5477 4.7233,-8.6411 2.4387,1.1556 z"
id="path168"
style="fill:#ffffff;fill-opacity:1;fill-rule:nonzero;stroke:none" /><g
transform="matrix(0.07454,-0.15477,0.16465,0.0793,0,0)"
id="g170"><path
d="m -4951.7391,3507.378 c -1.097,5e-4 -2.6659,0.055 -3.7224,0.017 -4.4059,-0.1556 -7.7822,-1.065 -11.844,-1.6073 -8.6868,-0.9827 -15.8817,-1.8093 -22.5771,-1.3039 -3.35,0.4646 -4.665,2.2876 -6.2153,3.8089 l -12.992,-18.4507 57.2536,2.7686 0.097,14.7677 z"
id="path172"
style="fill:none;stroke:#ffffff;stroke-width:0.26840001;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none" /></g><path
d="m 204.5333,1042.6297 c -0.0825,0.1693 -0.2072,0.4091 -0.2801,0.5746 -0.3024,0.6945 -0.405,1.2895 -0.6179,1.9602 -0.4859,1.423 -0.8861,2.6022 -1.4684,3.5983 -0.3265,0.4816 -0.7248,0.5408 -1.091,0.6599 l 2.0701,3.4738 3.8112,-9.0802 -2.4239,-1.1866 z"
id="path174"
style="fill:#ffffff;fill-opacity:1;fill-rule:nonzero;stroke:none" /><g
transform="matrix(0.07454,-0.15477,0.16465,0.0793,0,0)"
id="g176"><path
d="m -4951.7379,3483.9904 c -1.0953,0 -2.6683,-0.049 -3.7196,-0.014 -4.4074,0.1565 -7.7871,1.0633 -11.8438,1.6036 -8.687,0.987 -15.8847,1.8128 -22.5799,1.3073 -3.3508,-0.4663 -4.6681,-2.2904 -6.2186,-3.8114 l -12.989,18.4538 57.2508,-2.7706 0.1001,-14.7688 z"
id="path178"
style="fill:none;stroke:#ffffff;stroke-width:0.26840001;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none" /></g><path
d="m 219.1056,1072.1339 -0.6173,-2.6771 -19.7252,4.3226 0.868,3.9727 19.4745,-5.6182 z"
id="path180"
style="fill:#ffffff;fill-opacity:1;fill-rule:nonzero;stroke:none" /><g
transform="matrix(0.03822,0.16747,-0.17816,0.04066,0,0)"
id="g182"><path
d="m 6368.633,136.4414 -15.9914,0.0349 -1.0165,110.4948 23.6699,0.206 -6.662,-110.7357 z"
id="path184"
style="fill:none;stroke:#ffffff;stroke-width:0.41159999;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none" /></g><path
d="m 218.0798,1067.6375 0.6046,2.6794 -19.6481,4.659 -0.9405,-3.9555 19.984,-3.3829 z"
id="path186"
style="fill:#ffffff;fill-opacity:1;fill-rule:nonzero;stroke:none" /><g
transform="matrix(0.03822,0.16747,-0.17816,0.04066,0,0)"
id="g188"><path
d="m 6341.7858,136.44 15.9894,0.0363 0.993,110.4933 -23.6673,0.2019 6.6849,-110.7315 z"
id="path190"
style="fill:none;stroke:#ffffff;stroke-width:0.41159999;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none" /></g><path
d="m 225.5496,1066.1054 c -1.279,0.2923 -2.103,1.4613 -1.8405,2.6127 0.2631,1.1517 1.5126,1.8479 2.7913,1.5562 1.2793,-0.2914 2.1036,-1.4609 1.8411,-2.6127 -0.2631,-1.1514 -1.5123,-1.8482 -2.7919,-1.5562"
id="path192"
style="fill:#ffffff;fill-opacity:1;fill-rule:nonzero;stroke:none" /><g
transform="matrix(0.03657,0.16035,-0.17817,0.04063,0,0)"
id="g194"><path
d="m 6624.6812,93.8699 c 0,7.1821 5.8189,12.9977 13.0011,12.9988 7.1796,-0.003 12.9974,-5.8215 12.9971,-12.9986 0,-7.1795 -5.8161,-13.0019 -12.9985,-13.0013 -7.1813,0.0022 -13.0012,5.8223 -12.9997,13.0011 z"
id="path196"
style="fill:none;stroke:#ffffff;stroke-width:0.2744;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none" /></g><path
d="m 226.531,1070.2664 c -0.1826,0.042 -0.4439,0.1105 -0.6225,0.1443 -0.7435,0.1411 -1.3462,0.1085 -2.048,0.1669 -1.4947,0.1562 -2.7346,0.2844 -3.8345,0.6305 -0.5422,0.2103 -0.6891,0.5862 -0.8869,0.9164 l -2.9251,-2.7918 9.7005,-1.6937 0.6165,2.6274 z"
id="path198"
style="fill:#ffffff;fill-opacity:1;fill-rule:nonzero;stroke:none" /><g
transform="matrix(0.16748,-0.03822,0.04066,0.17817,0,0)"
id="g200"><path
d="m -100.5092,5985.5941 c -1.0916,-2e-4 -2.6645,0.05 -3.7196,0.012 -4.4028,-0.1522 -7.7808,-1.0598 -11.8414,-1.6029 -8.684,-0.9862 -15.8879,-1.8139 -22.5803,-1.3054 -3.3477,0.4654 -4.6655,2.2895 -6.2161,3.8103 l -12.9872,-18.4544 57.2485,2.7726 0.0961,14.7679 z"
id="path202"
style="fill:none;stroke:#ffffff;stroke-width:0.26840001;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none" /></g><path
d="m 225.5803,1066.0992 c -0.1834,0.042 -0.4485,0.093 -0.624,0.14 -0.731,0.1953 -1.26,0.4859 -1.9176,0.7379 -1.4148,0.5074 -2.5869,0.93 -3.7281,1.0953 -0.5794,0.046 -0.8754,-0.2288 -1.1965,-0.4405 l -1.4247,3.7839 9.4748,-2.6821 -0.5839,-2.6345 z"
id="path204"
style="fill:#ffffff;fill-opacity:1;fill-rule:nonzero;stroke:none" /><g
transform="matrix(0.16748,-0.03822,0.04066,0.17817,0,0)"
id="g206"><path
d="m -100.5077,5962.2049 c -1.0954,6e-4 -2.6652,-0.052 -3.7225,-0.013 -4.4022,0.1536 -7.7803,1.0581 -11.8358,1.6027 -8.6899,0.9836 -15.8896,1.8116 -22.5808,1.3036 -3.3505,-0.4626 -4.6715,-2.2858 -6.22,-3.8066 l -12.9867,18.4527 57.2477,-2.7737 0.0981,-14.766 z"
id="path208"
style="fill:none;stroke:#ffffff;stroke-width:0.26840001;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none" /></g><path
d="m 211.6429,1095.893 1.7084,-2.1515 -15.6779,-12.7267 -2.5639,3.1547 16.5334,11.7235 z"
id="path210"
style="fill:#ffffff;fill-opacity:1;fill-rule:nonzero;stroke:none" /><g
transform="matrix(0.10711,-0.1343,0.14287,0.11395,0,0)"
id="g212"><path
d="m -4219.3791,4644.5956 15.993,-0.032 1.012,-110.4928 -23.6625,-0.208 6.6575,110.7329 z"
id="path214"
style="fill:none;stroke:#ffffff;stroke-width:0.41159999;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none" /></g><path
d="m 214.52,1092.2873 -1.7192,2.1436 -15.8913,-12.4566 2.5053,-3.2014 15.1052,13.5144 z"
id="path216"
style="fill:#ffffff;fill-opacity:1;fill-rule:nonzero;stroke:none" /><g
transform="matrix(0.10711,-0.1343,0.14287,0.11395,0,0)"
id="g218"><path
d="m -4192.5257,4644.6018 -15.9959,-0.041 -0.9899,-110.4863 23.6639,-0.2055 -6.6781,110.7329 z"
id="path220"
style="fill:none;stroke:#ffffff;stroke-width:0.41159999;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none" /></g><path
d="m 220.3747,1097.1729 c -1.0256,-0.8181 -2.4542,-0.7331 -3.1901,0.1902 -0.7364,0.9229 -0.5017,2.3349 0.5233,3.1529 1.0261,0.8181 2.4542,0.7331 3.1909,-0.1899 0.7359,-0.923 0.5018,-2.3352 -0.5241,-3.1532"
id="path222"
style="fill:#ffffff;fill-opacity:1;fill-rule:nonzero;stroke:none" /><g
transform="matrix(0.10257,-0.12857,0.14286,0.11397,0,0)"
id="g224"><path
d="m -4379.2058,4686.834 c -3e-4,-7.1787 -5.8215,-12.9999 -13,-12.9971 -7.179,-6e-4 -13,5.8218 -13.0017,12.998 0,7.1813 5.8204,13.0011 13.0009,13.0005 7.1778,-5e-4 13.0022,-5.8192 13.0008,-13.0014 z"
id="path226"
style="fill:none;stroke:#ffffff;stroke-width:0.2744;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none" /></g><path
d="m 217.7337,1100.5347 c -0.1471,-0.1173 -0.364,-0.2786 -0.5012,-0.3962 -0.5737,-0.4938 -0.9241,-0.9859 -1.4068,-1.4976 -1.0554,-1.0718 -1.9273,-1.9613 -2.8843,-2.6053 -0.5026,-0.2925 -0.8872,-0.1735 -1.2691,-0.1216 l 0.3586,-4.028 7.3724,6.5284 -1.6696,2.1203 z"
id="path228"
style="fill:#ffffff;fill-opacity:1;fill-rule:nonzero;stroke:none" /><g
transform="matrix(0.1343,0.10711,-0.11395,0.14287,0,0)"
id="g230"><path
d="m 4985.5978,3965.3263 c -1.097,8e-4 -2.6683,0.052 -3.7208,0.016 -4.4036,-0.1551 -7.7842,-1.0647 -11.8383,-1.6067 -8.6933,-0.9845 -15.8919,-1.8139 -22.5811,-1.3048 -3.3517,0.4632 -4.6689,2.2839 -6.2198,3.8098 l -12.9886,-18.4561 57.2496,2.7757 0.099,14.7665 z"
id="path232"
style="fill:none;stroke:#ffffff;stroke-width:0.26840001;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none" /></g><path
d="m 220.3994,1097.193 c -0.1474,-0.1171 -0.3532,-0.2926 -0.4984,-0.4006 -0.6088,-0.4498 -1.1664,-0.6814 -1.7733,-1.0391 -1.2784,-0.7898 -2.34,-1.4429 -3.1811,-2.2323 -0.3968,-0.4247 -0.3662,-0.8266 -0.4008,-1.2101 l -3.8472,1.2452 8.0045,5.7362 1.6963,-2.0993 z"
id="path234"
style="fill:#ffffff;fill-opacity:1;fill-rule:nonzero;stroke:none" /><g
transform="matrix(0.1343,0.10711,-0.11395,0.14287,0,0)"
id="g236"><path
d="m 4985.5986,3941.9357 c -1.0956,0 -2.6665,-0.048 -3.7216,-0.013 -4.4042,0.1528 -7.7836,1.0656 -11.8429,1.6047 -8.6839,0.9848 -15.887,1.8114 -22.5788,1.3028 -3.3499,-0.4629 -4.6672,-2.2867 -6.2169,-3.8072 l -12.9903,18.4524 57.2507,-2.7706 0.1,-14.7687 z"
id="path238"
style="fill:none;stroke:#ffffff;stroke-width:0.26840001;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none" /></g><path
d="m 187.367,1086.9849 c -0.0426,-1.0035 -0.868,-1.8049 -1.8822,-1.8049 -0.4156,0 -0.7988,0.1338 -1.1101,0.3606 l -0.489,-0.2324 1.0012,-2.0696 4.3855,2.1152 -0.9958,2.0693 -0.9096,-0.4382 z"
id="path240"
style="fill:#ffffff;fill-opacity:1;fill-rule:nonzero;stroke:none" /><path
d="m 180.2086,1080.3039 c 0.7586,-0.6588 0.8697,-1.8043 0.2379,-2.5971 -0.2591,-0.3243 -0.603,-0.5409 -0.9746,-0.6429 l -0.1227,-0.5276 2.2419,-0.5076 1.0811,4.7477 -2.2393,0.5119 -0.2243,-0.9844 z"
id="path242"
style="fill:#ffffff;fill-opacity:1;fill-rule:nonzero;stroke:none" /><path
d="m 180.9609,1070.5462 c 0.9874,0.182 1.9525,-0.4448 2.1782,-1.4335 0.0927,-0.4045 0.0476,-0.8087 -0.1052,-1.1628 l 0.3365,-0.4243 1.7952,1.4357 -3.039,3.805 -1.7958,-1.4315 0.6301,-0.7886 z"
id="path244"
style="fill:#ffffff;fill-opacity:1;fill-rule:nonzero;stroke:none" /><path
d="m 189.059,1065.0538 c 0.4731,0.8861 1.5653,1.2495 2.4786,0.8095 0.3739,-0.1797 0.6613,-0.4665 0.8438,-0.8064 l 0.5415,0 -0.004,2.2986 -4.8699,0 0,-2.2975 1.0097,6e-4 z"
id="path246"
style="fill:#ffffff;fill-opacity:1;fill-rule:nonzero;stroke:none" /><path
d="m 198.3934,1067.993 c -0.3971,0.9233 -8e-4,2.003 0.9131,2.4426 0.3739,0.1809 0.777,0.2262 1.1562,0.1562 l 0.3391,0.4227 -1.7989,1.4309 -3.0339,-3.8095 1.7954,-1.4326 0.629,0.7897 z"
id="path248"
style="fill:#ffffff;fill-opacity:1;fill-rule:nonzero;stroke:none" /><path
d="m 201.9577,1077.1194 c -0.9688,0.2648 -1.5661,1.2481 -1.3408,2.2366 0.0922,0.4045 0.3079,0.7489 0.5987,1.0023 l -0.1182,0.5284 -2.2399,-0.5148 1.0862,-4.7472 2.2394,0.5103 -0.2254,0.9844 z"
id="path250"
style="fill:#ffffff;fill-opacity:1;fill-rule:nonzero;stroke:none" /><path
d="m 197.0442,1085.5809 c -0.811,-0.5922 -1.9525,-0.4462 -2.5844,0.3469 -0.2588,0.324 -0.394,0.7073 -0.4104,1.0928 l -0.4868,0.2369 -0.9935,-2.0726 4.388,-2.1104 0.9975,2.0695 -0.9104,0.4369 z"
id="path252"
style="fill:#ffffff;fill-opacity:1;fill-rule:nonzero;stroke:none" /><path
d="m 281.6892,1072.3652 c 0.7671,0.8949 1.5449,1.8431 2.3332,2.8442 0.7883,1.0015 1.5554,2.035 2.3012,3.1003 0.7458,1.0653 1.4595,2.0988 2.1413,3.1003 0.682,1.0014 1.2784,1.9496 1.7898,2.8445 l 9.4603,0 c -0.767,-0.9802 -1.6407,-2.0455 -2.6206,-3.196 -0.9802,-1.1506 -1.9922,-2.3228 -3.0365,-3.5159 -1.044,-1.1931 -2.088,-2.3436 -3.132,-3.4517 -1.044,-1.1078 -1.9922,-2.1305 -2.8445,-3.0682 1.0655,-1.0228 2.2161,-2.2479 3.4517,-3.6754 1.2359,-1.4276 2.4503,-2.9084 3.6437,-4.4425 1.1931,-1.5341 2.2903,-3.0682 3.2918,-4.6023 1.0015,-1.5341 1.8003,-2.8979 2.397,-4.091 l -9.0127,0 c -0.5114,1.0653 -1.1827,2.2692 -2.0138,3.6116 -0.8308,1.3423 -1.7149,2.6632 -2.6526,3.9632 -0.9375,1.2997 -1.8856,2.5353 -2.8443,3.7074 -0.959,1.1718 -1.8431,2.1413 -2.653,2.9083 l 0,-14.1905 -7.9262,0 0,43.0189 7.9262,1.3422 0,-26.2074 z"
id="path254"
style="fill:#ffffff;fill-opacity:1;fill-rule:nonzero;stroke:none" /><path
d="m 329.694,1055.2343 c -1.4491,-0.3836 -3.2069,-0.7671 -5.2736,-1.1506 -2.0668,-0.3835 -4.3146,-0.5752 -6.7436,-0.5752 -2.4719,0 -4.4958,0.3408 -6.0727,1.0225 -1.5766,0.682 -2.823,1.6515 -3.7392,2.9086 -0.9161,1.2572 -1.5449,2.738 -1.8859,4.4425 -0.3407,1.7045 -0.5113,3.5796 -0.5113,5.625 l 0,16.7474 7.8624,0 0,-15.7246 c 0,-2.77 0.3302,-4.826 0.9907,-6.1685 0.6605,-1.3422 1.9497,-2.0134 3.8673,-2.0134 1.1931,0 2.3862,0.1066 3.5796,0.3194 l 0,23.5871 7.9263,0 0,-29.0202 z"
id="path256"
style="fill:#ffffff;fill-opacity:1;fill-rule:nonzero;stroke:none" /><path
d="m 355.7095,1069.4248 c 0,2.8125 -0.4581,4.9646 -1.3743,6.4559 -0.9161,1.4916 -2.3119,2.2374 -4.1867,2.2374 -0.8524,0 -1.694,-0.1174 -2.5248,-0.3518 -0.8312,-0.2341 -1.5449,-0.5219 -2.1416,-0.8629 l 0,-16.2997 c 0.5967,-0.1279 1.2251,-0.2132 1.8859,-0.2557 0.6604,-0.043 1.2251,-0.064 1.6937,-0.064 2.0454,0 3.6649,0.6925 4.858,2.0775 1.1931,1.385 1.7898,3.7395 1.7898,7.0634 z m 7.9903,-0.1919 c 0,-2.3862 -0.3198,-4.5491 -0.959,-6.488 -0.6392,-1.9389 -1.5766,-3.59 -2.8125,-4.9538 -1.2359,-1.3637 -2.7485,-2.4182 -4.5383,-3.164 -1.7898,-0.7458 -3.8353,-1.1186 -6.1364,-1.1186 -2.0455,0 -4.1338,0.1491 -6.2643,0.4473 -2.1308,0.2982 -3.9206,0.6818 -5.3694,1.1506 l 0,42.124 7.8622,1.3422 0,-15.0214 c 1.108,0.5114 2.1733,0.8629 3.196,1.0548 1.0228,0.1916 2.0455,0.2877 3.0683,0.2877 1.9604,0 3.6861,-0.373 5.1777,-1.1188 1.4913,-0.7458 2.738,-1.8111 3.7392,-3.1961 1.0015,-1.3847 1.7581,-3.0362 2.2694,-4.9538 0.5114,-1.9176 0.7671,-4.0481 0.7671,-6.3921"
id="path258"
style="fill:#ffffff;fill-opacity:1;fill-rule:nonzero;stroke:none" /><path
d="m 367.5986,1068.9772 c 0,2.6422 0.405,4.9646 1.2146,6.9676 0.8096,2.0026 1.8751,3.6754 3.1961,5.0176 1.3209,1.3425 2.8338,2.3544 4.5382,3.0364 1.7048,0.6818 3.4518,1.0228 5.2416,1.0228 4.4319,0 7.7984,-1.3105 10.0995,-3.9314 2.3012,-2.6206 3.4518,-6.4451 3.4518,-11.4738 0,-0.5114 -0.0108,-1.044 -0.0321,-1.5979 -0.0212,-0.5542 -0.0532,-1.0015 -0.0958,-1.3425 l -19.4958,0 c 0,-1.9601 0.8096,-3.505 2.429,-4.634 1.6192,-1.1294 3.7074,-1.694 6.2643,-1.694 1.5766,0 3.0787,0.1703 4.5062,0.5113 1.4278,0.341 2.6314,0.6818 3.6116,1.0228 l 1.0868,-6.7119 c -1.3637,-0.4686 -2.8125,-0.8629 -4.3466,-1.1823 -1.5341,-0.3198 -3.2601,-0.4794 -5.1778,-0.4794 -2.5568,0 -4.8472,0.3303 -6.8714,0.9907 -2.0242,0.6605 -3.75,1.6407 -5.1775,2.9404 -1.4275,1.2997 -2.5251,2.9084 -3.2922,4.826 -0.767,1.9176 -1.1505,4.1547 -1.1505,6.7116 z m 20.1353,3.1323 c 0,0.8096 -0.1066,1.5874 -0.3198,2.3329 -0.2131,0.7458 -0.5538,1.417 -1.0227,2.0137 -0.4686,0.5964 -1.0653,1.0761 -1.7898,1.438 -0.7242,0.3623 -1.6191,0.5434 -2.6847,0.5434 -1.0227,0 -1.9069,-0.1703 -2.6526,-0.5113 -0.7458,-0.341 -1.3638,-0.8096 -1.8536,-1.4063 -0.4901,-0.5967 -0.8737,-1.2784 -1.1506,-2.0455 -0.2772,-0.767 -0.4581,-1.5553 -0.5434,-2.3649 l 12.0172,0 z"
id="path260"
style="fill:#ffffff;fill-opacity:1;fill-rule:nonzero;stroke:none" /><path
d="m 425.5109,1076.2642 c -0.5113,0.1279 -1.1185,0.2557 -1.8218,0.3835 -0.703,0.1279 -1.4168,0.2452 -2.1413,0.3515 -0.7245,0.1066 -1.4275,0.1812 -2.1092,0.224 -0.6821,0.043 -1.2572,0.064 -1.7261,0.064 -1.1077,0 -2.1945,-0.053 -3.2598,-0.1599 -1.0656,-0.1063 -2.1521,-0.309 -3.2601,-0.6072 l 0,-22.3084 -7.9263,0 0,28.0613 c 2.0883,0.7671 4.2296,1.385 6.4242,1.8539 2.1946,0.4685 4.7622,0.703 7.7026,0.703 0.426,0 1.0332,-0.021 1.8215,-0.064 0.7886,-0.043 1.6302,-0.1174 2.5251,-0.2239 0.8949,-0.1066 1.8003,-0.2345 2.7165,-0.3836 0.9161,-0.1491 1.7365,-0.3515 2.461,-0.6072 l -1.4063,-7.287 z"
id="path262"
style="fill:#ffffff;fill-opacity:1;fill-rule:nonzero;stroke:none" /><path
d="m 433.5647,1083.2956 c 1.4063,0.3835 3.1428,0.7458 5.2098,1.0868 2.0668,0.3407 4.3146,0.5113 6.7436,0.5113 2.3862,0 4.3679,-0.3305 5.9446,-0.991 1.5766,-0.6604 2.8233,-1.5978 3.7394,-2.8125 0.9162,-1.2144 1.5662,-2.6631 1.9497,-4.3466 0.3835,-1.6833 0.5752,-3.5476 0.5752,-5.5931 l 0,-16.939 -7.8622,0 0,15.9163 c 0,2.8125 -0.3198,4.8367 -0.959,6.0726 -0.6392,1.2357 -1.9389,1.8536 -3.899,1.8536 -0.5967,0 -1.1826,-0.021 -1.7578,-0.064 -0.5754,-0.043 -1.1826,-0.1065 -1.8218,-0.1919 l 0,-23.5868 -7.8625,0 0,29.0841 z"
id="path264"
style="fill:#ffffff;fill-opacity:1;fill-rule:nonzero;stroke:none" /><path
d="m 463.4796,1068.9772 c 0,2.6422 0.4051,4.9646 1.2146,6.9676 0.8096,2.0026 1.8752,3.6754 3.1961,5.0176 1.3209,1.3425 2.8338,2.3544 4.5383,3.0364 1.7047,0.6818 3.4517,1.0228 5.2415,1.0228 4.432,0 7.7984,-1.3105 10.0996,-3.9314 2.3011,-2.6206 3.4517,-6.4451 3.4517,-11.4738 0,-0.5114 -0.0108,-1.044 -0.032,-1.5979 -0.0213,-0.5542 -0.0533,-1.0015 -0.0958,-1.3425 l -19.4959,0 c 0,-1.9601 0.8096,-3.505 2.429,-4.634 1.6192,-1.1294 3.7075,-1.694 6.2643,-1.694 1.5767,0 3.0787,0.1703 4.5063,0.5113 1.4278,0.341 2.6314,0.6818 3.6116,1.0228 l 1.0868,-6.7119 c -1.3638,-0.4686 -2.8126,-0.8629 -4.3467,-1.1823 -1.5341,-0.3198 -3.2601,-0.4794 -5.1777,-0.4794 -2.5569,0 -4.8473,0.3303 -6.8715,0.9907 -2.0242,0.6605 -3.7499,1.6407 -5.1775,2.9404 -1.4275,1.2997 -2.5251,2.9084 -3.2921,4.826 -0.7671,1.9176 -1.1506,4.1547 -1.1506,6.7116 z m 20.1353,3.1323 c 0,0.8096 -0.1066,1.5874 -0.3197,2.3329 -0.2132,0.7458 -0.5539,1.417 -1.0228,2.0137 -0.4685,0.5964 -1.0652,1.0761 -1.7898,1.438 -0.7242,0.3623 -1.6191,0.5434 -2.6846,0.5434 -1.0228,0 -1.9069,-0.1703 -2.6527,-0.5113 -0.7458,-0.341 -1.3638,-0.8096 -1.8536,-1.4063 -0.4901,-0.5967 -0.8736,-1.2784 -1.1506,-2.0455 -0.2772,-0.767 -0.458,-1.5553 -0.5434,-2.3649 l 12.0172,0 z"
id="path266"
style="fill:#ffffff;fill-opacity:1;fill-rule:nonzero;stroke:none" /><path
d="m 502.5991,1077.7346 -6.8394,0 0,6.5199 6.8394,0 0,7.5424 7.8625,1.2785 0,-8.8209 12.5923,0 0,-6.5199 -12.5923,0 0,-12.1451 c 0,-1.1081 0.1066,-2.0029 0.3194,-2.6847 0.2132,-0.6817 0.5114,-1.2146 0.8949,-1.5982 0.3836,-0.3835 0.8524,-0.6392 1.4063,-0.767 0.5542,-0.1279 1.1718,-0.1916 1.8539,-0.1916 0.7242,0 1.3955,0.021 2.0134,0.064 0.618,0.043 1.2144,0.1174 1.7898,0.224 0.5752,0.1063 1.1718,0.2662 1.7898,0.4793 0.618,0.2132 1.2889,0.4901 2.0135,0.8309 l 1.0868,-6.7757 c -1.4491,-0.5964 -3.0153,-1.0225 -4.6985,-1.2781 -1.6832,-0.2557 -3.3131,-0.3836 -4.8897,-0.3836 -1.8326,0 -3.4518,0.1491 -4.8581,0.4473 -1.4062,0.2982 -2.5996,0.8737 -3.5795,1.7258 -0.9803,0.8524 -1.7261,2.0562 -2.2374,3.6116 -0.5114,1.5554 -0.7671,3.5904 -0.7671,6.1044 l 0,12.337 z"
id="path268"
style="fill:#ffffff;fill-opacity:1;fill-rule:nonzero;stroke:none" /><path
d="m 527.4003,1068.9772 c 0,2.6422 0.4051,4.9646 1.2146,6.9676 0.8096,2.0026 1.8751,3.6754 3.1961,5.0176 1.3209,1.3425 2.8338,2.3544 4.5383,3.0364 1.7047,0.6818 3.4517,1.0228 5.2415,1.0228 4.432,0 7.7984,-1.3105 10.0996,-3.9314 2.3011,-2.6206 3.4517,-6.4451 3.4517,-11.4738 0,-0.5114 -0.0108,-1.044 -0.032,-1.5979 -0.0213,-0.5542 -0.0533,-1.0015 -0.0958,-1.3425 l -19.4959,0 c 0,-1.9601 0.8096,-3.505 2.429,-4.634 1.6192,-1.1294 3.7075,-1.694 6.2643,-1.694 1.5766,0 3.0787,0.1703 4.5063,0.5113 1.4278,0.341 2.6314,0.6818 3.6116,1.0228 l 1.0868,-6.7119 c -1.3638,-0.4686 -2.8126,-0.8629 -4.3467,-1.1823 -1.5341,-0.3198 -3.2601,-0.4794 -5.1777,-0.4794 -2.5569,0 -4.8473,0.3303 -6.8715,0.9907 -2.0242,0.6605 -3.7499,1.6407 -5.1775,2.9404 -1.4275,1.2997 -2.5251,2.9084 -3.2921,4.826 -0.7671,1.9176 -1.1506,4.1547 -1.1506,6.7116 z m 20.1353,3.1323 c 0,0.8096 -0.1066,1.5874 -0.3197,2.3329 -0.2132,0.7458 -0.5539,1.417 -1.0228,2.0137 -0.4685,0.5964 -1.0652,1.0761 -1.7898,1.438 -0.7242,0.3623 -1.6191,0.5434 -2.6847,0.5434 -1.0227,0 -1.9068,-0.1703 -2.6526,-0.5113 -0.7458,-0.341 -1.3638,-0.8096 -1.8536,-1.4063 -0.4901,-0.5967 -0.8736,-1.2784 -1.1506,-2.0455 -0.2772,-0.767 -0.4581,-1.5553 -0.5434,-2.3649 l 12.0172,0 z"
id="path270"
style="fill:#ffffff;fill-opacity:1;fill-rule:nonzero;stroke:none" /><path
d="m 571.9533,1060.1561 c 1.9176,0 3.3876,0.1386 4.4104,0.4155 1.0227,0.277 1.5341,0.8416 1.5341,1.694 0,0.5539 -0.1599,1.0227 -0.4793,1.4063 -0.3198,0.3835 -0.7671,0.7137 -1.3425,0.9907 -0.5752,0.2769 -1.2357,0.5434 -1.9814,0.7991 -0.7458,0.2557 -1.5234,0.5113 -2.3332,0.767 -1.1506,0.3407 -2.3225,0.735 -3.5156,1.1826 -1.1931,0.4473 -2.2799,1.012 -3.2601,1.6937 -0.9799,0.6821 -1.7898,1.5449 -2.429,2.5889 -0.6392,1.044 -0.9587,2.3545 -0.9587,3.9311 0,1.2784 0.2449,2.4823 0.735,3.6116 0.4901,1.1294 1.2572,2.1305 2.3012,3.0042 1.044,0.8736 2.3757,1.5554 3.9952,2.0455 1.6191,0.4901 3.5583,0.7353 5.8166,0.7353 1.9605,0 3.782,-0.1494 5.4652,-0.4476 1.6835,-0.2982 3.1428,-0.7246 4.3787,-1.2784 l -1.2143,-6.6479 c -0.7246,0.2132 -1.8539,0.5647 -3.388,1.0548 -1.5341,0.4901 -3.2599,0.735 -5.1775,0.735 -2.003,0 -3.3559,-0.2449 -4.0589,-0.735 -0.7033,-0.4901 -1.0548,-1.0122 -1.0548,-1.5661 0,-0.4689 0.1599,-0.8841 0.4793,-1.2464 0.3198,-0.3623 0.7458,-0.6925 1.2785,-0.9907 0.5326,-0.2985 1.1506,-0.5859 1.8538,-0.8632 0.703,-0.2769 1.4593,-0.5431 2.2692,-0.7988 1.1505,-0.3835 2.3437,-0.8098 3.5796,-1.2784 1.2356,-0.4689 2.3544,-1.0548 3.3556,-1.7578 1.0015,-0.7032 1.8218,-1.5981 2.461,-2.6847 0.6393,-1.0868 0.959,-2.4185 0.959,-3.9951 0,-1.2359 -0.2344,-2.4078 -0.7033,-3.5158 -0.4688,-1.1078 -1.2464,-2.0668 -2.3332,-2.8763 -1.0865,-0.8096 -2.5035,-1.4488 -4.2505,-1.9177 -1.7473,-0.4688 -3.8994,-0.703 -6.4562,-0.703 -2.5994,0 -4.8152,0.2982 -6.6478,0.8946 -1.8323,0.5967 -3.3024,1.1294 -4.4104,1.5982 l 1.2143,6.5838 c 1.4916,-0.5964 3.079,-1.1506 4.7622,-1.662 1.6832,-0.5114 3.3985,-0.767 5.1458,-0.767"
id="path272"
style="fill:#ffffff;fill-opacity:1;fill-rule:nonzero;stroke:none" /></g></svg>

Before

Width:  |  Height:  |  Size: 41 KiB

3
www/master/.bowerrc Normal file
View File

@ -0,0 +1,3 @@
{
"directory": "../../third_party/ui/bower_components"
}

9
www/master/README.md Normal file
View File

@ -0,0 +1,9 @@
### Running the App in Production
master/ directory
This directory contains the source files. You will find the following directorys inside
jade/ This directory contains JADE files. This files need to be compiled into html files to be displayed by a browser
less/ This directory contains the LESS files for the core styles and material styles.
js/ Here you will find pure JS files. All this files are concatenated into the file app.js.

30
www/master/bower.json Normal file
View File

@ -0,0 +1,30 @@
{
"name": "kubernetes-ui",
"description": "UI for Kubernetes",
"version": "0.0.0",
"homepage": "https://github.com/kubernetes-ui/kubernetes-ui",
"license": "Apache",
"private": true,
"dependencies": {
"jquery": "~2.1.3",
"angular": "1.3.x",
"angular-route": "1.3.x",
"angular-css": "1.0.x",
"angular-mocks": "1.3.x",
"angular-animate": "1.3.x",
"angular-aria": "1.3.x",
"angular-material": "0.8.1",
"angularjs-jasmine-matchers": "0.2",
"hammerjs": "~2.0.2",
"angular-cookies": "1.3.x",
"modernizr": "2.8.3",
"ng-lodash": "~0.2.0",
"string-format-js": "~0.1.2",
"sprintf": "~1.0.2",
"jsonpath": "#3f6e79e",
"d3": "~3.5.5",
"d3-context-menu": "",
"angular-json-human": "~1.2.1",
"angular-filter": "~0.5.1"
}
}

View File

@ -0,0 +1,125 @@
Components
==========
A tab in the Kubernetes UI with its set of visualizations is referred to as a *component*. Components are separated from the UI chrome and base data providers to simplify the development of new visualizations. This document provides reference for creation and modification of components.
Each component has its own directory, which contains a manifest file, HTML views, Angular providers, CSS, Less and other assets. Below is the recommended directory structure for a component.
```
foo_component
├── config
├── css
├── img
├── js
│   └── modules
│   ├── controllers
│   ├── directives
│   └── services
├── less
├── pages
├── views
│   └── partials
└── manifest.json
```
###Manifest file
The JSON-formatted manifest file, named ```manifest.json```, is located at the root of a component. Based on the component directory name and the contents of the manifest, the Kubernetes UI automatically adds a tab to the chrome, a dependency on the component's AngularJS module to main AngularJS app and Angular routes for the component.
For example, consider a manifest file at ```master/components/foo_component/manifest.json```:
```
{
"routes": [
{
"url": "/",
"templateUrl": "/components/foo_component/pages/home.html"
},
{
"url": "/kittens",
"templateUrl": "/components/foo_component/pages/kittens.html",
"css": "/components/foo_component/css/kittens.css"
}
]
}
```
From the name of the component directory, the Kubernetes UI
* creates a tab called "Foo Component",
* adds Angular module ```kubernetesApp.components.fooComponent``` to the dependencies of ```kubernetesApp```, and
* defines Angular routes ```/foo_component/``` and ```/foo_component/kittens```.
Every tab links to ```/``` relative to its component, so it is important to always define a ```/``` route.
###Source files
In general, all files located in ```master/components/<component>``` are copied to ```app/components/<component>/``` on each gulp build. This includes (but is not limited to) HTML views, CSS and images. Exceptions to this copy are the ```config``` and ```less``` directories as well as all ```.js``` files.
The sections below describe how the exceptions are built into the UI.
####JavaScript
All JavaScript files located in the ```master/components/<component>/js``` are uglified and concatenated together with the rest of the UI's JavaScript. Once aggregated, the JavaScript file is minified and written to ```app/assets/js/app.js```.
####Configuration
Similar to the [UI-wide configuration](../../README.md#configuration), components can define different configuration for each environment. The gulp task creates the constant ```ENV``` under the ```kubernetesApp.config``` module for configuration, which is an object with a property for the root UI and each component.
For example, a configuration for the ```development``` environment specific to ```foo_component``` would be located at ```master/components/foo_component/config/development.json```. Such a component would access its ```development.json``` configuration verbatim at ```ENV.foo_component```:
```
angular.module('kubernetesApp.components.fooComponent', ['kubernetesApp.config'])
.provider('foo', ...)
.config(['fooProvider', 'ENV', function(fooProvider, ENV) {
// Configure fooProvider using ENV['foo_component'].
});
```
####Less
Like JavaScript, the component's Less files are built into one monolithic CSS file. All top-level Less files located at ```master/components/<component>/less/*.less``` are imported into the main UI's Less file. The result is then minified and copied to ```app/assets/css/app.css```.
Sub-directories of this path are watched for changes, but not directly imported. This is useful for defining common colors, mixins and other functions or variables used by the top-level Less files.
###Appendix
####Manifest schema
```
{
"$schema": "http://json-schema.org/draft-04/schema#",
"type": "object",
"properties": {
"description": {
"type": "string",
"description": "Very brief summary of the component. Use a README.md file for detailed descriptions."
},
"routes": {
"type": "array",
"description": "Angular routes for the component.",
"items": {
"type": "object",
"properties": {
"description": {
"type": "string",
"description": "Short description of the route."
},
"url": {
"type": "string",
"description": "Route location relative to '/<component>'."
},
"templateUrl": {
"type": "string",
"description": "Absolute location of the HTML template."
},
"css": {
"type": "string",
"description": "Absolute location of CSS to use with this route."
}
},
"required": ["url", "templateUrl"]
},
"minItems": 1
}
},
"required": ["routes"]
}
```
Content available under the [CC-By 3.0
license](http://creativecommons.org/licenses/by/3.0/)

View File

View File

@ -0,0 +1 @@
Dashboard Component for Kubernetes WebUI

View File

@ -0,0 +1 @@
{}

View File

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" width="18" height="18" viewBox="0 0 18 18"><path d="M5 8l4 4 4-4z"/></svg>

After

Width:  |  Height:  |  Size: 114 B

View File

@ -0,0 +1,4 @@
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24">
<path d="M7 10l5 5 5-5z"/>
<path d="M0 0h24v24h-24z" fill="none"/>
</svg>

After

Width:  |  Height:  |  Size: 166 B

View File

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" width="18" height="18" viewBox="0 0 18 18"><path d="M14.53 4.53l-1.06-1.06L9 7.94 4.53 3.47 3.47 4.53 7.94 9l-4.47 4.47 1.06 1.06L9 10.06l4.47 4.47 1.06-1.06L10.06 9z"/></svg>

After

Width:  |  Height:  |  Size: 215 B

View File

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><path d="M19 6.41L17.59 5 12 10.59 6.41 5 5 6.41 10.59 12 5 17.59 6.41 19 12 13.41 17.59 19 19 17.59 13.41 12z"/></svg>

After

Width:  |  Height:  |  Size: 202 B

View File

@ -0,0 +1,188 @@
app.controller('cAdvisorController', [
'$scope',
'$routeParams',
'k8sApi',
'lodash',
'cAdvisorService',
'$q',
'$interval',
function($scope, $routeParams, k8sApi, lodash, cAdvisorService, $q, $interval) {
$scope.k8sApi = k8sApi;
$scope.activeMinionDataById = {};
$scope.maxDataByById = {};
$scope.getData = function() {
$scope.loading = true;
k8sApi.getMinions().success(angular.bind(this, function(res) {
$scope.minions = res;
// console.log(res);
var promises = lodash.map(res.items, function(m) { return cAdvisorService.getDataForMinion(m.id); });
$q.all(promises).then(
function(dataArray) {
lodash.each(dataArray, function(data, i) {
var m = res.items[i];
var maxData = maxMemCpuInfo(m.id, data.memoryData, data.cpuData, data.filesystemData);
// console.log("maxData", maxData);
$scope.activeMinionDataById[m.id] =
transformMemCpuInfo(data.memoryData, data.cpuData, data.filesystemData, maxData, m.hostIP)
});
},
function(errorData) {
// console.log("Error: " + errorData);
$scope.loading = false;
});
$scope.loading = false;
})).error(angular.bind(this, this.handleError));
};
function getcAdvisorDataForMinion(m) {
var p = cAdvisorService.getDataForMinion(m.hostIP);
return p;
}
function handleError(data, status, headers, config) {
// console.log("Error (" + status + "): " + data);
$scope.loading = false;
};
// d3
function getColorForIndex(i, percentage) {
// var colors = ['red', 'blue', 'yellow', 'pink', 'purple', 'green', 'orange'];
// return colors[i];
var c = "color-" + (i + 1);
if (percentage && percentage >= 90)
c = c + ' color-critical';
else if (percentage && percentage >= 80)
c = c + ' color-warning';
return c;
}
function getMaxColorForIndex(i, percentage) {
// var colors = ['red', 'blue', 'yellow', 'pink', 'purple', 'green', 'orange'];
// return colors[i];
var c = "color-max-" + (i + 1);
if (percentage && percentage >= 90)
c = c + ' color-max-critical';
else if (percentage && percentage >= 80)
c = c + ' color-max-warning';
return c;
}
function maxMemCpuInfo(mId, mem, cpu, fsDataArray) {
if ($scope.maxDataByById[mId] === undefined) $scope.maxDataByById[mId] = {};
var currentMem = mem.current;
var currentCpu = cpu;
var items = [];
if ($scope.maxDataByById[mId]['cpu'] === undefined ||
$scope.maxDataByById[mId]['cpu'] < currentCpu.cpuPercentUsage) {
// console.log("New max cpu " + mId, $scope.maxDataByById[mId].cpu, currentCpu.cpuPercentUsage);
$scope.maxDataByById[mId]['cpu'] = currentCpu.cpuPercentUsage;
}
items.push({
maxValue: $scope.maxDataByById[mId]['cpu'],
maxTickClassNames: getColorForIndex(0, $scope.maxDataByById[mId]['cpu']),
maxClassNames: getMaxColorForIndex(0, $scope.maxDataByById[mId]['cpu'])
});
var memPercentage = Math.floor((currentMem.memoryUsage * 100.0) / currentMem.memoryLimit);
if ($scope.maxDataByById[mId]['mem'] === undefined || $scope.maxDataByById[mId]['mem'] < memPercentage)
$scope.maxDataByById[mId]['mem'] = memPercentage;
items.push({
maxValue: $scope.maxDataByById[mId]['mem'],
maxTickClassNames: getColorForIndex(1, $scope.maxDataByById[mId]['mem']),
maxClassNames: getMaxColorForIndex(1, $scope.maxDataByById[mId]['mem'])
});
for (var i = 0; i < fsDataArray.length; i++) {
var f = fsDataArray[i];
var fid = 'FS #' + f.filesystemNumber;
if ($scope.maxDataByById[mId][fid] === undefined || $scope.maxDataByById[mId][fid] < f.totalUsage)
$scope.maxDataByById[mId][fid] = f.totalUsage;
items.push({
maxValue: $scope.maxDataByById[mId][fid],
maxTickClassNames: getColorForIndex(2 + i, $scope.maxDataByById[mId][fid]),
maxClassNames: getMaxColorForIndex(2 + i, $scope.maxDataByById[mId][fid])
});
}
// console.log("Max Data is now " + mId, $scope.maxDataByById[mId]);
return items;
}
function transformMemCpuInfo(mem, cpu, fsDataArray, maxData, hostName) {
var currentMem = mem.current;
var currentCpu = cpu;
var items = [];
items.push({
label: 'CPU',
stats: currentCpu.cpuPercentUsage + '%',
value: currentCpu.cpuPercentUsage,
classNames: getColorForIndex(0, currentCpu.cpuPercentUsage),
maxData: maxData[0],
hostName: hostName
});
var memPercentage = Math.floor((currentMem.memoryUsage * 100.0) / currentMem.memoryLimit);
items.push({
label: 'Memory',
stats: currentMem.memoryUsageDescription + ' / ' + currentMem.memoryLimitDescription,
value: memPercentage,
classNames: getColorForIndex(1, memPercentage),
maxData: maxData[1],
hostName: hostName
});
for (var i = 0; i < fsDataArray.length; i++) {
var f = fsDataArray[i];
items.push({
label: 'FS #' + f.filesystemNumber,
stats: f.usageDescription + ' / ' + f.capacityDescription,
value: f.totalUsage,
classNames: getColorForIndex(2 + i, f.totalUsage),
maxData: maxData[2 + i],
hostName: hostName
});
}
var a = [];
var segments = {
segments: items
};
a.push(segments);
return a;
};
// end d3
var promise = $interval($scope.getData, 3000);
// Cancel interval on page changes
$scope.$on('$destroy', function() {
if (angular.isDefined(promise)) {
$interval.cancel(promise);
promise = undefined;
}
});
$scope.getData();
}
]);

View File

@ -0,0 +1,6 @@
/**=========================================================
* Module: Dashboard
* Visualizer for clusters
=========================================================*/
app.controller('DashboardCtrl', ['$scope', function($scope) {}]);

View File

@ -0,0 +1,223 @@
/**=========================================================
* Module: Group
* Visualizer for groups
=========================================================*/
app.controller('GroupCtrl', [
'$scope',
'$route',
'$interval',
'$routeParams',
'k8sApi',
'$rootScope',
'$location',
'lodash',
function($scope, $route, $interval, $routeParams, k8sApi, $rootScope, $location, _) {
'use strict';
$scope.doTheBack = function() { window.history.back(); };
$scope.capitalize = function(s) { return _.capitalize(s); };
$rootScope.doTheBack = $scope.doTheBack;
$scope.resetGroupLayout = function(group) { delete group.settings; };
$scope.handlePath = function(path) {
var parts = path.split("/");
// split leaves an empty string at the beginning.
parts = parts.slice(1);
if (parts.length === 0) {
return;
}
this.handleGroups(parts.slice(1));
};
$scope.getState = function(obj) { return Object.keys(obj)[0]; };
$scope.clearSelector = function(grouping) { $location.path("/dashboard/groups/" + grouping + "/selector/"); };
$scope.changeGroupBy = function() {
var grouping = $scope.selectedGroupBy;
var s = _.clone($location.search());
if ($scope.routeParams.grouping != grouping)
$location.path("/dashboard/groups/" + grouping + "/selector/").search(s);
};
$scope.createBarrier = function(count, callback) {
var barrier = count;
var barrierFunction = angular.bind(this, function(data) {
// JavaScript is single threaded so this is safe.
barrier--;
if (barrier === 0) {
if (callback) {
callback();
}
}
});
return barrierFunction;
};
$scope.handleGroups = function(parts, selector) {
$scope.groupBy = parts;
$scope.loading = true;
$scope.selector = selector;
var args = [];
var type = "";
if (selector && selector.length > 0) {
$scope.selectorPieces = selector.split(",");
var labels = [];
var fields = [];
for (var i = 0; i < $scope.selectorPieces.length; i++) {
var piece = $scope.selectorPieces[i];
if (piece[0] == '$') {
fields.push(piece.slice(2));
} else {
if (piece.indexOf("type=") === 0) {
var labelParts = piece.split("=");
if (labelParts.length > 1) {
type = labelParts[1];
}
} else {
labels.push(piece);
}
}
}
if (labels.length > 0) {
args.push("labels=" + encodeURI(labels.join(",")));
}
if (fields.length > 0) {
args.push("fields=" + encodeURI(fields.join(",")));
}
}
var query = "?" + args.join("&");
var list = [];
var count = type.length > 0 ? 1 : 3;
var barrier = $scope.createBarrier(count, function() {
$scope.groups = $scope.groupData(list, 0);
$scope.loading = false;
$scope.groupByOptions = buildGroupByOptions();
$scope.selectedGroupBy = $routeParams.grouping;
});
if (type === "" || type == "pod") {
k8sApi.getPods(query).success(function(data) {
$scope.addLabel("type", "pod", data.items);
for (var i = 0; data.items && i < data.items.length; ++i) {
data.items[i].labels.host = data.items[i].currentState.host;
list.push(data.items[i]);
}
barrier();
}).error($scope.handleError);
}
if (type === "" || type == "service") {
k8sApi.getServices(query).success(function(data) {
$scope.addLabel("type", "service", data.items);
for (var i = 0; data.items && i < data.items.length; ++i) {
list.push(data.items[i]);
}
barrier();
}).error($scope.handleError);
}
if (type === "" || type == "replicationController") {
k8sApi.getReplicationControllers(query).success(angular.bind(this, function(data) {
$scope.addLabel("type", "replicationController", data.items);
for (var i = 0; data.items && i < data.items.length; ++i) {
list.push(data.items[i]);
}
barrier();
})).error($scope.handleError);
}
};
$scope.addLabel = function(key, value, items) {
if (!items) {
return;
}
for (var i = 0; i < items.length; i++) {
if (!items[i].labels) {
items[i].labels = [];
}
items[i].labels[key] = value;
}
};
$scope.groupData = function(items, index) {
var result = {
"items": {},
"kind": "grouping"
};
for (var i = 0; i < items.length; i++) {
key = items[i].labels[$scope.groupBy[index]];
if (!key) {
key = "";
}
var list = result.items[key];
if (!list) {
list = [];
result.items[key] = list;
}
list.push(items[i]);
}
if (index + 1 < $scope.groupBy.length) {
for (var key in result.items) {
result.items[key] = $scope.groupData(result.items[key], index + 1);
}
}
return result;
};
$scope.getGroupColor = function(type) {
if (type === 'pod') {
return '#6193F0';
} else if (type === 'replicationController') {
return '#E008FE';
} else if (type === 'service') {
return '#7C43FF';
}
};
var groups = $routeParams.grouping;
if (!groups) {
groups = '';
}
$scope.routeParams = $routeParams;
$scope.route = $route;
$scope.handleGroups(groups.split('/'), $routeParams.selector);
$scope.handleError = function(data, status, headers, config) {
console.log("Error (" + status + "): " + data);
$scope_.loading = false;
};
function getDefaultGroupByOptions() { return [{name: 'Type', value: 'type'}, {name: 'Name', value: 'name'}]; }
function buildGroupByOptions() {
var g = $scope.groups;
var options = getDefaultGroupByOptions();
var newOptions = _.map(g.items, function(vals) { return _.map(vals, function(v) { return _.keys(v.labels); }); });
newOptions =
_.reject(_.uniq(_.flattenDeep(newOptions)), function(o) { return o == 'name' || o == 'type' || o == ""; });
newOptions = _.map(newOptions, function(o) {
return {
name: o,
value: o
};
});
options = options.concat(newOptions);
return options;
}
$scope.changeFilterBy = function(selector) {
var grouping = $scope.selectedGroupBy;
var s = _.clone($location.search());
if ($scope.routeParams.selector != selector)
$location.path("/dashboard/groups/" + $scope.routeParams.grouping + "/selector/" + selector).search(s);
};
}
]);

View File

@ -0,0 +1,27 @@
/**=========================================================
* Module: Header
* Visualizer for clusters
=========================================================*/
angular.module('kubernetesApp.components.dashboard', [])
.controller('HeaderCtrl', [
'$scope',
'$location',
function($scope, $location) {
'use strict';
$scope.$watch('Pages', function(newValue, oldValue) {
if (typeof newValue !== 'undefined') {
$location.path(newValue);
}
});
$scope.subPages = [
{category: 'dashboard', name: 'Explore', value: '/dashboard/groups/type/selector/'},
{category: 'dashboard', name: 'Pods', value: '/dashboard/pods'},
{category: 'dashboard', name: 'Minions', value: '/dashboard/minions'},
{category: 'dashboard', name: 'Replication Controllers', value: '/dashboard/replicationcontrollers'},
{category: 'dashboard', name: 'Services', value: '/dashboard/services'},
{category: 'dashboard', name: 'Events', value: '/dashboard/events'}
];
}
]);

View File

@ -0,0 +1,85 @@
/**=========================================================
* Module: List Events
* Visualizer list events
=========================================================*/
app.controller('ListEventsCtrl', [
'$scope',
'$routeParams',
'k8sApi',
'$location',
'$filter',
function($scope, $routeParams, k8sApi, $location, $filter) {
'use strict';
$scope.getData = getData;
$scope.loading = true;
$scope.k8sApi = k8sApi;
$scope.pods = null;
$scope.groupedPods = null;
$scope.serverView = false;
$scope.headers = [
{name: 'Time', field: 'time'},
{name: 'From', field: 'from'},
{name: 'Sub Object Path', field: 'subobject'},
{name: 'Reason', field: 'reason'},
{name: 'Message', field: 'message'}
];
$scope.custom = {
time: '',
from: 'grey',
subobject: 'grey',
reason: 'grey',
message: 'grey'
};
$scope.sortable = ['time', 'from', 'subobject'];
$scope.thumbs = 'thumb';
$scope.count = 10;
$scope.go = function(d) { $location.path('/dashboard/pods/' + d.id); };
$scope.moreClick = function(d, e) {
$location.path('/dashboard/pods/' + d.id);
e.stopPropagation();
};
function handleError(data, status, headers, config) {
console.log("Error (" + status + "): " + data);
$scope.loading = false;
}
$scope.content = [];
function getData(dataId) {
$scope.loading = true;
k8sApi.getEvents().success(function(data) {
$scope.loading = false;
var _fixComma = function(str) {
if (str.substring(0, 1) == ',') {
return str.substring(1);
} else {
return str;
}
};
data.items.forEach(function(event) {
$scope.content.push({
time: $filter('date')(event.timestamp, 'medium'),
from: event.source,
subobject: event.involvedObject.fieldPath,
reason: event.reason,
message: event.message
});
});
}).error($scope.handleError);
}
getData($routeParams.serviceId);
}
]);

View File

@ -0,0 +1,76 @@
/**=========================================================
* Module: Minions
* Visualizer for minions
=========================================================*/
app.controller('ListMinionsCtrl', [
'$scope',
'$routeParams',
'k8sApi',
'$location',
function($scope, $routeParams, k8sApi, $location) {
'use strict';
$scope.getData = getData;
$scope.loading = true;
$scope.k8sApi = k8sApi;
$scope.pods = null;
$scope.groupedPods = null;
$scope.serverView = false;
$scope.headers = [{name: 'Name', field: 'name'}, {name: 'IP', field: 'ip'}, {name: 'Status', field: 'status'}];
$scope.custom = {
name: '',
status: 'grey',
ip: 'grey'
};
$scope.sortable = ['name', 'status', 'ip'];
$scope.thumbs = 'thumb';
$scope.count = 10;
$scope.go = function(d) { $location.path('/dashboard/pods/' + d.id); };
$scope.moreClick = function(d, e) {
$location.path('/dashboard/pods/' + d.id);
e.stopPropagation();
};
function handleError(data, status, headers, config) {
console.log("Error (" + status + "): " + data);
$scope.loading = false;
}
$scope.content = [];
function getData(dataId) {
$scope.loading = true;
k8sApi.getMinions().success(function(data) {
$scope.loading = false;
var _fixComma = function(str) {
if (str.substring(0, 1) == ',') {
return str.substring(1);
} else {
return str;
}
};
data.items.forEach(function(minion) {
var _kind = '';
if (minion.status.conditions) {
Object.keys(minion.status.conditions)
.forEach(function(key) { _kind += minion.status.conditions[key].kind; });
}
$scope.content.push({name: minion.id, ip: minion.hostIP, status: _kind});
});
}).error($scope.handleError);
}
getData($routeParams.serviceId);
}
]);

View File

@ -0,0 +1,137 @@
app.controller('ListPodsCtrl', [
'$scope',
'$routeParams',
'k8sApi',
'lodash',
'$location',
function($scope, $routeParams, k8sApi, lodash, $location) {
var _ = lodash;
$scope.getData = getData;
$scope.loading = true;
$scope.k8sApi = k8sApi;
$scope.pods = null;
$scope.groupedPods = null;
$scope.serverView = false;
$scope.headers = [
{name: '', field: 'thumb'},
{name: 'Pod', field: 'pod'},
{name: 'IP', field: 'ip'},
{name: 'Status', field: 'status'},
{name: 'Containers', field: 'containers'},
{name: 'Images', field: 'images'},
{name: 'Host', field: 'host'},
{name: 'Labels', field: 'labels'}
];
$scope.custom = {
pod: '',
ip: 'grey',
containers: 'grey',
images: 'grey',
host: 'grey',
labels: 'grey',
status: 'grey'
};
$scope.sortable = ['pod', 'ip', 'status'];
$scope.thumbs = 'thumb';
$scope.count = 10;
$scope.go = function(d) { $location.path('/dashboard/pods/' + d.id); };
$scope.moreClick = function(d, e) {
$location.path('/dashboard/pods/' + d.id);
e.stopPropagation();
};
var orderedPodNames = [];
function handleError(data, status, headers, config) {
console.log("Error (" + status + "): " + data);
$scope.loading = false;
};
function getPodName(pod) { return _.has(pod.labels, 'name') ? pod.labels.name : pod.id; }
$scope.content = [];
function getData(dataId) {
$scope.loading = true;
k8sApi.getPods().success(angular.bind(this, function(data) {
$scope.loading = false;
var _fixComma = function(str) {
if (str.substring(0, 1) == ',') {
return str.substring(1);
} else {
return str;
}
};
data.items.forEach(function(pod) {
var _containers = '', _images = '', _labels = '', _uses = '';
if (pod.desiredState.manifest) {
Object.keys(pod.desiredState.manifest.containers)
.forEach(function(key) {
_containers += ', ' + pod.desiredState.manifest.containers[key].name;
_images += ', ' + pod.desiredState.manifest.containers[key].image;
});
}
Object.keys(pod.labels)
.forEach(function(key) {
if (key == 'name') {
_labels += ', ' + pod.labels[key];
}
if (key == 'uses') {
_uses += ', ' + pod.labels[key];
}
});
$scope.content.push({
thumb: '"assets/img/kubernetes.svg"',
pod: pod.id,
ip: pod.currentState.podIP,
containers: _fixComma(_containers),
images: _fixComma(_images),
host: pod.currentState.host,
labels: _fixComma(_labels) + ':' + _fixComma(_uses),
status: pod.currentState.status
});
});
})).error(angular.bind(this, handleError));
};
$scope.getPodRestarts = function(pod) {
var r = null;
var container = _.first(pod.desiredState.manifest.containers);
if (container) r = pod.currentState.info[container.name].restartCount;
return r;
};
$scope.otherLabels = function(labels) { return _.omit(labels, 'name') };
$scope.podStatusClass = function(pod) {
var s = pod.currentState.status.toLowerCase();
if (s == 'running' || s == 'succeeded')
return null;
else
return "status-" + s;
};
$scope.podIndexFromName = function(pod) {
var name = getPodName(pod);
return _.indexOf(orderedPodNames, name) + 1;
};
getData($routeParams.serviceId);
}
]);

View File

@ -0,0 +1,101 @@
/**=========================================================
* Module: Replication Controllers
* Visualizer for replication controllers
=========================================================*/
app.controller('ListReplicationControllersCtrl', [
'$scope',
'$routeParams',
'k8sApi',
'$location',
function($scope, $routeParams, k8sApi, $location) {
'use strict';
$scope.getData = getData;
$scope.loading = true;
$scope.k8sApi = k8sApi;
$scope.pods = null;
$scope.groupedPods = null;
$scope.serverView = false;
$scope.headers = [
{name: 'Controller', field: 'controller'},
{name: 'Containers', field: 'containers'},
{name: 'Images', field: 'images'},
{name: 'Selector', field: 'selector'},
{name: 'Replicas', field: 'replicas'}
];
$scope.custom = {
controller: '',
containers: 'grey',
images: 'grey',
selector: 'grey',
replicas: 'grey'
};
$scope.sortable = ['controller', 'containers', 'images'];
$scope.thumbs = 'thumb';
$scope.count = 10;
$scope.go = function(d) { $location.path('/dashboard/pods/' + d.id); };
$scope.moreClick = function(d, e) {
$location.path('/dashboard/pods/' + d.id);
e.stopPropagation();
};
function handleError(data, status, headers, config) {
console.log("Error (" + status + "): " + data);
$scope.loading = false;
}
$scope.content = [];
function getData(dataId) {
$scope.loading = true;
k8sApi.getReplicationControllers().success(function(data) {
$scope.loading = false;
var _fixComma = function(str) {
if (str.substring(0, 1) == ',') {
return str.substring(1);
} else {
return str;
}
};
data.items.forEach(function(replicationController) {
var _name = '', _image = '';
if (replicationController.desiredState.podTemplate.desiredState.manifest.containers) {
Object.keys(replicationController.desiredState.podTemplate.desiredState.manifest.containers)
.forEach(function(key) {
_name += replicationController.desiredState.podTemplate.desiredState.manifest.containers[key].name;
_image += replicationController.desiredState.podTemplate.desiredState.manifest.containers[key].image;
});
}
var _name_selector = '';
if (replicationController.desiredState.replicaSelector) {
Object.keys(replicationController.desiredState.replicaSelector)
.forEach(function(key) { _name_selector += replicationController.desiredState.replicaSelector[key]; });
}
$scope.content.push({
controller: replicationController.id,
containers: _name,
images: _image,
selector: _name_selector,
replicas: replicationController.currentState.replicas
});
});
}).error($scope.handleError);
}
getData($routeParams.serviceId);
}
]);

View File

@ -0,0 +1,110 @@
/**=========================================================
* Module: Services
* Visualizer for services
=========================================================*/
app.controller('ListServicesCtrl', [
'$scope',
'$interval',
'$routeParams',
'k8sApi',
'$rootScope',
function($scope, $interval, $routeParams, k8sApi, $rootScope) {
'use strict';
$scope.doTheBack = function() { window.history.back(); };
$scope.headers = [
{name: 'Name', field: 'name'},
{name: 'Labels', field: 'labels'},
{name: 'Selector', field: 'selector'},
{name: 'IP', field: 'ip'},
{name: 'Port', field: 'port'}
];
$scope.custom = {
name: '',
ip: 'grey',
selector: 'grey',
port: 'grey',
labels: 'grey'
};
$scope.sortable = ['name', 'ip', 'port'];
$scope.count = 10;
$scope.content = [];
$rootScope.doTheBack = $scope.doTheBack;
$scope.handleError = function(data, status, headers, config) {
console.log("Error (" + status + "): " + data);
$scope_.loading = false;
};
$scope.getData = function(dataId) {
$scope.loading = true;
k8sApi.getServices(dataId).success(angular.bind(this, function(data) {
$scope.services = data;
$scope.loading = false;
var _fixComma = function(str) {
if (str.substring(0, 1) == ',') {
return str.substring(1);
} else {
return str;
}
};
var addLabel = function(str, label) {
if (str) {
str = label + str;
}
return str;
};
if (data.items.constructor === Array) {
data.items.forEach(function(service) {
var _name = '', _uses = '', _component = '', _provider = '';
if (service.labels !== null && typeof service.labels === 'object') {
Object.keys(service.labels)
.forEach(function(key) {
if (key == 'name') {
_name += ',' + service.labels[key];
}
if (key == 'component') {
_component += ',' + service.labels[key];
}
if (key == 'provider') {
_provider += ',' + service.labels[key];
}
});
}
var _selectors = '';
if (service.selector !== null && typeof service.selector === 'object') {
Object.keys(service.selector)
.forEach(function(key) {
if (key == 'name') {
_selectors += ',' + service.selector[key];
}
});
}
$scope.content.push({
name: service.id,
ip: service.portalIP,
port: service.port,
selector: addLabel(_fixComma(_selectors), 'name='),
labels: addLabel(_fixComma(_name), 'name=') + ' ' + addLabel(_fixComma(_component), 'component=') + ' ' +
addLabel(_fixComma(_provider), 'provider=')
});
});
}
})).error($scope.handleError);
};
$scope.getData($routeParams.serviceId);
}
]);

View File

@ -0,0 +1,33 @@
/**=========================================================
* Module: Pods
* Visualizer for pods
=========================================================*/
app.controller('PodCtrl', [
'$scope',
'$interval',
'$routeParams',
'k8sApi',
'$rootScope',
function($scope, $interval, $routeParams, k8sApi, $rootScope) {
'use strict';
$scope.doTheBack = function() { window.history.back(); };
$rootScope.doTheBack = $scope.doTheBack;
$scope.handleError = function(data, status, headers, config) {
console.log("Error (" + status + "): " + data);
$scope_.loading = false;
};
$scope.handlePod = function(podId) {
$scope.loading = true;
k8sApi.getPods(podId).success(angular.bind(this, function(data) {
$scope.pod = data;
$scope.loading = false;
})).error($scope.handleError);
};
$scope.handlePod($routeParams.podId);
}
]);

View File

@ -0,0 +1,32 @@
/**=========================================================
* Module: Replication
* Visualizer for replication controllers
=========================================================*/
function ReplicationController() {
}
ReplicationController.prototype.getData = function(dataId) {
this.scope.loading = true;
this.k8sApi.getReplicationControllers(dataId).success(angular.bind(this, function(data) {
this.scope.replicationController = data;
this.scope.loading = false;
})).error(angular.bind(this, this.handleError));
};
ReplicationController.prototype.handleError = function(data, status, headers, config) {
console.log("Error (" + status + "): " + data);
this.scope.loading = false;
};
app.controller('ReplicationControllerCtrl', [
'$scope',
'$routeParams',
'k8sApi',
function($scope, $routeParams, k8sApi) {
$scope.controller = new ReplicationController();
$scope.controller.k8sApi = k8sApi;
$scope.controller.scope = $scope;
$scope.controller.getData($routeParams.replicationControllerId);
}
]);

View File

@ -0,0 +1,40 @@
/**=========================================================
* Module: Services
* Visualizer for services
=========================================================*/
function ServiceController() {
}
ServiceController.prototype.getData = function(dataId) {
this.scope.loading = true;
this.k8sApi.getServices(dataId).success(angular.bind(this, function(data) {
this.scope.service = data;
this.scope.loading = false;
})).error(angular.bind(this, this.handleError));
};
ServiceController.prototype.handleError = function(data, status, headers, config) {
console.log("Error (" + status + "): " + data);
this.scope.loading = false;
};
app.controller('ServiceCtrl', [
'$scope',
'$routeParams',
'k8sApi',
'$location',
function($scope, $routeParams, k8sApi, $location) {
$scope.controller = new ServiceController();
$scope.controller.k8sApi = k8sApi;
$scope.controller.scope = $scope;
$scope.controller.getData($routeParams.serviceId);
$scope.go = function(d) { $location.path('/dashboard/services/' + d.id); }
$scope.moreClick = function(d, e) {
$location.path('/dashboard/services/' + d.id);
e.stopPropagation();
}
}
]);

View File

@ -0,0 +1,346 @@
(function() {
'use strict';
angular.module('kubernetesApp.components.dashboard')
.directive('d3MinionBarGauge', [
'd3DashboardService',
function(d3DashboardService) {
return {
restrict: 'E',
scope: {
data: '=',
thickness: '@',
graphWidth: '@',
graphHeight: '@'
},
link: function(scope, element, attrs) {
var draw = function(d3) {
var svg = d3.select("svg.chart");
var legendSvg = d3.select("svg.legend");
window.onresize = function() { return scope.$apply(); };
scope.$watch(function() { return angular.element(window)[0].innerWidth; },
function() { return scope.render(scope.data); });
scope.$watch('data', function(newVals, oldVals) {
return initOrUpdate(newVals, oldVals);
}, true);
function initOrUpdate(newVals, oldVals) {
if (oldVals === null || oldVals === undefined) {
return scope.render(newVals);
} else {
return update(oldVals, newVals);
}
}
var textOffset = 10;
var el = null;
var radius = 100;
var oldData = [];
function init(options) {
var clone = options.data;
var preparedData = setData(clone);
setup(preparedData, options.width, options.height);
}
function setup(data, w, h) {
svg = d3.select(element[0]).append("svg").attr("width", "100%");
legendSvg = d3.select(element[0]).append("svg").attr("width", "100%");
var chart = svg.attr("class", "chart")
.attr("width", w)
.attr("height", h - 25)
.append("svg:g")
.attr("class", "concentricchart")
.attr("transform", "translate(" + ((w / 2)) + "," + h / 4 + ")");
var legend = legendSvg.attr("class", "legend").attr("width", w);
radius = Math.min(w, h) / 2;
var hostName = legendSvg.append("text")
.attr("class", "hostName")
.attr("transform", "translate(" + ((w - 120) / 2) + "," + 15 + ")");
var label_legend_area = legendSvg.append("svg:g")
.attr("class", "label_legend_area")
.attr("transform", "translate(" + ((w - 185) / 2) + "," + 35 + ")");
var legend_group = label_legend_area.append("svg:g").attr("class", "legend_group");
var label_group = label_legend_area.append("svg:g")
.attr("class", "label_group")
.attr("transform", "translate(" + 25 + "," + 11 + ")");
var stats_group = label_legend_area.append("svg:g")
.attr("class", "stats_group")
.attr("transform", "translate(" + 85 + "," + 11 + ")");
var path_group = chart.append("svg:g")
.attr("class", "path_group")
.attr("transform", "translate(0," + (h / 4) + ")");
var value_group = chart.append("svg:g")
.attr("class", "value_group")
.attr("transform", "translate(" + -(w * 0.205) + "," + -(h * 0.10) + ")");
generateArcs(chart, data);
}
function update(_oldData, _newData) {
if (_newData === undefined || _newData === null) {
return;
}
var clone = jQuery.extend(true, {}, _newData);
var cloneOld = jQuery.extend(true, {}, _oldData);
var preparedData = setData(clone);
oldData = setData(cloneOld);
animate(preparedData);
}
function animate(data) { generateArcs(null, data); }
function setData(data) {
var diameter = 2 * Math.PI * radius;
var localData = [];
$.each(data[0].segments, function(ri, value) {
function calcAngles(v) {
var segmentValueSum = 200;
if (v > segmentValueSum) {
v = segmentValueSum;
}
var segmentValue = v;
var fraction = segmentValue / segmentValueSum;
var arcBatchLength = fraction * 4 * Math.PI;
var arcPartition = arcBatchLength;
var startAngle = Math.PI * 2;
var endAngle = startAngle + arcPartition;
return {
startAngle: startAngle,
endAngle: endAngle
};
}
var valueData = calcAngles(value.value);
data[0].segments[ri].startAngle = valueData.startAngle;
data[0].segments[ri].endAngle = valueData.endAngle;
var maxData = value.maxData;
var maxTickData = calcAngles(maxData.maxValue + 0.2);
data[0].segments[ri].maxTickStartAngle = maxTickData.startAngle;
data[0].segments[ri].maxTickEndAngle = maxTickData.endAngle;
var maxArcData = calcAngles(maxData.maxValue);
data[0].segments[ri].maxArcStartAngle = maxArcData.startAngle;
data[0].segments[ri].maxArcEndAngle = maxArcData.endAngle;
data[0].segments[ri].index = ri;
});
localData.push(data[0].segments);
return localData[0];
}
function generateArcs(_svg, data) {
var chart = svg;
var transitionTime = 750;
$.each(data, function(index, value) {
if (oldData[index] !== undefined) {
data[index].previousEndAngle = oldData[index].endAngle;
} else {
data[index].previousEndAngle = 0;
}
});
var thickness = parseInt(scope.thickness, 10);
var ir = (parseInt(scope.graphWidth, 10) / 3);
var path_group = svg.select('.path_group');
var arc_group = path_group.selectAll(".arc_group").data(data);
var arcEnter = arc_group.enter().append("g").attr("class", "arc_group");
arcEnter.append("path").attr("class", "bg-circle").attr("d", getBackgroundArc(thickness, ir));
arcEnter.append("path")
.attr("class", function(d, i) { return 'max_tick_arc ' + d.maxData.maxTickClassNames; });
arcEnter.append("path")
.attr("class", function(d, i) { return 'max_bg_arc ' + d.maxData.maxClassNames; });
arcEnter.append("path").attr("class", function(d, i) { return 'value_arc ' + d.classNames; });
var max_tick_arc = arc_group.select(".max_tick_arc");
max_tick_arc.transition()
.attr("class", function(d, i) { return 'max_tick_arc ' + d.maxData.maxTickClassNames; })
.attr("d", function(d) {
var arc = maxArc(thickness, ir);
arc.startAngle(d.maxTickStartAngle);
arc.endAngle(d.maxTickEndAngle);
return arc(d);
});
var max_bg_arc = arc_group.select(".max_bg_arc");
max_bg_arc.transition()
.attr("class", function(d, i) { return 'max_bg_arc ' + d.maxData.maxClassNames; })
.attr("d", function(d) {
var arc = maxArc(thickness, ir);
arc.startAngle(d.maxArcStartAngle);
arc.endAngle(d.maxArcEndAngle);
return arc(d);
});
var value_arc = arc_group.select(".value_arc");
value_arc.transition().ease("exp").attr("class", function(d, i) {
return 'value_arc ' + d.classNames;
}).duration(transitionTime).attrTween("d", function(d) { return arcTween(d, thickness, ir); });
arc_group.exit()
.select(".value_arc")
.transition()
.ease("exp")
.duration(transitionTime)
.attrTween("d", function(d) { return arcTween(d, thickness, ir); })
.remove();
drawLabels(chart, data, ir, thickness);
buildLegend(chart, data);
}
function arcTween(b, thickness, ir) {
var prev = JSON.parse(JSON.stringify(b));
prev.endAngle = b.previousEndAngle;
var i = d3.interpolate(prev, b);
return function(t) { return getArc(thickness, ir)(i(t)); };
}
function maxArc(thickness, ir) {
var arc = d3.svg.arc().innerRadius(function(d) {
return getRadiusRing(ir, d.index);
}).outerRadius(function(d) { return getRadiusRing(ir + thickness, d.index); });
return arc;
}
function drawLabels(chart, data, ir, thickness) {
svg.select('.value_group').selectAll("*").remove();
var counts = data.length;
var value_group = chart.select('.value_group');
var valueLabels = value_group.selectAll("text.value").data(data);
valueLabels.enter()
.append("svg:text")
.attr("class", "value")
.attr(
"transform", function(d) { return "translate(" + (getRadiusRing(ir, counts - 1)) + ", 0)"; })
.attr("dx", function(d, i) { return 0; })
.attr("dy", function(d, i) { return (thickness + 3) * i; })
.attr("text-anchor", function(d) { return "start"; })
.text(function(d) { return d.value; });
valueLabels.transition().duration(300).attrTween(
"d", function(d) { return arcTween(d, thickness, ir); });
valueLabels.exit().remove();
}
function buildLegend(chart, data) {
var svg = legendSvg;
svg.select('.label_group').selectAll("*").remove();
svg.select('.legend_group').selectAll("*").remove();
svg.select('.stats_group').selectAll("*").remove();
var host_name = svg.select('.hostName');
var label_group = svg.select('.label_group');
var stats_group = svg.select('.stats_group');
host_name.text(data[0].hostName);
host_name = svg.selectAll("text.hostName").data(data);
host_name.attr("text-anchor", function(d) { return "start"; })
.text(function(d) { return d.hostName; });
host_name.exit().remove();
var labels = label_group.selectAll("text.labels").data(data);
labels.enter()
.append("svg:text")
.attr("class", "labels")
.attr("dy", function(d, i) { return 19 * i; })
.attr("text-anchor", function(d) { return "start"; })
.text(function(d) { return d.label; });
labels.exit().remove();
var stats = stats_group.selectAll("text.stats").data(data);
stats.enter()
.append("svg:text")
.attr("class", "stats")
.attr("dy", function(d, i) { return 19 * i; })
.attr("text-anchor", function(d) { return "start"; })
.text(function(d) { return d.stats; });
stats.exit().remove();
var legend_group = svg.select('.legend_group');
var legend = legend_group.selectAll("rect").data(data);
legend.enter()
.append("svg:rect")
.attr("x", 2)
.attr("y", function(d, i) { return 19 * i; })
.attr("width", 13)
.attr("height", 13)
.attr("class", function(d, i) { return "rect " + d.classNames; });
legend.exit().remove();
}
function getRadiusRing(ir, i) { return ir - (i * 20); }
function getArc(thickness, ir) {
var arc = d3.svg.arc()
.innerRadius(function(d) { return getRadiusRing(ir, d.index); })
.outerRadius(function(d) { return getRadiusRing(ir + thickness, d.index); })
.startAngle(function(d, i) { return d.startAngle; })
.endAngle(function(d, i) { return d.endAngle; });
return arc;
}
function getBackgroundArc(thickness, ir) {
var arc = d3.svg.arc()
.innerRadius(function(d) { return getRadiusRing(ir, d.index); })
.outerRadius(function(d) { return getRadiusRing(ir + thickness, d.index); })
.startAngle(0)
.endAngle(function() { return 2 * Math.PI; });
return arc;
}
scope.render = function(data) {
if (data === undefined || data === null) {
return;
}
svg.selectAll("*").remove();
var graph = $(element[0]);
var w = scope.graphWidth;
var h = scope.graphHeight;
var options = {
data: data,
width: w,
height: h
};
init(options);
};
};
d3DashboardService.d3().then(draw);
}
};
}
]);
}());

View File

@ -0,0 +1,101 @@
(function() {
'use strict';
angular.module('kubernetesApp.components.dashboard')
.directive(
'dashboardHeader',
function() {
'use strict';
return {
restrict: 'A',
replace: true,
scope: {user: '='},
templateUrl: "components/dashboard/pages/header.html",
controller: [
'$scope',
'$filter',
'$location',
'$rootScope',
function($scope, $filter, $location, $rootScope) {
$scope.$watch('page', function(newValue, oldValue) {
if (typeof newValue !== 'undefined') {
$location.path(newValue);
}
});
$scope.subpages = [
{
category: 'dashboard',
name: 'Groups',
value: '/dashboard/groups/type/selector/',
id: 'groupsView'
},
{category: 'dashboard', name: 'Pods', value: '/dashboard/pods', id: 'podsView'},
{category: 'dashboard', name: 'Minions', value: '/dashboard/minions', id: 'minionsView'},
{
category: 'dashboard',
name: 'Replication Controllers',
value: '/dashboard/replicationcontrollers',
id: 'rcView'
},
{category: 'dashboard', name: 'Services', value: '/dashboard/services', id: 'servicesView'},
{category: 'dashboard', name: 'Events', value: '/dashboard/events', id: 'eventsView'},
];
}
]
};
})
.directive('dashboardFooter',
function() {
'use strict';
return {
restrict: 'A',
replace: true,
templateUrl: "components/dashboard/pages/footer.html",
controller: ['$scope', '$filter', function($scope, $filter) {}]
};
})
.directive('mdTable', function() {
'use strict';
return {
restrict: 'E',
scope: {
headers: '=',
content: '=',
sortable: '=',
filters: '=',
customClass: '=customClass',
thumbs: '=',
count: '='
},
controller: function($scope, $filter, $window, $location) {
var orderBy = $filter('orderBy');
$scope.currentPage = 0;
$scope.nbOfPages = function() { return Math.ceil($scope.content.length / $scope.count); };
$scope.handleSort = function(field) {
if ($scope.sortable.indexOf(field) > -1) {
return true;
} else {
return false;
}
};
$scope.go = function(d) {
if (d.pod) {
$location.path('/dashboard/pods/' + d.pod);
} else if (d.name) {
$location.path('/dashboard/services/' + d.name);
}
};
$scope.order = function(predicate, reverse) {
$scope.content = orderBy($scope.content, predicate, reverse);
$scope.predicate = predicate;
};
$scope.order($scope.sortable[0], false);
$scope.getNumber = function(num) { return new Array(num); };
$scope.goToPage = function(page) { $scope.currentPage = page; };
},
templateUrl: 'views/partials/md-table.tmpl.html'
};
});
}());

View File

@ -0,0 +1,31 @@
angular.module('kubernetesApp.components.dashboard')
.factory('d3DashboardService', [
'$document',
'$q',
'$rootScope',
function($document, $q, $rootScope) {
var d = $q.defer();
function onScriptLoad() {
// Load client in the browser
$rootScope.$apply(function() { d.resolve(window.d3); });
}
// Create a script tag with d3 as the source
// and call our onScriptLoad callback when it
// has been loaded
var scriptTag = $document[0].createElement('script');
scriptTag.type = 'text/javascript';
scriptTag.async = true;
scriptTag.src = 'vendor/d3/d3.min.js';
scriptTag.onreadystatechange = function() {
if (this.readyState == 'complete') onScriptLoad();
};
scriptTag.onload = onScriptLoad;
var s = $document[0].getElementsByTagName('body')[0];
s.appendChild(scriptTag);
return {
d3: function() { return d.promise; }
};
}
]);

View File

@ -0,0 +1,76 @@
(function() {
'use strict';
angular.module('pods', []).service('podService', PodDataService);
/**
* Pod DataService
* Mock async data service.
*
* @returns {{loadAll: Function}}
* @constructor
*/
function PodDataService($q) {
var pods = {
"kind": "PodList",
"creationTimestamp": null,
"selfLink": "/api/v1beta1/pods",
"resourceVersion": 166552,
"apiVersion": "v1beta1",
"items": [{
"id": "hello",
"uid": "0fe3644e-ab53-11e4-8ae8-061695c59fcf",
"creationTimestamp": "2015-02-03T03:16:36Z",
"selfLink": "/api/v1beta1/pods/hello?namespace=default",
"resourceVersion": 466,
"namespace": "default",
"labels": {"environment": "testing", "name": "hello"},
"desiredState": {
"manifest": {
"version": "v1beta2",
"id": "",
"volumes": null,
"containers": [{
"name": "hello",
"image": "quay.io/kelseyhightower/hello",
"ports": [{"hostPort": 80, "containerPort": 80, "protocol": "TCP"}],
"imagePullPolicy": "PullIfNotPresent"
}],
"restartPolicy": {"always": {}},
"dnsPolicy": "ClusterFirst"
}
},
"currentState": {
"manifest": {"version": "", "id": "", "volumes": null, "containers": null, "restartPolicy": {}},
"status": "Running",
"host": "172.31.12.204",
"podIP": "10.244.73.2",
"info": {
"hello": {
"state": {"running": {"startedAt": "2015-02-03T03:16:51Z"}},
"restartCount": 0,
"image": "quay.io/kelseyhightower/hello",
"containerID": "docker://96ade8ff30a44c4489969eaf343a7899317671b07a9766ecd0963e9b41501256"
},
"net": {
"state": {"running": {"startedAt": "2015-02-03T03:16:41Z"}},
"restartCount": 0,
"podIP": "10.244.73.2",
"image": "kubernetes/pause:latest",
"containerID": "docker://93d32603cafbff7165dadb1d4527899c24246bca2f5e6770b8297fd3721b272c"
}
}
}
}]
};
// Uses promises
return {
loadAll: function() {
// Simulate async call
return $q.when(pods);
}
};
}
})();

View File

@ -0,0 +1,33 @@
(function() {
'use strict';
angular.module('replicationControllers', [])
.service('replicationControllerService', ReplicationControllerDataService);
/**
* Replication Controller DataService
* Mock async data service.
*
* @returns {{loadAll: Function}}
* @constructor
*/
function ReplicationControllerDataService($q) {
var replicationControllers = {
"kind": "ReplicationControllerList",
"creationTimestamp": null,
"selfLink": "/api/v1beta1/replicationControllers",
"resourceVersion": 166552,
"apiVersion": "v1beta1",
"items": []
};
// Uses promises
return {
loadAll: function() {
// Simulate async call
return $q.when(replicationControllers);
}
};
}
})();

View File

@ -0,0 +1,63 @@
(function() {
'use strict';
angular.module('services', []).service('serviceService', ServiceDataService);
/**
* Service DataService
* Mock async data service.
*
* @returns {{loadAll: Function}}
* @constructor
*/
function ServiceDataService($q) {
var services = {
"kind": "ServiceList",
"creationTimestamp": null,
"selfLink": "/api/v1beta1/services",
"resourceVersion": 166552,
"apiVersion": "v1beta1",
"items": [
{
"id": "kubernetes",
"uid": "626dd08d-ab51-11e4-8ae8-061695c59fcf",
"creationTimestamp": "2015-02-03T03:04:36Z",
"selfLink": "/api/v1beta1/services/kubernetes?namespace=default",
"resourceVersion": 11,
"namespace": "default",
"port": 443,
"protocol": "TCP",
"labels": {"component": "apiserver", "provider": "kubernetes"},
"selector": null,
"containerPort": 0,
"portalIP": "10.244.66.215",
"sessionAffinity": "None"
},
{
"id": "kubernetes-ro",
"uid": "626f9584-ab51-11e4-8ae8-061695c59fcf",
"creationTimestamp": "2015-02-03T03:04:36Z",
"selfLink": "/api/v1beta1/services/kubernetes-ro?namespace=default",
"resourceVersion": 12,
"namespace": "default",
"port": 80,
"protocol": "TCP",
"labels": {"component": "apiserver", "provider": "kubernetes"},
"selector": null,
"containerPort": 0,
"portalIP": "10.244.182.142",
"sessionAffinity": "None"
}
]
};
// Uses promises
return {
loadAll: function() {
// Simulate async call
return $q.when(services);
}
};
}
})();

View File

@ -0,0 +1,73 @@
.dashboard {
.body-wrapper {
padding: 25px;
}
// analagous to float:right when used with row layout
[flex-align-self="end"] {
-webkit-align-self: flex-end;
-ms-flex-align-self: end;
align-self: flex-end;
}
.back {
font-size: 18px;
line-height: 27px;
margin-bottom: 30px;
}
.heading {
font-size: 18px;
line-height: 21px;
color: #222222;
margin-bottom: 25px;
.label {
color: #777777;
}
}
@import "dashboard/pods";
@import "dashboard/tables";
@import "dashboard/servers";
@import "dashboard/groups";
.detail {
color: #222222;
.back {
font-size: 18px;
line-height: 27px;
margin-bottom: 30px;
}
.heading {
font-size: 18px;
line-height: 21px;
color: #222222;
margin-bottom: 25px;
.label {
color: #777777;
}
}
td.name {
font-size: 14px;
color: #222222;
line-height: 24px;
}
td.value {
margin-left: 50px;
font-size: 14px;
color: #888888;
line-height: 24px;
}
.containerTable {
td {
padding-right: 20px;
}
}
}
}

View File

@ -0,0 +1,122 @@
@color-codes:
#2962FF, //blue-deep
#AA00FF, //purple-deep
#00C853, //green-deep
#304FFE, //indigo-deep
#0091EA, //light-blue-deep
#FF6D00, //orange-deep
#00BFA5, //teal-deep
#C51162, //pink-deep
#64DD17, //light-green-deep
#6200EA, //deep-purple-deep
#FFD600, //yellow-deep
#00B8D4, //cyan-deep
#FFAB00, //amber-deep
#DD2C00, //deep-orange-deep
#2979FF, //blue-med
#D500F9, //purple-med
#00E676, //green-med
#3D5AFE, //indigo-med
#00B0FF, //light-blue-med
#FF9100, //orange-med
#1DE9B6, //teal-med
#F50057, //pink-med
#76FF03, //light-green-med
#651FFF, //deep-purple-med
#FFEA00, //yellow-med
#00E5FF, //cyan-med
#FFC400, //amber-med
#FF3D00, //deep-orange-med
#448AFF, //blue-light
#E040FB, //purple-light
#69F0AE, //green-light
#536DFE, //indigo-light
#40C4FF, //light-blue-light
#FFAB40, //orange-light
#64FFDA, //teal-light
#FF4081, //pink-light
#B2FF59, //light-green-light
#7C4DFF, //deep-purple-light
#FFFF00, //yellow-light
#18FFFF, //cyan-light
#FFD740, //amber-light
#FF6E40; //deep-orange-light
.dark-overlay {
background-color: #292935;
opacity: 0.5;
}
.light-overlay {
background-color: #FFFFFF;
opacity: 0.2;
}
// #D50000, //red-deep
// #FF1744, //red-med
// #FF5252, //red-light
@warningColor: #FF9800;
@criticalColor: #F44336;
@freeColor: #2E2E3B;
@errorColor: #FF1744;
@waitingColor: #DAD462;
.getColorIndex(@i){
@index: @i; //mod(@i - 1, 3) * 7 + round((@i - 1) / 3 - .3);
}
.creatcolorclasses(@i:1) when(@i <= length(@color-codes)) {
.getColorIndex(@i);
.color-@{index} {
background-color: extract(@color-codes, @i);
// border-color: lighten(extract(@color-codes, @i), 20%);
// border-width: 2px;
// border-style: solid;
fill: extract(@color-codes, @i);
stroke: extract(@color-codes, @i);
}
md-grid-list.list-color-@{i} md-grid-tile.colored {
background-color: extract(@color-codes, @i);
// border-color: darken(extract(@color-codes, @i), 10%);
// border-width: 1px;
// border-style: solid;
}
.creatcolorclasses((@i + 1));
}
.creatcolorclasses();
.color-warning {
background-color: @warningColor !important;
border-color: @warningColor !important;
fill: @warningColor !important;
stroke: @warningColor !important;
}
.color-critical {
background-color: @criticalColor !important;
border-color: @criticalColor !important;
fill: @criticalColor !important;
stroke: @criticalColor !important;
}
.status-waiting {
background-color: @freeColor !important;
border-color: @waitingColor !important;
border-width: 2px !important;
border-style: solid !important;
}
.status-succeeded {}
.status-terminated, .status-unknown {
background-color: @errorColor !important;
border-color: darken(@errorColor, 10%) !important;
border-width: 1px !important;
border-style: solid !important;
}

View File

@ -0,0 +1,158 @@
.groups {
font-size: 13px;
.header {
line-height: 21px;
a {
padding-left: 5px;
padding-right: 5px;
}
.selector-area {
.filter-area {
// font-size: 15px;
// padding-left: 20px;
}
.filter-label {
}
.filter-text {
font-size: 13px;
margin-left: 10px;
}
.cancel-button {
width: 18px;
height: 18px;
padding: 0;
&:focus, &:hover {
background-color: none !important;
}
}
.cancel-icon {
width: 15px;
height: 15px;
fill: #777777;
}
}
}
.group-heading {
.label {
// font-size: 18px;
}
}
.select-group-by {
min-width: 110px;
margin-left: 10px;
margin-right: 40px;
.md-select-label {
padding-top: 6px;
font-size: 13px;
}
}
.group-item {
padding-top: 20px;
.filter-button {
height: 18px;
width: 18px;
.filter-icon {
width: 18px;
height: 18px;
// fill: #777777;
}
}
}
.icon-area {
// padding-right: 10px;
min-width: 34px;
.group-icon {
border-radius: 21px;
width: 21px;
height: 21px;
}
}
.group-main-area {
// padding-bottom: 10px;
.subtype {
line-height: 21px;
}
}
md-divider {
margin-top: 40px;
margin-bottom: 30px;
}
.group-name {
padding-top: 10px;
}
.selectFilter {
padding-top: 10px;
margin-right: 30px;
.md-select-label {
border-bottom: none !important;
width: 17px;
min-width: 17px;
padding-right: 0;
}
}
md-select-menu {
min-height: 40px;
max-height: 40px;
}
.group-link-area {
padding-left: 15px;
padding-bottom: 15px;
button {
line-height: 12px;
// line-height: 12px;
}
}
.group-type-circle {
width: 21px;
height: 21px;
}
.group-type-circle span {
}
// .group-type-area .pod-color
// {
// background-color: rgb(57,73,171);
// }
// .group-type-area .replicationController-color
// {
// background-color: rgb(57,73,171);
// }
// .group-type-area .service-color
// {
// background-color: rgb(57,73,171);
// }
md-select {
margin-top: 0px;
}
}

View File

@ -0,0 +1,79 @@
.clear-bg {
background-color: transparent;
}
.list-pods {
.pod-group {
margin: 25px;
md-grid-list {
margin-top: 50px;
color: white;
// Header and footer
figcaption {
width:100%;
}
md-grid-tile-header {
padding-left: 10px;
.labels {
width: 100%;
}
}
md-grid-tile {
transition: all 700ms ease-in 50ms;
}
// inner body
.inner-box {
padding-left: 10px;
padding-right: 10px;
}
// Footer
md-grid-tile-footer {
background: rgba(0, 0, 0, 0.50);
// height: 36px;
.pod-title
{
margin-left: 10px;
}
.pod-host {
text-align: right;
padding-right: 15px;
}
a {
color: white;
}
}
// Restart button
.restarts {
width: 100%;
text-align: right;
padding-right: 10px;
.restart-button {
&, &:not([disabled]):hover, &:not([disabled]):focus, &:hover, &:focus {
background-color: @errorColor;
width:30px;
height:30px;
}
}
}
}
}
.gray {
background: #f5f5f5;
}
@import (multiple) "colors";
}

View File

@ -0,0 +1,125 @@
.server-overview {
@import (multiple) "colors";
.color-1 {
background-color: #512DA8;
border-color: #512DA8;
fill: #512DA8;
stroke: #512DA8;
}
.color-2 {
background-color: #9C27B0;
border-color: #9C27B0;
fill: #9C27B0;
stroke: #9C27B0;
}
.color-3 {
background-color: #00BCD4;
border-color: #00BCD4;
fill: #00BCD4;
stroke: #00BCD4;
}
.color-max-1 {
background-color: lighten(#512DA8, 40%);
border-color: lighten(#512DA8, 40%);
fill: lighten(#512DA8, 40%);
// stroke: lighten(#512DA8, 40%);
}
.color-max-2 {
background-color: lighten(#9C27B0, 40%);
border-color: lighten(#9C27B0, 40%);
fill: lighten(#9C27B0, 40%);
// stroke: lighten(#9C27B0, 40%);
}
.color-max-3 {
background-color: lighten(#00BCD4, 40%);
border-color: lighten(#00BCD4, 40%);
fill: lighten(#00BCD4, 40%);
// stroke: lighten(#00BCD4, 40%);
}
.color-max-warning {
background-color: lighten(@warningColor, 30%) !important;
border-color: lighten(@warningColor, 30%) !important;
fill: lighten(@warningColor, 30%) !important;
// stroke: lighten(@warningColor, 30%) !important;
}
.color-max-critical {
background-color: lighten(@criticalColor, 30%) !important;
border-color: lighten(@criticalColor, 30%) !important;
fill: lighten(@criticalColor, 30%) !important;
// stroke: lighten(@criticalColor, 30%) !important;
}
.max_tick_arc {
stroke: #FFF !important;
}
.concentric {
// float:left;
// border: 0px; /* solid black; */
}
// Chart
.concentricchart {
.bg-circle {
background: #F9F9F9;
fill: #F9F9F9;
stroke: #FFFFFF;
stroke-width: 1px;
}
path {
// stroke: #FFFFFF;
// stroke-width: 1px;
// opacity:0.8;
}
text {
font-size:12px;
font-family: 'Roboto', sans-serif;
}
.value_group {
fill: white;
}
.legend_group rect {
// stroke: #cccccc;
// stroke-width: 1px;
opacity:0.8;
}
}
svg.legend {
height: 115px;
text {
font-size:12px;
font-family: 'Roboto', sans-serif;
}
.hostName {
font-size:16px;
}
.rect {
}
}
.minion-name {
text-align: center;
vertical-align: bottom;
width: 100%;
}
.chart_area {
width: 325px;
height: 425px;
}
}

Some files were not shown because too many files have changed in this diff Show More