├── .gitignore ├── .travis.yml ├── 4-application-full-stack.yaml ├── DigiCertAssuredIDCAG2.crt.pem ├── Dockerfile ├── README.md ├── ReadMes └── IstioClass.md ├── account-service.json.enc ├── ca.crt ├── deploy.sh ├── docker-compose.yml ├── index.html ├── k8s ├── 4-application-full-stack.yaml ├── clusterRole.yaml ├── tikitaka-app.yaml └── tikitaka-istio.yaml ├── k8sTests ├── api-access-role-bindling.yaml └── tikitaka-auth.yaml ├── package-lock.json ├── package.json ├── server ├── config │ └── keys.js ├── controllers │ ├── abTestController.js │ ├── createDepController.js │ ├── historyController.js │ ├── testController.js │ └── testControllerIstio.js ├── models │ ├── abTestModel.js │ └── historyModels.js └── server.js ├── skaffold.yaml ├── src ├── assets │ ├── IMG_9802.png │ ├── deployment.json │ ├── destinationrules.json │ ├── gateway.json │ ├── github-resume.jpg │ ├── img1.png │ ├── mario.png │ ├── service.json │ └── virtualservice.json ├── components │ ├── About.tsx │ ├── App.tsx │ ├── Bullets.tsx │ ├── FetcherComponent.tsx │ ├── Sidebar.tsx │ ├── TestDisplay.tsx │ └── TestForm.tsx ├── context │ ├── historyContext.tsx │ └── tikitakaContext.tsx ├── index.tsx └── styles │ └── app.scss ├── token ├── tsconfig.json └── webpack.config.js /.gitignore: -------------------------------------------------------------------------------- 1 | # See https://help.github.com/articles/ignoring-files/ for more about ignoring files. 2 | 3 | # dependencies 4 | /node_modules 5 | 6 | # testing 7 | 8 | 9 | # production 10 | /build 11 | 12 | # misc 13 | .DS_Store 14 | .idea 15 | .idea/workspace.xml 16 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | --- 2 | sudo: required 3 | services: 4 | - docker 5 | env: 6 | global: 7 | - "SHA=$(git rev-parse HEAD)" 8 | - "CLOUDSDK_CORE_DISABLE_PROMPTS=1" 9 | before_install: 10 | - "openssl aes-256-cbc -K $encrypted_6e1b6df37de9_key -iv $encrypted_6e1b6df37de9_iv -in account-service.json.enc -out account-service.json -d" 11 | - "curl https://sdk.cloud.google.com | bash > /dev/null;" 12 | - "source ~/google-cloud-sdk/path.zsh.inc" 13 | - "gcloud components update kubectl" 14 | - "gcloud auth activate-service-account --key-file account-service.json" 15 | - "gcloud config set project tikitakastage" 16 | - "gcloud config set compute/region us-west2" 17 | - "gcloud container clusters get-credentials tikitakastage --region=us-west2" 18 | - "echo \"$DOCKER_PASSWORD\" | docker login -u \"$DOCKER_USERNAME\" --password-stdin" 19 | - "docker build -t tikitaka1/general -f ./Dockerfile ./" 20 | deploy: 21 | provider: script 22 | script: "bash ./deploy.sh" 23 | on: 24 | branch: master 25 | -------------------------------------------------------------------------------- /4-application-full-stack.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: apps/v1 2 | kind: Deployment 3 | metadata: 4 | name: position-simulator 5 | spec: 6 | selector: 7 | matchLabels: 8 | app: position-simulator 9 | replicas: 1 10 | template: # template for the pods 11 | metadata: 12 | labels: 13 | app: position-simulator 14 | spec: 15 | containers: 16 | - name: position-simulator 17 | image: richardchesterwood/istio-fleetman-position-simulator:5 18 | env: 19 | - name: SPRING_PROFILES_ACTIVE 20 | value: production-microservice 21 | command: ["java","-Xmx50m","-jar","webapp.jar"] 22 | imagePullPolicy: Always 23 | --- 24 | apiVersion: apps/v1 25 | kind: Deployment 26 | metadata: 27 | name: position-tracker 28 | spec: 29 | selector: 30 | matchLabels: 31 | app: position-tracker 32 | replicas: 1 33 | template: # template for the pods 34 | metadata: 35 | labels: 36 | app: position-tracker 37 | spec: 38 | containers: 39 | - name: position-tracker 40 | image: richardchesterwood/istio-fleetman-position-tracker:5 41 | env: 42 | - name: SPRING_PROFILES_ACTIVE 43 | value: production-microservice 44 | command: ["java","-Xmx50m","-jar","webapp.jar"] 45 | imagePullPolicy: Always 46 | --- 47 | apiVersion: apps/v1 48 | kind: Deployment 49 | metadata: 50 | name: api-gateway 51 | spec: 52 | selector: 53 | matchLabels: 54 | app: api-gateway 55 | replicas: 1 56 | template: # template for the pods 57 | metadata: 58 | labels: 59 | app: api-gateway 60 | version: v2 61 | spec: 62 | containers: 63 | - name: api-gateway 64 | image: richardchesterwood/istio-fleetman-api-gateway:5 65 | env: 66 | - name: SPRING_PROFILES_ACTIVE 67 | value: production-microservice 68 | command: ["java","-Xmx50m","-jar","webapp.jar"] 69 | imagePullPolicy: Always 70 | --- 71 | apiVersion: apps/v1 72 | kind: Deployment 73 | metadata: 74 | name: webapp 75 | spec: 76 | selector: 77 | matchLabels: 78 | app: webapp 79 | replicas: 1 80 | template: # template for the pods 81 | metadata: 82 | labels: 83 | app: webapp 84 | spec: 85 | containers: 86 | - name: webapp 87 | image: richardchesterwood/istio-fleetman-webapp-angular:5 88 | env: 89 | - name: SPRING_PROFILES_ACTIVE 90 | value: production-microservice 91 | imagePullPolicy: Always 92 | --- 93 | apiVersion: apps/v1 94 | kind: Deployment 95 | metadata: 96 | name: vehicle-telemetry 97 | spec: 98 | selector: 99 | matchLabels: 100 | app: vehicle-telemetry 101 | replicas: 1 102 | template: # template for the pods 103 | metadata: 104 | labels: 105 | app: vehicle-telemetry 106 | spec: 107 | containers: 108 | - name: vehicle-telemtry 109 | image: richardchesterwood/istio-fleetman-vehicle-telemetry:5 110 | env: 111 | - name: SPRING_PROFILES_ACTIVE 112 | value: production-microservice 113 | imagePullPolicy: Always 114 | --- 115 | apiVersion: apps/v1 116 | kind: Deployment 117 | metadata: 118 | name: staff-service 119 | spec: 120 | selector: 121 | matchLabels: 122 | app: staff-service 123 | replicas: 1 124 | template: # template for the pods 125 | metadata: 126 | labels: 127 | app: staff-service 128 | spec: 129 | containers: 130 | - name: staff-service 131 | image: richardchesterwood/istio-fleetman-staff-service:5 132 | env: 133 | - name: SPRING_PROFILES_ACTIVE 134 | value: production-microservice 135 | imagePullPolicy: Always 136 | --- 137 | apiVersion: apps/v1 138 | kind: Deployment 139 | metadata: 140 | name: photo-service 141 | spec: 142 | selector: 143 | matchLabels: 144 | app: photo-service 145 | replicas: 1 146 | template: # template for the pods 147 | metadata: 148 | labels: 149 | app: photo-service 150 | spec: 151 | containers: 152 | - name: photo-service 153 | image: richardchesterwood/istio-fleetman-photo-service:5 154 | env: 155 | - name: SPRING_PROFILES_ACTIVE 156 | value: production-microservice 157 | imagePullPolicy: Always 158 | --- 159 | apiVersion: v1 160 | kind: Service 161 | metadata: 162 | name: fleetman-webapp 163 | spec: 164 | # This defines which pods are going to be represented by this Service 165 | # The service becomes a network endpoint for either other services 166 | # or maybe external users to connect to (eg browser) 167 | selector: 168 | app: webapp 169 | ports: 170 | - name: http 171 | port: 80 172 | nodePort: 30080 173 | type: NodePort 174 | --- 175 | apiVersion: v1 176 | kind: Service 177 | metadata: 178 | name: fleetman-position-tracker 179 | 180 | spec: 181 | # This defines which pods are going to be represented by this Service 182 | # The service becomes a network endpoint for either other services 183 | # or maybe external users to connect to (eg browser) 184 | selector: 185 | app: position-tracker 186 | ports: 187 | - name: http 188 | port: 8080 189 | type: ClusterIP 190 | --- 191 | apiVersion: v1 192 | kind: Service 193 | metadata: 194 | name: fleetman-api-gateway 195 | spec: 196 | selector: 197 | app: api-gateway 198 | ports: 199 | - name: http 200 | port: 8080 201 | type: ClusterIP 202 | --- 203 | apiVersion: v1 204 | kind: Service 205 | metadata: 206 | name: fleetman-vehicle-telemetry 207 | spec: 208 | selector: 209 | app: vehicle-telemetry 210 | ports: 211 | - name: http 212 | port: 8080 213 | type: ClusterIP 214 | --- 215 | apiVersion: v1 216 | kind: Service 217 | metadata: 218 | name: fleetman-staff-service 219 | spec: 220 | selector: 221 | app: staff-service 222 | ports: 223 | - name: http 224 | port: 8080 225 | type: ClusterIP 226 | --- 227 | apiVersion: v1 228 | kind: Service 229 | metadata: 230 | name: fleetman-photo-service 231 | spec: 232 | selector: 233 | app: photo-service 234 | ports: 235 | - name: http 236 | port: 8080 237 | type: ClusterIP 238 | --- 239 | # See case #23 240 | apiVersion: networking.istio.io/v1alpha3 241 | kind: ServiceEntry 242 | metadata: 243 | name: fleetman-driver-monitoring 244 | spec: 245 | hosts: 246 | - 2oujlno5e4.execute-api.us-east-1.amazonaws.com 247 | location: MESH_EXTERNAL 248 | ports: 249 | - number: 80 250 | name: http-port 251 | protocol: HTTP 252 | - number: 443 253 | name: https-port-for-tls-origination 254 | protocol: HTTPS 255 | resolution: DNS 256 | --- 257 | # See case #23 - this is a legacy service that we're integrating with 258 | apiVersion: networking.istio.io/v1alpha3 259 | kind: VirtualService 260 | metadata: 261 | name: fleetman-driver-monitoring 262 | spec: 263 | hosts: 264 | - 2oujlno5e4.execute-api.us-east-1.amazonaws.com 265 | http: 266 | - match: 267 | - port: 80 268 | route: 269 | - destination: 270 | host: 2oujlno5e4.execute-api.us-east-1.amazonaws.com 271 | port: 272 | number: 443 273 | --- 274 | apiVersion: networking.istio.io/v1alpha3 275 | kind: DestinationRule 276 | metadata: 277 | name: fleetman-driver-monitoring 278 | spec: 279 | host: 2oujlno5e4.execute-api.us-east-1.amazonaws.com 280 | trafficPolicy: 281 | loadBalancer: 282 | simple: ROUND_ROBIN 283 | portLevelSettings: 284 | - port: 285 | number: 443 286 | tls: 287 | mode: SIMPLE 288 | -------------------------------------------------------------------------------- /DigiCertAssuredIDCAG2.crt.pem: -------------------------------------------------------------------------------- 1 | -----BEGIN CERTIFICATE----- 2 | MIIEmjCCA4KgAwIBAgIQD1/M/Ksg89+ObaPYR2fCkzANBgkqhkiG9w0BAQsFADBl 3 | MQswCQYDVQQGEwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3 4 | d3cuZGlnaWNlcnQuY29tMSQwIgYDVQQDExtEaWdpQ2VydCBBc3N1cmVkIElEIFJv 5 | b3QgRzIwHhcNMTMwODAxMTIwMDAwWhcNMjgwODAxMTIwMDAwWjBIMQswCQYDVQQG 6 | EwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMSIwIAYDVQQDExlEaWdpQ2VydCBB 7 | c3N1cmVkIElEIENBIEcyMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA 8 | tFLF3hmmiyDchhlz2ZMtsf4L23l6z+g5odzcrNDI8QcZTBUm9xV9w5YZ6bRXU7jw 9 | ZlKaaVtxrqugWHNdlNfKL27RzC4kn1aKvwdt5Gj9j+UMzc/BMx3ksvGiHwQMBMGF 10 | zPAVG9aUpVXCXdUt0vSOLZxzI3x2Oqz2jL9pxc2l/bmgf9uhE4v2noPxViUQ4DF2 11 | h/cDRzV5qyZUq8FPw9y1JUi8Pl6HHAzFei/4OvhAQogerd8lvl8chlkT5h9UNeS0 12 | zdv5uDrNs5C/hJqXidKZU7nZTqh08B/RD8vsu9f66HavmbyrKXa2CveC0uqZvfQZ 13 | 0SBCj4ykeElnxyveHfOvUQIDAQABo4IBYTCCAV0wEgYDVR0TAQH/BAgwBgEB/wIB 14 | ADAOBgNVHQ8BAf8EBAMCAYYwNAYIKwYBBQUHAQEEKDAmMCQGCCsGAQUFBzABhhho 15 | dHRwOi8vb2NzcC5kaWdpY2VydC5jb20wgYEGA1UdHwR6MHgwOqA4oDaGNGh0dHA6 16 | Ly9jcmw0LmRpZ2ljZXJ0LmNvbS9EaWdpQ2VydEFzc3VyZWRJRFJvb3RHMi5jcmww 17 | OqA4oDaGNGh0dHA6Ly9jcmwzLmRpZ2ljZXJ0LmNvbS9EaWdpQ2VydEFzc3VyZWRJ 18 | RFJvb3RHMi5jcmwwPQYDVR0gBDYwNDAyBgRVHSAAMCowKAYIKwYBBQUHAgEWHGh0 19 | dHBzOi8vd3d3LmRpZ2ljZXJ0LmNvbS9DUFMwHQYDVR0OBBYEFLHP9qHl5oHFZkeN 20 | eIR3e/mBy0qmMB8GA1UdIwQYMBaAFM7DSrmZVfK422C/qX69VrWXNqfWMA0GCSqG 21 | SIb3DQEBCwUAA4IBAQA/C7UkzAlgi5mvLq7ukp0JS9Vg+rMNyx3XVkBF84vz9exk 22 | 74Y0vPnniZoy/pAgOaFFGbsVBI7SNf/iGI5x5UG5PKWo+K0AP0fwkZFnfEAN0wJy 23 | Dezr2oLi95s2XCoqThxhtJe6t1A8Lh6klGQGkgREdyF7Y6obGQq+7hDz4VRR4MCM 24 | m5QHcPcYJzh0pQueTxadgp6AbksehUf/g9eGzwzHqdkkAIQ9mzpRRxjoqkaPTtSd 25 | kpPl/ZZcDqPzDzWXhzKfJs90lSuYy1z1fQEgFZP/X1DGT8SGzAlfv83e+FC3ja7w 26 | JZJ4ZTqw8UTUUpI52U2HVCMXKNtEFEyMYLeRJe+j 27 | -----END CERTIFICATE----- 28 | -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | FROM node:13 2 | WORKDIR /usr/src/app 3 | COPY ./package.json . 4 | RUN npm install 5 | COPY ./ ./ 6 | RUN npm run build 7 | EXPOSE 3000 8 | # CMD ["node", "./server/server.js"] 9 | CMD ["npm", "start"] 10 | 11 | 12 | # FROM node:13 13 | 14 | # WORKDIR /usr/src/app 15 | 16 | # COPY . . 17 | 18 | # RUN npm install 19 | 20 | # RUN npm run build 21 | 22 | # EXPOSE 3000 23 | 24 | # CMD ["node", "./server/server.js"] 25 | 26 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Istio-AB-testing -------------------------------------------------------------------------------- /ReadMes/IstioClass.md: -------------------------------------------------------------------------------- 1 | #### Istio 2 | 3 | ##### Sticky Sessions 4 | 5 | ##### What is Consistent Hashing Useful For 6 | - Feign equivalent for Node.js to be found, for header propagation. 7 | - Using headers for consistent hashing may not be very good because each heather needs to be propagated. 8 | - [] Envoy proxy issue 8167. 9 | - General Consistent Hashing useful for: equally distributing balancing traffic. 10 | - Sticky solution does not create a session, because pod that is hashed might fail. 11 | 12 | ##### Gateways 13 | ###### Ingress Gateways 14 | - Kuberetes Ingress Resource and Istio Gateway are different 15 | - In match conditions for a Virtual Service, the same level contions represents OR conditions. 16 | ``` 17 | kind: Virutal Service 18 | ........ 19 | http: 20 | - match: 21 | - uri: #IF 22 | prefix: "/experimental" 23 | - uri: #OR 24 | prefix: "/canary" 25 | route: #THEN 26 | - destination: 27 | host: fleetman-webapp 28 | subset: experimental 29 | weight: 100 30 | - match: 31 | - uri: 32 | prefix: "/" 33 | route: 34 | - destination: 35 | host: fleetman-webapp 36 | subset: original 37 | ``` 38 | #### Dark Releases 39 | > Definition: We can deploy another version of the app and give it a different header when making requests to the back 40 | > end, that header usually is only known by the developers, so it can be tested within the live server. 41 | ###### Header Routing Example: 42 | ``` 43 | kind: Virtual Service 44 | ........... 45 | http: 46 | - name: "rule 1" 47 | match: 48 | - headers: 49 | my-header: 50 | exact: canary 51 | route: 52 | - destination: 53 | host: fleetman-webapp 54 | subset: experimental 55 | - name: "rule 2" #CATCH ALL 56 | route: 57 | - destination: 58 | host: fleetman-webapp 59 | subset: original 60 | ``` 61 | - If you do not write a matching condition to the rule, it becomes a catch all route. 62 | -------------------------------------------------------------------------------- /account-service.json.enc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/oslabs-beta/Tikitaka/2e97fbb94890e95a831a101d88628cb1d95d1d6c/account-service.json.enc -------------------------------------------------------------------------------- /ca.crt: -------------------------------------------------------------------------------- 1 | -----BEGIN CERTIFICATE----- 2 | MIICyDCCAbCgAwIBAgIBADANBgkqhkiG9w0BAQsFADAVMRMwEQYDVQQDEwprdWJl 3 | cm5ldGVzMB4XDTIwMDEyNDA2MjgyOFoXDTMwMDEyMTA2MjgyOFowFTETMBEGA1UE 4 | AxMKa3ViZXJuZXRlczCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAMhj 5 | YAlh/Z6qjem0w+XdsEK5QKlax2C+mhbtffpsV2LWrshOPAIT4T78AMhIhGlbT2Ad 6 | UDZdR9dTM7jjOyc+S8vP0PV4OlG5VGWOvidnaK5/JTmSJBkMOvydQqx1cU4IzAnb 7 | GUts0N6rtpY95T9elWvOzr2C5CJgda3/iojW9FiDt3ovZdGrbJbrAXh/TNt0k/s8 8 | SEhQlR9PFm6bfgZWmJHFj8c1BtxQ3vNd38jiIUvR/kMm74xkDPUzAW2Rw8zSrhqK 9 | KdxiaH8BYY5lq/5iuhOE6FaRmNnADSO40H6U3sEjRXXuKD/PpWwVWfynvmQVufub 10 | g1/VHNVqHc3Hrl4aJhECAwEAAaMjMCEwDgYDVR0PAQH/BAQDAgKkMA8GA1UdEwEB 11 | /wQFMAMBAf8wDQYJKoZIhvcNAQELBQADggEBABIi7HvxaS0mnk+BHkiYQ8sSVu52 12 | 4ekDRw8NpaJbrSfip5kroLU+am/stODPjBXE3ALvyrMLiXseGLJDYR1cMZY1JvvA 13 | Y9/JUHpusc4Wd3g+s10P6P7sIgUh9RJ3Y83NWh76xAA98lUf/uFQOzAO/DPQT97y 14 | dlwOO2ydFNNNVwNhLQdYC1cZHT03NuWCGHXr8xVz1toEeUSYRmsBPRhgj7JZ9VzN 15 | HjOjxdbv/y+SnhcXgyxaaIUKLK+n/a62y//X9EzVeAr0HQsD7MpFzt/SAwXA/03U 16 | szYyd3UnItjzrHVXVONpODv149Z8wfnytRbgv02N7sHGhLJ38Jh0r+Hd3zk= 17 | -----END CERTIFICATE----- 18 | -------------------------------------------------------------------------------- /deploy.sh: -------------------------------------------------------------------------------- 1 | docker build -t tikitaka1/general:latest -t tikitaka1/general:$SHA -f ./Dockerfile ./ 2 | docker push tikitaka1/general:latest 3 | docker push armangurkan1/multi-client:$SHA 4 | kubectl apply -f ./k8s 5 | kubectl set image deployments/general-deployment general=tikitaka1/general:$SHA 6 | -------------------------------------------------------------------------------- /docker-compose.yml: -------------------------------------------------------------------------------- 1 | version: '3' 2 | 3 | services: 4 | tikitaka: 5 | restart: always 6 | build: 7 | dockerfile: Dockerfile 8 | context: ./ 9 | ports: 10 | - "3000:3000" 11 | volumes: 12 | - /usr/src/app/node_modules 13 | - ./:/usr/src/app 14 | -------------------------------------------------------------------------------- /index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | TIKITAKA 8 | 9 | 10 |
11 | 12 | -------------------------------------------------------------------------------- /k8s/4-application-full-stack.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: apps/v1 2 | kind: Deployment 3 | metadata: 4 | name: position-simulator 5 | spec: 6 | selector: 7 | matchLabels: 8 | app: position-simulator 9 | replicas: 1 10 | template: # template for the pods 11 | metadata: 12 | labels: 13 | app: position-simulator 14 | spec: 15 | containers: 16 | - name: position-simulator 17 | image: richardchesterwood/istio-fleetman-position-simulator:5 18 | env: 19 | - name: SPRING_PROFILES_ACTIVE 20 | value: production-microservice 21 | command: ["java","-Xmx50m","-jar","webapp.jar"] 22 | imagePullPolicy: Always 23 | --- 24 | apiVersion: apps/v1 25 | kind: Deployment 26 | metadata: 27 | name: position-tracker 28 | spec: 29 | selector: 30 | matchLabels: 31 | app: position-tracker 32 | replicas: 1 33 | template: # template for the pods 34 | metadata: 35 | labels: 36 | app: position-tracker 37 | spec: 38 | containers: 39 | - name: position-tracker 40 | image: richardchesterwood/istio-fleetman-position-tracker:5 41 | env: 42 | - name: SPRING_PROFILES_ACTIVE 43 | value: production-microservice 44 | command: ["java","-Xmx50m","-jar","webapp.jar"] 45 | imagePullPolicy: Always 46 | --- 47 | apiVersion: apps/v1 48 | kind: Deployment 49 | metadata: 50 | name: api-gateway 51 | spec: 52 | selector: 53 | matchLabels: 54 | app: api-gateway 55 | replicas: 1 56 | template: # template for the pods 57 | metadata: 58 | labels: 59 | app: api-gateway 60 | version: v2 61 | spec: 62 | containers: 63 | - name: api-gateway 64 | image: richardchesterwood/istio-fleetman-api-gateway:5 65 | env: 66 | - name: SPRING_PROFILES_ACTIVE 67 | value: production-microservice 68 | command: ["java","-Xmx50m","-jar","webapp.jar"] 69 | imagePullPolicy: Always 70 | --- 71 | apiVersion: apps/v1 72 | kind: Deployment 73 | metadata: 74 | name: webapp 75 | spec: 76 | selector: 77 | matchLabels: 78 | app: webapp 79 | replicas: 1 80 | template: # template for the pods 81 | metadata: 82 | labels: 83 | app: webapp 84 | spec: 85 | containers: 86 | - name: webapp 87 | image: richardchesterwood/istio-fleetman-webapp-angular:5 88 | env: 89 | - name: SPRING_PROFILES_ACTIVE 90 | value: production-microservice 91 | imagePullPolicy: Always 92 | --- 93 | apiVersion: apps/v1 94 | kind: Deployment 95 | metadata: 96 | name: vehicle-telemetry 97 | spec: 98 | selector: 99 | matchLabels: 100 | app: vehicle-telemetry 101 | replicas: 1 102 | template: # template for the pods 103 | metadata: 104 | labels: 105 | app: vehicle-telemetry 106 | spec: 107 | containers: 108 | - name: vehicle-telemtry 109 | image: richardchesterwood/istio-fleetman-vehicle-telemetry:5 110 | env: 111 | - name: SPRING_PROFILES_ACTIVE 112 | value: production-microservice 113 | imagePullPolicy: Always 114 | --- 115 | apiVersion: apps/v1 116 | kind: Deployment 117 | metadata: 118 | name: staff-service 119 | spec: 120 | selector: 121 | matchLabels: 122 | app: staff-service 123 | replicas: 1 124 | template: # template for the pods 125 | metadata: 126 | labels: 127 | app: staff-service 128 | spec: 129 | containers: 130 | - name: staff-service 131 | image: richardchesterwood/istio-fleetman-staff-service:5 132 | env: 133 | - name: SPRING_PROFILES_ACTIVE 134 | value: production-microservice 135 | imagePullPolicy: Always 136 | --- 137 | apiVersion: apps/v1 138 | kind: Deployment 139 | metadata: 140 | name: photo-service 141 | spec: 142 | selector: 143 | matchLabels: 144 | app: photo-service 145 | replicas: 1 146 | template: # template for the pods 147 | metadata: 148 | labels: 149 | app: photo-service 150 | spec: 151 | containers: 152 | - name: photo-service 153 | image: richardchesterwood/istio-fleetman-photo-service:5 154 | env: 155 | - name: SPRING_PROFILES_ACTIVE 156 | value: production-microservice 157 | imagePullPolicy: Always 158 | --- 159 | apiVersion: v1 160 | kind: Service 161 | metadata: 162 | name: fleetman-webapp 163 | spec: 164 | # This defines which pods are going to be represented by this Service 165 | # The service becomes a network endpoint for either other services 166 | # or maybe external users to connect to (eg browser) 167 | selector: 168 | app: webapp 169 | ports: 170 | - name: http 171 | port: 80 172 | nodePort: 30080 173 | type: NodePort 174 | --- 175 | apiVersion: v1 176 | kind: Service 177 | metadata: 178 | name: fleetman-position-tracker 179 | 180 | spec: 181 | # This defines which pods are going to be represented by this Service 182 | # The service becomes a network endpoint for either other services 183 | # or maybe external users to connect to (eg browser) 184 | selector: 185 | app: position-tracker 186 | ports: 187 | - name: http 188 | port: 8080 189 | type: ClusterIP 190 | --- 191 | apiVersion: v1 192 | kind: Service 193 | metadata: 194 | name: fleetman-api-gateway 195 | spec: 196 | selector: 197 | app: api-gateway 198 | ports: 199 | - name: http 200 | port: 8080 201 | type: ClusterIP 202 | --- 203 | apiVersion: v1 204 | kind: Service 205 | metadata: 206 | name: fleetman-vehicle-telemetry 207 | spec: 208 | selector: 209 | app: vehicle-telemetry 210 | ports: 211 | - name: http 212 | port: 8080 213 | type: ClusterIP 214 | --- 215 | apiVersion: v1 216 | kind: Service 217 | metadata: 218 | name: fleetman-staff-service 219 | spec: 220 | selector: 221 | app: staff-service 222 | ports: 223 | - name: http 224 | port: 8080 225 | type: ClusterIP 226 | --- 227 | apiVersion: v1 228 | kind: Service 229 | metadata: 230 | name: fleetman-photo-service 231 | spec: 232 | selector: 233 | app: photo-service 234 | ports: 235 | - name: http 236 | port: 8080 237 | type: ClusterIP 238 | --- 239 | # See case #23 240 | apiVersion: networking.istio.io/v1alpha3 241 | kind: ServiceEntry 242 | metadata: 243 | name: fleetman-driver-monitoring 244 | spec: 245 | hosts: 246 | - 2oujlno5e4.execute-api.us-east-1.amazonaws.com 247 | location: MESH_EXTERNAL 248 | ports: 249 | - number: 80 250 | name: http-port 251 | protocol: HTTP 252 | - number: 443 253 | name: https-port-for-tls-origination 254 | protocol: HTTPS 255 | resolution: DNS 256 | --- 257 | # See case #23 - this is a legacy service that we're integrating with 258 | apiVersion: networking.istio.io/v1alpha3 259 | kind: VirtualService 260 | metadata: 261 | name: fleetman-driver-monitoring 262 | spec: 263 | hosts: 264 | - 2oujlno5e4.execute-api.us-east-1.amazonaws.com 265 | http: 266 | - match: 267 | - port: 80 268 | route: 269 | - destination: 270 | host: 2oujlno5e4.execute-api.us-east-1.amazonaws.com 271 | port: 272 | number: 443 273 | --- 274 | apiVersion: networking.istio.io/v1alpha3 275 | kind: DestinationRule 276 | metadata: 277 | name: fleetman-driver-monitoring 278 | spec: 279 | host: 2oujlno5e4.execute-api.us-east-1.amazonaws.com 280 | trafficPolicy: 281 | loadBalancer: 282 | simple: ROUND_ROBIN 283 | portLevelSettings: 284 | - port: 285 | number: 443 286 | tls: 287 | mode: SIMPLE 288 | -------------------------------------------------------------------------------- /k8s/clusterRole.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: rbac.authorization.k8s.io/v1beta1 2 | kind: ClusterRole 3 | metadata: 4 | name: api-access 5 | rules: 6 | - 7 | apiGroups: 8 | - "*" 9 | resources: 10 | - "*" 11 | verbs: ["*"] 12 | - nonResourceURLs: ["*"] 13 | verbs: ["*"] 14 | --- 15 | apiVersion: rbac.authorization.k8s.io/v1beta1 16 | kind: ClusterRoleBinding 17 | metadata: 18 | name: api-access 19 | roleRef: 20 | apiGroup: rbac.authorization.k8s.io 21 | kind: ClusterRole 22 | name: api-access 23 | subjects: 24 | - kind: ServiceAccount 25 | name: api-service-account 26 | namespace: default -------------------------------------------------------------------------------- /k8s/tikitaka-app.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: Service 3 | metadata: 4 | name: tikitaka 5 | labels: 6 | app: tikitaka 7 | spec: 8 | selector: 9 | app: tikitaka 10 | ports: 11 | - name: http 12 | port: 3000 13 | --- 14 | apiVersion: apps/v1 15 | kind: Deployment 16 | metadata: 17 | name: tikitaka 18 | labels: 19 | version: v1 20 | spec: 21 | replicas: 1 22 | selector: 23 | matchLabels: 24 | app: tikitaka 25 | template: 26 | metadata: 27 | labels: 28 | app: tikitaka 29 | version: v1 30 | spec: 31 | serviceAccountName: api-service-account 32 | containers: 33 | - name: tikitaka 34 | image: tikitaka1/tikitaka-stage 35 | ports: 36 | - containerPort: 3000 37 | imagePullPolicy: Always 38 | -------------------------------------------------------------------------------- /k8s/tikitaka-istio.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: networking.istio.io/v1alpha3 2 | kind: Gateway 3 | metadata: 4 | name: tikitaka-gateway 5 | spec: 6 | selector: 7 | istio: ingressgateway 8 | servers: 9 | - port: 10 | number: 80 11 | name: http 12 | protocol: HTTP 13 | hosts: 14 | - "*" 15 | --- 16 | apiVersion: networking.istio.io/v1alpha3 17 | kind: VirtualService 18 | metadata: 19 | name: tikitaka 20 | spec: 21 | hosts: 22 | - "*" 23 | gateways: 24 | - tikitaka-gateway 25 | http: 26 | - route: 27 | - destination: 28 | host: tikitaka 29 | -------------------------------------------------------------------------------- /k8sTests/api-access-role-bindling.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: rbac.authorization.k8s.io/v1beta1 2 | kind: ClusterRole 3 | metadata: 4 | name: api-access 5 | rules: 6 | - 7 | apiGroups: ["*"] 8 | resources: ["*"] 9 | verbs: ["*"] 10 | - nonResourceURLs: ["*"] 11 | verbs: ["*"] 12 | --- 13 | apiVersion: rbac.authorization.k8s.io/v1beta1 14 | kind: ClusterRoleBinding 15 | metadata: 16 | name: api-access 17 | roleRef: 18 | apiGroup: rbac.authorization.k8s.io 19 | kind: ClusterRole 20 | name: api-access 21 | subjects: 22 | - kind: ServiceAccount 23 | name: api-service-account 24 | namespace: default 25 | -------------------------------------------------------------------------------- /k8sTests/tikitaka-auth.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: Service 3 | metadata: 4 | name: tikitaka 5 | labels: 6 | app: tikitaka 7 | spec: 8 | selector: 9 | app: tikitaka 10 | ports: 11 | - name: http 12 | port: 3000 13 | --- 14 | apiVersion: apps/v1 15 | kind: Deployment 16 | metadata: 17 | name: tikitaka 18 | labels: 19 | version: v1 20 | spec: 21 | replicas: 1 22 | selector: 23 | matchLabels: 24 | app: tikitaka 25 | template: 26 | metadata: 27 | labels: 28 | app: tikitaka 29 | version: v1 30 | spec: 31 | containers: 32 | - name: tikitaka 33 | image: tikitaka1/tikitaka-stage:latest 34 | ports: 35 | - containerPort: 3000 36 | imagePullPolicy: Always 37 | volumeMounts: 38 | - name: tikitaka-certs 39 | mountPath: /certs 40 | # Create on-disk volume to store exec logs 41 | - mountPath: /tmp 42 | name: tmp-volume 43 | volumes: 44 | - name: tikitaka-certs 45 | secret: 46 | secretName: tikitaka-certs 47 | - name: tmp-volume 48 | emptyDir: {} 49 | serviceAccountName: tikitaka 50 | # Comment the following tolerations if Dashboard must not be deployed on master 51 | # tolerations: 52 | # - key: node-role.kubernetes.io/master 53 | # effect: NoSchedule 54 | 55 | --- 56 | apiVersion: v1 57 | kind: ServiceAccount 58 | metadata: 59 | labels: 60 | app: tikitaka 61 | name: tikitaka 62 | namespace: default 63 | 64 | --- 65 | apiVersion: v1 66 | kind: Secret 67 | metadata: 68 | labels: 69 | app: tikitaka 70 | name: tikitaka-certs 71 | namespace: kube-system 72 | type: Opaque 73 | 74 | --- 75 | kind: ClusterRole 76 | apiVersion: rbac.authorization.k8s.io/v1 77 | metadata: 78 | name: tikitaka 79 | namespace: default 80 | rules: 81 | - apiGroups: ["", "apps", "*"] 82 | resources: ["*"] 83 | verbs: ["*"] 84 | 85 | --- 86 | 87 | kind: ClusterRoleBinding 88 | apiVersion: rbac.authorization.k8s.io/v1 89 | metadata: 90 | name: tikitaka-role-binding 91 | namespace: default 92 | subjects: 93 | - kind: ServiceAccount 94 | name: tikitaka 95 | namespace: default 96 | roleRef: 97 | apiGroup: rbac.authorization.k8s.io 98 | kind: ClusterRole 99 | name: tikitaka 100 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "dash", 3 | "version": "1.0.0", 4 | "description": "", 5 | "main": "index.js", 6 | "scripts": { 7 | "build": "webpack --config ./webpack.config.js", 8 | "start": "node ./server/server.js webpack-dev-server", 9 | "dev": "NODE_ENV=development nodemon ./server/server.js & webpack-dev-server --open --hot --inline --progress --colors --watch --content-base ./", 10 | "docker-dev:hot": "docker-compose -f docker-compose-dev-hot.yml up", 11 | "test": "NODE_ENV=test jest --coverage" 12 | }, 13 | "nodemonConfig": { 14 | "ignore": [ 15 | "build", 16 | "src" 17 | ] 18 | }, 19 | "author": "", 20 | "license": "ISC", 21 | "dependencies": { 22 | "@nivo/bullet": "^0.61.1", 23 | "@types/d3-shape": "^1.3.2", 24 | "@types/react-router-dom": "^5.1.3", 25 | "@vx/gradient": "0.0.193-alpha.2", 26 | "@vx/hierarchy": "0.0.193-alpha.2", 27 | "@vx/network": "0.0.193-alpha.2", 28 | "@vx/shape": "0.0.193-alpha.2", 29 | "bootstrap": "^4.4.1", 30 | "cors": "^2.8.5", 31 | "d3-hierarchy": "^1.1.9", 32 | "d3-shape": "^1.3.7", 33 | "express": "^4.17.1", 34 | "kubernetes-client": "^8.3.6", 35 | "node-fetch": "^2.6.0", 36 | "react": "^16.12.0", 37 | "react-bootstrap": "^1.0.0-beta.16", 38 | "react-dom": "^16.12.0", 39 | "react-router-dom": "^5.1.2", 40 | "request": "^2.88.0", 41 | "skaffold": "^1.0.11", 42 | "xmlhttprequest": "^1.8.0" 43 | }, 44 | "devDependencies": { 45 | "nodemon": "^2.0.2", 46 | "@babel/preset-env": "^7.8.3", 47 | "@babel/preset-react": "^7.8.3", 48 | "@types/react": "^16.9.19", 49 | "@types/react-dom": "^16.9.5", 50 | "css-loader": "^3.4.2", 51 | "html-webpack-plugin": "^3.2.0", 52 | "mini-css-extract-plugin": "^0.9.0", 53 | "node-sass": "^4.13.1", 54 | "sass-loader": "^8.0.2", 55 | "source-map-loader": "^0.2.4", 56 | "style-loader": "^1.1.3", 57 | "ts-loader": "^6.2.1", 58 | "typescript": "^3.7.5", 59 | "webpack": "^4.41.5", 60 | "webpack-cli": "^3.3.10", 61 | "webpack-dev-server": "^3.10.1" 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /server/config/keys.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | MONGO_URI: "mongodb+srv://tikitaka:tikitaka1234@cluster0-ldp4w.mongodb.net/test?retryWrites=true&w=majority" 3 | } -------------------------------------------------------------------------------- /server/controllers/abTestController.js: -------------------------------------------------------------------------------- 1 | const ABTest = require('../models/abTestModel'); 2 | 3 | const abTestController = {}; 4 | abTestController.verifyAB_compatibility = (req, res, next) => { 5 | //req body parameters of for testB field should all be included in testA fields or else return error. 6 | }; 7 | abTestController.createABTest = (req, res, next) => { 8 | 9 | }; 10 | abTestController.startABTest = (req, res, next) => {} 11 | abTestController.updateABTest = (req, res, next) => {} 12 | abTestController.getABTest = (req, res, next) => {} 13 | abTestController.getAllABTests = (req, res, next) => {} 14 | abTestController.stopABTest = (req, res, next) => {} 15 | abTestController.archiveABTest = (req, res, next) => {} 16 | abTestController.deleteABTest = (req, res, next) => {} 17 | module.exports = abTestController; 18 | -------------------------------------------------------------------------------- /server/controllers/createDepController.js: -------------------------------------------------------------------------------- 1 | const fetch = require('node-fetch'); 2 | const fs = require('fs'); 3 | 4 | const token = fs.readFileSync('/var/run/secrets/kubernetes.io/serviceaccount/token', 'utf8'); 5 | const body = { 6 | "kind": "Deployment", 7 | "metadata": { 8 | "name": "tiki-taka-rocks-hard", 9 | "labels": { 10 | "app": "reviews", 11 | "version": "v1" 12 | } 13 | }, 14 | "spec": { 15 | "replicas": 1, 16 | "selector": { 17 | "matchLabels": { 18 | "app": "reviews", 19 | "version": "v1" 20 | } 21 | }, 22 | "template": { 23 | "metadata": { 24 | "labels": { 25 | "app": "reviews", 26 | "version": "v1" 27 | } 28 | }, 29 | "spec": { 30 | "serviceAccountName": "bookinfo-reviews", 31 | "containers": [ 32 | { 33 | "name": "reviews", 34 | "image": "docker.io/istio/examples-bookinfo-reviews-v1:1.15.0", 35 | "imagePullPolicy": "IfNotPresent", 36 | "ports": [ 37 | { 38 | "containerPort": 9080 39 | } 40 | ] 41 | } 42 | ] 43 | } 44 | } 45 | } 46 | }; 47 | 48 | const createDepController = {}; 49 | process.env['NODE_TLS_REJECT_UNAUTHORIZED'] = '0'; 50 | createDepController.addVirtualService = (req, res, next) => { 51 | let newIm = {}; 52 | const { imageA, weightA, imageB, addressB, versionB } = req.body; 53 | fetch(`https://kubernetes.default.svc/apis/apps/v1/namespaces/default/deployments/reviews-v1`) 54 | .then(data => data.json()).then(json => {newIm = json}).catch(e => console.log(e)); 55 | 56 | fetch('https://kubernetes.default.svc/apis/apps/v1/namespaces/default/deployments/', { 57 | method: 'POST', 58 | body: JSON.stringify(body), 59 | requestCert: false, 60 | rejectUnauthorized: false, 61 | headers: { 'Content-Type': 'application/json', 62 | 'Authorization': `Bearer ${token}` 63 | }, 64 | ca: [fs.readFileSync('/var/run/secrets/kubernetes.io/serviceaccount/ca.crt', 'utf8'), fs.readFileSync('/usr/src/app/DigiCertAssuredIDCAG2.crt.pem', 'utf8') ], 65 | }) 66 | .then(r => r.json()).then(json => console.log(json)).catch(e => console.log(e)); 67 | return next(); 68 | }; 69 | module.exports = createDepController; 70 | -------------------------------------------------------------------------------- /server/controllers/historyController.js: -------------------------------------------------------------------------------- 1 | // const { History } = require('../models/historyModels'); 2 | 3 | // const historyController = {}; 4 | 5 | // historyController.getHistory((req, res, next) => { 6 | // return next(); 7 | // }); 8 | 9 | // module.exports = historyController; -------------------------------------------------------------------------------- /server/controllers/testController.js: -------------------------------------------------------------------------------- 1 | const testController = {}; 2 | const fetch = require("node-fetch"); 3 | 4 | /** 5 | * Sending cluster pods information 6 | */ 7 | testController.getData = async (req, res, next) => { 8 | console.log('data'); 9 | const url = 'http://localhost:8081/apis/apps/v1/namespaces/default/deployments/' 10 | await fetch(url) 11 | .then (resp => resp.json()) 12 | .then (data => { 13 | res.locals.data = data; 14 | console.log(data); 15 | }) 16 | .catch (err => console.log(err)); 17 | return next(); 18 | } 19 | 20 | /** 21 | * testingAB - 22 | */ 23 | testController.testingAB = (req, res, next) => { 24 | console.log('ab'); 25 | return next(); 26 | } 27 | 28 | module.exports = testController; 29 | -------------------------------------------------------------------------------- /server/controllers/testControllerIstio.js: -------------------------------------------------------------------------------- 1 | const testController = {}; 2 | 3 | /** 4 | * Sending cluster pods information 5 | */ 6 | testController.setup = (req, res, next) => { 7 | async function main () { 8 | try { 9 | const backend = new Request(Request.config.getInCluster()) 10 | const client = new Client({ backend }) 11 | await client.loadSpec() 12 | 13 | // 14 | // Fetch the Deployment from cluste 15 | // 16 | const deployment = await client.apis.apps.v1.namespaces('default').deployments().get(); 17 | const data = []; 18 | deployment.body.items.forEach((item) => { 19 | data.push(item.metadata); 20 | console.log(item.metadata); 21 | }); 22 | res.locals.data = data; 23 | } catch (err) { 24 | res.locals.data = err; 25 | console.error('Error: ', err) 26 | } 27 | next(); 28 | } 29 | main(); 30 | } 31 | 32 | /** 33 | * testingAB - 34 | */ 35 | testController.testingAB = (req, res, next) => { 36 | async function applyDeploy () { 37 | try { 38 | const backend = new Request(Request.config.getInCluster()) 39 | const client = new Client({ backend }) 40 | await client.loadSpec() 41 | 42 | let opts = { 43 | apiVersion: 'networking.istio.io/v1alpha3', 44 | kind: 'VirtualService', 45 | metadata: { 46 | name: 'bookinfotest', 47 | namespace: 'default' 48 | }, 49 | spec: { 50 | gateways:[ 51 | 'bookinfo-gateway', 52 | ], 53 | hosts: [ 54 | 'holllaaa' 55 | ], 56 | http: [{ 57 | route: [ 58 | { 59 | destination: { 60 | host: '*', 61 | subset: 'safe' 62 | } 63 | } 64 | ] 65 | }] 66 | } 67 | } 68 | opts = JSON.stringify(opts); 69 | try{ 70 | const create = await client.apis['networking.istio.io'].v1alpha3.namespaces('default').virtualservices.post({body:opts}) 71 | res.locals.data = create.body; 72 | console.log('Create:', create); 73 | }catch (err){ 74 | res.locals.data = err; 75 | } 76 | } catch (err) { 77 | if (err.code !== 409) throw err 78 | } 79 | next(); 80 | } 81 | applyDeploy() 82 | } 83 | 84 | module.exports = testController; -------------------------------------------------------------------------------- /server/models/abTestModel.js: -------------------------------------------------------------------------------- 1 | const mongoose = require('mongoose'); 2 | const Schema = mongoose.Schema; 3 | 4 | const abTestSchema = new Schema({ 5 | name: {type: String, required: true, unique: true}, 6 | //the A Test props 7 | testA: {}, 8 | //the B test props would be a list of annotations those are changed from test A 9 | //if a property set here is not present in Test A it should give an error. 10 | //string is chosen as the type of values for predictability 11 | //Todo: handle any different values different than testA has in creation. 12 | //Todo: handle any different values different than testA has on update. 13 | testB: { 14 | type: Map, 15 | of: String 16 | }, 17 | archived: Boolean, 18 | active: {type: Boolean, required: true, default: true}, 19 | relatedCustomResourceDefinitions: { 20 | type: Map, 21 | //todo: check this if you have to relate to any other Schema 22 | of: [String] 23 | }, 24 | relatedServices: [String], 25 | relatedGateways: [String] 26 | 27 | 28 | }); 29 | 30 | 31 | module.exports = mongoose.model('abTest', abTestSchema); 32 | -------------------------------------------------------------------------------- /server/models/historyModels.js: -------------------------------------------------------------------------------- 1 | // 'use strict' 2 | // const mongoose = require('mongoose'); 3 | // const Schema = mongoose.Schema; 4 | 5 | // const historySchema = new Schema({ 6 | // podAName: { type: String, required: true }, 7 | // podAWeight: { type: Number }, 8 | // podAContent: { type: String }, 9 | // podBName: { type: String, required: true }, 10 | // podBWeight: { type: Number }, 11 | // podBContent: { type: String }, 12 | // createdAt: { type: Date, default: Date.now } 13 | // }); 14 | 15 | // const History = mongoose.model('History', historySchema); 16 | 17 | // module.exports = { History }; -------------------------------------------------------------------------------- /server/server.js: -------------------------------------------------------------------------------- 1 | const express = require('express'); 2 | const bodyParser = require('body-parser'); 3 | const path = require('path'); 4 | const testController = require('./controllers/testController'); 5 | const createDepController = require('./controllers/createDepController'); 6 | // const mongoose = require('mongoose'); 7 | // const { History } = require('./models/historyModels'); 8 | // const db = require('./config/keys').MONGO_URI; 9 | 10 | // mongoose 11 | // .connect(db, { 12 | // useNewUrlParser: true, 13 | // useUnifiedTopology: true, 14 | // dbName: 'tikitaka' 15 | // }) 16 | // .then(() => console.log('Connected to mongoDB')) 17 | // .catch(err => console.log(err)); 18 | 19 | const app = express(); 20 | 21 | const PORT = process.env.PORT || '3000'; 22 | 23 | app.use(bodyParser.json()); 24 | app.use(bodyParser.urlencoded({ extended: true })); 25 | app.use(express.static(path.join(__dirname, '../build/'))); 26 | 27 | // app.get('/getDeploys',testController.setup, (req,res) => { 28 | // res.json(res.locals.data); 29 | // }); 30 | // app.get('/testing-ab',testController.testingAB, (req,res) => { 31 | // res.json(res.locals.data); 32 | // }); 33 | app.all('/dothis', createDepController.addVirtualService, (req, res) => { 34 | return res.json('do it please please'); 35 | }); 36 | 37 | // app.get('/history', (req, res) => { 38 | // History.find({}) 39 | // .exec() 40 | // .then(docs => { 41 | // res.status(200).json(docs) 42 | // }); 43 | // }); 44 | 45 | // app.post('/history', (req, res) => { 46 | // console.log(req.body); 47 | // History.create({ 48 | // podAName: 'cat', 49 | // podAWeight: 33, 50 | // podAContent: '', 51 | // podBName: 'dog', 52 | // podBWeight: 67, 53 | // podBContent: '', 54 | // createdAt: Date.now() 55 | // }); 56 | // }); 57 | 58 | /** 59 | * 404 handler for any request to unknown routes 60 | */ 61 | app.use('*',(req,res) => { 62 | res.sendStatus(404); 63 | }); 64 | 65 | /** 66 | * Global error handler 67 | */ 68 | app.use((err, req, res, next) => { 69 | // console.error(err.stack); 70 | // Creating our default error 71 | const defaulErr = { 72 | log: 'Express error handler caught unknown middleware error', 73 | status: 400, 74 | message: { err: 'An error ocurred'}, 75 | }; 76 | const errorObj = Object.assign(defaulErr, err); 77 | console.log(errorObj.log); 78 | res.status(errorObj.status).json(errorObj.message); 79 | }); 80 | 81 | app.listen(PORT, () => { 82 | console.log(`Server listening on port: ${PORT}`); 83 | }); 84 | -------------------------------------------------------------------------------- /skaffold.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: skaffold/v2alpha3 2 | kind: Config 3 | metadata: 4 | name: istio-ab-testing 5 | build: 6 | artifacts: 7 | - image: tikitaka1/tikitaka-stage 8 | deploy: 9 | kubectl: 10 | manifests: 11 | - k8s/tikitaka-app.yaml 12 | -------------------------------------------------------------------------------- /src/assets/IMG_9802.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/oslabs-beta/Tikitaka/2e97fbb94890e95a831a101d88628cb1d95d1d6c/src/assets/IMG_9802.png -------------------------------------------------------------------------------- /src/assets/deployment.json: -------------------------------------------------------------------------------- 1 | { 2 | "apiVersion": "apps/v1", 3 | "kind": "Deployment", 4 | "metadata": { 5 | "name": "NAMEOFTHEDEPLOYMENT", 6 | "labels": { 7 | "version": "v1" 8 | } 9 | }, 10 | "spec": { 11 | "replicas": 1, 12 | "selector": { 13 | "matchLabels": { 14 | "app": "NAMEOFAPP" 15 | } 16 | }, 17 | "template": { 18 | "metadata": { 19 | "labels": { 20 | "app": "NAMEOFAPP", 21 | "version": "v1" 22 | } 23 | } 24 | }, 25 | "spec": { 26 | "containers":[ 27 | { 28 | "name": "NAMEOFAP", 29 | "image": "IMAGESOURCE", 30 | "ports": [ 31 | { 32 | "containerPort": 3000 33 | } 34 | ], 35 | "imagePullPolicy": "Always" 36 | } 37 | ] 38 | } 39 | } 40 | } -------------------------------------------------------------------------------- /src/assets/destinationrules.json: -------------------------------------------------------------------------------- 1 | { 2 | "apiVersion": "networking.istio.io/v1alpha3", 3 | "kind": "DestinationRule", 4 | "metadata": { 5 | "name": "DESTINATIONNAME", 6 | "namespace": "default" 7 | }, 8 | "spec": { 9 | "host": "NAMESERVICE", 10 | "trafficPolicy": null, 11 | "subsets": [ 12 | { 13 | "name": "safe", 14 | "labels": { 15 | "version": "safe" 16 | } 17 | }, 18 | { 19 | "name": "test", 20 | "labels": { 21 | "version": "test" 22 | } 23 | } 24 | ] 25 | } 26 | } -------------------------------------------------------------------------------- /src/assets/gateway.json: -------------------------------------------------------------------------------- 1 | { 2 | "apiVersion": "networking.istio.io/v1alpha3", 3 | "kind": "Gateway", 4 | "metadata": { 5 | "name": "GATEWAYNAME" 6 | }, 7 | "spec": { 8 | "selector": { 9 | "istio": "ingressgateway" 10 | }, 11 | "servers": [ 12 | { 13 | "port": { 14 | "number": 80, 15 | "name": "http", 16 | "protocol": "HTTP" 17 | }, 18 | "hosts": [ 19 | "*" 20 | ] 21 | } 22 | ] 23 | } 24 | } -------------------------------------------------------------------------------- /src/assets/github-resume.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/oslabs-beta/Tikitaka/2e97fbb94890e95a831a101d88628cb1d95d1d6c/src/assets/github-resume.jpg -------------------------------------------------------------------------------- /src/assets/img1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/oslabs-beta/Tikitaka/2e97fbb94890e95a831a101d88628cb1d95d1d6c/src/assets/img1.png -------------------------------------------------------------------------------- /src/assets/mario.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/oslabs-beta/Tikitaka/2e97fbb94890e95a831a101d88628cb1d95d1d6c/src/assets/mario.png -------------------------------------------------------------------------------- /src/assets/service.json: -------------------------------------------------------------------------------- 1 | { 2 | "apiVersion": "v1", 3 | "kind": "Service", 4 | "metadata": { 5 | "name": "NAMEOFTHESERVICE", 6 | "labels": { 7 | "app": "NAMEOFTHEAPP" 8 | } 9 | }, 10 | "spec": { 11 | "selector": { 12 | "app": "NAMEOFTHEAPP" 13 | }, 14 | "ports": [ 15 | { 16 | "name": "http", 17 | "port": 3000 18 | } 19 | ] 20 | } 21 | } -------------------------------------------------------------------------------- /src/assets/virtualservice.json: -------------------------------------------------------------------------------- 1 | { 2 | "apiVersion": "networking.istio.io/v1alpha3", 3 | "kind": "VirtualService", 4 | "metadata": { 5 | "name": "NAMEOFTHEVIRTUAL", 6 | "namespace": "default" 7 | }, 8 | "spec": { 9 | "gateways":[ 10 | "NAMEOFGATEWAY" 11 | ], 12 | "hosts": [ 13 | "*" 14 | ], 15 | "http": [{ 16 | "route": [ 17 | { 18 | "destination": { 19 | "host": "*", 20 | "subset": "safe" 21 | }, 22 | "weight": 50 23 | }, 24 | { 25 | "destination": { 26 | "host": "*", 27 | "subset": "test" 28 | }, 29 | "weight": 50 30 | } 31 | ] 32 | }] 33 | } 34 | } -------------------------------------------------------------------------------- /src/components/About.tsx: -------------------------------------------------------------------------------- 1 | import * as React from 'react'; 2 | 3 | interface Iprops{ 4 | 5 | } 6 | 7 | export default class TestForm extends React.Component{ 8 | public render(): JSX.Element{ 9 | return( 10 |
11 |
12 |
13 | ... 14 |
15 |
16 |

Arman Gurkan

17 |

Baby lover

18 |
19 | 20 | 21 |
22 |
23 |
24 | 25 |
26 |
27 | ... 28 |
29 |
30 |

Jie-Yun Catherine Cheng

31 |

Cat lover

32 |
33 | 34 | 35 |
36 |
37 |
38 | 39 |
40 |
41 | ... 42 |
43 |
44 |

Pati Honores

45 |

Dog lover

46 |
47 | 48 | 49 |
50 |
51 |
52 | 53 |
54 |
55 | ... 56 |
57 |
58 |

Ryan SukWoo Kim

59 |

Song lover

60 |
61 | 62 | 63 |
64 |
65 |
66 | 67 |
68 |
69 | ... 70 |
71 |
72 |

Yunho Cho

73 |

Code Life

74 |
75 | 76 | 77 |
78 |
79 |
80 |
81 | ) 82 | } 83 | } -------------------------------------------------------------------------------- /src/components/App.tsx: -------------------------------------------------------------------------------- 1 | import * as React from 'react'; 2 | import {Container, Row, Col} from 'react-bootstrap'; 3 | import Navbar from 'react-bootstrap/Navbar'; 4 | import Nav from 'react-bootstrap/Nav'; 5 | import { BrowserRouter as Router, Route, Link, Switch } from 'react-router-dom'; 6 | import Bullets from './Bullets'; 7 | import Sidebar from './Sidebar'; 8 | import TestForm from './TestForm'; 9 | import About from './About'; 10 | import TestDisplay from './TestDisplay'; 11 | import { HistoryProvider } from '../context/historyContext'; 12 | 13 | //===================================================================================================// 14 | // INTERFACE // 15 | //===================================================================================================// 16 | // Typescript: because states are given, need to define as interface 17 | interface IState 18 | { 19 | currentWindow: String; 20 | } 21 | 22 | // 23 | export class App extends React.Component<{}, IState>{ 24 | //===================================================================================================// 25 | // CONSTRUCTOR // 26 | //===================================================================================================// 27 | constructor(props: {}) { 28 | super(props); 29 | 30 | // Defining state 31 | this.state = { 32 | currentWindow:"home" 33 | } 34 | } 35 | //===================================================================================================// 36 | // METHODS // 37 | //===================================================================================================// 38 | 39 | //=============================================PUBLIC 40 | // public handleSubmit(e: React.FormEvent): void{ 41 | // // Type of e is called = React.FormEvent 42 | // e.preventDefault(); 43 | // this.setState({ 44 | // currentTask: "", 45 | // tasks: [ 46 | // ...this.state.tasks, 47 | // { 48 | // id: this._timeInMilliseconds(), 49 | // value: this.state.currentTask, 50 | // completed: false 51 | // } 52 | // ] 53 | // }) 54 | // } 55 | 56 | // public deleteTask(taskId: number): void { 57 | // const filteredTask: Array = this.state.tasks.filter( 58 | // (task: ITask) => task.id !== taskId 59 | // ); 60 | // this.setState({tasks: filteredTask}); 61 | // } 62 | 63 | // public toggleDone(index: number): void { 64 | // let task: ITask[] = this.state.tasks.splice(index, 1); 65 | // task[0].completed = !task[0].completed; 66 | // const tasks: ITask[] = [...this.state.tasks, ...task]; 67 | // this.setState({tasks}); 68 | // } 69 | 70 | // public renderTasks(): JSX.Element[] { 71 | // return this.state.tasks.map((task: ITask, index: number) => { 72 | // return ( 73 | //
75 | // {task.value} 76 | // 77 | // 78 | //
79 | // ) 80 | // }) 81 | // } 82 | //===================================================================================================// 83 | // RENDER // 84 | //===================================================================================================// 85 | public render(): JSX.Element{ 86 | return ( 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | Welcome 97 | 98 | 99 | 100 | 101 | 102 |
103 | 104 | TIKITAKA 105 | 106 | 107 | 113 | 114 | 115 | 116 | 117 | 118 | 119 | 120 | 121 | 127 | 128 | 129 | 130 | 131 | 132 | 133 | 134 | 135 |
136 |
137 | 138 |
139 |
140 |
141 | ); 142 | } 143 | 144 | 145 | //=============================================PRIVATE 146 | // private _timeInMilliseconds(): number { 147 | // const date: Date = new Date(); 148 | // return date.getTime(); 149 | // } 150 | } 151 | -------------------------------------------------------------------------------- /src/components/Bullets.tsx: -------------------------------------------------------------------------------- 1 | import * as React from 'react'; 2 | import { Group } from '@vx/group'; 3 | import { Tree } from '@vx/hierarchy'; 4 | import { LinearGradient } from '@vx/gradient'; 5 | import { hierarchy } from 'd3-hierarchy'; 6 | import { pointRadial } from 'd3-shape'; 7 | // import data from './Data'; 8 | 9 | 10 | import { 11 | LinkHorizontal, 12 | LinkVertical, 13 | LinkRadial, 14 | LinkHorizontalStep, 15 | LinkVerticalStep, 16 | LinkRadialStep, 17 | LinkHorizontalCurve, 18 | LinkVerticalCurve, 19 | LinkRadialCurve, 20 | LinkHorizontalLine, 21 | LinkVerticalLine, 22 | LinkRadialLine 23 | } from '@vx/shape'; 24 | 25 | // @Pati, @Ryan TODO: this constant json has to be fetched from the backend 26 | const json: any = [{"traceId":"f8a0f44e7cef050b4e8767cc370e1cc5","parentId":"1fbb77f8a48f052a","id":"70028bc7b7f58a23","kind":"CLIENT","name":"get /zipkin/api/v2/traces","timestamp":1580415321633826,"duration":14387,"localEndpoint":{"serviceName":"getting-started"},"tags":{"component":"http","http.flavor":"1.1","http.host":"localhost:9411","http.method":"GET","http.status_code":"200","http.status_text":"OK","http.target":"/zipkin/api/v2/traces","http.url":"http://localhost:9411/zipkin/api/v2/traces","net.peer.ip":"127.0.0.1","net.peer.name":"localhost","net.peer.port":"9411","net.transport":"IP.TCP","ot.status_code":"OK"}},{"traceId":"f8a0f44e7cef050b4e8767cc370e1cc5","parentId":"6c7d4c9563f118aa","id":"ac6d1225d090ceba","kind":"CLIENT","name":"get /zipkin/api/v2/traces","timestamp":1580415321631938,"duration":18843,"localEndpoint":{"serviceName":"getting-started"},"tags":{"component":"http","http.flavor":"1.1","http.host":"localhost:9411","http.method":"GET","http.status_code":"200","http.status_text":"OK","http.target":"/zipkin/api/v2/traces","http.url":"http://localhost:9411/zipkin/api/v2/traces","net.peer.ip":"127.0.0.1","net.peer.name":"localhost","net.peer.port":"9411","net.transport":"IP.TCP","ot.status_code":"OK"}},{"traceId":"f8a0f44e7cef050b4e8767cc370e1cc5","parentId":"65b6b12e14496a0b","id":"0ac9e828af6c26e7","kind":"CLIENT","name":"get /zipkin/api/v2/traces","timestamp":1580415321636640,"duration":25915,"localEndpoint":{"serviceName":"getting-started"},"tags":{"component":"http","http.flavor":"1.1","http.host":"localhost:9411","http.method":"GET","http.status_code":"200","http.status_text":"OK","http.target":"/zipkin/api/v2/traces","http.url":"http://localhost:9411/zipkin/api/v2/traces","net.peer.ip":"127.0.0.1","net.peer.name":"localhost","net.peer.port":"9411","net.transport":"IP.TCP","ot.status_code":"OK"}},{"traceId":"f8a0f44e7cef050b4e8767cc370e1cc5","parentId":"6c7d4c9563f118aa","id":"81b0093f2ef24d46","kind":"CLIENT","name":"get /zipkin/api/v2/traces","timestamp":1580415321652339,"duration":14573,"localEndpoint":{"serviceName":"getting-started"},"tags":{"component":"http","http.flavor":"1.1","http.host":"localhost:9411","http.method":"GET","http.status_code":"200","http.status_text":"OK","http.target":"/zipkin/api/v2/traces","http.url":"http://localhost:9411/zipkin/api/v2/traces","net.peer.ip":"127.0.0.1","net.peer.name":"localhost","net.peer.port":"9411","net.transport":"IP.TCP","ot.status_code":"OK"}},{"traceId":"f8a0f44e7cef050b4e8767cc370e1cc5","parentId":"65b6b12e14496a0b","id":"b569b61faa18d09f","kind":"CLIENT","name":"get /zipkin/api/v2/traces","timestamp":1580415321636329,"duration":23881,"localEndpoint":{"serviceName":"getting-started"},"tags":{"component":"http","http.flavor":"1.1","http.host":"localhost:9411","http.method":"GET","http.status_code":"200","http.status_text":"OK","http.target":"/zipkin/api/v2/traces","http.url":"http://localhost:9411/zipkin/api/v2/traces","net.peer.ip":"127.0.0.1","net.peer.name":"localhost","net.peer.port":"9411","net.transport":"IP.TCP","ot.status_code":"OK"}},{"traceId":"f8a0f44e7cef050b4e8767cc370e1cc5","parentId":"1fbb77f8a48f052a","id":"6ca93a585e73d737","kind":"CLIENT","name":"get /zipkin/api/v2/traces","timestamp":1580415321649933,"duration":25618,"localEndpoint":{"serviceName":"getting-started"},"tags":{"component":"http","http.flavor":"1.1","http.host":"localhost:9411","http.method":"GET","http.status_code":"200","http.status_text":"OK","http.target":"/zipkin/api/v2/traces","http.url":"http://localhost:9411/zipkin/api/v2/traces","net.peer.ip":"127.0.0.1","net.peer.name":"localhost","net.peer.port":"9411","net.transport":"IP.TCP","ot.status_code":"OK"}},{"traceId":"f8a0f44e7cef050b4e8767cc370e1cc5","parentId":"65b6b12e14496a0b","id":"b4993d57e8bfb02d","kind":"CLIENT","name":"get /zipkin/api/v2/traces","timestamp":1580415321661963,"duration":30949,"localEndpoint":{"serviceName":"getting-started"},"tags":{"component":"http","http.flavor":"1.1","http.host":"localhost:9411","http.method":"GET","http.status_code":"200","http.status_text":"OK","http.target":"/zipkin/api/v2/traces","http.url":"http://localhost:9411/zipkin/api/v2/traces","net.peer.ip":"127.0.0.1","net.peer.name":"localhost","net.peer.port":"9411","net.transport":"IP.TCP","ot.status_code":"OK"}},{"traceId":"f8a0f44e7cef050b4e8767cc370e1cc5","parentId":"1fbb77f8a48f052a","id":"701d41c2a814e088","kind":"CLIENT","name":"get /backend","timestamp":1580415321634189,"duration":61127,"localEndpoint":{"serviceName":"getting-started"},"tags":{"component":"http","http.flavor":"1.1","http.host":"localhost:8080","http.method":"GET","http.status_code":"200","http.status_text":"OK","http.target":"/backend","http.url":"http://localhost:8080/backend","net.peer.ip":"127.0.0.1","net.peer.name":"localhost","net.peer.port":"8080","net.transport":"IP.TCP","ot.status_code":"OK"}},{"traceId":"f8a0f44e7cef050b4e8767cc370e1cc5","parentId":"701d41c2a814e088","id":"65b6b12e14496a0b","kind":"SERVER","name":"get /backend","timestamp":1580415321635074,"duration":57367,"localEndpoint":{"serviceName":"getting-started"},"tags":{"component":"http","http.flavor":"1.1","http.host":"localhost:8080","http.method":"GET","http.route":"/backend","http.status_code":"200","http.status_text":"OK","http.target":"/backend","http.url":"http://localhost:8080/backend","http.user_agent":"axios/0.19.2","net.host.ip":"::ffff:127.0.0.1","net.host.name":"localhost","net.host.port":"8080","net.peer.ip":"::ffff:127.0.0.1","net.peer.port":"59793","net.transport":"IP.TCP","ot.status_code":"OK"}},{"traceId":"f8a0f44e7cef050b4e8767cc370e1cc5","parentId":"65b6b12e14496a0b","id":"6bbd0e9e2e60ab06","kind":"CLIENT","name":"get /zipkin/api/v2/traces","timestamp":1580415321665660,"duration":24684,"localEndpoint":{"serviceName":"getting-started"},"tags":{"component":"http","http.flavor":"1.1","http.host":"localhost:9411","http.method":"GET","http.status_code":"200","http.status_text":"OK","http.target":"/zipkin/api/v2/traces","http.url":"http://localhost:9411/zipkin/api/v2/traces","net.peer.ip":"127.0.0.1","net.peer.name":"localhost","net.peer.port":"9411","net.transport":"IP.TCP","ot.status_code":"OK"}},{"traceId":"f8a0f44e7cef050b4e8767cc370e1cc5","parentId":"41510564ad7c6bfd","id":"7839dfc70cc40bb4","kind":"CLIENT","name":"get /zipkin/api/v2/traces","timestamp":1580415321697071,"duration":8282,"localEndpoint":{"serviceName":"getting-started"},"tags":{"component":"http","http.flavor":"1.1","http.host":"localhost:9411","http.method":"GET","http.status_code":"200","http.status_text":"OK","http.target":"/zipkin/api/v2/traces","http.url":"http://localhost:9411/zipkin/api/v2/traces","net.peer.ip":"127.0.0.1","net.peer.name":"localhost","net.peer.port":"9411","net.transport":"IP.TCP","ot.status_code":"OK"}},{"traceId":"f8a0f44e7cef050b4e8767cc370e1cc5","parentId":"41510564ad7c6bfd","id":"a16aab873555210f","kind":"CLIENT","name":"get /zipkin/api/v2/traces","timestamp":1580415321697331,"duration":9938,"localEndpoint":{"serviceName":"getting-started"},"tags":{"component":"http","http.flavor":"1.1","http.host":"localhost:9411","http.method":"GET","http.status_code":"200","http.status_text":"OK","http.target":"/zipkin/api/v2/traces","http.url":"http://localhost:9411/zipkin/api/v2/traces","net.peer.ip":"127.0.0.1","net.peer.name":"localhost","net.peer.port":"9411","net.transport":"IP.TCP","ot.status_code":"OK"}},{"traceId":"f8a0f44e7cef050b4e8767cc370e1cc5","parentId":"41510564ad7c6bfd","id":"027c15ee6d4111f3","kind":"CLIENT","name":"get /zipkin/api/v2/traces","timestamp":1580415321706800,"duration":9753,"localEndpoint":{"serviceName":"getting-started"},"tags":{"component":"http","http.flavor":"1.1","http.host":"localhost:9411","http.method":"GET","http.status_code":"200","http.status_text":"OK","http.target":"/zipkin/api/v2/traces","http.url":"http://localhost:9411/zipkin/api/v2/traces","net.peer.ip":"127.0.0.1","net.peer.name":"localhost","net.peer.port":"9411","net.transport":"IP.TCP","ot.status_code":"OK"}},{"traceId":"f8a0f44e7cef050b4e8767cc370e1cc5","parentId":"41510564ad7c6bfd","id":"0fc40aac3028665f","kind":"CLIENT","name":"get /zipkin/api/v2/traces","timestamp":1580415321708606,"duration":12115,"localEndpoint":{"serviceName":"getting-started"},"tags":{"component":"http","http.flavor":"1.1","http.host":"localhost:9411","http.method":"GET","http.status_code":"200","http.status_text":"OK","http.target":"/zipkin/api/v2/traces","http.url":"http://localhost:9411/zipkin/api/v2/traces","net.peer.ip":"127.0.0.1","net.peer.name":"localhost","net.peer.port":"9411","net.transport":"IP.TCP","ot.status_code":"OK"}},{"traceId":"f8a0f44e7cef050b4e8767cc370e1cc5","parentId":"1fbb77f8a48f052a","id":"98d9f024cf0a13cf","kind":"CLIENT","name":"get /backend","timestamp":1580415321695763,"duration":27396,"localEndpoint":{"serviceName":"getting-started"},"tags":{"component":"http","http.flavor":"1.1","http.host":"localhost:8080","http.method":"GET","http.status_code":"200","http.status_text":"OK","http.target":"/backend","http.url":"http://localhost:8080/backend","net.peer.ip":"127.0.0.1","net.peer.name":"localhost","net.peer.port":"8080","net.transport":"IP.TCP","ot.status_code":"OK"}},{"traceId":"f8a0f44e7cef050b4e8767cc370e1cc5","parentId":"98d9f024cf0a13cf","id":"41510564ad7c6bfd","kind":"SERVER","name":"get /backend","timestamp":1580415321696658,"duration":25746,"localEndpoint":{"serviceName":"getting-started"},"tags":{"component":"http","http.flavor":"1.1","http.host":"localhost:8080","http.method":"GET","http.route":"/backend","http.status_code":"200","http.status_text":"OK","http.target":"/backend","http.url":"http://localhost:8080/backend","http.user_agent":"axios/0.19.2","net.host.ip":"::ffff:127.0.0.1","net.host.name":"localhost","net.host.port":"8080","net.peer.ip":"::ffff:127.0.0.1","net.peer.port":"59810","net.transport":"IP.TCP","ot.status_code":"OK"}},{"traceId":"f8a0f44e7cef050b4e8767cc370e1cc5","parentId":"6c7d4c9563f118aa","id":"600376586b3d82e0","kind":"CLIENT","name":"get /middle-tier","timestamp":1580415321632257,"duration":92833,"localEndpoint":{"serviceName":"getting-started"},"tags":{"component":"http","http.flavor":"1.1","http.host":"localhost:8080","http.method":"GET","http.status_code":"200","http.status_text":"OK","http.target":"/middle-tier","http.url":"http://localhost:8080/middle-tier","net.peer.ip":"127.0.0.1","net.peer.name":"localhost","net.peer.port":"8080","net.transport":"IP.TCP","ot.status_code":"OK"}},{"traceId":"f8a0f44e7cef050b4e8767cc370e1cc5","parentId":"600376586b3d82e0","id":"1fbb77f8a48f052a","kind":"SERVER","name":"get /middle-tier","timestamp":1580415321633363,"duration":91019,"localEndpoint":{"serviceName":"getting-started"},"tags":{"component":"http","http.flavor":"1.1","http.host":"localhost:8080","http.method":"GET","http.route":"/middle-tier","http.status_code":"200","http.status_text":"OK","http.target":"/middle-tier","http.url":"http://localhost:8080/middle-tier","http.user_agent":"axios/0.19.2","net.host.ip":"::ffff:127.0.0.1","net.host.name":"localhost","net.host.port":"8080","net.peer.ip":"::ffff:127.0.0.1","net.peer.port":"59790","net.transport":"IP.TCP","ot.status_code":"OK"}},{"traceId":"f8a0f44e7cef050b4e8767cc370e1cc5","parentId":"798341b81e0957fb","id":"dc22ebafb6bf6c0d","kind":"CLIENT","name":"get /zipkin/api/v2/traces","timestamp":1580415321726968,"duration":26575,"localEndpoint":{"serviceName":"getting-started"},"tags":{"component":"http","http.flavor":"1.1","http.host":"localhost:9411","http.method":"GET","http.status_code":"200","http.status_text":"OK","http.target":"/zipkin/api/v2/traces","http.url":"http://localhost:9411/zipkin/api/v2/traces","net.peer.ip":"127.0.0.1","net.peer.name":"localhost","net.peer.port":"9411","net.transport":"IP.TCP","ot.status_code":"OK"}},{"traceId":"f8a0f44e7cef050b4e8767cc370e1cc5","parentId":"8083f844a54532aa","id":"4989998b12c862b2","kind":"CLIENT","name":"get /zipkin/api/v2/traces","timestamp":1580415321729038,"duration":19346,"localEndpoint":{"serviceName":"getting-started"},"tags":{"component":"http","http.flavor":"1.1","http.host":"localhost:9411","http.method":"GET","http.status_code":"200","http.status_text":"OK","http.target":"/zipkin/api/v2/traces","http.url":"http://localhost:9411/zipkin/api/v2/traces","net.peer.ip":"127.0.0.1","net.peer.name":"localhost","net.peer.port":"9411","net.transport":"IP.TCP","ot.status_code":"OK"}},{"traceId":"f8a0f44e7cef050b4e8767cc370e1cc5","parentId":"8083f844a54532aa","id":"3f80161fab04fe24","kind":"CLIENT","name":"get /zipkin/api/v2/traces","timestamp":1580415321728774,"duration":22664,"localEndpoint":{"serviceName":"getting-started"},"tags":{"component":"http","http.flavor":"1.1","http.host":"localhost:9411","http.method":"GET","http.status_code":"200","http.status_text":"OK","http.target":"/zipkin/api/v2/traces","http.url":"http://localhost:9411/zipkin/api/v2/traces","net.peer.ip":"127.0.0.1","net.peer.name":"localhost","net.peer.port":"9411","net.transport":"IP.TCP","ot.status_code":"OK"}},{"traceId":"f8a0f44e7cef050b4e8767cc370e1cc5","parentId":"3190e8d818239c3a","id":"8083f844a54532aa","kind":"SERVER","name":"get /backend","timestamp":1580415321728259,"duration":39000,"localEndpoint":{"serviceName":"getting-started"},"tags":{"component":"http","http.flavor":"1.1","http.host":"localhost:8080","http.method":"GET","http.route":"/backend","http.status_code":"200","http.status_text":"OK","http.target":"/backend","http.url":"http://localhost:8080/backend","http.user_agent":"axios/0.19.2","net.host.ip":"::ffff:127.0.0.1","net.host.name":"localhost","net.host.port":"8080","net.peer.ip":"::ffff:127.0.0.1","net.peer.port":"59824","net.transport":"IP.TCP","ot.status_code":"OK"}},{"traceId":"f8a0f44e7cef050b4e8767cc370e1cc5","parentId":"8083f844a54532aa","id":"4843ede811d77635","kind":"CLIENT","name":"get /zipkin/api/v2/traces","timestamp":1580415321752874,"duration":20371,"localEndpoint":{"serviceName":"getting-started"},"tags":{"component":"http","http.flavor":"1.1","http.host":"localhost:9411","http.method":"GET","http.status_code":"200","http.status_text":"OK","http.target":"/zipkin/api/v2/traces","http.url":"http://localhost:9411/zipkin/api/v2/traces","net.peer.ip":"127.0.0.1","net.peer.name":"localhost","net.peer.port":"9411","net.transport":"IP.TCP","ot.status_code":"OK"}},{"traceId":"f8a0f44e7cef050b4e8767cc370e1cc5","parentId":"798341b81e0957fb","id":"ed4bcc1944d99c24","kind":"CLIENT","name":"get /zipkin/api/v2/traces","timestamp":1580415321754991,"duration":12854,"localEndpoint":{"serviceName":"getting-started"},"tags":{"component":"http","http.flavor":"1.1","http.host":"localhost:9411","http.method":"GET","http.status_code":"200","http.status_text":"OK","http.target":"/zipkin/api/v2/traces","http.url":"http://localhost:9411/zipkin/api/v2/traces","net.peer.ip":"127.0.0.1","net.peer.name":"localhost","net.peer.port":"9411","net.transport":"IP.TCP","ot.status_code":"OK"}},{"traceId":"f8a0f44e7cef050b4e8767cc370e1cc5","parentId":"798341b81e0957fb","id":"3190e8d818239c3a","kind":"CLIENT","name":"get /backend","timestamp":1580415321727229,"duration":45129,"localEndpoint":{"serviceName":"getting-started"},"tags":{"component":"http","http.flavor":"1.1","http.host":"localhost:8080","http.method":"GET","http.status_code":"200","http.status_text":"OK","http.target":"/backend","http.url":"http://localhost:8080/backend","net.peer.ip":"127.0.0.1","net.peer.name":"localhost","net.peer.port":"8080","net.transport":"IP.TCP","ot.status_code":"OK"}},{"traceId":"f8a0f44e7cef050b4e8767cc370e1cc5","parentId":"8083f844a54532aa","id":"cb3bed81ea2eb9b6","kind":"CLIENT","name":"get /zipkin/api/v2/traces","timestamp":1580415321750126,"duration":15171,"localEndpoint":{"serviceName":"getting-started"},"tags":{"component":"http","http.flavor":"1.1","http.host":"localhost:9411","http.method":"GET","http.status_code":"200","http.status_text":"OK","http.target":"/zipkin/api/v2/traces","http.url":"http://localhost:9411/zipkin/api/v2/traces","net.peer.ip":"127.0.0.1","net.peer.name":"localhost","net.peer.port":"9411","net.transport":"IP.TCP","ot.status_code":"OK"}},{"traceId":"f8a0f44e7cef050b4e8767cc370e1cc5","parentId":"b37f6ce85e7a502e","id":"17e37d937c577652","kind":"CLIENT","name":"get /zipkin/api/v2/traces","timestamp":1580415321778382,"duration":19183,"localEndpoint":{"serviceName":"getting-started"},"tags":{"component":"http","http.flavor":"1.1","http.host":"localhost:9411","http.method":"GET","http.status_code":"200","http.status_text":"OK","http.target":"/zipkin/api/v2/traces","http.url":"http://localhost:9411/zipkin/api/v2/traces","net.peer.ip":"127.0.0.1","net.peer.name":"localhost","net.peer.port":"9411","net.transport":"IP.TCP","ot.status_code":"OK"}},{"traceId":"f8a0f44e7cef050b4e8767cc370e1cc5","parentId":"b37f6ce85e7a502e","id":"826a29e7acb3b350","kind":"CLIENT","name":"get /zipkin/api/v2/traces","timestamp":1580415321778114,"duration":17364,"localEndpoint":{"serviceName":"getting-started"},"tags":{"component":"http","http.flavor":"1.1","http.host":"localhost:9411","http.method":"GET","http.status_code":"200","http.status_text":"OK","http.target":"/zipkin/api/v2/traces","http.url":"http://localhost:9411/zipkin/api/v2/traces","net.peer.ip":"127.0.0.1","net.peer.name":"localhost","net.peer.port":"9411","net.transport":"IP.TCP","ot.status_code":"OK"}}]; 27 | 28 | interface TrimmedDownJson { 29 | index: number; 30 | traceId: string; 31 | parentId: string; 32 | id: string; 33 | } 34 | 35 | const trimmedDownJson: Array = []; 36 | json.forEach((trace: any, index: number) => { 37 | const { traceId, parentId, id } = trace; 38 | trimmedDownJson.push({ 39 | index, 40 | traceId, 41 | parentId, 42 | id 43 | }); 44 | }); 45 | // console.log('trimmedDownJson: ', trimmedDownJson); 46 | 47 | const parentId: Array = trimmedDownJson.map(trace => trace.parentId); 48 | // console.log('parentId: ', parentId); 49 | 50 | const id: Array = trimmedDownJson.map(trace => trace.id); 51 | // console.log('id: ', id); 52 | 53 | interface Relationship { 54 | parentId: string; 55 | parentIndex: number; 56 | childrenId: string; 57 | childIndex: number; 58 | } 59 | 60 | const relationships: Array = []; 61 | id.forEach((id, parentIndex) => { 62 | if (parentId.includes(id)) { 63 | // console.log('parent id: ', id, 'at index: ', parentIndex); 64 | const childIndex = parentId.findIndex(parentId => parentId === id); 65 | // console.log('child id: ', trimmedDownJson[childIndex].id, 'at index: ', childIndex); 66 | relationships.push({ parentId: id, parentIndex, childrenId: trimmedDownJson[childIndex].id, childIndex }); 67 | } 68 | }); 69 | // console.log('relationships: ', relationships); 70 | 71 | interface Node { 72 | name: string; 73 | children?: Node[]; 74 | } 75 | 76 | const nodes: Array = []; 77 | id.forEach(id => { 78 | nodes.push({name: id}); 79 | }); 80 | 81 | relationships.forEach(relationship => { 82 | if (nodes[relationship.parentIndex].children) { 83 | nodes[relationship.parentIndex].children.push(nodes[relationship.childIndex]); 84 | } else { 85 | nodes[relationship.parentIndex].children = [nodes[relationship.childIndex]]; 86 | } 87 | }); 88 | // console.log('nodes: ', nodes); 89 | 90 | relationships.map(relationship => relationship.childIndex).sort((a, b) => a - b).reverse().forEach(childIndex => nodes.splice(childIndex, 1)); 91 | // console.log('after deleting duplicate nodes: ', nodes); 92 | 93 | interface Data { 94 | [x: string]: any; 95 | name: string; 96 | children?: Data[]; 97 | } 98 | const data: Data = { 99 | name: trimmedDownJson[0].traceId, 100 | children: nodes 101 | }; 102 | // console.log('data: ', data); 103 | 104 | // const data:Data = { 105 | // name: 'HOST', 106 | // children: [ 107 | // { 108 | // name: 'IP1', 109 | // children: [ 110 | // { name: 'AAsasasasasas1' }, 111 | // { name: 'A2' }, 112 | // { name: 'A3' }, 113 | // { 114 | // name: 'C', 115 | // children: [ 116 | // { 117 | // name: 'C1' 118 | // }, 119 | // { 120 | // name: 'D', 121 | // children: [ 122 | // { 123 | // name: 'D1', 124 | // children: [ 125 | // { 126 | // name: 'D1' 127 | // }, 128 | // { 129 | // name: 'D2' 130 | // }, 131 | // { 132 | // name: 'D3' 133 | // } 134 | // ] 135 | // }, 136 | // { 137 | // name: 'D2' 138 | // }, 139 | // { 140 | // name: 'D3' 141 | // } 142 | // ] 143 | // } 144 | // ] 145 | // } 146 | // ] 147 | // }, 148 | // { name: 'Z' }, 149 | // { 150 | // name: 'B', 151 | // children: [{ name: 'B1' }, { name: 'B2' }, { name: 'B3' }] 152 | // } 153 | // ] 154 | // }; 155 | interface Iprops { 156 | width: number; 157 | height: number; 158 | margin: any; 159 | } 160 | 161 | export default class Bullets extends React.Component { 162 | state = { 163 | layout: 'cartesian', 164 | orientation: 'horizontal', 165 | linkType: 'diagonal', 166 | stepPercent: 0.5 167 | }; 168 | 169 | render() { 170 | const { 171 | width, 172 | height, 173 | margin = { 174 | top: 30, 175 | left: 30, 176 | right: 30, 177 | bottom: 30 178 | } 179 | } = this.props; 180 | 181 | const { layout, orientation, linkType, stepPercent } = this.state; 182 | 183 | const innerWidth = width - margin.left - margin.right; 184 | const innerHeight = height - margin.top - margin.bottom; 185 | 186 | let origin: { y: any; x: any; }; 187 | let sizeWidth; 188 | let sizeHeight; 189 | 190 | if (layout === 'polar') { 191 | origin = { 192 | x: innerWidth / 2, 193 | y: innerHeight / 2 194 | }; 195 | sizeWidth = 2 * Math.PI; 196 | sizeHeight = Math.min(innerWidth, innerHeight) / 2; 197 | } else { 198 | origin = { x: 0, y: 0 }; 199 | if (orientation === 'vertical') { 200 | sizeWidth = innerWidth; 201 | sizeHeight = innerHeight; 202 | } else { 203 | sizeWidth = innerHeight; 204 | sizeHeight = innerWidth; 205 | } 206 | } 207 | 208 | return ( 209 |
210 |
211 | 212 | 220 | 221 | 222 | 231 | 232 | 233 | 243 | 244 | 245 | e.stopPropagation()} 247 | type="range" 248 | min={0} 249 | max={1} 250 | step={0.1} 251 | onChange={e => this.setState({ stepPercent: e.target.value })} 252 | value={stepPercent} 253 | disabled={linkType !== 'step' || layout === 'polar'} 254 | /> 255 |
256 | 257 | 258 | 259 | 260 | 261 | (d.isExpanded ? null : d.children))} 263 | size={[sizeWidth, sizeHeight]} 264 | separation={(a, b) => (a.parent == b.parent ? 1 : 0.5) / a.depth} 265 | > 266 | {data => ( 267 | 268 | {data.links().map((link, i) => { 269 | let LinkComponent: any; 270 | 271 | if (layout === 'polar') { 272 | if (linkType === 'step') { 273 | LinkComponent = LinkRadialStep; 274 | } else if (linkType === 'curve') { 275 | LinkComponent = LinkRadialCurve; 276 | } else if (linkType === 'line') { 277 | LinkComponent = LinkRadialLine; 278 | } else { 279 | LinkComponent = LinkRadial; 280 | } 281 | } else { 282 | if (orientation === 'vertical') { 283 | if (linkType === 'step') { 284 | LinkComponent = LinkVerticalStep; 285 | } else if (linkType === 'curve') { 286 | LinkComponent = LinkVerticalCurve; 287 | } else if (linkType === 'line') { 288 | LinkComponent = LinkVerticalLine; 289 | } else { 290 | LinkComponent = LinkVertical; 291 | } 292 | } else { 293 | if (linkType === 'step') { 294 | LinkComponent = LinkHorizontalStep; 295 | } else if (linkType === 'curve') { 296 | LinkComponent = LinkHorizontalCurve; 297 | } else if (linkType === 'line') { 298 | LinkComponent = LinkHorizontalLine; 299 | } else { 300 | LinkComponent = LinkHorizontal; 301 | } 302 | } 303 | } 304 | 305 | return ( 306 | (event: any) => { 314 | console.log(data); 315 | }} 316 | /> 317 | ); 318 | })} 319 | 320 | {data.descendants().map((node, key) => { 321 | const width = 40; 322 | const height = 20; 323 | 324 | let top; 325 | let left; 326 | if (layout === 'polar') { 327 | const [radialX, radialY] = pointRadial(node.x, node.y); 328 | top = radialY; 329 | left = radialX; 330 | } else { 331 | if (orientation === 'vertical') { 332 | top = node.y; 333 | left = node.x; 334 | } else { 335 | top = node.x; 336 | left = node.y; 337 | } 338 | } 339 | 340 | return ( 341 | 342 | {node.depth === 0 && ( 343 | { 347 | node.data.isExpanded = !node.data.isExpanded; 348 | console.log(node); 349 | this.forceUpdate(); 350 | }} 351 | /> 352 | )} 353 | {node.depth !== 0 && ( 354 | { 366 | node.data.isExpanded = !node.data.isExpanded; 367 | console.log(node); 368 | this.forceUpdate(); 369 | }} 370 | /> 371 | )} 372 | 380 | {node.data.name} 381 | 382 | 383 | ); 384 | })} 385 | 386 | )} 387 | 388 | 389 | 390 |
391 | ); 392 | } 393 | } -------------------------------------------------------------------------------- /src/components/FetcherComponent.tsx: -------------------------------------------------------------------------------- 1 | import * as React from 'react'; 2 | import { useState, useEffect } from 'react'; 3 | const opt = { 4 | "apiVersion": "networking.istio.io/v1alpha3", 5 | "kind": "VirtualService", 6 | "metadata": { 7 | "name": "tikitaka" 8 | }, 9 | "spec": { 10 | "hosts": [ 11 | "*" 12 | ], 13 | "gateways": [ 14 | "tikitaka-gateway" 15 | ], 16 | "http": [ 17 | { 18 | "route": [ 19 | { 20 | "destination": { 21 | "host": "tikitaka" 22 | } 23 | } 24 | ] 25 | } 26 | ] 27 | } 28 | }; 29 | const Fetcher: React.FC = props => { 30 | const [virtualServices, addVirtualService] = useState([]); 31 | const getContainers = async () => { 32 | let response = await fetch('http://localhost:8081/apis/apps/v1/namespaces/default/deployments/',{ 33 | method: 'POST', 34 | body: JSON.stringify(opt) 35 | }); 36 | let vS = await response.json(); 37 | addVirtualService([...virtualServices, vS]); 38 | }; 39 | useEffect(() => {getContainers()} 40 | , []); 41 | const result: any[] = [...virtualServices]; 42 | return ( 43 |
44 | {result} 45 |
46 | ) 47 | }; 48 | 49 | export default Fetcher; 50 | -------------------------------------------------------------------------------- /src/components/Sidebar.tsx: -------------------------------------------------------------------------------- 1 | import * as React from 'react'; 2 | import { useState, useEffect } from 'react'; 3 | import Form from 'react-bootstrap/Form' 4 | import { Col, Row } from 'react-bootstrap'; 5 | import Button from 'react-bootstrap/Button' 6 | import InputGroup from 'react-bootstrap/InputGroup' 7 | interface Iprops{ 8 | }; 9 | interface ReqOptions { 10 | method: string; 11 | headers: any; 12 | body: any; 13 | redirect: any; 14 | } 15 | const myHeaders = new Headers(); 16 | myHeaders.append("Content-Type", "application/json"); 17 | const vS = JSON.stringify({"apiVersion":"networking.istio.io/v1alpha3","kind":"VirtualService","metadata":{"annotations":{},"name":"aaa-cluster-ip-service","namespace":"default"},"spec":{"gateways":["ingress-gateway-configuration"],"hosts":["*"],"http":[{"match":[{"uri":{"prefix":"/topitop"}}],"route":[{"destination":{"host":"arman-cluster-ip-service"}}]},{"match":[{"uri":{"prefix":"/taksi"}}],"route":[{"destination":{"host":"client-cluster-ip-service","subset":"original"},"weight":90},{"destination":{"host":"client-cluster-ip-service","subset":"experimental"},"weight":10}]}]}}); 18 | const reqOption:ReqOptions = { 19 | method: 'POST', 20 | headers: myHeaders, 21 | body: vS, 22 | redirect: 'follow' 23 | }; 24 | 25 | 26 | 27 | const TestForm: React.FC = () => { 28 | interface eachMetadataType { 29 | name: string; 30 | namespace: string; 31 | selfLink: string; 32 | uid: string; 33 | resourceVersion: string; 34 | generation: number; 35 | creationTimestamp: string; 36 | } 37 | interface eachItemType { 38 | metadata: eachMetadataType; 39 | spec: object; 40 | status: object; 41 | } 42 | interface containersType { 43 | kind: string; 44 | apiVersion: string; 45 | metadata: object; 46 | items: eachItemType[]; 47 | } 48 | const [containers, setContainers] = useState({ 49 | kind: '', 50 | apiVersion: '', 51 | metadata: {}, 52 | items: [{metadata: {name: "api-gateway", 53 | namespace: "default", 54 | selfLink: "/apis/apps/v1/namespaces/default/deployments/api-gateway", 55 | uid: "2ad6806d-0071-45cc-929f-09da9d21a9c9", 56 | resourceVersion: "116386", 57 | generation: 1, 58 | creationTimestamp: "2020-02-10T20:01:48Z"}, spec: {}, status: {}}] 59 | }); 60 | // useEffect(() => { 61 | // const getContainers = async () => { 62 | // let r = await fetch('http://localhost:8001/apis/apps/v1/namespaces/default/deployments/'); 63 | // let containers = await r.json(); 64 | // setContainers(containers); 65 | // }; 66 | // getContainers(); 67 | // },[]); 68 | const dropDown = []; 69 | if (containers.items.length > 1) { 70 | for(let i = 0; i < containers.items.length; i++) { 71 | let list = 72 | dropDown.push(list) 73 | } 74 | } else { 75 | dropDown.push(); 76 | } 77 | 78 | 79 | useEffect(() => { 80 | const addVirtualService = async () => { 81 | let r = await fetch('/dothis'); 82 | let c = await r.json(); 83 | console.log(c); 84 | }; 85 | addVirtualService(); 86 | },[]); 87 | return ( 88 |
89 | 90 | 91 |

Docker Image A:

92 | 93 | {dropDown} 94 | 95 |
96 | 97 | 101 | 102 | % 103 | weight 104 | 105 | 106 |
107 | 108 |

Docker Image B:

109 | 110 | 111 | Name: 112 | 113 | 117 | 118 |
119 | 120 | 121 | Address: 122 | 123 | 128 | 129 |
130 | 131 | 132 | Version: 133 | 134 | 139 | 140 |
141 |
142 |
143 | 146 | 149 |
150 | ); 151 | } 152 | export default TestForm; 153 | -------------------------------------------------------------------------------- /src/components/TestDisplay.tsx: -------------------------------------------------------------------------------- 1 | import * as React from 'react'; 2 | import { useContext } from 'react'; 3 | import Carousel from 'react-bootstrap/Carousel'; 4 | import { HistoryContext } from '../context/historyContext'; 5 | 6 | interface Iprops{} 7 | 8 | const TestDisplay: React.FC = () => { 9 | const { history } = useContext(HistoryContext); 10 | console.log('history from testdisplay component', history); 11 | 12 | const srcImg = ["https://encrypted-tbn0.gstatic.com/images?q=tbn%3AANd9GcQsr2CnfzB8gKPvCCLwe3AQTTleO4k2QRq4T7L35lWF7djy8RFy", 13 | 'https://graniteworx.com/wp-content/uploads/2015/01/Absolute-Black-INDIA1-400x400.jpg', 14 | "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAOEAAADhCAMAAAAJbSJIAAAAA1BMVEXukY3/VYS2AAAASElEQVR4nO3BgQAAAADDoPlTX+AIVQEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADwDcaiAAFXD1ujAAAAAElFTkSuQmCC", 15 | "https://encrypted-tbn0.gstatic.com/images?q=tbn%3AANd9GcS4QPHeQXFYFZG7vzBj7jtyvXkK9g2GCWxvRCvtrPCraKTv6iKs"]; 16 | 17 | const carousel = []; 18 | for(let i = 0; i < history.length; i++) { 19 | carousel.push( 20 | 21 | First slide 26 | 27 |

image A: {`${history[i].imageA}`}

28 |

weight A: {`${history[i].weightA}`}

29 |

image B: {`${history[i].imageB}`}

30 |

address B: {`${history[i].addressB}`}

31 |

version B: {`${history[i].versionB}`}

32 | 33 |
34 |
); 35 | } 36 | return( 37 | 38 | {carousel} 39 | 40 | ) 41 | } 42 | 43 | export default TestDisplay; -------------------------------------------------------------------------------- /src/components/TestForm.tsx: -------------------------------------------------------------------------------- 1 | import * as React from 'react'; 2 | import { useState, useEffect, useContext, createContext } from 'react'; 3 | import { Col, Row } from 'react-bootstrap'; 4 | import Form from 'react-bootstrap/Form'; 5 | import Button from 'react-bootstrap/Button'; 6 | import InputGroup from 'react-bootstrap/InputGroup'; 7 | import { HistoryContext } from '../context/historyContext'; 8 | 9 | interface Iprops{}; 10 | 11 | const TestForm: React.FC = (props) => { 12 | const { history, setHistory } = useContext(HistoryContext); 13 | interface eachMetadataType { 14 | name: string; 15 | namespace: string; 16 | selfLink: string; 17 | uid: string; 18 | resourceVersion: string; 19 | generation: number; 20 | creationTimestamp: string; 21 | }; 22 | interface eachItemType { 23 | metadata: eachMetadataType; 24 | spec: object; 25 | status: object; 26 | }; 27 | interface containersType { 28 | kind: string; 29 | apiVersion: string; 30 | metadata: object; 31 | items: eachItemType[]; 32 | }; 33 | const [containers, setContainers] = useState({ 34 | kind: '', 35 | apiVersion: '', 36 | metadata: {}, 37 | items: [{metadata: {name: "api-gateway", 38 | namespace: "default", 39 | selfLink: "/apis/apps/v1/namespaces/default/deployments/api-gateway", 40 | uid: "2ad6806d-0071-45cc-929f-09da9d21a9c9", 41 | resourceVersion: "116386", 42 | generation: 1, 43 | creationTimestamp: "2020-02-10T20:01:48Z"}, spec: {}, status: {}}] 44 | }); 45 | const getContainers = async () => { 46 | const r = await fetch('http://localhost:8081/apis/apps/v1/namespaces/default/deployments/'); 47 | const containers = await r.json(); 48 | setContainers(containers); 49 | }; 50 | useEffect(() => {getContainers()}, []); 51 | const dropDown = []; 52 | if (containers.items.length > 1) { 53 | for(let i = 0; i < containers.items.length; i++) { 54 | dropDown.push(); 55 | } 56 | } else { 57 | dropDown.push(); 58 | dropDown.push(); 59 | dropDown.push(); 60 | } 61 | 62 | ///////////// 63 | // button // 64 | //////////// 65 | interface arrayType { 66 | nameA: string; 67 | weightA: number; 68 | nameB: string; 69 | addressB: string; 70 | versionB: string; 71 | }; 72 | 73 | const [imageA, setImageA] = useState(''); 74 | const dropdownHandler = (e:any) =>{ 75 | setImageA(e.target.value); 76 | console.log(e.target.value) 77 | }; 78 | const [weightA, setWeightA] = useState(0); 79 | const weightAHandler = (e:any) => { 80 | setWeightA(e.target.value); 81 | console.log(weightA); 82 | }; 83 | 84 | const [imageB, setImageB] = useState(''); 85 | const imageHandler = (e:any) => { 86 | setImageB(e.target.value); 87 | console.log(imageB); 88 | }; 89 | 90 | const [addressB, setAddressB] = useState(''); 91 | const addressHandler = (e:any) => { 92 | setAddressB(e.target.value); 93 | console.log(addressB); 94 | }; 95 | 96 | const [versionB, setVersionB] = useState(''); 97 | const versionHandler = (e:any) => { 98 | setVersionB(e.target.value); 99 | console.log(versionB) 100 | }; 101 | 102 | 103 | interface ITheme { 104 | Aimage: string, 105 | Aweight: number, 106 | Bimage: string, 107 | Baddress: string, 108 | Bversion: string, 109 | }; 110 | 111 | // The standard way to create context. It takes an initial value object 112 | const ThemeContext = createContext({ 113 | Aimage: imageA, 114 | Aweight: weightA, 115 | Bimage: imageB, 116 | Baddress: addressB, 117 | Bversion: versionB, 118 | }); 119 | 120 | // Accessing context in a child component 121 | const themeContext = useContext(ThemeContext); 122 | 123 | const handleSubmit = (e:any) => { 124 | e.preventDefault(); 125 | console.log('successfully bound handleSubmit to the button'); 126 | // console.log('request context', requestContext); 127 | // const req = useContext(requestContext.imageA) 128 | // console.log('req', req); 129 | fetch('/dothis').then(data => data.json()).then(json => console.log(json)).catch(e => console.log(e)); 130 | setHistory([...history, { imageA, weightA, imageB, addressB, versionB }]); 131 | }; 132 | console.log('history now: ', history); 133 | 134 | return ( 135 | 136 | 137 |
138 | 139 | 140 |

Docker Image A:

141 | {dropdownHandler(e)}}> 142 | 143 | {dropDown} 144 | 145 |
146 | 147 | 152 | 153 | % 154 | weight 155 | 156 | 157 |
158 | 159 |

Docker Image B:

160 | 161 | 162 | Name: 163 | 164 | 169 | 170 |
171 | 172 | 173 | Address: 174 | 175 | 181 | 182 |
183 | 184 | 185 | Version: 186 | 187 | 193 | 194 |
195 |
196 |
197 | {/* */} 200 | 201 |
202 |
203 | ); 204 | }; 205 | export default TestForm; 206 | -------------------------------------------------------------------------------- /src/context/historyContext.tsx: -------------------------------------------------------------------------------- 1 | import * as React from 'react'; 2 | import { createContext, useState } from 'react'; 3 | export const HistoryContext = createContext(null); 4 | 5 | export const HistoryProvider = (props: any) => { 6 | const [history, setHistory] = useState([{ 7 | imageA: 'exampleNameA', 8 | weightA: 100, 9 | imageB: 'exampleNameB', 10 | addressB: '7AB0035', 11 | versionB: '1.0.2' 12 | }]); 13 | 14 | return ( 15 | 16 | {props.children} 17 | 18 | ) 19 | }; 20 | 21 | interface tikitakaContextType { 22 | imageA: string; 23 | weightA: number; 24 | imageB: string; 25 | addressB: string; 26 | versionB: string; 27 | } 28 | 29 | export const TikitakaContext = createContext({ 30 | imageA: 'skskskssk', 31 | weightA: 0, 32 | imageB: '', 33 | addressB: '', 34 | versionB: '' 35 | }); 36 | -------------------------------------------------------------------------------- /src/context/tikitakaContext.tsx: -------------------------------------------------------------------------------- 1 | import React, {createContext, useState} from 'react'; 2 | export const tikitakaContext = createContext([]); 3 | 4 | 5 | const request = { 6 | imageA: 'skskskssk', 7 | weightA: 0, 8 | imageB: '', 9 | addressB: '', 10 | versionB: '' 11 | } 12 | 13 | export const requestContext = createContext(request) -------------------------------------------------------------------------------- /src/index.tsx: -------------------------------------------------------------------------------- 1 | import * as React from 'react'; 2 | import * as ReactDOM from 'react-dom'; 3 | import { App } from "./components/App" 4 | import "./styles/app.scss" 5 | 6 | ReactDOM.render( 7 | , 8 | document.querySelector('.root') 9 | ); -------------------------------------------------------------------------------- /src/styles/app.scss: -------------------------------------------------------------------------------- 1 | 2 | /* RESETS 3 | 4 | ============================================ */ 5 | 6 | html { 7 | -webkit-box-sizing: border-box; 8 | box-sizing: border-box; 9 | } 10 | *, *:before, *:after { 11 | -webkit-box-sizing: inherit; 12 | box-sizing: inherit; 13 | } 14 | 15 | body { 16 | margin: 20px 0; 17 | padding-left: 232px; 18 | line-height: 1; 19 | font-family: 'Roboto', sans-serif; 20 | color: #202020; 21 | background-color: #fbfbfb; 22 | -webkit-font-smoothing: antialiased; 23 | -moz-osx-font-smoothing: grayscale; 24 | } 25 | 26 | body{ 27 | padding-top:90px 28 | } 29 | 30 | @media (min-width:768px){ 31 | body{padding-top:0} 32 | } 33 | 34 | @media (min-width:768px){ 35 | body{margin-left:232px; 36 | } 37 | } 38 | 39 | .navbar.fixed-left{ 40 | position:fixed; 41 | top:0; 42 | left:0; 43 | right:0; 44 | z-index:1030; 45 | } 46 | 47 | a{ 48 | color: rgba(255, 255, 255, 0.5) !important; 49 | padding-right: 0.5rem; 50 | padding-left: 0.5rem; 51 | padding: 0.5rem 1rem !important; 52 | } 53 | 54 | .flex-container{ 55 | display: flex; 56 | align-items: center; 57 | justify-content: flex-start; 58 | } 59 | 60 | .thumbnail img{ 61 | overflow: hidden; 62 | width: 180px; 63 | height: 180px; 64 | position: relative; 65 | overflow: hidden; 66 | border: 1px solid black; 67 | border-radius: 50%; 68 | } 69 | 70 | .caption .githubIcon{ 71 | padding-top: 0.45px; 72 | width: 60px; 73 | } 74 | 75 | .caption .linkedinIcon{ 76 | width: 46px; 77 | } 78 | 79 | @media (min-width:768px){ 80 | .navbar.fixed-left{ 81 | bottom:0; 82 | width:232px; 83 | flex-flow:column nowrap; 84 | align-items:flex-start; 85 | } 86 | .navbar.fixed-left .navbar-collapse{ 87 | flex-grow:0; 88 | flex-direction:column; 89 | width:100% 90 | } 91 | .navbar.fixed-left .navbar-collapse .navbar-nav{ 92 | flex-direction:column; 93 | width:100% 94 | }.navbar.fixed-left .navbar-collapse .navbar-nav .nav-item{ 95 | width:100% 96 | }.navbar.fixed-left .navbar-collapse .navbar-nav .nav-item .dropdown-menu{ 97 | top:0 98 | } 99 | } 100 | 101 | @media (min-width:768px){ 102 | .navbar.fixed-left{ 103 | right:auto; 104 | }.navbar.fixed-left .navbar-nav .nav-item .dropdown-toggle:after{ 105 | border-top:.3em solid transparent; 106 | border-left:.3em solid; 107 | border-bottom:.3em solid transparent; 108 | border-right:none; 109 | vertical-align:baseline 110 | } 111 | .navbar.fixed-left .navbar-nav .nav-item .dropdown-menu{ 112 | left:100% 113 | } 114 | } 115 | $navbar-dark-active-color: rgb(255, 0, 55); 116 | 117 | .header{ 118 | padding-left: 232px !important; 119 | } 120 | 121 | .createTestBtn { 122 | margin-right: 10px !important; 123 | } 124 | .carousel { 125 | width:500px; 126 | height:500px; 127 | margin: auto; 128 | } 129 | 130 | 131 | 132 | .content { 133 | padding-top: 100px; 134 | } 135 | @import "../../node_modules/bootstrap/scss/bootstrap"; 136 | @import "../../node_modules/bootstrap/scss/functions"; 137 | @import "../../node_modules/bootstrap/scss/variables"; 138 | @import "../../node_modules/bootstrap/scss/mixins"; -------------------------------------------------------------------------------- /token: -------------------------------------------------------------------------------- 1 | eyJhbGciOiJSUzI1NiIsImtpZCI6IiJ9.eyJpc3MiOiJrdWJlcm5ldGVzL3NlcnZpY2VhY2NvdW50Iiwia3ViZXJuZXRlcy5pby9zZXJ2aWNlYWNjb3VudC9uYW1lc3BhY2UiOiJkZWZhdWx0Iiwia3ViZXJuZXRlcy5pby9zZXJ2aWNlYWNjb3VudC9zZWNyZXQubmFtZSI6ImRlZmF1bHQtdG9rZW4tbmM0djYiLCJrdWJlcm5ldGVzLmlvL3NlcnZpY2VhY2NvdW50L3NlcnZpY2UtYWNjb3VudC5uYW1lIjoiZGVmYXVsdCIsImt1YmVybmV0ZXMuaW8vc2VydmljZWFjY291bnQvc2VydmljZS1hY2NvdW50LnVpZCI6IjNlZTdmN2YyLTRkMDUtNGMwZi04NDUwLWM0ODE2YzEyMDdkMSIsInN1YiI6InN5c3RlbTpzZXJ2aWNlYWNjb3VudDpkZWZhdWx0OmRlZmF1bHQifQ.FKgY9GcvPg_hs2dAPgSa1j983ZCaX_oaxNYCanPPu2BiyBx21V9BlsJZOdL_BUSedpFaCtaEtcnU8OQLzRy0_3oo6iDSauRU9hz-CRGFbiK4mWS9RRTFcjt88H8oQcZRXVsAwP8vylWJfA41_ADc1-R7Bgwef_dyiLaFRSyaYlyOQ6onl9fOXy346xrjrZY9AVSWtfkkg7GAjZDgSe1dQoaedfYTN-QyuvxXqWrx4NL0vokBSDOSePuI4GQpepmUzlEdi4BiuNrAqtL8gEVoEmYrCsw8RIxxPKIhE90I2aa7aGN42XD2YQ8vpzN7hvJPYuZR3rGJlAfhjWLVQozNXw# 2 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "allowSyntheticDefaultImports": true, 4 | "jsx": "react", 5 | "module": "commonjs", 6 | "noImplicitAny": true, 7 | "outDir": "./build/", 8 | "preserveConstEnums": true, 9 | "removeComments": true, 10 | "sourceMap": true, 11 | "target": "es5" 12 | }, 13 | "include": [ 14 | "./src/**/*" 15 | ] 16 | } -------------------------------------------------------------------------------- /webpack.config.js: -------------------------------------------------------------------------------- 1 | const path = require('path'); 2 | const HtmlWebpackPlugin = require('html-webpack-plugin'); 3 | const MiniCssExtractPlugin = require("mini-css-extract-plugin"); 4 | 5 | module.exports = { 6 | entry: './src/index.tsx', 7 | output: { 8 | path: path.resolve(__dirname, 'build'), 9 | publicPath: '/', 10 | filename: 'bundle.js', 11 | }, 12 | mode: 'development', 13 | devServer: { 14 | // Required for Docker to work with dev server 15 | // host: '0.0.0.0', 16 | host:'localhost', 17 | port: 8080, 18 | // match the output path 19 | contentBase: path.resolve(__dirname, 'build'), 20 | //enable HMR on the devServer 21 | hot: true, 22 | //match the output 'publicPath' 23 | publicPath: '/', 24 | // fallback to root for other urls 25 | historyApiFallback: true, 26 | inline: true, 27 | headers: { 'Access-Control-Allow-Origin': '*' }, 28 | 29 | // proxy is required in order to make api calls to express server while using hot-reload webpack server 30 | // routes api fetch requests from localhost:8080/api/* (webpack dev server) to localhost:3000/api/* (where our Express server is running) 31 | proxy: { 32 | '/testing-ab': { 33 | target: 'http://localhost:3000/', 34 | secure: false, 35 | }, 36 | '/data': { 37 | target: 'http://localhost:3000/', 38 | secure: false, 39 | }, 40 | }, 41 | }, 42 | module: { 43 | rules: [ 44 | { 45 | test: /\.tsx?$/, 46 | exclude: /node_modules/, 47 | use: 48 | { 49 | loader: "ts-loader", 50 | } 51 | }, 52 | { 53 | enforce: "pre", 54 | test: /\.js$/, 55 | use: 56 | { 57 | loader: "source-map-loader" 58 | } 59 | }, 60 | { 61 | test: /\.scss$/, 62 | use: [ 63 | MiniCssExtractPlugin.loader, 64 | { loader: 'css-loader', options: { sourceMap: true, importLoaders: 1 } }, 65 | { loader: 'sass-loader', options: { sourceMap: true } }, 66 | ], 67 | }, 68 | { 69 | test: /\.css$/, 70 | use: ['style-loader', 'css-loader'] 71 | } 72 | ] 73 | }, 74 | plugins: [ 75 | new HtmlWebpackPlugin({ 76 | template: "./index.html" 77 | }), 78 | new MiniCssExtractPlugin({ 79 | // Options similar to the same options in webpackOptions.output 80 | // both options are optional 81 | filename: '[name].css', 82 | chunkFilename: '[id].css', 83 | }) 84 | ], 85 | devtool: "source-map", 86 | resolve: { 87 | extensions: [".js", ".ts", ".tsx"] 88 | } 89 | } --------------------------------------------------------------------------------