├── Dockerfile ├── app.py ├── kube-files ├── webapp-pod-definition.yaml └── webapp-service.yaml ├── requirements.txt ├── static ├── app.js ├── css │ ├── animation.css │ ├── blue.css │ └── red.css ├── img │ ├── failed.png │ └── success.jpg └── view1 │ ├── view1.html │ └── view1.js └── templates ├── bower.json └── index.html /Dockerfile: -------------------------------------------------------------------------------- 1 | FROM python:3.6-alpine 2 | 3 | RUN apk add --no-cache curl python pkgconfig python-dev openssl-dev libffi-dev musl-dev 4 | 5 | ADD ./requirements.txt /opt/webapp-conntest/ 6 | 7 | WORKDIR /opt/webapp-conntest 8 | 9 | RUN pip install -r requirements.txt 10 | 11 | ADD . /opt/webapp-conntest 12 | 13 | EXPOSE 8080 14 | 15 | ENTRYPOINT ["python", "app.py"] 16 | -------------------------------------------------------------------------------- /app.py: -------------------------------------------------------------------------------- 1 | from flask import Flask, request 2 | from flask import render_template 3 | import requests 4 | import os.path 5 | 6 | TEMPLATES_FOLDER = 'templates' 7 | STATICS_FOLDER = 'static' 8 | app = Flask(__name__, static_url_path='', static_folder=STATICS_FOLDER, template_folder=TEMPLATES_FOLDER) 9 | 10 | SA_TOKEN = None 11 | SA_TOKEN_FROM_PATH = None 12 | SA_TOKEN_PATH = '/var/run/secrets/kubernetes.io/serviceaccount/token' 13 | 14 | APP_NAME = "APP_NAME" in os.environ and os.environ.get('APP_NAME') or "My Kubernetes Dashboard" 15 | 16 | 17 | @app.route('/test', methods=['POST']) 18 | def test(): 19 | global SA_TOKEN 20 | json_data = request.get_json(silent=True) 21 | 22 | SA_TOKEN = "token" in json_data and json_data["token"] or SA_TOKEN_FROM_PATH 23 | 24 | print("SA_TOKEN=" + str(SA_TOKEN)) 25 | 26 | test_results = requests.get('https://kubernetes.default.svc/api/v1/namespaces/default/pods', headers={'Authorization': 'Bearer ' + str(SA_TOKEN)}, 27 | verify=False) 28 | 29 | return (test_results.text, test_results.status_code, test_results.headers.items()) 30 | 31 | 32 | @app.route('/') 33 | def main(): 34 | try: 35 | return render_template('index.html', theme='blue', app_name=APP_NAME, backgroundcolor='#2980b9') 36 | except Exception as ex: 37 | print(str(ex)) 38 | 39 | 40 | if __name__ == "__main__": 41 | 42 | if os.path.exists(SA_TOKEN_PATH): 43 | f = open(SA_TOKEN_PATH, "r") 44 | SA_TOKEN_FROM_PATH = f.read() 45 | app.run(host="0.0.0.0", port=8080) -------------------------------------------------------------------------------- /kube-files/webapp-pod-definition.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: apps/v1 2 | kind: Deployment 3 | metadata: 4 | name: webapp-dashboard 5 | spec: 6 | selector: 7 | matchLabels: 8 | name: webapp-dashboard 9 | template: 10 | metadata: 11 | labels: 12 | name: webapp-dashboard 13 | spec: 14 | containers: 15 | - name: webapp-dashboard 16 | image: kodekloud/my-kubernetes-dashboard 17 | ports: 18 | - containerPort: 8080 19 | -------------------------------------------------------------------------------- /kube-files/webapp-service.yaml: -------------------------------------------------------------------------------- 1 | kind: Service 2 | apiVersion: v1 3 | metadata: 4 | name: webapp-service 5 | spec: 6 | selector: 7 | name: webapp-dashboard 8 | type: NodePort 9 | ports: 10 | - port: 8080 11 | targetPort: 8080 12 | nodePort: 30080 13 | -------------------------------------------------------------------------------- /requirements.txt: -------------------------------------------------------------------------------- 1 | Flask==0.10.1 2 | Flask-Bower==1.3.0 3 | requests==2.19.1 -------------------------------------------------------------------------------- /static/app.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | // Declare app level module which depends on views, and components 4 | angular.module('myApp', [ 5 | 'ngRoute', 6 | 'myApp.view1' 7 | ]). 8 | config(['$locationProvider', '$routeProvider', function($locationProvider, $routeProvider) { 9 | $locationProvider.hashPrefix('!'); 10 | 11 | $routeProvider.otherwise({redirectTo: '/view1'}); 12 | }]); 13 | -------------------------------------------------------------------------------- /static/css/animation.css: -------------------------------------------------------------------------------- 1 | .shake-vertical { 2 | -webkit-animation: shake-vertical 0.8s cubic-bezier(0.455, 0.030, 0.515, 0.955) both; 3 | animation: shake-vertical 0.8s cubic-bezier(0.455, 0.030, 0.515, 0.955) both; 4 | } 5 | 6 | /* ---------------------------------------------- 7 | * Generated by Animista on 2018-9-19 0:37:34 8 | * w: http://animista.net, t: @cssanimista 9 | * ---------------------------------------------- */ 10 | 11 | /** 12 | * ---------------------------------------- 13 | * animation shake-vertical 14 | * ---------------------------------------- 15 | */ 16 | @-webkit-keyframes shake-vertical { 17 | 0%, 18 | 100% { 19 | -webkit-transform: translateY(0); 20 | transform: translateY(0); 21 | } 22 | 10%, 23 | 30%, 24 | 50%, 25 | 70% { 26 | -webkit-transform: translateY(-8px); 27 | transform: translateY(-8px); 28 | } 29 | 20%, 30 | 40%, 31 | 60% { 32 | -webkit-transform: translateY(8px); 33 | transform: translateY(8px); 34 | } 35 | 80% { 36 | -webkit-transform: translateY(6.4px); 37 | transform: translateY(6.4px); 38 | } 39 | 90% { 40 | -webkit-transform: translateY(-6.4px); 41 | transform: translateY(-6.4px); 42 | } 43 | } 44 | @keyframes shake-vertical { 45 | 0%, 46 | 100% { 47 | -webkit-transform: translateY(0); 48 | transform: translateY(0); 49 | } 50 | 10%, 51 | 30%, 52 | 50%, 53 | 70% { 54 | -webkit-transform: translateY(-8px); 55 | transform: translateY(-8px); 56 | } 57 | 20%, 58 | 40%, 59 | 60% { 60 | -webkit-transform: translateY(8px); 61 | transform: translateY(8px); 62 | } 63 | 80% { 64 | -webkit-transform: translateY(6.4px); 65 | transform: translateY(6.4px); 66 | } 67 | 90% { 68 | -webkit-transform: translateY(-6.4px); 69 | transform: translateY(-6.4px); 70 | } 71 | } 72 | 73 | .shake-horizontal { 74 | -webkit-animation: shake-horizontal 0.8s cubic-bezier(0.455, 0.030, 0.515, 0.955) both; 75 | animation: shake-horizontal 0.8s cubic-bezier(0.455, 0.030, 0.515, 0.955) both; 76 | } 77 | 78 | /* ---------------------------------------------- 79 | * Generated by Animista on 2018-9-19 0:9:15 80 | * w: http://animista.net, t: @cssanimista 81 | * ---------------------------------------------- */ 82 | 83 | /** 84 | * ---------------------------------------- 85 | * animation shake-horizontal 86 | * ---------------------------------------- 87 | */ 88 | @-webkit-keyframes shake-horizontal { 89 | 0%, 90 | 100% { 91 | -webkit-transform: translateX(0); 92 | transform: translateX(0); 93 | } 94 | 10%, 95 | 30%, 96 | 50%, 97 | 70% { 98 | -webkit-transform: translateX(-10px); 99 | transform: translateX(-10px); 100 | } 101 | 20%, 102 | 40%, 103 | 60% { 104 | -webkit-transform: translateX(10px); 105 | transform: translateX(10px); 106 | } 107 | 80% { 108 | -webkit-transform: translateX(8px); 109 | transform: translateX(8px); 110 | } 111 | 90% { 112 | -webkit-transform: translateX(-8px); 113 | transform: translateX(-8px); 114 | } 115 | } 116 | @keyframes shake-horizontal { 117 | 0%, 118 | 100% { 119 | -webkit-transform: translateX(0); 120 | transform: translateX(0); 121 | } 122 | 10%, 123 | 30%, 124 | 50%, 125 | 70% { 126 | -webkit-transform: translateX(-10px); 127 | transform: translateX(-10px); 128 | } 129 | 20%, 130 | 40%, 131 | 60% { 132 | -webkit-transform: translateX(10px); 133 | transform: translateX(10px); 134 | } 135 | 80% { 136 | -webkit-transform: translateX(8px); 137 | transform: translateX(8px); 138 | } 139 | 90% { 140 | -webkit-transform: translateX(-8px); 141 | transform: translateX(-8px); 142 | } 143 | } 144 | -------------------------------------------------------------------------------- /static/css/blue.css: -------------------------------------------------------------------------------- 1 | /* app css stylesheet */ 2 | 3 | .menu { 4 | list-style: none; 5 | border-bottom: 0.1em solid black; 6 | margin-bottom: 2em; 7 | padding: 0 0 0.5em; 8 | } 9 | 10 | .menu:before { 11 | content: "["; 12 | } 13 | 14 | .menu:after { 15 | content: "]"; 16 | } 17 | 18 | .menu > li { 19 | display: inline; 20 | } 21 | 22 | .menu > li + li:before { 23 | content: "|"; 24 | padding-right: 0.3em; 25 | } 26 | 27 | 28 | body { 29 | /** display: flex; */ 30 | width: 100vw; 31 | /* height: 100vh; */ 32 | align-items: center; 33 | justify-content: center; 34 | background-size: cover; 35 | background-position: center; 36 | background-color: #014d6d; 37 | background-repeat: no-repeat; 38 | } 39 | 40 | .container { 41 | padding-bottom: 200px; 42 | } 43 | 44 | .main-card-panel { 45 | 46 | background-color: #003f62 !important; 47 | min-width: 500px; 48 | color: #e1f5fe; 49 | opacity: .90; 50 | 51 | } 52 | 53 | .question_text { 54 | color: #e1f5fe; 55 | } 56 | 57 | .answer_options { 58 | margin-top: 50px; 59 | } 60 | 61 | .fa-close { 62 | color: #d86050 63 | } 64 | 65 | .btn, .btn-large, .btn-small, .btn-floating { 66 | background-color: #01579b; 67 | color: #bddddc 68 | } 69 | 70 | 71 | .collapsible-header { 72 | background-color: #003f62 !important; 73 | color: #e1f5fe; 74 | padding: 5px; 75 | } 76 | 77 | .collapsible-body { 78 | background-color: #003f62 !important; 79 | color: #e1f5fe; 80 | padding: 5px; 81 | } 82 | 83 | .status-bar-items { 84 | font-size: 9px; 85 | border-radius: 2px; 86 | margin: 2px; 87 | padding: 5px; 88 | color: white; 89 | background-color: #2e5569; 90 | } 91 | 92 | .hint { 93 | border: none; 94 | } 95 | 96 | footer { 97 | text-align: center; 98 | background: #003f62; 99 | color: #e1f5fe; 100 | opacity: .80; 101 | } -------------------------------------------------------------------------------- /static/css/red.css: -------------------------------------------------------------------------------- 1 | /* app css stylesheet */ 2 | 3 | .menu { 4 | list-style: none; 5 | border-bottom: 0.1em solid black; 6 | margin-bottom: 2em; 7 | padding: 0 0 0.5em; 8 | } 9 | 10 | .menu:before { 11 | content: "["; 12 | } 13 | 14 | .menu:after { 15 | content: "]"; 16 | } 17 | 18 | .menu > li { 19 | display: inline; 20 | } 21 | 22 | .menu > li + li:before { 23 | content: "|"; 24 | padding-right: 0.3em; 25 | } 26 | 27 | 28 | body { 29 | /** display: flex; */ 30 | width: 100vw; 31 | /* height: 100vh; */ 32 | align-items: center; 33 | justify-content: center; 34 | background-size: cover; 35 | background-position: center; 36 | background-color: #e74c3c; 37 | background-repeat: no-repeat; 38 | } 39 | 40 | .container { 41 | padding-bottom: 200px; 42 | } 43 | 44 | .main-card-panel { 45 | 46 | background-color: #7d0d04 !important; 47 | min-width: 500px; 48 | color: #e1f5fe; 49 | opacity: .90; 50 | 51 | } 52 | 53 | .question_text { 54 | color: #e1f5fe; 55 | } 56 | 57 | .answer_options { 58 | margin-top: 50px; 59 | } 60 | 61 | .fa-close { 62 | color: #d86050 63 | } 64 | 65 | .btn, .btn-large, .btn-small, .btn-floating { 66 | background-color: #f44336; 67 | color: #bddddc 68 | } 69 | 70 | 71 | .collapsible-header { 72 | background-color: #003f62 !important; 73 | color: #e1f5fe; 74 | padding: 5px; 75 | } 76 | 77 | .collapsible-body { 78 | background-color: #003f62 !important; 79 | color: #e1f5fe; 80 | padding: 5px; 81 | } 82 | 83 | .status-bar-items { 84 | font-size: 9px; 85 | border-radius: 2px; 86 | margin: 2px; 87 | padding: 5px; 88 | color: white; 89 | background-color: #2e5569; 90 | } 91 | 92 | .hint { 93 | border: none; 94 | } 95 | 96 | footer { 97 | text-align: center; 98 | background: #003f62; 99 | color: #e1f5fe; 100 | opacity: .80; 101 | } -------------------------------------------------------------------------------- /static/img/failed.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kodekloudhub/my-kubernetes-dashboard/e7ee16d47c4e2beeaf175f4a64479c815bc40e90/static/img/failed.png -------------------------------------------------------------------------------- /static/img/success.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kodekloudhub/my-kubernetes-dashboard/e7ee16d47c4e2beeaf175f4a64479c815bc40e90/static/img/success.jpg -------------------------------------------------------------------------------- /static/view1/view1.html: -------------------------------------------------------------------------------- 1 |
2 | 3 |
4 | 5 | 6 | 7 |
8 |
9 | 10 | 11 | 12 | 13 |
14 | 15 | 16 | 17 | 18 |
19 |
20 | 21 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 |
NameStateContainersService AccountIP
{{ item.metadata.name }}{{ item.status.phase }}{{ item.status.containerStatuses.length }}{{ item.spec.serviceAccountName }}{{ item.status.podIP }}
47 | 48 |

49 | {{ alert_message }} 50 |

51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 |
63 | 64 |
65 | 66 |
67 | 68 | -------------------------------------------------------------------------------- /static/view1/view1.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | angular.module('myApp.view1', ['ngRoute']) 4 | 5 | .config(['$routeProvider', function($routeProvider) { 6 | $routeProvider.when('/view1', { 7 | templateUrl: 'view1/view1.html', 8 | controller: 'View1Ctrl' 9 | }); 10 | }]) 11 | 12 | .controller('View1Ctrl', ["$scope", "$http", "$timeout", function($scope, $http, $timeout) { 13 | $scope.at_least_one_question_loaded = false; 14 | $scope.answer_choice = {}; 15 | $scope.alert_message = ""; 16 | $scope.alert_additional_details = []; 17 | $scope.is_loading = false; 18 | $scope.staging = false; 19 | $scope.show_log = false; 20 | $scope.question_tests = []; 21 | 22 | $scope.token = "" 23 | 24 | $scope.all_question_statuses = [] 25 | 26 | $scope.loadData = function(){ 27 | $scope.is_loading = true; 28 | $scope.pods_data = null; 29 | $http.post("/test", {token: $scope.token}).then(function(response){ 30 | $scope.is_loading = false; 31 | $scope.alert_message = response.data.message; 32 | $scope.pods_data = response.data; 33 | $scope.alert_type = "success" 34 | }, function(response){ 35 | $scope.is_loading = false; 36 | $scope.alert_message = response.data.message || response.data; 37 | $scope.alert_type = "failure" 38 | console.error(response) 39 | }) 40 | 41 | } 42 | 43 | $scope.loadData(); 44 | 45 | }]); -------------------------------------------------------------------------------- /templates/bower.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "angular-seed", 3 | "description": "A starter project for AngularJS", 4 | "version": "0.0.0", 5 | "homepage": "https://github.com/angular/angular-seed", 6 | "license": "MIT", 7 | "private": true, 8 | "dependencies": { 9 | "angular": "~1.5.0", 10 | "angular-route": "~1.5.0", 11 | "angular-loader": "~1.5.0", 12 | "angular-mocks": "~1.5.0", 13 | "html5-boilerplate": "^5.3.0" 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /templates/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | Kubernetes Dashboard 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 |

{{ app_name }}

22 | 23 | 26 | 27 |
28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | --------------------------------------------------------------------------------