From 716d98c39eedb53df6e2cf052115b5cfbcafedaf Mon Sep 17 00:00:00 2001 From: Patrick Reilly Date: Sun, 19 Apr 2015 04:45:14 -0700 Subject: [PATCH] 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 various path fixes make svg path relative work around missing mime type Signed-off-by: Patrick Reilly fix paths fix karma path remove bad protractor test --- .gitignore | 3 + hack/build-ui.sh | 2 +- pkg/ui/datafile.go | 19295 ++++++++++++++-- www/README.md | 121 + www/app/assets/css/app.css | 1508 ++ www/app/assets/img/docArrow.png | Bin 0 -> 373 bytes www/app/assets/img/icons/arrow-back.png | Bin 0 -> 635 bytes www/app/assets/img/icons/favicon.png | Bin 0 -> 1663 bytes .../img/icons/ic_arrow_forward_24px.svg | 1 + www/app/assets/img/icons/ic_cancel_24px.svg | 1 + www/app/assets/img/icons/ic_close_24px.svg | 1 + www/app/assets/img/icons/ic_menu.svg | 13 + www/app/assets/img/icons/ic_menu_24px.svg | 23 + .../assets/img/icons/list_control_down.png | Bin 0 -> 309 bytes www/app/assets/img/kubernetes.svg | 6 + www/app/assets/js/.gitkeep | 0 www/app/assets/js/app.js | 2359 ++ www/app/assets/js/base.js | 26 + www/app/components/README.md | 125 + www/app/components/dashboard/README.md | 1 + .../img/icons/ic_arrow_drop_down_18px.svg | 1 + .../img/icons/ic_arrow_drop_down_24px.svg | 4 + .../dashboard/img/icons/ic_close_18px.svg | 1 + .../dashboard/img/icons/ic_close_24px.svg | 1 + www/app/components/dashboard/manifest.json | 60 + .../components/dashboard/pages/footer.html | 1 + .../components/dashboard/pages/header.html | 20 + www/app/components/dashboard/pages/home.html | 7 + .../dashboard/protractor/smoke.spec.js | 1 + .../components/dashboard/views/groups.html | 34 + .../dashboard/views/listEvents.html | 7 + .../dashboard/views/listMinions.html | 7 + .../components/dashboard/views/listPods.html | 7 + .../dashboard/views/listPodsCards.html | 59 + .../dashboard/views/listPodsVisualizer.html | 17 + .../views/listReplicationControllers.html | 7 + .../dashboard/views/listServices.html | 7 + .../dashboard/views/partials/cadvisor.html | 9 + .../dashboard/views/partials/groupBox.html | 20 + .../dashboard/views/partials/groupItem.html | 42 + .../views/partials/podTilesByName.html | 34 + .../views/partials/podTilesByServer.html | 34 + www/app/components/dashboard/views/pod.html | 97 + .../dashboard/views/replication.html | 82 + .../components/dashboard/views/service.html | 97 + www/app/index.html | 60 + www/app/shared/assets/sampleData1.json | 11993 ++++++++++ www/app/vendor/.gitkeep | 0 .../dist/angular-json-human.css | 1 + .../angular-material/angular-material.css | 6 + www/app/vendor/d3/d3.min.js | 5 + www/app/views/partials/404.html | 1 + .../partials/kubernetes-ui-menu.tmpl.html | 14 + www/app/views/partials/md-table.tmpl.html | 45 + www/app/views/partials/menu-toggle.tmpl.html | 14 + www/box.ng | 62 - www/index.html | 39 - www/k8s-style.css | 108 - www/logotext.svg | 374 - www/master/.bowerrc | 3 + www/master/README.md | 9 + www/master/bower.json | 30 + www/master/components/README.md | 125 + www/master/components/dashboard/.gitkeep | 0 www/master/components/dashboard/README.md | 1 + .../dashboard/config/development.example.json | 1 + www/master/components/dashboard/img/.gitkeep | 0 .../img/icons/ic_arrow_drop_down_18px.svg | 1 + .../img/icons/ic_arrow_drop_down_24px.svg | 4 + .../dashboard/img/icons/ic_close_18px.svg | 1 + .../dashboard/img/icons/ic_close_24px.svg | 1 + www/master/components/dashboard/js/.gitkeep | 0 .../components/dashboard/js/modules/.gitkeep | 0 .../dashboard/js/modules/controllers/.gitkeep | 0 .../modules/controllers/cAdvisorController.js | 188 + .../js/modules/controllers/dashboard.js | 6 + .../js/modules/controllers/groupController.js | 223 + .../js/modules/controllers/header.js | 27 + .../controllers/listEventsController.js | 85 + .../controllers/listMinionsController.js | 76 + .../modules/controllers/listPodsController.js | 137 + .../listReplicationControllersController.js | 101 + .../controllers/listServicesController.js | 110 + .../js/modules/controllers/podController.js | 33 + .../controllers/replicationController.js | 32 + .../modules/controllers/serviceController.js | 40 + .../dashboard/js/modules/directives/.gitkeep | 0 .../js/modules/directives/d3MinionBarGauge.js | 346 + .../js/modules/directives/dashboard.js | 101 + .../dashboard/js/modules/services/.gitkeep | 0 .../dashboard/js/modules/services/d3.js | 31 + .../dashboard/js/modules/services/podsMock.js | 76 + .../services/replicationControllersMock.js | 33 + .../js/modules/services/servicesMock.js | 63 + www/master/components/dashboard/less/.gitkeep | 0 .../components/dashboard/less/dashboard.less | 73 + .../dashboard/less/dashboard/colors.less | 122 + .../dashboard/less/dashboard/groups.less | 158 + .../dashboard/less/dashboard/pods.less | 79 + .../dashboard/less/dashboard/servers.less | 125 + .../dashboard/less/dashboard/tables.less | 95 + www/master/components/dashboard/manifest.json | 60 + .../components/dashboard/pages/.gitkeep | 0 .../components/dashboard/pages/footer.html | 1 + .../components/dashboard/pages/header.html | 20 + .../components/dashboard/pages/home.html | 7 + .../dashboard/protractor/.gitignore | 0 .../dashboard/protractor/smoke.spec.js | 114 + www/master/components/dashboard/test/.gitkeep | 0 .../dashboard/test/controllers/.gitkeep | 0 .../dashboard/test/directives/.gitkeep | 0 .../dashboard/test/services/.gitkeep | 0 .../components/dashboard/views/.gitkeep | 0 .../components/dashboard/views/groups.html | 34 + .../dashboard/views/listEvents.html | 7 + .../dashboard/views/listMinions.html | 7 + .../components/dashboard/views/listPods.html | 7 + .../dashboard/views/listPodsCards.html | 59 + .../dashboard/views/listPodsVisualizer.html | 17 + .../views/listReplicationControllers.html | 7 + .../dashboard/views/listServices.html | 7 + .../dashboard/views/partials/.gitkeep | 0 .../dashboard/views/partials/cadvisor.html | 9 + .../dashboard/views/partials/groupBox.html | 20 + .../dashboard/views/partials/groupItem.html | 42 + .../views/partials/podTilesByName.html | 34 + .../views/partials/podTilesByServer.html | 34 + .../components/dashboard/views/pod.html | 97 + .../dashboard/views/replication.html | 82 + .../components/dashboard/views/service.html | 97 + www/master/gulpfile.js | 517 + www/master/js/app.config.js | 29 + www/master/js/app.directive.js | 56 + www/master/js/app.init.js | 16 + www/master/js/app.preinit.js | 1 + www/master/js/app.provider.js | 0 www/master/js/app.run.js | 5 + www/master/js/app.service.js | 25 + www/master/js/app.skeleton.json | 3 + www/master/js/sections.js | 1 + www/master/js/tabs.js | 1 + www/master/karma.conf.js | 35 + www/master/less/app/base.less | 456 + www/master/package.json | 55 + www/master/protractor/.gitkeep | 0 www/master/protractor/chrome/.gitkeep | 0 www/master/protractor/chrome/smoke.spec.js | 17 + www/master/protractor/conf.js | 4 + www/master/shared/assets/sampleData1.json | 11993 ++++++++++ .../shared/config/development.example.json | 9 + www/master/shared/config/development.json | 9 + www/master/shared/config/generated-config.js | 17 + www/master/shared/js/modules/config.js | 4 + www/master/shared/js/modules/constants.js | 4 + .../shared/js/modules/controllers/main.js | 4 + .../js/modules/controllers/tabs-global.js | 25 + .../shared/js/modules/directives/sidebar.js | 4 + .../shared/js/modules/services/cAdvisor.js | 195 + .../js/modules/services/k8sApiService.js | 42 + .../shared/js/modules/services/pollK8sData.js | 187 + .../js/modules/services/toggle-state.js | 4 + www/master/test/modules/controllers/.gitkeep | 0 .../controllers/cAdvisorController.spec.js | 3 + .../controllers/groupController.spec.js | 3 + .../controllers/listEventsController.spec.js | 3 + .../controllers/listMinionsController.spec.js | 3 + .../controllers/listPodsController.spec.js | 3 + ...stReplicationControllersController.spec.js | 3 + .../listServicesController.spec.js | 3 + .../controllers/replicationController.js | 3 + .../controllers/serviceController.spec.js | 3 + www/master/test/modules/directives/.gitkeep | 0 www/master/test/modules/services/.gitkeep | 0 www/master/vendor.base.json | 18 + www/master/vendor.json | 5 + www/partials/groups.html | 20 - www/partials/pod.html | 63 - www/podcontroller.js | 232 - www/server/sidebar-menu.json | 1 + www/test/e2e/protractor.conf.js | 13 + www/test/e2e/scenarios.js | 32 + www/titlelogo.svg | 404 - 182 files changed, 51951 insertions(+), 2743 deletions(-) create mode 100644 www/README.md create mode 100644 www/app/assets/css/app.css create mode 100644 www/app/assets/img/docArrow.png create mode 100644 www/app/assets/img/icons/arrow-back.png create mode 100644 www/app/assets/img/icons/favicon.png create mode 100644 www/app/assets/img/icons/ic_arrow_forward_24px.svg create mode 100644 www/app/assets/img/icons/ic_cancel_24px.svg create mode 100644 www/app/assets/img/icons/ic_close_24px.svg create mode 100644 www/app/assets/img/icons/ic_menu.svg create mode 100644 www/app/assets/img/icons/ic_menu_24px.svg create mode 100644 www/app/assets/img/icons/list_control_down.png create mode 100644 www/app/assets/img/kubernetes.svg create mode 100644 www/app/assets/js/.gitkeep create mode 100644 www/app/assets/js/app.js create mode 100644 www/app/assets/js/base.js create mode 100644 www/app/components/README.md create mode 100644 www/app/components/dashboard/README.md create mode 100644 www/app/components/dashboard/img/icons/ic_arrow_drop_down_18px.svg create mode 100644 www/app/components/dashboard/img/icons/ic_arrow_drop_down_24px.svg create mode 100644 www/app/components/dashboard/img/icons/ic_close_18px.svg create mode 100644 www/app/components/dashboard/img/icons/ic_close_24px.svg create mode 100644 www/app/components/dashboard/manifest.json create mode 100644 www/app/components/dashboard/pages/footer.html create mode 100644 www/app/components/dashboard/pages/header.html create mode 100644 www/app/components/dashboard/pages/home.html create mode 100644 www/app/components/dashboard/protractor/smoke.spec.js create mode 100644 www/app/components/dashboard/views/groups.html create mode 100644 www/app/components/dashboard/views/listEvents.html create mode 100644 www/app/components/dashboard/views/listMinions.html create mode 100644 www/app/components/dashboard/views/listPods.html create mode 100644 www/app/components/dashboard/views/listPodsCards.html create mode 100644 www/app/components/dashboard/views/listPodsVisualizer.html create mode 100644 www/app/components/dashboard/views/listReplicationControllers.html create mode 100644 www/app/components/dashboard/views/listServices.html create mode 100644 www/app/components/dashboard/views/partials/cadvisor.html create mode 100644 www/app/components/dashboard/views/partials/groupBox.html create mode 100644 www/app/components/dashboard/views/partials/groupItem.html create mode 100644 www/app/components/dashboard/views/partials/podTilesByName.html create mode 100644 www/app/components/dashboard/views/partials/podTilesByServer.html create mode 100644 www/app/components/dashboard/views/pod.html create mode 100644 www/app/components/dashboard/views/replication.html create mode 100644 www/app/components/dashboard/views/service.html create mode 100644 www/app/index.html create mode 100644 www/app/shared/assets/sampleData1.json create mode 100644 www/app/vendor/.gitkeep create mode 100644 www/app/vendor/angular-json-human/dist/angular-json-human.css create mode 100644 www/app/vendor/angular-material/angular-material.css create mode 100644 www/app/vendor/d3/d3.min.js create mode 100644 www/app/views/partials/404.html create mode 100644 www/app/views/partials/kubernetes-ui-menu.tmpl.html create mode 100644 www/app/views/partials/md-table.tmpl.html create mode 100644 www/app/views/partials/menu-toggle.tmpl.html delete mode 100644 www/box.ng delete mode 100644 www/index.html delete mode 100644 www/k8s-style.css delete mode 100644 www/logotext.svg create mode 100644 www/master/.bowerrc create mode 100644 www/master/README.md create mode 100644 www/master/bower.json create mode 100644 www/master/components/README.md create mode 100644 www/master/components/dashboard/.gitkeep create mode 100644 www/master/components/dashboard/README.md create mode 100644 www/master/components/dashboard/config/development.example.json create mode 100644 www/master/components/dashboard/img/.gitkeep create mode 100644 www/master/components/dashboard/img/icons/ic_arrow_drop_down_18px.svg create mode 100644 www/master/components/dashboard/img/icons/ic_arrow_drop_down_24px.svg create mode 100644 www/master/components/dashboard/img/icons/ic_close_18px.svg create mode 100644 www/master/components/dashboard/img/icons/ic_close_24px.svg create mode 100644 www/master/components/dashboard/js/.gitkeep create mode 100644 www/master/components/dashboard/js/modules/.gitkeep create mode 100644 www/master/components/dashboard/js/modules/controllers/.gitkeep create mode 100644 www/master/components/dashboard/js/modules/controllers/cAdvisorController.js create mode 100644 www/master/components/dashboard/js/modules/controllers/dashboard.js create mode 100644 www/master/components/dashboard/js/modules/controllers/groupController.js create mode 100644 www/master/components/dashboard/js/modules/controllers/header.js create mode 100644 www/master/components/dashboard/js/modules/controllers/listEventsController.js create mode 100644 www/master/components/dashboard/js/modules/controllers/listMinionsController.js create mode 100644 www/master/components/dashboard/js/modules/controllers/listPodsController.js create mode 100644 www/master/components/dashboard/js/modules/controllers/listReplicationControllersController.js create mode 100644 www/master/components/dashboard/js/modules/controllers/listServicesController.js create mode 100644 www/master/components/dashboard/js/modules/controllers/podController.js create mode 100644 www/master/components/dashboard/js/modules/controllers/replicationController.js create mode 100644 www/master/components/dashboard/js/modules/controllers/serviceController.js create mode 100644 www/master/components/dashboard/js/modules/directives/.gitkeep create mode 100644 www/master/components/dashboard/js/modules/directives/d3MinionBarGauge.js create mode 100644 www/master/components/dashboard/js/modules/directives/dashboard.js create mode 100644 www/master/components/dashboard/js/modules/services/.gitkeep create mode 100644 www/master/components/dashboard/js/modules/services/d3.js create mode 100644 www/master/components/dashboard/js/modules/services/podsMock.js create mode 100644 www/master/components/dashboard/js/modules/services/replicationControllersMock.js create mode 100644 www/master/components/dashboard/js/modules/services/servicesMock.js create mode 100644 www/master/components/dashboard/less/.gitkeep create mode 100755 www/master/components/dashboard/less/dashboard.less create mode 100644 www/master/components/dashboard/less/dashboard/colors.less create mode 100644 www/master/components/dashboard/less/dashboard/groups.less create mode 100644 www/master/components/dashboard/less/dashboard/pods.less create mode 100644 www/master/components/dashboard/less/dashboard/servers.less create mode 100644 www/master/components/dashboard/less/dashboard/tables.less create mode 100644 www/master/components/dashboard/manifest.json create mode 100644 www/master/components/dashboard/pages/.gitkeep create mode 100644 www/master/components/dashboard/pages/footer.html create mode 100644 www/master/components/dashboard/pages/header.html create mode 100644 www/master/components/dashboard/pages/home.html create mode 100644 www/master/components/dashboard/protractor/.gitignore create mode 100644 www/master/components/dashboard/protractor/smoke.spec.js create mode 100644 www/master/components/dashboard/test/.gitkeep create mode 100644 www/master/components/dashboard/test/controllers/.gitkeep create mode 100644 www/master/components/dashboard/test/directives/.gitkeep create mode 100644 www/master/components/dashboard/test/services/.gitkeep create mode 100644 www/master/components/dashboard/views/.gitkeep create mode 100644 www/master/components/dashboard/views/groups.html create mode 100644 www/master/components/dashboard/views/listEvents.html create mode 100644 www/master/components/dashboard/views/listMinions.html create mode 100644 www/master/components/dashboard/views/listPods.html create mode 100644 www/master/components/dashboard/views/listPodsCards.html create mode 100644 www/master/components/dashboard/views/listPodsVisualizer.html create mode 100644 www/master/components/dashboard/views/listReplicationControllers.html create mode 100644 www/master/components/dashboard/views/listServices.html create mode 100644 www/master/components/dashboard/views/partials/.gitkeep create mode 100644 www/master/components/dashboard/views/partials/cadvisor.html create mode 100644 www/master/components/dashboard/views/partials/groupBox.html create mode 100644 www/master/components/dashboard/views/partials/groupItem.html create mode 100644 www/master/components/dashboard/views/partials/podTilesByName.html create mode 100644 www/master/components/dashboard/views/partials/podTilesByServer.html create mode 100644 www/master/components/dashboard/views/pod.html create mode 100644 www/master/components/dashboard/views/replication.html create mode 100644 www/master/components/dashboard/views/service.html create mode 100644 www/master/gulpfile.js create mode 100644 www/master/js/app.config.js create mode 100644 www/master/js/app.directive.js create mode 100644 www/master/js/app.init.js create mode 100644 www/master/js/app.preinit.js create mode 100644 www/master/js/app.provider.js create mode 100644 www/master/js/app.run.js create mode 100644 www/master/js/app.service.js create mode 100644 www/master/js/app.skeleton.json create mode 100644 www/master/js/sections.js create mode 100644 www/master/js/tabs.js create mode 100644 www/master/karma.conf.js create mode 100644 www/master/less/app/base.less create mode 100644 www/master/package.json create mode 100644 www/master/protractor/.gitkeep create mode 100644 www/master/protractor/chrome/.gitkeep create mode 100644 www/master/protractor/chrome/smoke.spec.js create mode 100644 www/master/protractor/conf.js create mode 100644 www/master/shared/assets/sampleData1.json create mode 100644 www/master/shared/config/development.example.json create mode 100644 www/master/shared/config/development.json create mode 100644 www/master/shared/config/generated-config.js create mode 100644 www/master/shared/js/modules/config.js create mode 100644 www/master/shared/js/modules/constants.js create mode 100644 www/master/shared/js/modules/controllers/main.js create mode 100644 www/master/shared/js/modules/controllers/tabs-global.js create mode 100644 www/master/shared/js/modules/directives/sidebar.js create mode 100644 www/master/shared/js/modules/services/cAdvisor.js create mode 100644 www/master/shared/js/modules/services/k8sApiService.js create mode 100644 www/master/shared/js/modules/services/pollK8sData.js create mode 100644 www/master/shared/js/modules/services/toggle-state.js create mode 100644 www/master/test/modules/controllers/.gitkeep create mode 100644 www/master/test/modules/controllers/cAdvisorController.spec.js create mode 100644 www/master/test/modules/controllers/groupController.spec.js create mode 100644 www/master/test/modules/controllers/listEventsController.spec.js create mode 100644 www/master/test/modules/controllers/listMinionsController.spec.js create mode 100644 www/master/test/modules/controllers/listPodsController.spec.js create mode 100644 www/master/test/modules/controllers/listReplicationControllersController.spec.js create mode 100644 www/master/test/modules/controllers/listServicesController.spec.js create mode 100644 www/master/test/modules/controllers/replicationController.js create mode 100644 www/master/test/modules/controllers/serviceController.spec.js create mode 100644 www/master/test/modules/directives/.gitkeep create mode 100644 www/master/test/modules/services/.gitkeep create mode 100644 www/master/vendor.base.json create mode 100644 www/master/vendor.json delete mode 100644 www/partials/groups.html delete mode 100644 www/partials/pod.html delete mode 100644 www/podcontroller.js create mode 100644 www/server/sidebar-menu.json create mode 100644 www/test/e2e/protractor.conf.js create mode 100644 www/test/e2e/scenarios.js delete mode 100644 www/titlelogo.svg diff --git a/.gitignore b/.gitignore index aa8b207dbbb31..43253f2328376 100644 --- a/.gitignore +++ b/.gitignore @@ -50,3 +50,6 @@ network_closure.sh .kubeconfig .tags* + +# Web UI +www/master/node_modules/ diff --git a/hack/build-ui.sh b/hack/build-ui.sh index d38715b60b145..647bd5fff5c6a 100755 --- a/hack/build-ui.sh +++ b/hack/build-ui.sh @@ -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 diff --git a/pkg/ui/datafile.go b/pkg/ui/datafile.go index 5c77e6c7d01d9..bfb4d4b9b57ed 100644 --- a/pkg/ui/datafile.go +++ b/pkg/ui/datafile.go @@ -59,1437 +59,17711 @@ func (fi bindata_file_info) Sys() interface{} { return nil } -var _www_box_ng = []byte(` + + + + + + + +`) + +func www_app_assets_img_icons_ic_menu_svg_bytes() ([]byte, error) { + return _www_app_assets_img_icons_ic_menu_svg, nil +} + +func www_app_assets_img_icons_ic_menu_svg() (*asset, error) { + bytes, err := www_app_assets_img_icons_ic_menu_svg_bytes() + if err != nil { + return nil, err + } + + info := bindata_file_info{name: "www/app/assets/img/icons/ic_menu.svg", size: 791, mode: os.FileMode(436), modTime: time.Unix(1429444413, 0)} + a := &asset{bytes: bytes, info: info} + return a, nil +} + +var _www_app_assets_img_icons_ic_menu_24px_svg = []byte(` + + + + + + + + + + + + + + + + + + + + + +`) + +func www_app_assets_img_icons_ic_menu_24px_svg_bytes() ([]byte, error) { + return _www_app_assets_img_icons_ic_menu_24px_svg, nil +} + +func www_app_assets_img_icons_ic_menu_24px_svg() (*asset, error) { + bytes, err := www_app_assets_img_icons_ic_menu_24px_svg_bytes() + if err != nil { + return nil, err + } + + info := bindata_file_info{name: "www/app/assets/img/icons/ic_menu_24px.svg", size: 841, mode: os.FileMode(436), modTime: time.Unix(1429444413, 0)} + a := &asset{bytes: bytes, info: info} + return a, nil +} + +var _www_app_assets_img_icons_list_control_down_png = []byte("\x89PNG\r\n\x1a\n\x00\x00\x00\rIHDR\x00\x00\x000\x00\x00\x000\b\x03\x00\x00\x00`\xdc\t\xb5\x00\x00\x00\x19tEXtSoftware\x00Adobe ImageReadyq\xc9e<\x00\x00\x00'PLTE\xa8\xa8\xa8\xfc\xfc\xfc\xc0\xc0\xc0\xb6\xb6\xb6\xf7\xf7\xf7\xaa\xaa\xaa\xf6\xf6\xf6\xe6\xe6\xe6\xe8\xe8赵\xb5\xc3\xc3\xc3\xe5\xe5\xe5\xff\xff\xffZLu\xde\x00\x00\x00\rtRNS\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\x00=\xe8\"\x86\x00\x00\x00\x8bIDATx\xda\xec\xd4I\x12\x80 \fD\xd1\x04\x04\x9c\xee\u007f^ˁ\xd2`\xb7\x96k\xc3\xd2\xfao\xa1\t\xca\xfc\xf1\x88\x03\a\xbf\x01}\xd7\x06\xb9\u007f\x02S\x8a\x8d\xc8C*\x1c\x94$bE\x1eD\xac0`\x14\xb1\"\xc7\xf5\xc9H\x81\x06+\xba\xad\x0f\xca\xdf\xc1\nԷ_\xe9*`\u007f\x9b\xc3)p\u007f\x1f\\\x15\xa4\a\x93>\x04\xe9\xd1j\xec\x82\xf4p\x97\xaa@=^\xbe]\xc0\x9el\xeb*p\xcf\xd6[\x03\xe9\xe9}P\xf5\x9f\x80\x03\a\xafg\x11`\x00\xb0\xe4e\a\x17\x87\xea}\x00\x00\x00\x00IEND\xaeB`\x82") + +func www_app_assets_img_icons_list_control_down_png_bytes() ([]byte, error) { + return _www_app_assets_img_icons_list_control_down_png, nil +} + +func www_app_assets_img_icons_list_control_down_png() (*asset, error) { + bytes, err := www_app_assets_img_icons_list_control_down_png_bytes() + if err != nil { + return nil, err + } + + info := bindata_file_info{name: "www/app/assets/img/icons/list_control_down.png", size: 309, mode: os.FileMode(436), modTime: time.Unix(1429444413, 0)} + a := &asset{bytes: bytes, info: info} + return a, nil +} + +var _www_app_assets_img_kubernetes_svg = []byte(` + + + + +`) + +func www_app_assets_img_kubernetes_svg_bytes() ([]byte, error) { + return _www_app_assets_img_kubernetes_svg, nil +} + +func www_app_assets_img_kubernetes_svg() (*asset, error) { + bytes, err := www_app_assets_img_kubernetes_svg_bytes() + if err != nil { + return nil, err + } + + info := bindata_file_info{name: "www/app/assets/img/kubernetes.svg", size: 11663, mode: os.FileMode(436), modTime: time.Unix(1429444413, 0)} + a := &asset{bytes: bytes, info: info} + return a, nil +} + +var _www_app_assets_js_gitkeep = []byte(``) + +func www_app_assets_js_gitkeep_bytes() ([]byte, error) { + return _www_app_assets_js_gitkeep, nil +} + +func www_app_assets_js_gitkeep() (*asset, error) { + bytes, err := www_app_assets_js_gitkeep_bytes() + if err != nil { + return nil, err + } + + info := bindata_file_info{name: "www/app/assets/js/.gitkeep", size: 0, mode: os.FileMode(436), modTime: time.Unix(1429444413, 0)} + a := &asset{bytes: bytes, info: info} + return a, nil +} + +var _www_app_assets_js_app_js = []byte(`var componentNamespaces = ["kubernetesApp.components.dashboard"]; +// APP START +// **************************** +// /www/app/assets/app.js is autogenerated. Do not modify. +// Changes should be made in /master/modules/js or /master/components//js +// **************************** +// ----------------------------------- + +var app = angular.module('kubernetesApp', [ + 'ngRoute', + 'ngMaterial', + 'ngLodash', + 'door3.css', + 'kubernetesApp.config', + 'kubernetesApp.services', + 'angular.filter' +].concat(componentNamespaces)); + +angular.module('kubernetesApp.config', []); +angular.module('kubernetesApp.services', ['kubernetesApp.config']); + +app.config([ + '$routeProvider', + function($routeProvider) { + $routeProvider.when("/404", {templateUrl: "views/partials/404.html"}) + // else 404 + .otherwise({redirectTo: "/404"}); + } +]) + .config([ + '$routeProvider', + 'manifestRoutes', + function($routeProvider, manifestRoutes) { + angular.forEach(manifestRoutes, function(r) { + var route = { + templateUrl: r.templateUrl + }; + if (r.controller) { + route.controller = r.controller; + } + if (r.css) { + route.css = r.css; + } + $routeProvider.when(r.url, route); + }); + } + ]); + +app.directive('includeReplace', + function() { + 'use strict'; + return { + require: 'ngInclude', + restrict: 'A', /* optional */ + link: function(scope, el, attrs) { el.replaceWith(el.children()); } + }; + }) + .directive('compile', + ["$compile", function($compile) { + 'use strict'; + return function(scope, element, attrs) { + scope.$watch(function(scope) { return scope.$eval(attrs.compile); }, + function(value) { + element.html(value); + $compile(element.contents())(scope); + }); + }; + }]) + .directive("kubernetesUiMenu", + function() { + 'use strict'; + return { + templateUrl: "views/partials/kubernetes-ui-menu.tmpl.html" + }; + }) + .directive('menuToggle', function() { + 'use strict'; + return { + scope: {section: '='}, + templateUrl: 'views/partials/menu-toggle.tmpl.html', + link: function($scope, $element) { + var controller = $element.parent().controller(); + + $scope.isOpen = function() { return controller.isOpen($scope.section); }; + $scope.toggle = function() { controller.toggleOpen($scope.section); }; + + var parentNode = $element[0].parentNode.parentNode.parentNode; + if (parentNode.classList.contains('parent-list-item')) { + var heading = parentNode.querySelector('h2'); + $element[0].firstChild.setAttribute('aria-describedby', heading.id); + } + } + }; + }); + +app.filter('startFrom', + function() { + 'use strict'; + return function(input, start) { return input.slice(start); }; + }) + .filter('nospace', function() { + 'use strict'; + return function(value) { return (!value) ? '' : value.replace(/ /g, ''); }; + }); + +app.run(['$route', angular.noop]) + .run(["lodash", function(lodash) { + // Alias lodash + window['_'] = lodash; + }]); + +app.service('SidebarService', [ + '$rootScope', + function($rootScope) { + var service = this; + service.sidebarItems = []; + + service.clearSidebarItems = function() { service.sidebarItems = []; }; + + service.renderSidebar = function() { + var _entries = ''; + service.sidebarItems.forEach(function(entry) { _entries += entry.Html; }); + + if (_entries) { + $rootScope.sidenavLeft = '
' + _entries + '
'; + } + }; + + service.addSidebarItem = function(item) { + + service.sidebarItems.push(item); + + service.sidebarItems.sort(function(a, b) { return (a.order > b.order) ? 1 : ((b.order > a.order) ? -1 : 0); }); + }; + } +]); + + +app.value("tabs", [{"component":"dashboard","title":"Dashboard"}]); +app.constant("manifestRoutes", [{"description":"Dashboard visualization.","url":"/dashboard/","templateUrl":"components/dashboard/pages/home.html"},{"description":"Pods","url":"/dashboard/pods","templateUrl":"components/dashboard/views/listPods.html"},{"description":"Pod Visualizer","url":"/dashboard/visualpods","templateUrl":"components/dashboard/views/listPodsVisualizer.html"},{"description":"Services","url":"/dashboard/services","templateUrl":"components/dashboard/views/listServices.html"},{"description":"Replication Controllers","url":"/dashboard/replicationcontrollers","templateUrl":"components/dashboard/views/listReplicationControllers.html"},{"description":"Events","url":"/dashboard/events","templateUrl":"components/dashboard/views/listEvents.html"},{"description":"Minions","url":"/dashboard/minions","templateUrl":"components/dashboard/views/listMinions.html"},{"description":"Replication Controller","url":"/dashboard/replicationcontrollers/:replicationControllerId","templateUrl":"components/dashboard/views/replication.html"},{"description":"Service","url":"/dashboard/services/:serviceId","templateUrl":"components/dashboard/views/service.html"},{"description":"Explore","url":"/dashboard/groups/:grouping*?/selector/:selector*?","templateUrl":"components/dashboard/views/groups.html"},{"description":"Pod","url":"/dashboard/pods/:podId","templateUrl":"components/dashboard/views/pod.html"}]); + +angular.module("kubernetesApp.config", []) + +.constant("ENV", { + "/": { + "k8sApiServer": "/api/v1beta2", + "k8sDataServer": "/cluster", + "k8sDataPollMinIntervalSec": 10, + "k8sDataPollMaxIntervalSec": 120, + "k8sDataPollErrorThreshold": 5, + "cAdvisorProxy": "", + "cAdvisorPort": "4194" + } +}) + +.constant("ngConstant", true) + +; +/**========================================================= + * Module: config.js + * App routes and resources configuration + =========================================================*/ +/**========================================================= + * Module: constants.js + * Define constants to inject across the application + =========================================================*/ +/**========================================================= + * Module: main.js + * Main Application Controller + =========================================================*/ +/**========================================================= + * Module: tabs-global.js + * Page Controller + =========================================================*/ + +app.controller('TabCtrl', [ + '$scope', + '$location', + 'tabs', + function($scope, $location, tabs) { + $scope.tabs = tabs; + + $scope.switchTab = function(index) { + var location_path = $location.path(); + var tab = tabs[index]; + + if (tab) { + var path = '/%s'.format(tab.component); + if (location_path.indexOf(path) == -1) { + $location.path(path); + } + } + }; + } +]); + +/**========================================================= + * Module: sidebar.js + * Wraps the sidebar and handles collapsed state + =========================================================*/ +(function() { + "use strict"; + + angular.module('kubernetesApp.services') + .service('cAdvisorService', ["$http", "$q", "ENV", function($http, $q, ENV) { + var _baseUrl = function(minionIp) { + var minionPort = ENV['/']['cAdvisorPort'] || "8081"; + var proxy = ENV['/']['cAdvisorProxy'] || "/api/v1beta2/proxy/nodes/"; + + return proxy + minionIp + ':' + minionPort + '/api/v1.0/'; + }; + + this.getMachineInfo = getMachineInfo; + + function getMachineInfo(minionIp) { + var fullUrl = _baseUrl(minionIp) + 'machine'; + var deferred = $q.defer(); + + // hack + $http.get(fullUrl).success(function(data) { + deferred.resolve(data); + }).error(function(data, status) { deferred.reject('There was an error') }); + return deferred.promise; + } + + this.getContainerInfo = getContainerInfo; + // containerId optional + function getContainerInfo(minionIp, containerId) { + containerId = (typeof containerId === "undefined") ? "/" : containerId; + + var fullUrl = _baseUrl(minionIp) + 'containers' + containerId; + var deferred = $q.defer(); + + var request = { + "num_stats": 10, + "num_samples": 0 + }; + + $http.post(fullUrl, request) + .success(function(data) { deferred.resolve(data); }) + .error(function() { deferred.reject('There was an error') }); + return deferred.promise; + } + + this.getDataForMinion = function(minionIp) { + var machineData, containerData; + var deferred = $q.defer(); + + var p = $q.all([getMachineInfo(minionIp), getContainerInfo(minionIp)]) + .then( + function(dataArray) { + machineData = dataArray[0]; + containerData = dataArray[1]; + + var memoryData = parseMemory(machineData, containerData); + var cpuData = parseCpu(machineData, containerData); + var fsData = parseFilesystems(machineData, containerData); + deferred.resolve({ + memoryData: memoryData, + cpuData: cpuData, + filesystemData: fsData, + machineData: machineData, + containerData: containerData + }); + + }, + function(errorData) { deferred.reject(errorData); }); + + return deferred.promise; + }; + + // Utils to process cadvisor data + function humanize(num, size, units) { + var unit; + for (unit = units.pop(); units.length && num >= size; unit = units.pop()) { + num /= size; + } + return [num, unit]; + } + + // Following the IEC naming convention + function humanizeIEC(num) { + var ret = humanize(num, 1024, ["TiB", "GiB", "MiB", "KiB", "Bytes"]); + return ret[0].toFixed(2) + " " + ret[1]; + } + + // Following the Metric naming convention + function humanizeMetric(num) { + var ret = humanize(num, 1000, ["TB", "GB", "MB", "KB", "Bytes"]); + return ret[0].toFixed(2) + " " + ret[1]; + } + + function hasResource(stats, resource) { return stats.stats.length > 0 && stats.stats[0][resource]; } + + // Gets the length of the interval in nanoseconds. + function getInterval(current, previous) { + var cur = new Date(current); + var prev = new Date(previous); + + // ms -> ns. + return (cur.getTime() - prev.getTime()) * 1000000; + } + + function parseCpu(machineInfo, containerInfo) { + var cur = containerInfo.stats[containerInfo.stats.length - 1]; + var results = []; + + var cpuUsage = 0; + if (containerInfo.spec.has_cpu && containerInfo.stats.length >= 2) { + var prev = containerInfo.stats[containerInfo.stats.length - 2]; + var rawUsage = cur.cpu.usage.total - prev.cpu.usage.total; + var intervalInNs = getInterval(cur.timestamp, prev.timestamp); + + // Convert to millicores and take the percentage + cpuUsage = Math.round(((rawUsage / intervalInNs) / machineInfo.num_cores) * 100); + if (cpuUsage > 100) { + cpuUsage = 100; + } + } + + return { + cpuPercentUsage: cpuUsage + }; + } + + function parseFilesystems(machineInfo, containerInfo) { + var cur = containerInfo.stats[containerInfo.stats.length - 1]; + if (!cur.filesystem) { + return; + } + + var filesystemData = []; + for (var i = 0; i < cur.filesystem.length; i++) { + var data = cur.filesystem[i]; + var totalUsage = Math.floor((data.usage * 100.0) / data.capacity); + + var f = { + device: data.device, + filesystemNumber: i + 1, + usage: data.usage, + usageDescription: humanizeMetric(data.usage), + capacity: data.capacity, + capacityDescription: humanizeMetric(data.capacity), + totalUsage: Math.floor((data.usage * 100.0) / data.capacity) + }; + + filesystemData.push(f); + } + return filesystemData; + } + + var oneMegabyte = 1024 * 1024; + var oneGigabyte = 1024 * oneMegabyte; + + function parseMemory(machineInfo, containerInfo) { + if (containerInfo.spec.has_memory && !hasResource(containerInfo, "memory")) { + return; + } + + // var titles = ["Time", "Total", "Hot"]; + var data = []; + for (var i = 0; i < containerInfo.stats.length; i++) { + var cur = containerInfo.stats[i]; + + var elements = []; + elements.push(cur.timestamp); + elements.push(cur.memory.usage / oneMegabyte); + elements.push(cur.memory.working_set / oneMegabyte); + data.push(elements); + } + + // Get the memory limit, saturate to the machine size. + var memory_limit = machineInfo.memory_capacity; + if (containerInfo.spec.memory.limit && (containerInfo.spec.memory.limit < memory_limit)) { + memory_limit = containerInfo.spec.memory.limit; + } + + var cur = containerInfo.stats[containerInfo.stats.length - 1]; + + var r = { + current: { + memoryUsage: cur.memory.usage, + workingMemoryUsage: cur.memory.working_set, + memoryLimit: memory_limit, + memoryUsageDescription: humanizeMetric(cur.memory.usage), + workingMemoryUsageDescription: humanizeMetric(cur.memory.working_set), + memoryLimitDescription: humanizeMetric(memory_limit) + }, + historical: data + }; + + return r; + } + }]); +})(); + +app.provider('k8sApi', + function() { + + var urlBase = ''; + + this.setUrlBase = function(value) { urlBase = value; }; + + var _get = function($http, baseUrl, query) { + var _fullUrl = baseUrl; + if (query !== undefined) { + _fullUrl += '/' + query; + } + + return $http.get(_fullUrl); + }; + + this.$get = ["$http", "$q", function($http, $q) { + var api = {}; + + api.getUrlBase = function() { return urlBase; }; + + api.getPods = function(query) { return _get($http, urlBase + '/pods', query); }; + + api.getMinions = function(query) { return _get($http, urlBase + '/minions', query); }; + + api.getServices = function(query) { return _get($http, urlBase + '/services', query); }; + + api.getReplicationControllers = function(query) { + return _get($http, urlBase + '/replicationControllers', query) + }; + + api.getEvents = function(query) { return _get($http, urlBase + '/events', query); }; + + return api; + }]; + }) + .config(["k8sApiProvider", "ENV", function(k8sApiProvider, ENV) { + if (ENV && ENV['/'] && ENV['/']['k8sApiServer']) { + var proxy = ENV['/']['cAdvisorProxy'] || ''; + k8sApiProvider.setUrlBase(proxy + ENV['/']['k8sApiServer']); + } + }]); + +(function() { + "use strict"; + + var pollK8sDataServiceProvider = function PollK8sDataServiceProvider(_) { + // A set of configuration controlling the polling behavior. + // Their values should be configured in the application before + // creating the service instance. + + var useSampleData = false; + this.setUseSampleData = function(value) { useSampleData = value; }; + + var sampleDataFiles = ["shared/assets/sampleData1.json"]; + this.setSampleDataFiles = function(value) { sampleDataFiles = value; }; + + var dataServer = "http://localhost:5555/cluster"; + this.setDataServer = function(value) { dataServer = value; }; + + var pollMinIntervalSec = 10; + this.setPollMinIntervalSec = function(value) { pollMinIntervalSec = value; }; + + var pollMaxIntervalSec = 120; + this.setPollMaxIntervalSec = function(value) { pollMaxIntervalSec = value; }; + + var pollErrorThreshold = 5; + this.setPollErrorThreshold = function(value) { pollErrorThreshold = value; }; + + this.$get = function($http, $timeout) { + // Now the sequenceNumber will be used for debugging and verification purposes. + var k8sdatamodel = { + "data": undefined, + "sequenceNumber": 0, + "useSampleData": useSampleData + }; + var pollingError = 0; + var promise = undefined; + + // Implement fibonacci back off when the service is down. + var pollInterval = pollMinIntervalSec; + var pollIncrement = pollInterval; + + // Reset polling interval. + var resetCounters = function() { + pollInterval = pollMinIntervalSec; + pollIncrement = pollInterval; + }; + + // Bump error count and polling interval. + var bumpCounters = function() { + // Bump the error count. + pollingError++; + + // TODO: maybe display an error in the UI to the end user. + if (pollingError % pollErrorThreshold === 0) { + console.log("Error: " + pollingError + " consecutive polling errors for " + dataServer + "."); + } + + // Bump the polling interval. + var oldIncrement = pollIncrement; + pollIncrement = pollInterval; + pollInterval += oldIncrement; + + // Reset when limit reached. + if (pollInterval > pollMaxIntervalSec) { + resetCounters(); + } + }; + + var updateModel = function(newModel) { + var dedupe = function(dataModel) { + if (dataModel.resources) { + dataModel.resources = _.uniq(dataModel.resources, function(resource) { return resource.id; }); + } + + if (dataModel.relations) { + dataModel.relations = + _.uniq(dataModel.relations, function(relation) { return relation.source + relation.target; }); + } + }; + + dedupe(newModel); + + var newModelString = JSON.stringify(newModel); + var oldModelString = ""; + if (k8sdatamodel.data) { + oldModelString = JSON.stringify(k8sdatamodel.data); + } + + if (newModelString !== oldModelString) { + k8sdatamodel.data = newModel; + k8sdatamodel.sequenceNumber++; + } + + pollingError = 0; + resetCounters(); + }; + + var nextSampleDataFile = 0; + var getSampleDataFile = function() { + var result = ""; + if (sampleDataFiles.length > 0) { + result = sampleDataFiles[nextSampleDataFile % sampleDataFiles.length]; + ++nextSampleDataFile; + } + + return result; + }; + + var pollOnce = function(scope, repeat) { + var dataSource = (k8sdatamodel.useSampleData) ? getSampleDataFile() : dataServer; + $.getJSON(dataSource) + .done(function(newModel, jqxhr, textStatus) { + if (newModel && newModel.success) { + delete newModel.success; // Remove success indicator. + delete newModel.timestamp; // Remove changing timestamp. + updateModel(newModel); + scope.$apply(); + promise = repeat ? $timeout(function() { pollOnce(scope, true); }, pollInterval * 1000) : undefined; + return; + } + + bumpCounters(); + promise = repeat ? $timeout(function() { pollOnce(scope, true); }, pollInterval * 1000) : undefined; + }) + .fail(function(jqxhr, textStatus, error) { + bumpCounters(); + promise = repeat ? $timeout(function() { pollOnce(scope, true); }, pollInterval * 1000) : undefined; + }); + }; + + var isPolling = function() { return promise ? true : false; }; + + var start = function(scope) { + // If polling has already started, then calling start() again would + // just reset the counters and polling interval, but it will not + // start a new thread polling in parallel to the existing polling + // thread. + resetCounters(); + if (!promise) { + k8sdatamodel.data = undefined; + pollOnce(scope, true); + } + }; + + var stop = function() { + if (promise) { + $timeout.cancel(promise); + promise = undefined; + } + }; + + var refresh = function(scope) { + stop(scope); + resetCounters(); + k8sdatamodel.data = undefined; + pollOnce(scope, false); + }; + + return { + "k8sdatamodel": k8sdatamodel, + "isPolling": isPolling, + "refresh": refresh, + "start": start, + "stop": stop + }; + }; + }; + + angular.module("kubernetesApp.services") + .provider("pollK8sDataService", ["lodash", pollK8sDataServiceProvider]) + .config(["pollK8sDataServiceProvider", "ENV", function(pollK8sDataServiceProvider, ENV) { + if (ENV && ENV['/']) { + if (ENV['/']['k8sDataServer']) { + pollK8sDataServiceProvider.setDataServer(ENV['/']['k8sDataServer']); + } + if (ENV['/']['k8sDataPollIntervalMinSec']) { + pollK8sDataServiceProvider.setPollIntervalSec(ENV['/']['k8sDataPollIntervalMinSec']); + } + if (ENV['/']['k8sDataPollIntervalMaxSec']) { + pollK8sDataServiceProvider.setPollIntervalSec(ENV['/']['k8sDataPollIntervalMaxSec']); + } + if (ENV['/']['k8sDataPollErrorThreshold']) { + pollK8sDataServiceProvider.setPollErrorThreshold(ENV['/']['k8sDataPollErrorThreshold']); + } + } + }]); + +}()); + +/**========================================================= + * Module: toggle-state.js + * Services to share toggle state functionality + =========================================================*/ + + +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(); + + } +]); + +/**========================================================= + * Module: Dashboard + * Visualizer for clusters + =========================================================*/ + +app.controller('DashboardCtrl', ['$scope', function($scope) {}]); + +/**========================================================= + * 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); + }; + } +]); + +/**========================================================= + * 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'} + ]; + } + ]); + +/**========================================================= + * 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); + + } +]); + +/**========================================================= + * 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); + + } +]); + + + +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); + + } +]); + +/**========================================================= + * 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); + + } +]); + +/**========================================================= + * 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); + } +]); + +/**========================================================= + * 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); + } +]); + +/**========================================================= + * 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); + } +]); + +/**========================================================= + * 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(); + } + } +]); + +(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); + } + }; + } + ]); +}()); + +(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: ["$scope", "$filter", "$window", "$location", 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' + }; + }); + +}()); + +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; } + }; + } + ]); + +(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); + } + }; + } + PodDataService.$inject = ["$q"]; + +})(); + +(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); + } + }; + } + ReplicationControllerDataService.$inject = ["$q"]; + +})(); + +(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); + } + }; + } + ServiceDataService.$inject = ["$q"]; + +})(); +`) + +func www_app_assets_js_app_js_bytes() ([]byte, error) { + return _www_app_assets_js_app_js, nil +} + +func www_app_assets_js_app_js() (*asset, error) { + bytes, err := www_app_assets_js_app_js_bytes() + if err != nil { + return nil, err + } + + info := bindata_file_info{name: "www/app/assets/js/app.js", size: 79552, mode: os.FileMode(436), modTime: time.Unix(1429574518, 0)} + a := &asset{bytes: bytes, info: info} + return a, nil +} + +var _www_app_assets_js_base_js = []byte(`!function(e,t){"object"==typeof module&&"object"==typeof module.exports?module.exports=e.document?t(e,!0):function(e){if(!e.document)throw new Error("jQuery requires a window with a document");return t(e)}:t(e)}("undefined"!=typeof window?window:this,function(e,t){function n(e){var t=e.length,n=Z.type(e);return"function"===n||Z.isWindow(e)?!1:1===e.nodeType&&t?!0:"array"===n||0===t||"number"==typeof t&&t>0&&t-1 in e}function r(e,t,n){if(Z.isFunction(t))return Z.grep(e,function(e,r){return!!t.call(e,r,e)!==n});if(t.nodeType)return Z.grep(e,function(e){return e===t!==n});if("string"==typeof t){if(ae.test(t))return Z.filter(t,e,n);t=Z.filter(t,e)}return Z.grep(e,function(e){return U.call(t,e)>=0!==n})}function i(e,t){for(;(e=e[t])&&1!==e.nodeType;);return e}function o(e){var t=he[e]={};return Z.each(e.match(de)||[],function(e,n){t[n]=!0}),t}function s(){J.removeEventListener("DOMContentLoaded",s,!1),e.removeEventListener("load",s,!1),Z.ready()}function a(){Object.defineProperty(this.cache={},0,{get:function(){return{}}}),this.expando=Z.expando+a.uid++}function u(e,t,n){var r;if(void 0===n&&1===e.nodeType)if(r="data-"+t.replace(be,"-$1").toLowerCase(),n=e.getAttribute(r),"string"==typeof n){try{n="true"===n?!0:"false"===n?!1:"null"===n?null:+n+""===n?+n:xe.test(n)?Z.parseJSON(n):n}catch(i){}ye.set(e,t,n)}else n=void 0;return n}function l(){return!0}function c(){return!1}function f(){try{return J.activeElement}catch(e){}}function p(e,t){return Z.nodeName(e,"table")&&Z.nodeName(11!==t.nodeType?t:t.firstChild,"tr")?e.getElementsByTagName("tbody")[0]||e.appendChild(e.ownerDocument.createElement("tbody")):e}function d(e){return e.type=(null!==e.getAttribute("type"))+"/"+e.type,e}function h(e){var t=Pe.exec(e.type);return t?e.type=t[1]:e.removeAttribute("type"),e}function g(e,t){for(var n=0,r=e.length;r>n;n++)ve.set(e[n],"globalEval",!t||ve.get(t[n],"globalEval"))}function m(e,t){var n,r,i,o,s,a,u,l;if(1===t.nodeType){if(ve.hasData(e)&&(o=ve.access(e),s=ve.set(t,o),l=o.events)){delete s.handle,s.events={};for(i in l)for(n=0,r=l[i].length;r>n;n++)Z.event.add(t,i,l[i][n])}ye.hasData(e)&&(a=ye.access(e),u=Z.extend({},a),ye.set(t,u))}}function v(e,t){var n=e.getElementsByTagName?e.getElementsByTagName(t||"*"):e.querySelectorAll?e.querySelectorAll(t||"*"):[];return void 0===t||t&&Z.nodeName(e,t)?Z.merge([e],n):n}function y(e,t){var n=t.nodeName.toLowerCase();"input"===n&&Ne.test(e.type)?t.checked=e.checked:("input"===n||"textarea"===n)&&(t.defaultValue=e.defaultValue)}function x(t,n){var r,i=Z(n.createElement(t)).appendTo(n.body),o=e.getDefaultComputedStyle&&(r=e.getDefaultComputedStyle(i[0]))?r.display:Z.css(i[0],"display");return i.detach(),o}function b(e){var t=J,n=$e[e];return n||(n=x(e,t),"none"!==n&&n||(We=(We||Z("