├── .dockerignore ├── .editorconfig ├── .gitignore ├── Dockerfile ├── LICENSE.md ├── Notes.md ├── README.md ├── angular.json ├── e2e ├── app.e2e-spec.ts ├── app.po.ts └── tsconfig.e2e.json ├── karma.conf.js ├── package.json ├── pics ├── Golang-Service-Diagram-with-gRPC.png ├── architecture.png ├── kiali_new.png ├── ui_docker.png ├── ui_new.png └── ui_v2.png ├── protractor.conf.js ├── src ├── app │ ├── app.component.css │ ├── app.component.html │ ├── app.component.spec.ts │ ├── app.component.ts │ ├── app.module.ts │ └── observe │ │ ├── Greeting.ts │ │ ├── Greetings.ts │ │ ├── observe.component.css │ │ ├── observe.component.html │ │ ├── observe.component.spec.ts │ │ ├── observe.component.ts │ │ ├── observe.service.spec.ts │ │ └── observe.service.ts ├── assets │ └── .gitkeep ├── environments │ ├── environment.docker.ts │ ├── environment.prod.ts │ └── environment.ts ├── favicon.ico ├── index.html ├── main.ts ├── polyfills.ts ├── styles.css ├── test.ts ├── tsconfig.app.json ├── tsconfig.spec.json └── typings.d.ts ├── tsconfig.json └── tslint.json /.dockerignore: -------------------------------------------------------------------------------- 1 | node_modules/ 2 | e2e/ 3 | dist/ 4 | pics/ 5 | .idea/ 6 | .git/ 7 | 8 | .editorconfig 9 | .gitignore 10 | .dockerignore 11 | 12 | Dockerfile 13 | karma.conf.js 14 | LICENSE.md 15 | protractor.conf.js 16 | README.md 17 | Notes.md 18 | package-*.json 19 | .yarn* 20 | tslint.json 21 | .DS_Store 22 | -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | # Editor configuration, see http://editorconfig.org 2 | root = true 3 | 4 | [*] 5 | charset = utf-8 6 | indent_style = space 7 | indent_size = 2 8 | insert_final_newline = true 9 | trim_trailing_whitespace = true 10 | 11 | [*.md] 12 | max_line_length = off 13 | trim_trailing_whitespace = false 14 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # See http://help.github.com/ignore-files/ for more about ignoring files. 2 | 3 | # compiled output 4 | /dist 5 | /tmp 6 | /out-tsc 7 | 8 | # dependencies 9 | /node_modules 10 | 11 | # IDEs and editors 12 | /.idea 13 | .project 14 | .classpath 15 | .c9/ 16 | *.launch 17 | .settings/ 18 | *.sublime-workspace 19 | 20 | # IDE - VSCode 21 | .vscode/* 22 | !.vscode/settings.json 23 | !.vscode/tasks.json 24 | !.vscode/launch.json 25 | !.vscode/extensions.json 26 | 27 | # misc 28 | /.sass-cache 29 | /connect.lock 30 | /coverage 31 | /libpeerconnection.log 32 | npm-debug.log 33 | testem.log 34 | /typings 35 | 36 | # e2e 37 | /e2e/*.js 38 | /e2e/*.map 39 | 40 | # System Files 41 | .DS_Store 42 | Thumbs.db 43 | .yarn* 44 | .pnp.js 45 | yarn.lock 46 | package-*.json 47 | -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | # https://www.codementor.io/yomateo/angular-docker-dockerize-your-app-in-5-minutes-video-included-oohw2mzuj 2 | FROM node:lts-alpine3.13 AS builder 3 | WORKDIR /app 4 | COPY . . 5 | RUN ls -alh && \ 6 | npm install && \ 7 | npm install i -D typescript@4.2.4 && \ 8 | npm run build --configuration=production 9 | 10 | FROM nginx:alpine 11 | COPY --from=builder /app/dist/* /usr/share/nginx/html/ 12 | -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2019 Gary A. Stafford 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /Notes.md: -------------------------------------------------------------------------------- 1 | ## Misc Commands 2 | 3 | ```bash 4 | # update Angular to 5.0.0 5 | david 6 | 7 | # migrate code to Angular 5 8 | ng update @angular/cli --migrate-only --from=5.0.0 9 | 10 | yarn install 11 | #yarn add typescript@">=3.1.1 and <3.3.0" 12 | ng serve --open 13 | 14 | time docker build -t garystafford/angular-observe:1.6.7 . --no-cache 15 | # 2.19s user 1.96s system 0% cpu 9:26.70 total 16 | 17 | docker push docker.io/garystafford/angular-observe:1.6.7 18 | 19 | docker run -p 80:80 garystafford/angular-observe:1.6.7 20 | ``` 21 | 22 | ```shell 23 | npm update david -g 24 | david 25 | david update 26 | npm install 27 | ng build --configuration=docker 28 | ``` 29 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Kubernetes-based Microservice Observability with Istio Service Mesh 2 | 3 | Source code for the two-part blog 4 | post, [Kubernetes-based Microservice Observability with Istio Service Mesh](https://garystafford.medium.com/kubernetes-based-microservice-observability-with-istio-service-mesh-part-1-of-2-19084d13a866). See the post for detailed instructions. 5 | 6 | ![Kiali](pics/kiali_new.png) 7 | 8 | ## Frontend UI 9 | 10 | An Angular 12 front-end UI to the API is located on 11 | Github: [k8s-istio-observe-frontend](https://github.com/garystafford/k8s-istio-observe-frontend/tree/2021-istio). 12 | 13 | ![preview](pics/ui_new.png) 14 | 15 | ## Reference Platform Architecture 16 | 17 | ![Architecture Diagram](pics/architecture.png) 18 | 19 | ## Service Responses 20 | 21 | On the reference platform, each upstream service responds to requests from downstream services by returning a small 22 | informational JSON payload (termed a greeting in the source code). 23 | 24 | ```json 25 | { 26 | "id": "fc65e292-eb40-43ee-810f-f819764cae61", 27 | "service": "Service-A", 28 | "message": "Hello, from Service-A!", 29 | "created": "2021-05-22T23:31:10.489864529Z" 30 | } 31 | ``` 32 | 33 | ### Complete response from Service A 34 | 35 | ```json 36 | [ 37 | { 38 | "id": "c90f542d-22e6-401c-8361-422fea719f15", 39 | "service": "Service-D", 40 | "message": "Shalom (שָׁלוֹם), from Service-D!", 41 | "created": "2021-05-22T23:31:10.402441212Z" 42 | }, 43 | { 44 | "id": "713156eb-f91c-4591-ab51-07d107185d61", 45 | "service": "Service-G", 46 | "message": "Ahlan (أهلا), from Service-G!", 47 | "created": "2021-05-22T23:31:10.4324182Z" 48 | }, 49 | { 50 | "id": "22bdf2c8-1262-441e-b799-b3c4d68392df", 51 | "service": "Service-H", 52 | "message": "Ciao, from Service-H!", 53 | "created": "2021-05-22T23:31:10.454751483Z" 54 | }, 55 | { 56 | "id": "f292a648-0f35-4248-8852-fde4035052dc", 57 | "service": "Service-E", 58 | "message": "Bonjour, from Service-E!", 59 | "created": "2021-05-22T23:31:10.468721423Z" 60 | }, 61 | { 62 | "id": "523ddd56-5068-4d32-83dc-511545fc5fbb", 63 | "service": "Service-B", 64 | "message": "Namasté, from Service-B!", 65 | "created": "2021-05-22T23:31:10.469495924Z" 66 | }, 67 | { 68 | "id": "d6cca80b-f238-428d-9db9-3658ba072e1e", 69 | "service": "Service-C", 70 | "message": "Konnichiwa (こんにちは), from Service-C!", 71 | "created": "2021-05-22T23:31:10.471593632Z" 72 | }, 73 | { 74 | "id": "fc65e292-eb40-43ee-810f-f819764cae61", 75 | "service": "Service-A", 76 | "message": "Hello, from Service-A!", 77 | "created": "2021-05-22T23:31:10.489864529Z" 78 | } 79 | ] 80 | ``` 81 | 82 | ## Optional Docker Swarm Deployment 83 | 84 | In addition to Kubernetes, you can deploy the reference platform to Docker Swarm. Create Docker overlay network, and deploy Docker Swarm, locally, consisting of (12) containers: 85 | 86 | - (1) Angular 12 frontend UI 87 | - (8) backend Go-based microservices 88 | - (1) RabbitMQ server with (1) queue 89 | - (1) MongoDB server with (4) databases 90 | - (1) Mongo Express 91 | 92 | ```bash 93 | docker network create -d overlay --attachable golang-demo 94 | 95 | docker stack deploy -c docker_swarm/stack.yml golang-demo 96 | ``` 97 | 98 | ### View the Running Stack 99 | 100 | ```text 101 | > docker stack services golang-demo --format "table {{.Name}}\t{{.Image}}\t{{.Ports}}" | sort 102 | 103 | NAME IMAGE PORTS 104 | golang-demo_angular-ui garystafford/angular-observe:1.6.5 *:80->80/tcp 105 | golang-demo_mongodb mongo:4.4.6 *:27017->27017/tcp 106 | golang-demo_mongo_express mongo-express:0.54.0 *:8081->8081/tcp 107 | golang-demo_rabbitmq rabbitmq:3.8.16-management-alpine *:5672->5672/tcp, *:15672->15672/tcp 108 | golang-demo_service-a garystafford/go-srv-a:1.6.5 *:8080->8080/tcp 109 | golang-demo_service-b garystafford/go-srv-b:1.6.5 110 | golang-demo_service-c garystafford/go-srv-c:1.6.5 111 | golang-demo_service-d garystafford/go-srv-d:1.6.5 112 | golang-demo_service-e garystafford/go-srv-e:1.6.5 113 | golang-demo_service-f garystafford/go-srv-f:1.6.5 114 | golang-demo_service-g garystafford/go-srv-g:1.6.5 115 | golang-demo_service-h garystafford/go-srv-h:1.6.5 116 | ``` 117 | 118 | ### Accessing the Docker Swarm-based Stack 119 | 120 | To start, call the Angular 12 Frontend UI: 121 | 122 | The backend API's URL will be: 123 | 124 | Alternately, call Service A, the system's edge service, directly: 125 | 126 | To observe the queue traffic, use the RabbitMQ Management Console: 127 | 128 | To observe the databases, use Mongo Express: 129 | 130 | ### View of UI running on Docker 131 | 132 | ![UI from Docker](pics/ui_docker.png) 133 | 134 | --- 135 | 136 | The contents of this repository represent my viewpoints and not of my past or current employers, including Amazon Web Services (AWS). All third-party libraries, modules, plugins, and SDKs are the property of their respective owners. 137 | -------------------------------------------------------------------------------- /angular.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "./node_modules/@angular/cli/lib/config/schema.json", 3 | "cli": { 4 | "analytics": "a5f7db31-aa39-4f01-92d3-37d9f089dbe6" 5 | }, 6 | "version": 1, 7 | "newProjectRoot": "projects", 8 | "projects": { 9 | "observe-stack": { 10 | "root": "", 11 | "sourceRoot": "src", 12 | "projectType": "application", 13 | "architect": { 14 | "build": { 15 | "builder": "@angular-devkit/build-angular:browser", 16 | "options": { 17 | "outputPath": "dist", 18 | "index": "src/index.html", 19 | "main": "src/main.ts", 20 | "tsConfig": "src/tsconfig.app.json", 21 | "polyfills": "src/polyfills.ts", 22 | "assets": [ 23 | "src/assets", 24 | "src/favicon.ico" 25 | ], 26 | "styles": [ 27 | "node_modules/bootstrap/dist/css/bootstrap.css", 28 | "src/styles.css" 29 | ], 30 | "scripts": [ 31 | "node_modules/bootstrap/dist/js/bootstrap.js" 32 | ] 33 | }, 34 | "configurations": { 35 | "production": { 36 | "optimization": true, 37 | "outputHashing": "all", 38 | "sourceMap": false, 39 | "extractCss": true, 40 | "namedChunks": false, 41 | "aot": true, 42 | "extractLicenses": true, 43 | "vendorChunk": false, 44 | "buildOptimizer": true, 45 | "fileReplacements": [ 46 | { 47 | "replace": "src/environments/environment.ts", 48 | "with": "src/environments/environment.prod.ts" 49 | } 50 | ] 51 | } 52 | } 53 | }, 54 | "serve": { 55 | "builder": "@angular-devkit/build-angular:dev-server", 56 | "options": { 57 | "browserTarget": "observe-stack:build" 58 | }, 59 | "configurations": { 60 | "production": { 61 | "browserTarget": "observe-stack:build:production" 62 | } 63 | } 64 | }, 65 | "extract-i18n": { 66 | "builder": "@angular-devkit/build-angular:extract-i18n", 67 | "options": { 68 | "browserTarget": "observe-stack:build" 69 | } 70 | }, 71 | "test": { 72 | "builder": "@angular-devkit/build-angular:karma", 73 | "options": { 74 | "main": "src/test.ts", 75 | "karmaConfig": "./karma.conf.js", 76 | "polyfills": "src/polyfills.ts", 77 | "tsConfig": "src/tsconfig.spec.json", 78 | "scripts": [], 79 | "styles": [ 80 | "src/styles.css" 81 | ], 82 | "assets": [ 83 | "src/assets", 84 | "src/favicon.ico" 85 | ] 86 | } 87 | }, 88 | "lint": { 89 | "builder": "@angular-devkit/build-angular:tslint", 90 | "options": { 91 | "tsConfig": [ 92 | "src/tsconfig.app.json", 93 | "src/tsconfig.spec.json" 94 | ], 95 | "exclude": [ 96 | "**/node_modules/**" 97 | ] 98 | } 99 | } 100 | } 101 | }, 102 | "observe-stack-e2e": { 103 | "root": "e2e", 104 | "sourceRoot": "e2e", 105 | "projectType": "application", 106 | "architect": { 107 | "e2e": { 108 | "builder": "@angular-devkit/build-angular:protractor", 109 | "options": { 110 | "protractorConfig": "./protractor.conf.js", 111 | "devServerTarget": "observe-stack:serve" 112 | } 113 | }, 114 | "lint": { 115 | "builder": "@angular-devkit/build-angular:tslint", 116 | "options": { 117 | "tsConfig": [ 118 | "e2e/tsconfig.e2e.json" 119 | ], 120 | "exclude": [ 121 | "**/node_modules/**" 122 | ] 123 | } 124 | } 125 | } 126 | } 127 | }, 128 | "defaultProject": "observe-stack", 129 | "schematics": { 130 | "@schematics/angular:component": { 131 | "style": "css" 132 | }, 133 | "@schematics/angular:directive": { 134 | "prefix": "app" 135 | } 136 | } 137 | } 138 | -------------------------------------------------------------------------------- /e2e/app.e2e-spec.ts: -------------------------------------------------------------------------------- 1 | import { AppPage } from './app.po'; 2 | 3 | describe('observe-stack App', () => { 4 | let page: AppPage; 5 | 6 | beforeEach(() => { 7 | page = new AppPage(); 8 | }); 9 | 10 | it('should display welcome message', () => { 11 | page.navigateTo(); 12 | expect(page.getParagraphText()).toEqual('Welcome to app!'); 13 | }); 14 | }); 15 | -------------------------------------------------------------------------------- /e2e/app.po.ts: -------------------------------------------------------------------------------- 1 | import { browser, by, element } from 'protractor'; 2 | 3 | export class AppPage { 4 | navigateTo() { 5 | return browser.get('/'); 6 | } 7 | 8 | getParagraphText() { 9 | return element(by.css('app-root h1')).getText(); 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /e2e/tsconfig.e2e.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../tsconfig.json", 3 | "compilerOptions": { 4 | "outDir": "../out-tsc/e2e", 5 | "baseUrl": "./", 6 | "module": "commonjs", 7 | "target": "es5", 8 | "types": [ 9 | "jasmine", 10 | "jasminewd2", 11 | "node" 12 | ] 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /karma.conf.js: -------------------------------------------------------------------------------- 1 | // Karma configuration file, see link for more information 2 | // https://karma-runner.github.io/1.0/config/configuration-file.html 3 | 4 | module.exports = function (config) { 5 | config.set({ 6 | basePath: '', 7 | frameworks: ['jasmine', '@angular-devkit/build-angular'], 8 | plugins: [ 9 | require('karma-jasmine'), 10 | require('karma-chrome-launcher'), 11 | require('karma-jasmine-html-reporter'), 12 | require('karma-coverage-istanbul-reporter'), 13 | require('@angular-devkit/build-angular/plugins/karma') 14 | ], 15 | client:{ 16 | clearContext: false // leave Jasmine Spec Runner output visible in browser 17 | }, 18 | coverageIstanbulReporter: { 19 | dir: require('path').join(__dirname, 'coverage'), reports: [ 'html', 'lcovonly' ], 20 | fixWebpackSourcePaths: true 21 | }, 22 | 23 | reporters: ['progress', 'kjhtml'], 24 | port: 9876, 25 | colors: true, 26 | logLevel: config.LOG_INFO, 27 | autoWatch: true, 28 | browsers: ['Chrome'], 29 | singleRun: false 30 | }); 31 | }; 32 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "k8s-istio-observe-frontend", 3 | "version": "1.6.0", 4 | "license": "MIT", 5 | "scripts": { 6 | "ng": "ng", 7 | "start": "ng serve", 8 | "build": "ng build --prod", 9 | "test": "ng test", 10 | "lint": "ng lint", 11 | "e2e": "ng e2e" 12 | }, 13 | "private": true, 14 | "dependencies": { 15 | "@angular/animations": "^12.0.1", 16 | "@angular/common": "^12.0.1", 17 | "@angular/compiler": "^12.0.1", 18 | "@angular/core": "^12.0.1", 19 | "@angular/forms": "^12.0.1", 20 | "@angular/platform-browser": "^12.0.1", 21 | "@angular/platform-browser-dynamic": "^12.0.1", 22 | "@angular/router": "^12.0.1", 23 | "bootstrap": "^5.0.1", 24 | "build": "^0.1.4", 25 | "core-js": "^3.13.0", 26 | "jquery": "^3.6.0", 27 | "ngx-logger": "^4.2.2", 28 | "npm": "^7.14.0", 29 | "run": "^1.4.0", 30 | "rxjs": "^6.4.0", 31 | "tslib": "^2.2.0", 32 | "zone.js": "^0.11.4" 33 | }, 34 | "devDependencies": { 35 | "@angular-devkit/build-angular": "^12.0.1", 36 | "@angular-devkit/core": "^12.0.1", 37 | "@angular/cli": "^12.0.1", 38 | "@angular/compiler-cli": "^12.0.1", 39 | "@angular/language-service": "^12.0.1", 40 | "@types/jasmine": "^3.7.4", 41 | "@types/jasminewd2": "^2.0.2", 42 | "@types/node": "^15.6.1", 43 | "codelyzer": "^6.0.2", 44 | "eslint": "^7.27.0", 45 | "jasmine-core": "^3.7.1", 46 | "jasmine-spec-reporter": "^7.0.0", 47 | "karma": "^6.3.2", 48 | "karma-chrome-launcher": "^3.1.0", 49 | "karma-cli": "^2.0.0", 50 | "karma-coverage-istanbul-reporter": "^3.0.3", 51 | "karma-jasmine": "^4.0.1", 52 | "karma-jasmine-html-reporter": "^1.6.0", 53 | "protractor": "^7.0.0", 54 | "ts-node": "^10.0.0", 55 | "tslint": "^6.1.3", 56 | "typescript": "^4.2.4" 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /pics/Golang-Service-Diagram-with-gRPC.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/garystafford/k8s-istio-observe-frontend/1e13e3e9bb40fa7b5f5c0e3df8f14f7ed245b3df/pics/Golang-Service-Diagram-with-gRPC.png -------------------------------------------------------------------------------- /pics/architecture.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/garystafford/k8s-istio-observe-frontend/1e13e3e9bb40fa7b5f5c0e3df8f14f7ed245b3df/pics/architecture.png -------------------------------------------------------------------------------- /pics/kiali_new.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/garystafford/k8s-istio-observe-frontend/1e13e3e9bb40fa7b5f5c0e3df8f14f7ed245b3df/pics/kiali_new.png -------------------------------------------------------------------------------- /pics/ui_docker.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/garystafford/k8s-istio-observe-frontend/1e13e3e9bb40fa7b5f5c0e3df8f14f7ed245b3df/pics/ui_docker.png -------------------------------------------------------------------------------- /pics/ui_new.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/garystafford/k8s-istio-observe-frontend/1e13e3e9bb40fa7b5f5c0e3df8f14f7ed245b3df/pics/ui_new.png -------------------------------------------------------------------------------- /pics/ui_v2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/garystafford/k8s-istio-observe-frontend/1e13e3e9bb40fa7b5f5c0e3df8f14f7ed245b3df/pics/ui_v2.png -------------------------------------------------------------------------------- /protractor.conf.js: -------------------------------------------------------------------------------- 1 | // Protractor configuration file, see link for more information 2 | // https://github.com/angular/protractor/blob/master/lib/config.ts 3 | 4 | const { SpecReporter } = require('jasmine-spec-reporter'); 5 | 6 | exports.config = { 7 | allScriptsTimeout: 11000, 8 | specs: [ 9 | './e2e/**/*.e2e-spec.ts' 10 | ], 11 | capabilities: { 12 | 'browserName': 'chrome' 13 | }, 14 | directConnect: true, 15 | baseUrl: 'http://localhost:4200/', 16 | framework: 'jasmine', 17 | jasmineNodeOpts: { 18 | showColors: true, 19 | defaultTimeoutInterval: 30000, 20 | print: function() {} 21 | }, 22 | onPrepare() { 23 | require('ts-node').register({ 24 | project: 'e2e/tsconfig.e2e.json' 25 | }); 26 | jasmine.getEnv().addReporter(new SpecReporter({ spec: { displayStacktrace: true } })); 27 | } 28 | }; 29 | -------------------------------------------------------------------------------- /src/app/app.component.css: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/garystafford/k8s-istio-observe-frontend/1e13e3e9bb40fa7b5f5c0e3df8f14f7ed245b3df/src/app/app.component.css -------------------------------------------------------------------------------- /src/app/app.component.html: -------------------------------------------------------------------------------- 1 | 2 |
3 | 4 |
5 | -------------------------------------------------------------------------------- /src/app/app.component.spec.ts: -------------------------------------------------------------------------------- 1 | import {async, TestBed} from '@angular/core/testing'; 2 | import {AppComponent} from './app.component'; 3 | 4 | describe('AppComponent', () => { 5 | beforeEach(async(() => { 6 | TestBed.configureTestingModule({ 7 | declarations: [ 8 | AppComponent 9 | ], 10 | }).compileComponents(); 11 | })); 12 | it('should create the app', async(() => { 13 | const fixture = TestBed.createComponent(AppComponent); 14 | const app = fixture.debugElement.componentInstance; 15 | expect(app).toBeTruthy(); 16 | })); 17 | it(`should have as title 'app'`, async(() => { 18 | const fixture = TestBed.createComponent(AppComponent); 19 | const app = fixture.debugElement.componentInstance; 20 | expect(app.title).toEqual('app'); 21 | })); 22 | it('should render title in a h1 tag', async(() => { 23 | const fixture = TestBed.createComponent(AppComponent); 24 | fixture.detectChanges(); 25 | const compiled = fixture.debugElement.nativeElement; 26 | expect(compiled.querySelector('h1').textContent).toContain('Welcome to app!'); 27 | })); 28 | }); 29 | -------------------------------------------------------------------------------- /src/app/app.component.ts: -------------------------------------------------------------------------------- 1 | import {Component} from '@angular/core'; 2 | 3 | @Component({ 4 | selector: 'app-root', 5 | templateUrl: './app.component.html', 6 | styleUrls: ['./app.component.css'] 7 | }) 8 | export class AppComponent { 9 | } 10 | -------------------------------------------------------------------------------- /src/app/app.module.ts: -------------------------------------------------------------------------------- 1 | import {BrowserModule} from '@angular/platform-browser'; 2 | import {NgModule} from '@angular/core'; 3 | import {HttpClientModule} from '@angular/common/http'; 4 | import {FormsModule} from '@angular/forms'; 5 | import {LoggerModule, NgxLoggerLevel} from 'ngx-logger'; 6 | import {AppComponent} from './app.component'; 7 | import {ObserveService} from './observe/observe.service'; 8 | import {ObserveComponent} from './observe/observe.component'; 9 | import {environment} from '../environments/environment'; 10 | 11 | 12 | @NgModule({ 13 | declarations: [ 14 | AppComponent, 15 | ObserveComponent 16 | ], 17 | imports: [ 18 | BrowserModule, 19 | HttpClientModule, 20 | FormsModule, 21 | LoggerModule.forRoot({ 22 | level: !environment.production ? 23 | NgxLoggerLevel.DEBUG : NgxLoggerLevel.INFO, 24 | serverLogLevel: NgxLoggerLevel.INFO 25 | }) 26 | ], 27 | providers: [ObserveService], 28 | bootstrap: [AppComponent] 29 | }) 30 | export class AppModule { 31 | } 32 | -------------------------------------------------------------------------------- /src/app/observe/Greeting.ts: -------------------------------------------------------------------------------- 1 | export interface Greeting { 2 | service: string; 3 | id: string; 4 | message: string; 5 | created: Date; 6 | hostname: string; 7 | } 8 | -------------------------------------------------------------------------------- /src/app/observe/Greetings.ts: -------------------------------------------------------------------------------- 1 | import {Greeting} from './Greeting'; 2 | 3 | export interface Greetings { 4 | greeting: Greeting[]; 5 | } 6 | -------------------------------------------------------------------------------- /src/app/observe/observe.component.css: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/garystafford/k8s-istio-observe-frontend/1e13e3e9bb40fa7b5f5c0e3df8f14f7ed245b3df/src/app/observe/observe.component.css -------------------------------------------------------------------------------- /src/app/observe/observe.component.html: -------------------------------------------------------------------------------- 1 |
2 |
3 |   4 |
5 |    6 | 7 |
8 | {{apiURLFull}} 9 |
10 |
11 |
12 |

API Response

13 |
14 |
15 |
16 |
17 |
18 |
{{greet.service}}
19 | {{greet.message}}
20 | created: {{greet.created | date: 'yyyy-MM-dd HH:mm:ss.SSS'}} 21 |
22 | hostname: {{greet.hostname}} 23 |
24 |
25 |
26 |
27 |
28 |
29 | {{timingMessage}} 30 |
31 |
32 |
33 | -------------------------------------------------------------------------------- /src/app/observe/observe.component.spec.ts: -------------------------------------------------------------------------------- 1 | import {async, ComponentFixture, TestBed} from '@angular/core/testing'; 2 | 3 | import {ObserveComponent} from './observe.component'; 4 | 5 | describe('ObserveComponent', () => { 6 | let component: ObserveComponent; 7 | let fixture: ComponentFixture; 8 | 9 | beforeEach(async(() => { 10 | TestBed.configureTestingModule({ 11 | declarations: [ObserveComponent] 12 | }) 13 | .compileComponents(); 14 | })); 15 | 16 | beforeEach(() => { 17 | fixture = TestBed.createComponent(ObserveComponent); 18 | component = fixture.componentInstance; 19 | fixture.detectChanges(); 20 | }); 21 | 22 | it('should create', () => { 23 | expect(component).toBeTruthy(); 24 | }); 25 | }); 26 | -------------------------------------------------------------------------------- /src/app/observe/observe.component.ts: -------------------------------------------------------------------------------- 1 | import {Component, OnInit} from '@angular/core'; 2 | import {ObserveService} from './observe.service'; 3 | import {NGXLogger} from 'ngx-logger'; 4 | import {Greetings} from './Greetings'; 5 | import {environment} from '../../environments/environment'; 6 | 7 | @Component({ 8 | selector: 'app-observe', 9 | templateUrl: './observe.component.html', 10 | styleUrls: ['./observe.component.css'] 11 | }) 12 | export class ObserveComponent implements OnInit { 13 | 14 | public greetings: Greetings; 15 | public apiURL: string; 16 | public apiURLFull: string; 17 | public timingMessage: string; 18 | private _timing: number; 19 | 20 | constructor(private _service: ObserveService, private _logger: NGXLogger) { 21 | } 22 | 23 | ngOnInit() { 24 | this.apiURL = environment.greetUrl; // 'api.dev.example-api.com' 25 | this.apiURLFull = this.apiURL + '/api/greeting'; 26 | } 27 | 28 | changeApiUrl() { 29 | this.apiURLFull = this.apiURL + '/api/greeting'; 30 | this._logger.info('apiURLFull:', this.apiURLFull); 31 | 32 | const start = new Date().getTime(); 33 | this._service.getGreetings(this.apiURLFull).subscribe( 34 | data => { 35 | this.greetings = data; 36 | this._logger.info('greetings:', this.greetings); 37 | 38 | this._timing = (new Date().getTime() - start); 39 | this.timingMessage = 'Response time: ~' + this._timing + ' ms'; 40 | }, 41 | err => this._logger.error(err.message), 42 | () => { 43 | this._logger.info(this.timingMessage); 44 | } 45 | ); 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /src/app/observe/observe.service.spec.ts: -------------------------------------------------------------------------------- 1 | import {inject, TestBed} from '@angular/core/testing'; 2 | 3 | import {ObserveService} from './observe.service'; 4 | 5 | describe('ObserveService', () => { 6 | beforeEach(() => { 7 | TestBed.configureTestingModule({ 8 | providers: [ObserveService] 9 | }); 10 | }); 11 | 12 | it('should be created', inject([ObserveService], (service: ObserveService) => { 13 | expect(service).toBeTruthy(); 14 | })); 15 | }); 16 | -------------------------------------------------------------------------------- /src/app/observe/observe.service.ts: -------------------------------------------------------------------------------- 1 | import {Injectable} from '@angular/core'; 2 | import {HttpClient, HttpHeaders} from '@angular/common/http'; 3 | import {NGXLogger} from 'ngx-logger'; 4 | import {Greetings} from './Greetings'; 5 | 6 | const httpOptions = { 7 | headers: new HttpHeaders({'Content-Type': 'application/json'}) 8 | }; 9 | 10 | @Injectable() 11 | export class ObserveService { 12 | constructor(private _http: HttpClient, private _logger: NGXLogger) { 13 | } 14 | 15 | getGreetings(apiURL) { 16 | this._logger.debug('apiURL:', apiURL); 17 | this._logger.debug('httpOptions:', httpOptions); 18 | 19 | return this._http.get(apiURL, httpOptions); 20 | } 21 | 22 | } 23 | -------------------------------------------------------------------------------- /src/assets/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/garystafford/k8s-istio-observe-frontend/1e13e3e9bb40fa7b5f5c0e3df8f14f7ed245b3df/src/assets/.gitkeep -------------------------------------------------------------------------------- /src/environments/environment.docker.ts: -------------------------------------------------------------------------------- 1 | // The file contents for the current environment will overwrite these during build. 2 | // The build system defaults to the dev environment which uses `environment.ts`, but if you do 3 | // `ng build --env=prod` then `environment.prod.ts` will be used instead. 4 | // The list of which env maps to which file can be found in `.angular-cli.json`. 5 | 6 | export const environment = { 7 | production: false, 8 | configUrl: 'http://localhost:8080' 9 | }; 10 | -------------------------------------------------------------------------------- /src/environments/environment.prod.ts: -------------------------------------------------------------------------------- 1 | export const environment = { 2 | production: true, 3 | greetUrl: 'http://localhost' 4 | }; 5 | -------------------------------------------------------------------------------- /src/environments/environment.ts: -------------------------------------------------------------------------------- 1 | // The file contents for the current environment will overwrite these during build. 2 | // The build system defaults to the dev environment which uses `environment.ts`, but if you do 3 | // `ng build --env=prod` then `environment.prod.ts` will be used instead. 4 | // The list of which env maps to which file can be found in `.angular-cli.json`. 5 | 6 | export const environment = { 7 | production: false, 8 | greetUrl: 'http://localhost' 9 | }; 10 | -------------------------------------------------------------------------------- /src/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/garystafford/k8s-istio-observe-frontend/1e13e3e9bb40fa7b5f5c0e3df8f14f7ed245b3df/src/favicon.ico -------------------------------------------------------------------------------- /src/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Istio Observability Demo 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | -------------------------------------------------------------------------------- /src/main.ts: -------------------------------------------------------------------------------- 1 | import { enableProdMode } from '@angular/core'; 2 | import { platformBrowserDynamic } from '@angular/platform-browser-dynamic'; 3 | 4 | import { AppModule } from './app/app.module'; 5 | import { environment } from './environments/environment'; 6 | 7 | if (environment.production) { 8 | enableProdMode(); 9 | } 10 | 11 | platformBrowserDynamic().bootstrapModule(AppModule) 12 | .catch(err => console.log(err)); 13 | -------------------------------------------------------------------------------- /src/polyfills.ts: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | /** 5 | * Required to support Web Animations `@angular/platform-browser/animations`. 6 | * Needed for: All but Chrome, Firefox and Opera. http://caniuse.com/#feat=web-animation 7 | **/ 8 | // import 'web-animations-js'; // Run `npm install --save web-animations-js`. 9 | 10 | 11 | 12 | /*************************************************************************************************** 13 | * Zone JS is required by default for Angular itself. 14 | */ 15 | import 'zone.js/dist/zone'; // Included with Angular CLI. 16 | 17 | 18 | 19 | /*************************************************************************************************** 20 | * APPLICATION IMPORTS 21 | */ 22 | -------------------------------------------------------------------------------- /src/styles.css: -------------------------------------------------------------------------------- 1 | div.container { 2 | margin-top: 18px; 3 | /*margin-left: 36px;*/ 4 | } 5 | 6 | div#greetings { 7 | margin-top: 18px; 8 | } 9 | 10 | div#timing { 11 | margin-top: 18px; 12 | font-size: 14px; 13 | } 14 | 15 | small { 16 | margin-top:-6pt; 17 | color: darkgray; 18 | } 19 | .card-columns { 20 | column-count: 3; 21 | } 22 | .card { 23 | -webkit-column-break-inside: avoid; 24 | page-break-inside: avoid; 25 | break-inside: avoid; 26 | margin-bottom: 18px; 27 | 28 | } 29 | -------------------------------------------------------------------------------- /src/test.ts: -------------------------------------------------------------------------------- 1 | // This file is required by karma.conf.js and loads recursively all the .spec and framework files 2 | 3 | import 'zone.js/dist/long-stack-trace-zone'; 4 | import 'zone.js/dist/proxy.js'; 5 | import 'zone.js/dist/sync-test'; 6 | import 'zone.js/dist/jasmine-patch'; 7 | import 'zone.js/dist/async-test'; 8 | import 'zone.js/dist/fake-async-test'; 9 | import { getTestBed } from '@angular/core/testing'; 10 | import { 11 | BrowserDynamicTestingModule, 12 | platformBrowserDynamicTesting 13 | } from '@angular/platform-browser-dynamic/testing'; 14 | 15 | // Unfortunately there's no typing for the `__karma__` variable. Just declare it as any. 16 | declare const __karma__: any; 17 | declare const require: any; 18 | 19 | // Prevent Karma from running prematurely. 20 | __karma__.loaded = function () {}; 21 | 22 | // First, initialize the Angular testing environment. 23 | getTestBed().initTestEnvironment( 24 | BrowserDynamicTestingModule, 25 | platformBrowserDynamicTesting() 26 | ); 27 | // Then we find all the tests. 28 | const context = require.context('./', true, /\.spec\.ts$/); 29 | // And load the modules. 30 | context.keys().map(context); 31 | // Finally, start Karma to run the tests. 32 | __karma__.start(); 33 | -------------------------------------------------------------------------------- /src/tsconfig.app.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../tsconfig.json", 3 | "compilerOptions": { 4 | "outDir": "../out-tsc/app", 5 | "baseUrl": "./", 6 | "module": "es2020", 7 | "types": [] 8 | }, 9 | "exclude": [ 10 | "test.ts", 11 | "**/*.spec.ts" 12 | ] 13 | } 14 | -------------------------------------------------------------------------------- /src/tsconfig.spec.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../tsconfig.json", 3 | "compilerOptions": { 4 | "outDir": "../out-tsc/spec", 5 | "baseUrl": "./", 6 | "module": "commonjs", 7 | "target": "es5", 8 | "types": [ 9 | "jasmine", 10 | "node" 11 | ] 12 | }, 13 | "files": [ 14 | "test.ts", 15 | "polyfills.ts" 16 | ], 17 | "include": [ 18 | "**/*.spec.ts", 19 | "**/*.d.ts" 20 | ] 21 | } 22 | -------------------------------------------------------------------------------- /src/typings.d.ts: -------------------------------------------------------------------------------- 1 | /* SystemJS module definition */ 2 | declare var module: NodeModule; 3 | interface NodeModule { 4 | id: string; 5 | } 6 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compileOnSave": false, 3 | "compilerOptions": { 4 | "baseUrl": "./", 5 | "outDir": "./dist/out-tsc", 6 | "sourceMap": true, 7 | "declaration": false, 8 | "downlevelIteration": true, 9 | "experimentalDecorators": true, 10 | "moduleResolution": "node", 11 | "importHelpers": true, 12 | "target": "es2015", 13 | "module": "es2020", 14 | "lib": [ 15 | "es2018", 16 | "dom" 17 | ] 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /tslint.json: -------------------------------------------------------------------------------- 1 | { 2 | "rulesDirectory": [ 3 | "node_modules/codelyzer" 4 | ], 5 | "rules": { 6 | "arrow-return-shorthand": true, 7 | "callable-types": true, 8 | "class-name": true, 9 | "comment-format": [ 10 | true, 11 | "check-space" 12 | ], 13 | "curly": true, 14 | "deprecation": { 15 | "severity": "warn" 16 | }, 17 | "eofline": true, 18 | "forin": true, 19 | "import-blacklist": [ 20 | true, 21 | "rxjs/Rx" 22 | ], 23 | "import-spacing": true, 24 | "indent": [ 25 | true, 26 | "spaces" 27 | ], 28 | "interface-over-type-literal": true, 29 | "label-position": true, 30 | "max-line-length": [ 31 | true, 32 | 140 33 | ], 34 | "member-access": false, 35 | "member-ordering": [ 36 | true, 37 | { 38 | "order": [ 39 | "static-field", 40 | "instance-field", 41 | "static-method", 42 | "instance-method" 43 | ] 44 | } 45 | ], 46 | "no-arg": true, 47 | "no-bitwise": true, 48 | "no-console": [ 49 | true, 50 | "debug", 51 | "info", 52 | "time", 53 | "timeEnd", 54 | "trace" 55 | ], 56 | "no-construct": true, 57 | "no-debugger": true, 58 | "no-duplicate-super": true, 59 | "no-empty": false, 60 | "no-empty-interface": true, 61 | "no-eval": true, 62 | "no-inferrable-types": [ 63 | true, 64 | "ignore-params" 65 | ], 66 | "no-misused-new": true, 67 | "no-non-null-assertion": true, 68 | "no-shadowed-variable": true, 69 | "no-string-literal": false, 70 | "no-string-throw": true, 71 | "no-switch-case-fall-through": true, 72 | "no-trailing-whitespace": true, 73 | "no-unnecessary-initializer": true, 74 | "no-unused-expression": true, 75 | "no-use-before-declare": true, 76 | "no-var-keyword": true, 77 | "object-literal-sort-keys": false, 78 | "one-line": [ 79 | true, 80 | "check-open-brace", 81 | "check-catch", 82 | "check-else", 83 | "check-whitespace" 84 | ], 85 | "prefer-const": true, 86 | "quotemark": [ 87 | true, 88 | "single" 89 | ], 90 | "radix": true, 91 | "semicolon": [ 92 | true, 93 | "always" 94 | ], 95 | "triple-equals": [ 96 | true, 97 | "allow-null-check" 98 | ], 99 | "typedef-whitespace": [ 100 | true, 101 | { 102 | "call-signature": "nospace", 103 | "index-signature": "nospace", 104 | "parameter": "nospace", 105 | "property-declaration": "nospace", 106 | "variable-declaration": "nospace" 107 | } 108 | ], 109 | "typeof-compare": true, 110 | "unified-signatures": true, 111 | "variable-name": false, 112 | "whitespace": [ 113 | true, 114 | "check-branch", 115 | "check-decl", 116 | "check-operator", 117 | "check-separator", 118 | "check-type" 119 | ], 120 | "directive-selector": [ 121 | true, 122 | "attribute", 123 | "app", 124 | "camelCase" 125 | ], 126 | "component-selector": [ 127 | true, 128 | "element", 129 | "app", 130 | "kebab-case" 131 | ], 132 | "no-output-on-prefix": true, 133 | "use-input-property-decorator": true, 134 | "use-output-property-decorator": true, 135 | "use-host-property-decorator": true, 136 | "no-input-rename": true, 137 | "no-output-rename": true, 138 | "use-life-cycle-interface": true, 139 | "use-pipe-transform-interface": true, 140 | "component-class-suffix": true, 141 | "directive-class-suffix": true 142 | } 143 | } 144 | --------------------------------------------------------------------------------