├── .gitignore ├── .gitmodules ├── .workshop ├── build ├── develop-settings.sh ├── settings.sh └── setup ├── Dockerfile ├── LICENSE ├── README.md └── workshop ├── _modules.yml ├── _rhsummit18.yml ├── content ├── acl.adoc ├── authentication.adoc ├── circuit-breaking.adoc ├── fault-injection.adoc ├── images │ ├── 503.png │ ├── auth-fail.png │ ├── auth-success.png │ ├── blacklist_v3_blocked.png │ ├── circuit-breaking-graph-slowv2.png │ ├── circuit-graph.png │ ├── delay.png │ ├── failfast.png │ ├── kiali-graph-1.png │ ├── kiali-graph-2.png │ ├── kiali-tracing-1.png │ ├── kiali-tracing-2.png │ ├── kiali-tracing-3.png │ ├── kiali-tracing-4.png │ ├── mtls_initial.png │ ├── mtls_no_destination_rule.png │ ├── mtls_permissive.png │ ├── mtls_policy_and_rule.png │ ├── nocircuit-graph.png │ ├── nocircuit.png │ ├── rate.png │ ├── rbac-fail.png │ ├── rbac-success.png │ ├── retry.png │ ├── routing-graph-1.png │ ├── routing-graph-2.png │ ├── routing-graph-3.png │ ├── timeout-v1.png │ ├── timeout-v2.png │ ├── timeout.png │ └── whitelist_v2_fail.png ├── intro.adoc ├── kiali.adoc ├── mtls.adoc ├── rate-limiting.adoc ├── routing.adoc ├── scripts │ ├── build-images.sh │ ├── curl_customer.sh │ ├── curl_customer_long.sh │ ├── curl_customer_preference.sh │ ├── curl_customer_preference_quiet.sh │ ├── curl_customer_quiet.sh │ ├── curl_customer_token.sh │ ├── curl_customer_token_quiet.sh │ ├── curl_recommendation_args.sh │ ├── deploy.sh │ ├── loadtest.sh │ ├── loadtest_quiet.sh │ ├── retries.sh │ └── setup.sh ├── setup-environment.adoc ├── src │ ├── customer │ │ ├── .dockerignore │ │ ├── .gitignore │ │ ├── Dockerfile │ │ ├── pom.xml │ │ └── src │ │ │ ├── main │ │ │ ├── docker │ │ │ │ ├── Dockerfile.jvm │ │ │ │ └── Dockerfile.native │ │ │ ├── java │ │ │ │ └── com │ │ │ │ │ └── redhat │ │ │ │ │ └── developer │ │ │ │ │ └── demos │ │ │ │ │ └── customer │ │ │ │ │ └── rest │ │ │ │ │ ├── BaggageHeadersFactory.java │ │ │ │ │ ├── CustomerResource.java │ │ │ │ │ └── PreferenceService.java │ │ │ └── resources │ │ │ │ └── application.properties │ │ │ └── test │ │ │ └── java │ │ │ └── com │ │ │ └── redhat │ │ │ └── developer │ │ │ └── demos │ │ │ └── customer │ │ │ └── rest │ │ │ ├── CustomerResourceTest.java │ │ │ └── NativeCustomerResourceIT.java │ ├── deployments │ │ ├── curl.yaml │ │ ├── customer.yaml │ │ ├── gateway.yaml │ │ ├── preference.yaml │ │ └── recommendation.yaml │ ├── istiofiles │ │ ├── acl-blacklist.yml │ │ ├── acl-whitelist.yml │ │ ├── authentication-enable-tls.yml │ │ ├── authorization-enable-rbac.yml │ │ ├── controlplane.yaml │ │ ├── destination-rule-recommendation_cb_policy_version_v2.yml │ │ ├── destination-rule-tls.yml │ │ ├── destination-rule.yml │ │ ├── namespace-rbac-policy.yml │ │ ├── policy-jwt.yaml │ │ ├── policy-permissive-tls.yml │ │ ├── recommendation_rate_limit.yml │ │ ├── recommendation_requestcount.yml │ │ ├── routing-canary.yaml │ │ ├── routing-mirroring.yaml │ │ ├── routing-weighted.yaml │ │ ├── virtual-service-20-80.yml │ │ ├── virtual-service-80-20.yml │ │ ├── virtual-service-recommendation-503.yml │ │ ├── virtual-service-recommendation-delay.yml │ │ ├── virtual-service-recommendation-split.yml │ │ └── virtual-service-recommendation-timeout.yml │ ├── preference │ │ ├── .dockerignore │ │ ├── .gitignore │ │ ├── Dockerfile │ │ ├── pom.xml │ │ └── src │ │ │ ├── main │ │ │ ├── docker │ │ │ │ ├── Dockerfile.jvm │ │ │ │ └── Dockerfile.native │ │ │ ├── java │ │ │ │ └── com │ │ │ │ │ └── redhat │ │ │ │ │ └── developer │ │ │ │ │ └── demos │ │ │ │ │ └── customer │ │ │ │ │ └── rest │ │ │ │ │ ├── PreferenceResource.java │ │ │ │ │ └── RecommendationService.java │ │ │ └── resources │ │ │ │ └── application.properties │ │ │ └── test │ │ │ └── java │ │ │ └── com │ │ │ └── redhat │ │ │ └── developer │ │ │ └── demos │ │ │ └── customer │ │ │ └── rest │ │ │ ├── NativePreferenceResourceIT.java │ │ │ └── PreferenceResourceTest.java │ └── recommendation │ │ ├── .dockerignore │ │ ├── .gitignore │ │ ├── Dockerfile │ │ ├── pom.xml │ │ └── src │ │ ├── main │ │ ├── docker │ │ │ ├── Dockerfile.jvm │ │ │ └── Dockerfile.native │ │ └── java │ │ │ └── com │ │ │ └── redhat │ │ │ └── developer │ │ │ └── demos │ │ │ └── recommendation │ │ │ └── rest │ │ │ └── RecommendationResource.java │ │ └── test │ │ └── java │ │ └── com │ │ └── redhat │ │ └── developer │ │ └── demos │ │ └── recommendation │ │ └── rest │ │ ├── NativeRecommendationResourceIT.java │ │ └── RecommendationResourceTest.java ├── workshop-overview.adoc └── workshop-summary.adoc ├── modules.yaml └── workshop.yaml /.gitignore: -------------------------------------------------------------------------------- 1 | .kube 2 | .config 3 | .bash_history 4 | *.swp 5 | -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule ".workshop/scripts"] 2 | path = .workshop/scripts 3 | url = https://github.com/openshift-homeroom/spawner-scripts.git 4 | branch = stable/1.x 5 | -------------------------------------------------------------------------------- /.workshop/build: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # This 'build' script is where you can add steps that should be run when 4 | # building the image for your custom workshop. 5 | 6 | # Move the workshop content to '/opt/app-root/workshop'. It could be left 7 | # at it's default location of '/opt/app-root/src/workshop', but by moving it, 8 | # it is out of view of the user doing the workshop and they aren't likely to 9 | # delete it by accident and break the display of the workshop content. 10 | 11 | mv workshop /opt/app-root/workshop 12 | 13 | # Also delete some of the other files from the top of the Git repository we 14 | # don't need for running the workshop. 15 | 16 | rm -f Dockerfile README.md LICENSE 17 | 18 | # If the workshop requires Git, it is necessary to set some defaults for 19 | # the name and email of the user for Git. 20 | 21 | git config --global user.email "you@example.com" 22 | git config --global user.name "Your Name" 23 | -------------------------------------------------------------------------------- /.workshop/develop-settings.sh: -------------------------------------------------------------------------------- 1 | AUTH_USERNAME=workshop 2 | AUTH_PASSWORD=workshop 3 | -------------------------------------------------------------------------------- /.workshop/settings.sh: -------------------------------------------------------------------------------- 1 | WORKSHOP_NAME=lab-ossm 2 | WORKSHOP_IMAGE=quay.io/thoraxe/lab-ossm:production 3 | RESOURCE_BUDGET=medium 4 | MAX_SESSION_AGE=3600 5 | IDLE_TIMEOUT=300 6 | SPAWNER_MODE=hosted-workshop 7 | -------------------------------------------------------------------------------- /.workshop/setup: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # This 'setup' script is where you can add steps that should be run each 4 | # time the container for the workshop is started. Note that if you are 5 | # using persistent storage with a workshop and make changes to files from 6 | # this script, or are deploying applications, your scripts must cope with 7 | # the steps having already been run. This is because this script will be 8 | # run a second time if the container were restarted for some reason. 9 | -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | FROM quay.io/openshifthomeroom/workshop-dashboard:4.1.1 2 | 3 | USER root 4 | 5 | COPY . /tmp/src 6 | 7 | RUN rm -rf /tmp/src/.git* && \ 8 | chown -R 1001 /tmp/src && \ 9 | chgrp -R 0 /tmp/src && \ 10 | chmod -R g+w /tmp/src 11 | 12 | ENV TERMINAL_TAB=split 13 | 14 | USER 1001 15 | 16 | RUN /usr/libexec/s2i/assemble 17 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | 178 | APPENDIX: How to apply the Apache License to your work. 179 | 180 | To apply the Apache License to your work, attach the following 181 | boilerplate notice, with the fields enclosed by brackets "[]" 182 | replaced with your own identifying information. (Don't include 183 | the brackets!) The text should be enclosed in the appropriate 184 | comment syntax for the file format. We also recommend that a 185 | file or class name and description of purpose be included on the 186 | same "printed page" as the copyright notice for easier 187 | identification within third-party archives. 188 | 189 | Copyright [yyyy] [name of copyright owner] 190 | 191 | Licensed under the Apache License, Version 2.0 (the "License"); 192 | you may not use this file except in compliance with the License. 193 | You may obtain a copy of the License at 194 | 195 | http://www.apache.org/licenses/LICENSE-2.0 196 | 197 | Unless required by applicable law or agreed to in writing, software 198 | distributed under the License is distributed on an "AS IS" BASIS, 199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 200 | See the License for the specific language governing permissions and 201 | limitations under the License. 202 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Red Hat OpenShift Service Mesh in Action 2 | This repository contains the content for a Homeroom-based workshop that 3 | provides several exercises exploring Red Hat OpenShift Service Mesh (RHOSSM). 4 | 5 | More information about Homeroom can be found [here](https://github.com/openshift-homeroom/lab-workshop-content). 6 | 7 | ## Contributing and Workflow 8 | Contributions are most welcome. Please feel free to file issues. 9 | 10 | When submitting pull requests, make sure to submit them against the `develop` 11 | branch. Once merged, those changes will automatically be built into a new 12 | image that lands on Quay.io: https://quay.io/repository/thoraxe/lab-ossm 13 | 14 | ## Code Repository Tagging 15 | This repository is cloned as part of a provisioning process associated with 16 | the Red Hat Product Demo System (RHPDS). As such, specific tags of this 17 | repository will always be fetched such that RHPDS can lock to a point-in-time 18 | of the repository. A normal versioning scheme that roughly aligns with the Service Mesh version should be used. 19 | 20 | ## Image Tagging 21 | The container image on Quay.io will be built continually for both the 22 | `master` and `develop` branches, resulting in image tags of `latest`, 23 | `master`, and `develop`. 24 | 25 | A manual tag for `production` should be created when an update to what RHPDS 26 | deploys is desired. The tag for `production` should point at the image built 27 | from whatever code repository tag is being deployed by RHPDS. 28 | Example workflow: 29 | 30 | * Pull request merged to `develop` 31 | * Image built and tagged `develop` and `latest` 32 | * Testing performed and, if successful: 33 | * Pull request from `develop` to `master` 34 | * Image built and tagged `master` and `latest` 35 | * Tag `master` code repository with a version eg: 1.0.3 36 | * Tag `master` image in Quay.io with `production` and `1.0.3` 37 | * Ensure RHPDS production is cloning repository tag `1.0.3` 38 | 39 | ## Deploying to your own cluster 40 | Deploying this experience to your own cluster is not particularly difficult. 41 | You will need to understand the difference between the way the lab guide is 42 | written and the names you give your own resources in your own cluster. 43 | 44 | We are using the prefix `my-` for Project names. The lab guide will be using 45 | your OpenShift username as the prefix. If you know your username is `john`, 46 | then you should use the prefix `john-` when creating Projects. That being 47 | said, you need cluster administrator access to install the operators. 48 | However, *you **cannot** use the lab guide as `kubeadmin`*. The `kubeadmin` 49 | user is an alias for `kube:admin`, and the colon causes the lab guide to 50 | attempt to use a serviceaccount with a colon, which is invalid, and breaks 51 | the lab guide. 52 | 53 | If you don't already have real users in your cluster, you can use an 54 | [htpasswd identity 55 | provider](https://docs.openshift.com/container-platform/4.4/authentication/identity_providers/configuring-htpasswd-identity-provider.html) 56 | to create users easily and quickly. 57 | 58 | There are three main steps to getting the lab going in your cluster. 59 | 60 | ### Installing the Service Mesh operators and Control Plane 61 | 62 | 1. Install an OpenShift 4 environment. You will need cluster-administrator access. 63 | 1. Follow the documentation for [installing the service mesh operators](https://docs.openshift.com/container-platform/4.4/service_mesh/service_mesh_install/installing-ossm.html#installing-ossm) 64 | 1. Create a project called `my-smcp` where the Service Mesh control plane will be installed. 65 | 1. Follow the documentation for [installing the service mesh control plane](https://docs.openshift.com/container-platform/4.4/service_mesh/service_mesh_install/installing-ossm.html#ossm-control-plane-deploy-operatorhub_installing-ossm) and install it into your `my-smcp` project (not `istio-system`). 66 | 1. While you are waiting for the control plane to install, create a new project called `my-tutorial`. 67 | 1. Follow the documentation for [creating the member role](https://docs.openshift.com/container-platform/4.4/service_mesh/service_mesh_install/installing-ossm.html#ossm-member-roll-create-console_installing-ossm) being sure to use the project you created (`my-tutorial`) and to do so *inside* the `my-smcp` project. 68 | 69 | Wait for the above steps to complete before continuing. You can track the 70 | progress of the Service Mesh control plane installation by looking at the 71 | control plane details (assuming defaults): 72 | 73 | Installed Operators -> `my-smcp` -> Red Hat OpenShift Service Mesh -> Istio Service Mesh Control Plane -> `basic install` 74 | 75 | In the Conditions area, you will see that _Installed_, _Reconciled_, and 76 | _Ready_ are all _True_ once the installation is successful. 77 | 78 | ### Deploying the tutorial application 79 | It will be easiest to do this from the command line. Note that the following 80 | scriptlet assumes you have created a project called `my-tutorial`: 81 | 82 | ``` 83 | oc create -n my-tutorial -f https://gist.githubusercontent.com/thoraxe/a189ade0d2dd19c8c275179b48577117/raw/02c4ea130cb6b1ebd83c7fd7a4cb2824fac179b7/curl.yaml 84 | oc create -n my-tutorial -f https://gist.githubusercontent.com/thoraxe/a189ade0d2dd19c8c275179b48577117/raw/02c4ea130cb6b1ebd83c7fd7a4cb2824fac179b7/customer.yaml 85 | oc create -n my-tutorial -f https://gist.githubusercontent.com/thoraxe/a189ade0d2dd19c8c275179b48577117/raw/02c4ea130cb6b1ebd83c7fd7a4cb2824fac179b7/gateway.yaml 86 | oc create -n my-tutorial -f https://gist.githubusercontent.com/thoraxe/a189ade0d2dd19c8c275179b48577117/raw/02c4ea130cb6b1ebd83c7fd7a4cb2824fac179b7/preference.yaml 87 | oc create -n my-tutorial -f https://gist.githubusercontent.com/thoraxe/a189ade0d2dd19c8c275179b48577117/raw/0f55e6623bdd0a32b31be92f5b0869a0d7abf648/recommendation.yaml 88 | ``` 89 | 90 | ### Deploying the lab guide 91 | 92 | 1. Create a Project to hold the lab guide -- `lab-ossm` 93 | 1. You will need the cluster's default route subdomain to use with `new-app`. The following will put it into a `bash` variable: 94 | 95 | export SUBDOMAIN=$(oc get ingresses.config.openshift.io cluster -o jsonpath='{.spec.domain}') 96 | 97 | 1. Then, use the `new-app` subcommand of the `oc` CLI to deploy the lab guide: 98 | 99 | oc new-app -n lab-ossm https://raw.githubusercontent.com/openshift-homeroom/workshop-spawner/7.1.0/templates/hosted-workshop-production.json \ 100 | --param CLUSTER_SUBDOMAIN="$SUBDOMAIN" \ 101 | --param SPAWNER_NAMESPACE="lab-ossm" \ 102 | --param WORKSHOP_NAME="lab-ossm" \ 103 | --param WORKSHOP_IMAGE="quay.io/thoraxe/lab-ossm:production" \ 104 | --param OC_VERSION="4.3" 105 | 106 | Take special note of the route that is created. It will be something like `lab-ossm-lab-ossm.xxxxxx`. 107 | 108 | Access the lab guide and then make sure to login using the username and 109 | password you use to access the OpenShift environment. Since you needed 110 | cluster administrative access to install the Service Mesh operators, you're 111 | probably fine. 112 | 113 | ### Caveats 114 | None of the variables are appropriately set in this environment. You will 115 | need to pay careful attention to all commands to make sure they look like 116 | they will evaluate correctly. The various bash scripts use an environment 117 | variable `JUPYTERHUB_USER` as a prefix to Project names (see the note above). 118 | Make sure it is set correctly in the terminals. -------------------------------------------------------------------------------- /workshop/_modules.yml: -------------------------------------------------------------------------------- 1 | modules: 2 | intro: 3 | name: Introduction and Setup 4 | kiali: 5 | name: Observing the Service Mesh using Kiali 6 | routing: 7 | name: Routing within the Service Mesh 8 | mtls: 9 | name: Enabling Mutual TLS 10 | authentication: 11 | name: Authentication (JWT and RBAC) 12 | acl: 13 | name: Whitelisting/Blacklisting 14 | circuit-breaking: 15 | name: Retries, Timeouts and Circuit Breaking 16 | rate-limiting: 17 | name: Rate Limiting/Policy 18 | fault-injection: 19 | name: Fault Injection 20 | # extra-credit: 21 | # name: Extra Credit 22 | -------------------------------------------------------------------------------- /workshop/_rhsummit18.yml: -------------------------------------------------------------------------------- 1 | id: "istio" 2 | name: "Red Hat OpenShift Service Mesh in Action" 3 | 4 | content: 5 | url: https://raw.githubusercontent.com/thoraxe/istio-lab-summit-2019/master 6 | 7 | vars: 8 | KUBEADMIN_PASSWORD: "default" 9 | API_URL: "default" 10 | MASTER_URL: "default" 11 | OCP_USERNAME: "defaultocpusername" 12 | 13 | modules: 14 | activate: 15 | - intro 16 | - kiali 17 | - routing 18 | - mtls 19 | - authentication 20 | - acl 21 | - circuit-breaking 22 | - rate-limiting 23 | - fault-injection 24 | -------------------------------------------------------------------------------- /workshop/content/acl.adoc: -------------------------------------------------------------------------------- 1 | = Access Control Lists 2 | 3 | Access control lists set whitelists/blacklists to restrict communication. 4 | They are a part of the policy management feature set that is provided by 5 | Istio's Mixer, via various adapters. 6 | 7 | :toc: 8 | 9 | == What we will learn in this module 10 | This module will provide instruction on how to configure whitelists and blacklists, 11 | which control what services a service is able to access. 12 | 13 | == Before Starting 14 | You only need the `customer` Virtual Service and Gateway, but if you have the 15 | `recommendation` Destination Rule from other exercises, that's OK: 16 | 17 | [source,bash,role="execute-1"] 18 | ---- 19 | oc -n %username%-tutorial get istio-io 20 | ---- 21 | 22 | And you should see something like the following: 23 | 24 | ---- 25 | NAME AGE 26 | gateway.networking.istio.io/customer-gateway 3h16m 27 | 28 | NAME GATEWAYS HOSTS AGE 29 | virtualservice.networking.istio.io/customer [customer-gateway] [*] 3h16m 30 | 31 | NAME HOST AGE 32 | destinationrule.networking.istio.io/recommendation recommendation 36m 33 | ---- 34 | 35 | If you have any scripts running in the bottom terminal, make sure to click 36 | there and then press ctrl+c to terminate them. 37 | 38 | == Patience is Required 39 | The access control rules take some time to be applied and reflected. Be patient here! 40 | 41 | [#whitelist] 42 | == Whitelist 43 | We’ll create a whitelist on the preference service to only allow requests to 44 | the recommendation service if the version is v1 or v3. Requests to the v2 45 | version of the recommendation service will return a 404 Not Found HTTP error 46 | code. 47 | 48 | First, start generating traffic: 49 | 50 | [source,bash,role="execute-2"] 51 | ---- 52 | bash /opt/app-root/workshop/content/scripts/curl_customer_quiet.sh 53 | ---- 54 | 55 | You can also run the command below a couple times to see what the output from 56 | the shell script above is like: 57 | 58 | [source,bash,role="execute-1"] 59 | ---- 60 | export INGRESS_GATEWAY=$(oc get route -n %username%-smcp istio-ingressgateway -o 'jsonpath={.spec.host}') 61 | for x in $(seq 1 10); do curl http://${INGRESS_GATEWAY}; done 62 | ---- 63 | 64 | You will see something like: 65 | 66 | ---- 67 | customer => preference => recommendation v1 from '765d4bc49d-ddgg7': 2949 68 | customer => preference => recommendation v2 from '7679d466f8-2hwcd': 2950 69 | ---- 70 | 71 | Now create the whitelist: 72 | 73 | [source,bash,role="execute-1"] 74 | ---- 75 | oc apply -f /opt/app-root/workshop/content//src/istiofiles/acl-whitelist.yml -n %username%-tutorial 76 | ---- 77 | 78 | The YAML you just created looks like: 79 | 80 | [source, yaml] 81 | ---- 82 | apiVersion: "config.istio.io/v1alpha2" 83 | kind: handler 84 | metadata: 85 | name: preferencewhitelist 86 | spec: 87 | compiledAdapter: listchecker 88 | params: 89 | overrides: ["v1", "v3"] 90 | blacklist: false 91 | --- 92 | apiVersion: "config.istio.io/v1alpha2" 93 | kind: instance 94 | metadata: 95 | name: preferencesource 96 | spec: 97 | compiledTemplate: listentry 98 | params: 99 | value: destination.labels["version"] 100 | --- 101 | apiVersion: "config.istio.io/v1alpha2" 102 | kind: rule 103 | metadata: 104 | name: checktorecommendation 105 | spec: 106 | match: destination.labels["app"] == "recommendation" 107 | actions: 108 | - handler: preferencewhitelist 109 | instances: [ preferencesource ] 110 | ---- 111 | 112 | If you look at Kiali, you will notice that traffic is now making it to the v1 113 | and v3 versions of the service, but 100% of the traffic to the v2 service is 114 | being rejected with `403 Forbidden`. 115 | 116 | image::images/whitelist_v2_fail.png[] 117 | 118 | You can use `curl` via a script to see the 403 error: 119 | 120 | [source,bash,role="execute-1"] 121 | ---- 122 | bash /opt/app-root/workshop/content/scripts/curl_recommendation_args.sh v2 123 | ---- 124 | 125 | [#whitelist-cleanup] 126 | == Cleanup 127 | 128 | [source,bash,role="execute-1"] 129 | ---- 130 | oc delete -n %username%-tutorial -f /opt/app-root/workshop/content/src/istiofiles/acl-whitelist.yml 131 | ---- 132 | 133 | [#blacklist] 134 | == Blacklist 135 | Whereas whitelists allow communication only to the listed hosts, blacklists 136 | deny traffic to the listed hosts. We'll demonstrate using a blacklist on 137 | version 3 of the recomemendation service. Any requests to v3 will result in a 138 | `403 Forbidden` HTTP error code. 139 | 140 | If you have stopped previous load script, start it again. 141 | 142 | [source,bash,role="execute-2"] 143 | ---- 144 | bash /opt/app-root/workshop/content/scripts/curl_customer_quiet.sh 145 | ---- 146 | 147 | Run the below once or twice to see actual output: 148 | 149 | [source,bash,role="execute-1"] 150 | ---- 151 | export INGRESS_GATEWAY=$(oc get route -n %username%-smcp istio-ingressgateway -o 'jsonpath={.spec.host}') 152 | for x in $(seq 1 10); do curl http://${INGRESS_GATEWAY}; done 153 | ---- 154 | 155 | You will see something like: 156 | 157 | ---- 158 | customer => preference => recommendation v1 from '765d4bc49d-ddgg7': 2953 159 | customer => preference => recommendation v3 from '6d4bf9cff8-5nvw2': 834 160 | customer => preference => recommendation v2 from '7679d466f8-2hwcd': 2954 161 | customer => preference => recommendation v1 from '765d4bc49d-ddgg7': 2954 162 | ---- 163 | 164 | You can see traffic is hitting all of the service versions. 165 | 166 | *Note:* it can take several minutes for the deletion of the whitelist to propagate. If you don't see the above, double check that you deleted the whitelist and then create the blacklist anyway. 167 | 168 | Now, create the blacklist: 169 | 170 | [source,bash,role="execute-1"] 171 | ---- 172 | oc apply -f /opt/app-root/workshop/content/src/istiofiles/acl-blacklist.yml -n %username%-tutorial 173 | ---- 174 | 175 | The blacklist YAML looks like the following: 176 | 177 | [source, yaml] 178 | ---- 179 | apiVersion: "config.istio.io/v1alpha2" 180 | kind: handler 181 | metadata: 182 | name: denycustomerhandler 183 | spec: 184 | compiledAdapter: denier 185 | params: 186 | status: 187 | code: 7 188 | message: Not allowed 189 | --- 190 | apiVersion: "config.istio.io/v1alpha2" 191 | kind: instance 192 | metadata: 193 | name: denycustomerrequests 194 | spec: 195 | compiledTemplate: checknothing 196 | --- 197 | apiVersion: "config.istio.io/v1alpha2" 198 | kind: rule 199 | metadata: 200 | name: denycustomer 201 | spec: 202 | match: source.labels["app"]=="preference" && destination.labels["app"] == "recommendation" && destination.labels["version"] == "v3" 203 | actions: 204 | - handler: denycustomerhandler 205 | instances: [ denycustomerrequests ] 206 | ---- 207 | 208 | Eventually, if you look at Kiali, you can now see that requests to the v3 version of the 209 | service are failing: 210 | 211 | image::images/blacklist_v3_blocked.png[] 212 | 213 | // TODO: add some kind of curl to demonstrate the 4xx 214 | 215 | You can also see this from the command line: 216 | 217 | [source,bash,role="execute-1"] 218 | ---- 219 | export INGRESS_GATEWAY=$(oc get route -n %username%-smcp istio-ingressgateway -o 'jsonpath={.spec.host}') 220 | for x in $(seq 1 10); do curl http://${INGRESS_GATEWAY}; done 221 | ---- 222 | 223 | You'll see only `v1` and `v2`: 224 | 225 | ---- 226 | customer => preference => recommendation v2 from '556d799fbf-rbcpt': 2381 227 | customer => preference => recommendation v1 from '7dbd669545-hzp4x': 5130 228 | customer => preference => recommendation v2 from '556d799fbf-rbcpt': 2382 229 | customer => preference => recommendation v1 from '7dbd669545-hzp4x': 5131 230 | ---- 231 | 232 | [#blacklist-cleanup] 233 | == Cleanup 234 | 235 | [source,bash,role="execute-1"] 236 | ---- 237 | oc delete -n %username%-tutorial -f /opt/app-root/workshop/content/src/istiofiles/acl-blacklist.yml 238 | ---- 239 | -------------------------------------------------------------------------------- /workshop/content/authentication.adoc: -------------------------------------------------------------------------------- 1 | = Authentication with JWT and RBAC 2 | In addition to enforcing security with mutual TLS, Red Hat OpenShift Service 3 | Mesh can also perform origin authentication, or _end-user authentication_ at 4 | the request level using JSON Web Token (JWT). 5 | 6 | == What we will learn in this module 7 | In this module, we are going to see how to enable authenticating end users 8 | with the Service Mesh. We will also see how to use the Service Mesh 9 | authorization feature to provide access control for services in the mesh. 10 | 11 | == Before Starting 12 | You only need the `customer` Virtual Service and Gateway, but if you have the 13 | `recommendation` Destination Rule from other exercises, that's OK: 14 | 15 | [source,bash,role="execute-1"] 16 | ---- 17 | oc -n %username%-tutorial get istio-io 18 | ---- 19 | 20 | And you should see something like the following: 21 | 22 | ---- 23 | NAME AGE 24 | gateway.networking.istio.io/customer-gateway 3h16m 25 | 26 | NAME GATEWAYS HOSTS AGE 27 | virtualservice.networking.istio.io/customer [customer-gateway] [*] 3h16m 28 | 29 | NAME HOST AGE 30 | destinationrule.networking.istio.io/recommendation recommendation 36m 31 | ---- 32 | 33 | If you have any scripts running in the bottom terminal, make sure to click 34 | there and then press ctrl+c to terminate them. 35 | 36 | [#enablingauthentication] 37 | == Enabling User-End Authentication 38 | 39 | Now it is time to enable end-user authentication. 40 | 41 | The first thing you need to do is validate that it is possible to communicate 42 | between all services without authentication. 43 | 44 | Execute the following: 45 | 46 | [source,bash,role="execute-1"] 47 | ---- 48 | export INGRESS_GATEWAY=$(oc get route -n %username%-smcp istio-ingressgateway -o 'jsonpath={.spec.host}') 49 | for x in $(seq 1 10); do curl http://${INGRESS_GATEWAY}; done 50 | ---- 51 | 52 | You will see something like: 53 | 54 | ---- 55 | customer => preference => recommendation v1 from '7dbd669545-cwv9h': 704 56 | customer => preference => recommendation v2 from '556d799fbf-nc5cz': 705 57 | customer => preference => recommendation v3 from '69df5546f-vb9nb': 706 58 | customer => preference => recommendation v1 from '7dbd669545-cwv9h': 705 59 | customer => preference => recommendation v2 from '556d799fbf-nc5cz': 706 60 | customer => preference => recommendation v3 from '69df5546f-vb9nb': 707 61 | customer => preference => recommendation v1 from '7dbd669545-cwv9h': 706 62 | customer => preference => recommendation v2 from '556d799fbf-nc5cz': 707 63 | customer => preference => recommendation v3 from '69df5546f-vb9nb': 708 64 | customer => preference => recommendation v1 from '7dbd669545-cwv9h': 707 65 | ---- 66 | 67 | Now we will add an end-user authentication policy. Take a look at the 68 | authentication YAML: 69 | 70 | [source,yaml] 71 | ---- 72 | apiVersion: "authentication.istio.io/v1alpha1" 73 | kind: "Policy" 74 | metadata: 75 | name: "jwt-example" 76 | spec: 77 | targets: 78 | - name: customer 79 | origins: 80 | - jwt: 81 | issuer: "testing@secure.istio.io" 82 | jwksUri: "https://raw.githubusercontent.com/istio/istio/release-1.1/security/tools/jwt/samples/jwks.json" 83 | principalBinding: USE_ORIGIN 84 | --- 85 | ---- 86 | 87 | And then run the following to deploy the policy: 88 | 89 | [source,bash,role="execute-1"] 90 | ---- 91 | oc apply -f /opt/app-root/workshop/content/src/istiofiles/policy-jwt.yaml -n %username%-tutorial 92 | ---- 93 | 94 | After about 1 minute, let's run the curl again: 95 | 96 | [source,bash,role="execute-1"] 97 | ---- 98 | export INGRESS_GATEWAY=$(oc get route -n %username%-smcp istio-ingressgateway -o 'jsonpath={.spec.host}') 99 | for x in $(seq 1 10); do curl http://${INGRESS_GATEWAY}; done 100 | ---- 101 | 102 | You will see: 103 | 104 | ---- 105 | Origin authentication failed. 106 | ---- 107 | 108 | If not, wait a few more moments and try again. If you want more details, run 109 | the following: 110 | 111 | [source,bash,role="execute-1"] 112 | ---- 113 | curl -sv http://${INGRESS_GATEWAY} 114 | ---- 115 | 116 | You'll notice a `401 Unauthorized` error message. This is because the service 117 | mesh is now enforcing authentication, and your curl is not authenticated (no valid JWT). 118 | 119 | === Kiali's Graph 120 | 121 | Within the Kiali UI select the *Graph* option from the left hand navigation 122 | and then choose 123 | 124 | * Namespace: %username%-tutorial 125 | * Versioned app graph 126 | * Requests percentage 127 | * Last 1m 128 | * Every 10s 129 | 130 | [#img-auth-fail] 131 | .Kiali Graph Authentication Failures 132 | image::images/auth-fail.png[] 133 | 134 | Note the 100% failure rate from the end-user (`istio-ingressgateway`) to the 135 | customer service. You may need to execute the failed curl a bunch of times to 136 | generate enough traffic for Kiali to display. 137 | 138 | When we created the policy, it referenced an external origin. There is also a 139 | token that goes with it. The following commands will fetch a valid token and 140 | then use that token in the `Authorization` header of the curl request. 141 | Execute it several times so that Kiali gets enough traffic to graph: 142 | 143 | [source,bash,role="execute-1"] 144 | ---- 145 | export INGRESS_GATEWAY=$(oc get route -n %username%-smcp istio-ingressgateway -o 'jsonpath={.spec.host}') 146 | TOKEN=$(curl https://raw.githubusercontent.com/istio/istio/release-1.1/security/tools/jwt/samples/demo.jwt -s) 147 | for x in $(seq 1 10); do curl --header "Authorization: Bearer $TOKEN" $INGRESS_GATEWAY -s; done 148 | ---- 149 | 150 | And you will see something like: 151 | 152 | ---- 153 | customer => preference => recommendation v3 from '588747fd55-m8mj9': 27103 154 | customer => preference => recommendation v1 from '7f8755bb79-vjwq2': 27140 155 | customer => preference => recommendation v2 from '74f48f4cbc-snwfm': 18439 156 | customer => preference => recommendation v3 from '588747fd55-m8mj9': 27104 157 | customer => preference => recommendation v1 from '7f8755bb79-vjwq2': 27141 158 | customer => preference => recommendation v2 from '74f48f4cbc-snwfm': 18440 159 | customer => preference => recommendation v3 from '588747fd55-m8mj9': 27105 160 | customer => preference => recommendation v1 from '7f8755bb79-vjwq2': 27142 161 | ---- 162 | 163 | === Kiali's Graph 164 | If you look back at Kiali's graph, you will note the 100% success rate from 165 | the authenticated end-user to customer. It may take a refresh cycle or two 166 | before the errors from previous runs disappear: 167 | 168 | [#cleanup] 169 | === Clean Up 170 | 171 | [source,bash,role="execute-1"] 172 | ---- 173 | oc delete -n %username%-tutorial policy.authentication.istio.io/jwt-example 174 | ---- 175 | 176 | = Service Mesh Role Based Access Control (RBAC) 177 | 178 | [#enabling-rbac] 179 | == Enabling RBAC 180 | 181 | `RbacConfig` objects are used to enable and configure Authorization in the 182 | service mesh. Take a look at the following YAML: 183 | 184 | [source,yaml] 185 | ---- 186 | apiVersion: "rbac.istio.io/v1alpha1" 187 | kind: RbacConfig 188 | metadata: 189 | name: default 190 | spec: 191 | mode: 'ON_WITH_INCLUSION' 192 | inclusion: 193 | namespaces: ["%username%-tutorial"] 194 | ---- 195 | 196 | Run this command to deploy the RBAC: 197 | 198 | [source,bash,role="execute-1"] 199 | ---- 200 | sed "s/CHANGEIT/$JUPYTERHUB_USER-tutorial/" /opt/app-root/workshop/content/src/istiofiles/authorization-enable-rbac.yml | oc apply -n %username%-tutorial -f - 201 | ---- 202 | 203 | Now RBAC is enabled on your mesh. Wait a few moments and then run the curl to 204 | test the RBAC: 205 | 206 | [source,bash,role="execute-1"] 207 | ---- 208 | export INGRESS_GATEWAY=$(oc get route -n %username%-smcp istio-ingressgateway -o 'jsonpath={.spec.host}') 209 | for x in $(seq 1 10); do curl http://${INGRESS_GATEWAY}; done 210 | ---- 211 | 212 | 213 | You will see an error: 214 | 215 | ---- 216 | RBAC: access denied 217 | ---- 218 | 219 | For more specific details, execute one curl: 220 | 221 | [source,bash,role="execute-1"] 222 | ---- 223 | export INGRESS_GATEWAY=$(oc get route -n %username%-smcp istio-ingressgateway -o 'jsonpath={.spec.host}') 224 | curl http://${INGRESS_GATEWAY} -sv 225 | ---- 226 | 227 | Notice that you get a `403 Forbidden` HTTP response. 228 | 229 | === Kiali's Graph 230 | 231 | Within the Kiali UI select the *Graph* option from the left hand navigation 232 | and then choose: 233 | 234 | * Namespace: %username%-tutorial 235 | * Versioned app graph 236 | * Requests percentage 237 | * Last 1m 238 | * Every 10s 239 | 240 | [#img-rbac-fail] 241 | .Kiali Graph Denied RBAC 242 | image::images/auth-fail.png[] 243 | 244 | Note the 100% failure rate due to denied RBAC 245 | 246 | By default, Istio uses a _deny by default_ strategy, meaning that nothing is 247 | permitted until you explicitly define access control policy to grant access 248 | to any service. At this point we have enabled RBAC but we have no roles and 249 | are not supplying any auth, so we are _denied by default_. 250 | 251 | [#grant-access] 252 | == Granting Access 253 | 254 | Let's grant access to any user to any service of our mesh (`customer`, 255 | `preference`, `recommendation`) only and only if the communication is an HTTP 256 | `GET`. 257 | 258 | [source,yaml] 259 | ---- 260 | apiVersion: "rbac.istio.io/v1alpha1" 261 | kind: ServiceRole 262 | metadata: 263 | name: service-viewer 264 | spec: 265 | rules: 266 | - services: ["*"] 267 | methods: ["GET"] 268 | constraints: 269 | - key: "destination.labels[app]" 270 | values: ["customer", "recommendation", "preference"] 271 | --- 272 | apiVersion: "rbac.istio.io/v1alpha1" 273 | kind: ServiceRoleBinding 274 | metadata: 275 | name: bind-service-viewer 276 | spec: 277 | subjects: 278 | - user: "*" 279 | roleRef: 280 | kind: ServiceRole 281 | name: "service-viewer" 282 | ---- 283 | 284 | Note the _Service Role_ `service-viewer` is configured for all users (*) and 285 | limits access to the GET method for the three services. This _Service Role_ 286 | object exists in your tutorial app's namespace, so its behavior is applied 287 | there. 288 | 289 | Run this command to deploy the role and role binding: 290 | 291 | [source,bash,role="execute-1"] 292 | ---- 293 | oc apply -f /opt/app-root/workshop/content/src/istiofiles/namespace-rbac-policy.yml -n %username%-tutorial 294 | ---- 295 | 296 | Wait a few moments and then send some requests by executing the following a few times: 297 | 298 | [source,bash,role="execute-1"] 299 | ---- 300 | export INGRESS_GATEWAY=$(oc get route -n %username%-smcp istio-ingressgateway -o 'jsonpath={.spec.host}') 301 | for x in $(seq 1 10); do curl http://${INGRESS_GATEWAY}; done 302 | ---- 303 | 304 | You will see something like: 305 | 306 | ---- 307 | customer => preference => recommendation v1 from '7f8755bb79-vjwq2': 27224 308 | customer => preference => recommendation v2 from '74f48f4cbc-snwfm': 18522 309 | customer => preference => recommendation v3 from '588747fd55-m8mj9': 27187 310 | customer => preference => recommendation v1 from '7f8755bb79-vjwq2': 27225 311 | customer => preference => recommendation v2 from '74f48f4cbc-snwfm': 18523 312 | customer => preference => recommendation v3 from '588747fd55-m8mj9': 27188 313 | customer => preference => recommendation v1 from '7f8755bb79-vjwq2': 27226 314 | ---- 315 | 316 | The communication now is possible. We aren't specifying a user, but no user 317 | is "any" user, so it works. 318 | 319 | === Kiali's Graph 320 | 321 | Within the Kiali UI select the *Graph* option from the left hand navigation 322 | and then choose: 323 | 324 | * Namespace: %username%-tutorial 325 | * Versioned app graph 326 | * Requests percentage 327 | * Last 1m 328 | * Every 10s 329 | 330 | [#img-rbac-success] 331 | .Kiali Graph Allowed RBAC 332 | image::images/kiali-graph-2.png[] 333 | 334 | Note the 100% success rate. 335 | 336 | [#cleanup] 337 | == Clean Up 338 | 339 | [source,bash,role="execute-1"] 340 | ---- 341 | oc delete -n %username%-tutorial rbacconfig.rbac.istio.io/default servicerole.rbac.istio.io/service-viewer servicerolebinding.rbac.istio.io/bind-service-viewer 342 | ---- 343 | 344 | You will see something like: 345 | 346 | ---- 347 | servicerole.rbac.istio.io "service-viewer" deleted 348 | servicerolebinding.rbac.istio.io "bind-service-viewer" deleted 349 | rbacconfig.rbac.istio.io "default" deleted 350 | ---- 351 | 352 | = What we learned in this module 353 | Red Hat OpenShift Service Mesh provides the capability to authenticate 354 | end-users via JWT and to enforce service RBAC. Kiali provides the mechanism 355 | to visialize end-user authentication and RBAC failures. 356 | -------------------------------------------------------------------------------- /workshop/content/circuit-breaking.adoc: -------------------------------------------------------------------------------- 1 | = Circuit Breaking/Timeouts 2 | 3 | Istio provides opt-in failure recovery and fault injection features that you 4 | can configure dynamically at runtime. Using these features helps your 5 | applications operate reliably, ensuring that the service mesh can tolerate 6 | failing nodes and preventing localized failures from cascading to other 7 | nodes. 8 | 9 | == What we will learn in this module 10 | This module will provide instruction on request retries, handling of service 11 | timeouts, and how to apply circuit breaking. This module will also show how to 12 | visualize these capabilities in Kiali. 13 | 14 | == Before Starting 15 | You only need the `customer` Virtual Service and Gateway, but if you have the 16 | `recommendation` Destination Rule from other exercises, that's OK: 17 | 18 | [source,bash,role="execute-1"] 19 | ---- 20 | oc -n %username%-tutorial get istio-io 21 | ---- 22 | 23 | And you should see something like the following: 24 | 25 | ---- 26 | NAME AGE 27 | gateway.networking.istio.io/customer-gateway 3h16m 28 | 29 | NAME GATEWAYS HOSTS AGE 30 | virtualservice.networking.istio.io/customer [customer-gateway] [*] 3h16m 31 | 32 | NAME HOST AGE 33 | destinationrule.networking.istio.io/recommendation recommendation 36m 34 | ---- 35 | 36 | If you have any scripts running in the bottom terminal, make sure to click 37 | there and then press ctrl+c to terminate them. 38 | 39 | [#retry] 40 | == Retry 41 | 42 | To add resilience to your application, you can ask the Service Mesh to retry 43 | failed connections N more times. Istio by default implements retries for 44 | `VirtualService` endpoints. You will make now make recommendation-v2 fail 45 | 100% of the time so that we can observe this retry behavior: 46 | 47 | [source,bash,role="execute-1"] 48 | ---- 49 | bash /opt/app-root/workshop/content/scripts/retries.sh misbehave 50 | ---- 51 | 52 | You will see something like: 53 | 54 | ---- 55 | Following requests to / will return a 503 56 | ---- 57 | 58 | This is a special endpoint that will toggle the recommendation application to 59 | return only `503`s. You can validate that the v2 endpoint now only returns 60 | `503` with the following: 61 | 62 | [source,bash,role="execute-1"] 63 | ---- 64 | bash /opt/app-root/workshop/content/scripts/curl_recommendation_args.sh v2 65 | ---- 66 | 67 | However, you will see that a normal request that goes through the entire set 68 | of microservices will work every time. This is because the mesh is 69 | implementing retries: 70 | 71 | [source,bash,role="execute-1"] 72 | ---- 73 | export INGRESS_GATEWAY=$(oc get route -n %username%-smcp istio-ingressgateway -o 'jsonpath={.spec.host}') 74 | for x in $(seq 1 10); do curl http://${INGRESS_GATEWAY}; done 75 | ---- 76 | 77 | Start generating traffic in the background: 78 | 79 | [source,bash,role="execute-2"] 80 | ---- 81 | bash /opt/app-root/workshop/content/scripts/curl_customer_quiet.sh 82 | ---- 83 | 84 | === Kiali's Graph 85 | In the Kiali graph, you will notice 0% of the traffic going to v2. You can 86 | click on the red V2 box and see no traffic is being logged: 87 | 88 | [#img-503] 89 | .Kiali Graph Retry 90 | image:images/retry.png[] 91 | 92 | Even though the recommendation v2 has no traffic logged, the client has a 93 | 100% success rate due to the retries. 94 | 95 | Now, make the pod v2 behave well again: 96 | 97 | [source,bash,role="execute-1"] 98 | ---- 99 | bash /opt/app-root/workshop/content/scripts/retries.sh behave 100 | ---- 101 | 102 | You will see something like: 103 | 104 | ---- 105 | Following requests to / will return 200 106 | ---- 107 | 108 | The application is back to random load-balancing between v1, v2 and v3. 109 | 110 | [#timeout] 111 | == Timeout 112 | 113 | Now we will configure a service to wait only N seconds before giving up and 114 | failing. 115 | 116 | First, introduce some wait time in `recommendation v2` by making it a slow 117 | performer with a 3 second delay by running the command 118 | 119 | [source,bash,role="execute-1"] 120 | ---- 121 | bash /opt/app-root/workshop/content/scripts/retries.sh timeout?timeout=3 122 | ---- 123 | 124 | You will see something like: 125 | 126 | ---- 127 | Timeout has been set to 3 seconds 128 | ---- 129 | 130 | Keep the load script running. 131 | 132 | In Kiali Graph, switch to viewing Response time (it's the second dropdown 133 | menu, to the right of _Versioned app graph_). 134 | 135 | See the load-balancing between v1, v2 and v3 but with v2 taking a bit of time 136 | to respond 137 | 138 | [#img-] 139 | .Kiali Graph Showing slow v2 endpoint response time 140 | image:images/circuit-breaking-graph-slowv2.png[] 141 | 142 | If you click the "Distributed Tracing" button in Kiali, you can look at the 143 | Jaeger traces for these calls. You'll see that the 3s time for the calls that 144 | hit V2 make the trace look bizarre, because the rest of the calls take 145 | single-digit _milliseconds_ in comparison. 146 | 147 | *Note:* If you see an error when trying to access the distributed tracing 148 | information, make sure that you visit the Jaeger UI directly in order to 149 | accept the SSL certificate and grant permissions to Jaeger. In this lab 150 | guide, you can do this by clicking on "Console" to open the OpenShift web 151 | console, then click "Networking" -> "Routes" and make sure to select the 152 | `%username%-smcp` project (at the top) in order to find the URL for Jaeger. 153 | 154 | [#img-timeout-v1] 155 | .Kiali Distributed Tracing for Recommendation v1 156 | image:images/timeout-v1.png[] 157 | 158 | [#img-timeout-v2] 159 | .Kiali Distributed Tracing for Recommendation v2 160 | image:images/timeout-v2.png[] 161 | 162 | Note the duration of v2 is > 3s compared to the ms time of v1 and v3 163 | 164 | Now, add the timeout rule: 165 | 166 | [source,bash,role="execute-1"] 167 | ---- 168 | oc create -n %username%-tutorial -f /opt/app-root/workshop/content/src/istiofiles/virtual-service-recommendation-timeout.yml 169 | ---- 170 | 171 | This file looks like: 172 | 173 | [source,yaml] 174 | ---- 175 | apiVersion: networking.istio.io/v1alpha3 176 | kind: VirtualService 177 | metadata: 178 | name: recommendation 179 | spec: 180 | hosts: 181 | - recommendation 182 | http: 183 | - route: 184 | - destination: 185 | host: recommendation 186 | timeout: 1.000s 187 | ---- 188 | 189 | This tells Istio to wait no longer than 1.000s before declaring the endpoint 190 | timed out and retrying. 191 | 192 | You will see it return v1 or v3 after waiting about 1 second. You don't see 193 | v2 anymore because the response from v2 expires after the timeout period and 194 | it is never returned: 195 | 196 | [source,bash,role="execute-1"] 197 | ---- 198 | export INGRESS_GATEWAY=$(oc get route -n %username%-smcp istio-ingressgateway -o 'jsonpath={.spec.host}') 199 | for x in $(seq 1 10); do curl http://${INGRESS_GATEWAY}; done 200 | ---- 201 | 202 | You can also observe this in Kiali: 203 | 204 | [#img-timeout] 205 | .Kiali Graph for Timeout Rule 206 | image:images/timeout.png[] 207 | 208 | Note that recommendation v2 now has a 100% failure rate due to the timeout 209 | rule. Also note that the response time will never be less than 1s because 210 | every third request, which would hit v2, ends up being timed out and retried. 211 | 212 | === Clean up 213 | 214 | Change the implementation of `v2` back to the image that responds without the 215 | delay of 3 seconds: 216 | 217 | [source,bash,role="execute-1"] 218 | ---- 219 | bash /opt/app-root/workshop/content/scripts/retries.sh timeout?timeout=0 220 | ---- 221 | 222 | You will see something like: 223 | 224 | ---- 225 | Timeout has been set to 0 seconds 226 | ---- 227 | 228 | Then delete the virtual service created for timeout by: 229 | 230 | [source,bash,role="execute-1"] 231 | ---- 232 | oc delete -n %username%-tutorial -f /opt/app-root/workshop/content/src/istiofiles/virtual-service-recommendation-timeout.yml 233 | ---- 234 | 235 | You will see something like: 236 | 237 | ---- 238 | virtualservice.networking.istio.io "recommendation" deleted 239 | ---- 240 | 241 | Lastly, terminate the load script running in the bottom terminal with Control+C. 242 | 243 | [#failfast] 244 | == Fail Fast with Max Connections and Max Pending Requests 245 | 246 | Let's use a 34/33/33 split of traffic: 247 | 248 | [source,bash,role="execute-1"] 249 | ---- 250 | oc create -n %username%-tutorial -f /opt/app-root/workshop/content/src/istiofiles/virtual-service-recommendation-split.yml 251 | ---- 252 | 253 | This YAML creates a `DestinationRule` and a `VirtualService` to control the traffic: 254 | 255 | [source,yaml,subs="+macros,+attributes"] 256 | ---- 257 | apiVersion: networking.istio.io/v1alpha3 258 | kind: VirtualService 259 | metadata: 260 | name: recommendation 261 | spec: 262 | hosts: 263 | - recommendation 264 | http: 265 | - route: 266 | - destination: 267 | host: recommendation 268 | subset: v1 269 | weight: 34 270 | - destination: 271 | host: recommendation 272 | subset: v2 273 | weight: 33 274 | - destination: 275 | host: recommendation 276 | subset: v3 277 | weight: 33 278 | --- 279 | ---- 280 | 281 | Note the weighting of the 3 recommendation destination versions. 282 | 283 | Run the following a few times to generate some load for Kiali to capture: 284 | 285 | [source,bash,role="execute-1"] 286 | ---- 287 | export INGRESS_GATEWAY=$(oc get route -n %username%-smcp istio-ingressgateway -o 'jsonpath={.spec.host}') 288 | for x in $(seq 1 10); do curl http://${INGRESS_GATEWAY}; done 289 | ---- 290 | 291 | Now go to "Distributed Tracing" in Kiali and look at the response times. Note that all recommendation hits respond within single-digit milliseconds. 292 | 293 | [#img-failfast] 294 | .Kiali Distributed Tracing for Base Fail Fast 295 | image:images/failfast.png[] 296 | 297 | [#nocircuitbreaker] 298 | === Load test without circuit breaker 299 | 300 | Next, introduce some wait time in `recommendation v2` by making it a slow 301 | performer with a 3 second delay by running the command: 302 | 303 | [source,bash,role="execute-1"] 304 | ---- 305 | bash /opt/app-root/workshop/content/scripts/retries.sh timeout?timeout=3 306 | ---- 307 | 308 | You will see something like: 309 | 310 | ---- 311 | Timeout has been set to 3 seconds 312 | ---- 313 | 314 | Run the following a few times to generate new, "delayed" traffic: 315 | 316 | [source,bash,role="execute-1"] 317 | ---- 318 | export INGRESS_GATEWAY=$(oc get route -n %username%-smcp istio-ingressgateway -o 'jsonpath={.spec.host}') 319 | for x in $(seq 1 10); do curl http://${INGRESS_GATEWAY}; done 320 | ---- 321 | 322 | [#img-nocicuit] 323 | .Kiali Distributed Tracing for Fail Fast w/no Circuit Breaking 324 | image:images/nocircuit.png[] 325 | 326 | All of the requests to our system were successful, but 1/3 of the requests 327 | took longer time, as the `v2` instance/pod was a slow performer. 328 | 329 | [#circuitbreaker] 330 | === Load test with circuit breaker 331 | 332 | But suppose that in a production system this 3s delay was caused by too many 333 | concurrent requests to the same instance/pod. We don't want multiple requests 334 | getting queued or making the instance/pod even slower. So we'll add a circuit 335 | breaker that will *open* whenever we have more than 1 request being handled 336 | by any instance/pod: 337 | 338 | [source,bash,role="execute-1"] 339 | ---- 340 | oc apply -n %username%-tutorial -f /opt/app-root/workshop/content/src/istiofiles/destination-rule-recommendation_cb_policy_version_v2.yml 341 | ---- 342 | 343 | This file defines the Istio circuit breaker logic to prevent connections to 344 | V2 from piling up: 345 | 346 | [source,yaml,subs="+macros,+attributes"] 347 | ---- 348 | apiVersion: networking.istio.io/v1alpha3 349 | kind: DestinationRule 350 | metadata: 351 | name: recommendation 352 | spec: 353 | host: recommendation 354 | subsets: 355 | - name: v1 356 | labels: 357 | version: v1 358 | - name: v2 359 | labels: 360 | version: v2 361 | trafficPolicy: 362 | connectionPool: 363 | http: 364 | http1MaxPendingRequests: 1 365 | maxRequestsPerConnection: 1 366 | tcp: 367 | maxConnections: 1 368 | outlierDetection: 369 | baseEjectionTime: 120.000s 370 | consecutiveErrors: 1 371 | interval: 1.000s 372 | maxEjectionPercent: 100 373 | - name: v3 374 | labels: 375 | version: v3 376 | ---- 377 | 378 | Note the connection pool with a max of 1 pending request and a traffic policy 379 | where 100% of single consecutive errors fail. `oc apply` causes the existing 380 | object(`DestinationRule` called `recommendation`) to be updated. 381 | 382 | You will see something like: 383 | 384 | ---- 385 | destinationrule.networking.istio.io/recommendation configured 386 | ---- 387 | 388 | Now let's see what is the behavior of the system running some load again. 389 | This script runs roughly 20 concurrent requests (it performs `curl` in the 390 | background without waiting for the command to finish): 391 | 392 | [source,bash,role="execute-2"] 393 | ---- 394 | bash /opt/app-root/workshop/content/scripts/loadtest_quiet.sh 395 | ---- 396 | 397 | If you then look at the Kiali graph: 398 | 399 | [#img-cicuit] [] 400 | .Kiali Graph Fail Fast w/Circuit Breaking 401 | image:images/circuit-graph.png[] 402 | 403 | When looking at request percentage, you will see a teeny tiny percentage of 404 | requests hitting v2. That's the circuit breaker being opened whenever Istio 405 | detects more than 1 pending request being handled by the v2 instance/pod. You 406 | can also see the little lightning icon indicating a breaker is configured. 407 | 408 | === Clean up 409 | 410 | Change the implementation of `v2` back to the image that responds without the 411 | delay of 3 seconds: 412 | 413 | [source,bash,role="execute-1"] 414 | ---- 415 | bash /opt/app-root/workshop/content/scripts/retries.sh timeout?timeout=0 416 | ---- 417 | 418 | You will see something like: 419 | 420 | ---- 421 | Timeout has been set to 0 seconds 422 | ---- 423 | 424 | Then delete the virtual service and the destination rule created for circuit breaking by: 425 | 426 | [source,bash,role="execute-1"] 427 | ---- 428 | oc delete -n %username%-tutorial virtualservice.networking.istio.io/recommendation 429 | ---- 430 | -------------------------------------------------------------------------------- /workshop/content/fault-injection.adoc: -------------------------------------------------------------------------------- 1 | # Fault Injection 2 | 3 | Chaos engineering involves intentionally designing failure into components to 4 | ensure that the larger system is resilient. This can involve HTTP errors or 5 | network delays or even killing components all together. Understanding failure 6 | scenarios is a critical aspect of microservices architecture (aka distributed 7 | computing). Istio provides mechanisms to perform fault injection. 8 | 9 | ## What we will learn in this module 10 | This module will provide instruction on how to introduce service failures and 11 | delays as a part of understanding, testing, and designing microservices. 12 | 13 | ## Before Starting 14 | You only need the `customer` Virtual Service and Gateway, but if you have the 15 | `recommendation` Destination Rule from other exercises, that's OK: 16 | 17 | [source,bash,role="execute-1"] 18 | ---- 19 | oc -n %username%-tutorial get istio-io 20 | ---- 21 | 22 | And you should see something like the following: 23 | 24 | ---- 25 | NAME AGE 26 | gateway.networking.istio.io/customer-gateway 3h16m 27 | 28 | NAME GATEWAYS HOSTS AGE 29 | virtualservice.networking.istio.io/customer [customer-gateway] [*] 3h16m 30 | 31 | NAME HOST AGE 32 | destinationrule.networking.istio.io/recommendation recommendation 36m 33 | ---- 34 | 35 | If you have any scripts running in the bottom terminal, make sure to click 36 | there and then press ctrl+c to terminate them. 37 | 38 | 39 | [#503error] 40 | == HTTP Error 503 41 | In the absence of any traffic management policies or routing rules, 42 | recommendation v1, v2 and v3 are being randomly load-balanced as that is the 43 | default behavior in Service Mesh. 44 | 45 | [source,bash,role="execute-1"] 46 | ---- 47 | oc -n %username%-tutorial get pods -l app=recommendation 48 | ---- 49 | 50 | You will see something like: 51 | 52 | ---- 53 | NAME READY STATUS RESTARTS AGE 54 | recommendation-v1-7fbb8f794-ngw58 2/2 Running 0 21h 55 | recommendation-v2-77bc7bb94d-8wjxz 2/2 Running 0 17h 56 | recommendation-v3-5786cd744d-96q79 2/2 Running 0 17h 57 | ---- 58 | 59 | Then, start running load in the background and bottom terminal: 60 | 61 | [source,bash,role="execute-2"] 62 | ---- 63 | bash /opt/app-root/workshop/content/scripts/curl_customer_quiet.sh 64 | ---- 65 | 66 | Go ahead and create a `DestinationRule` that will inject 503's for approximately 50% of the requests: 67 | 68 | [source,bash,role="execute-1"] 69 | ---- 70 | oc apply -f /opt/app-root/workshop/content/src/istiofiles/virtual-service-recommendation-503.yml -n %username%-tutorial 71 | ---- 72 | 73 | You will see something like: 74 | 75 | ---- 76 | destinationrule.networking.istio.io/recommendation created 77 | virtualservice.networking.istio.io/recommendation created 78 | ---- 79 | 80 | The following is what you just configured: 81 | 82 | [source,yaml] 83 | ---- 84 | apiVersion: networking.istio.io/v1alpha3 85 | kind: DestinationRule 86 | metadata: 87 | name: recommendation 88 | spec: 89 | host: recommendation 90 | subsets: 91 | - labels: 92 | version: v1 93 | name: v1 94 | - labels: 95 | version: v2 96 | name: v2 97 | - labels: 98 | version: v3 99 | name: v3 100 | --- 101 | apiVersion: networking.istio.io/v1alpha3 102 | kind: VirtualService 103 | metadata: 104 | name: recommendation 105 | spec: 106 | hosts: 107 | - recommendation 108 | http: 109 | - route: 110 | - destination: 111 | host: recommendation 112 | subset: v1 113 | weight: 34 114 | - destination: 115 | host: recommendation 116 | subset: v2 117 | weight: 33 118 | - destination: 119 | host: recommendation 120 | subset: v3 121 | weight: 33 122 | fault: 123 | abort: 124 | percentage: 125 | value: 50 126 | httpStatus: 503 127 | --- 128 | ---- 129 | 130 | Note that this file creates 2 resources. First, a DestinationRule 131 | `recommendation` with subsets for v1, v2, and v3 (which may have already 132 | existed). Second, a VirtualService `recommendation` that splits traffic 133 | equally between v1, v2, and v3 but also introduces a 503 fault for 50% of the 134 | requests. 135 | 136 | Execute the following a few times: 137 | 138 | [source,bash,role="execute-1"] 139 | ---- 140 | export INGRESS_GATEWAY=$(oc get route -n %username%-smcp istio-ingressgateway -o 'jsonpath={.spec.host}') 141 | for x in $(seq 1 10); do curl http://${INGRESS_GATEWAY}; done 142 | ---- 143 | 144 | Because Istio has built-in default retries, you are almost guaranteed to not 145 | see the 503 failures at all. When the preference -> recommendation call fails 146 | it will be retried by Istio three times. And, if other calls fail, they, too, 147 | are retried. Thus there is a high probability that the client will not see a 148 | failure but you will see that the failure rate is accurately reflected in the 149 | Kiali console. 150 | 151 | === Kiali's Graph 152 | 153 | Within the Kiali UI select the *Graph* option from the left hand navigation 154 | and then choose: 155 | 156 | * Namespace: %username%-tutorial 157 | * Versioned app graph 158 | * Requests percentage 159 | * Last 1m 160 | * Every 10s 161 | 162 | [#img-503] 163 | .Kiali Graph Showing 503 Failures 164 | image::images/503.png[] 165 | 166 | Note the 50% failure rate from preference to recommendation. It will be 167 | clearer if you click on the line between the two services. 168 | 169 | === Clean up 170 | 171 | [source,bash,role="execute-1"] 172 | ---- 173 | oc delete -f /opt/app-root/workshop/content/src/istiofiles/virtual-service-recommendation-503.yml -n %username%-tutorial 174 | ---- 175 | 176 | You will see something like: 177 | 178 | ---- 179 | destinationrule.networking.istio.io "recommendation" deleted 180 | virtualservice.networking.istio.io "recommendation" deleted 181 | ---- 182 | 183 | [#delay] 184 | == Delay 185 | 186 | The most insidious of possible distributed computing faults is not a "down" 187 | service but a service that is responding slowly, potentially causing a 188 | cascading failure in your network of services. To see how to inject such a 189 | delay, take a look at the following YAML: 190 | 191 | [source,yaml,subs="+macros,+attributes"] 192 | ---- 193 | apiVersion: networking.istio.io/v1alpha3 194 | kind: DestinationRule 195 | metadata: 196 | name: recommendation 197 | spec: 198 | host: recommendation 199 | subsets: 200 | - labels: 201 | version: v1 202 | name: v1 203 | - labels: 204 | version: v2 205 | name: v2 206 | - labels: 207 | version: v3 208 | name: v3 209 | --- 210 | apiVersion: networking.istio.io/v1alpha3 211 | kind: VirtualService 212 | metadata: 213 | name: recommendation 214 | spec: 215 | hosts: 216 | - recommendation 217 | http: 218 | - route: 219 | - destination: 220 | host: recommendation 221 | subset: v1 222 | weight: 34 223 | - destination: 224 | host: recommendation 225 | subset: v2 226 | weight: 33 227 | - destination: 228 | host: recommendation 229 | subset: v3 230 | weight: 33 231 | fault: 232 | delay: 233 | fixedDelay: 7.000s 234 | percent: 50 235 | --- 236 | ---- 237 | 238 | Note that this file creates 2 resources. First, a DestinationRule 239 | `recommendation` with subsets for v1, v2, and v3. Second, a VirtualService 240 | `recommendation` that splits traffic equally between v1, v2, and v3 but also 241 | introduces a 7s delay for 50% of the requests. 242 | 243 | [source,bash,role="execute-1"] 244 | ---- 245 | oc apply -f /opt/app-root/workshop/content/src/istiofiles/virtual-service-recommendation-delay.yml -n %username%-tutorial 246 | ---- 247 | 248 | You will see something like: 249 | 250 | ---- 251 | destinationrule.networking.istio.io/recommendation created 252 | virtualservice.networking.istio.io/recommendation created 253 | ---- 254 | 255 | You will notice that 50% of requests to the customer endpoint now have a 256 | delay: 257 | 258 | [source,bash,role="execute-1"] 259 | ---- 260 | export INGRESS_GATEWAY=$(oc get route -n %username%-smcp istio-ingressgateway -o 'jsonpath={.spec.host}') 261 | for x in $(seq 1 10); do curl http://${INGRESS_GATEWAY}; done 262 | ---- 263 | 264 | === Kiali's Distributed Tracing 265 | 266 | Within the Kiali UI select the *Distributed Tracing* option from the left 267 | hand navigation and then choose: 268 | 269 | * Namespace: %username%-tutorial 270 | * Service: recommendation 271 | * Lookback: Last 1m 272 | 273 | and finally press the *Search* button. 274 | 275 | [#img-503] 276 | .Kiali Graph Showing Delays 277 | image::images/delay.png[] 278 | 279 | Note that 50% of the traces are slightly over the artificial 7s delay while 280 | the other 50% are in the low ms range. 281 | 282 | === Clean up 283 | 284 | [source,bash,role="execute-1"] 285 | ---- 286 | oc delete -f /opt/app-root/workshop/content/src/istiofiles/virtual-service-recommendation-delay.yml -n %username%-tutorial 287 | ---- 288 | 289 | You will see something like: 290 | 291 | ---- 292 | destinationrule.networking.istio.io "recommendation" deleted 293 | virtualservice.networking.istio.io "recommendation" deleted 294 | ---- 295 | 296 | == What we learned in this module 297 | Istio provides a simple mechanism to simulate service and network 298 | failures and delays to improve microservice testing and resiliency. Kiali 299 | provides a rich console to visualize the service failure rates and service 300 | delays. 301 | -------------------------------------------------------------------------------- /workshop/content/images/503.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/thoraxe/lab-ossm/0f633a3cb887cd12464e65dfa0d9c4459fb00573/workshop/content/images/503.png -------------------------------------------------------------------------------- /workshop/content/images/auth-fail.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/thoraxe/lab-ossm/0f633a3cb887cd12464e65dfa0d9c4459fb00573/workshop/content/images/auth-fail.png -------------------------------------------------------------------------------- /workshop/content/images/auth-success.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/thoraxe/lab-ossm/0f633a3cb887cd12464e65dfa0d9c4459fb00573/workshop/content/images/auth-success.png -------------------------------------------------------------------------------- /workshop/content/images/blacklist_v3_blocked.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/thoraxe/lab-ossm/0f633a3cb887cd12464e65dfa0d9c4459fb00573/workshop/content/images/blacklist_v3_blocked.png -------------------------------------------------------------------------------- /workshop/content/images/circuit-breaking-graph-slowv2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/thoraxe/lab-ossm/0f633a3cb887cd12464e65dfa0d9c4459fb00573/workshop/content/images/circuit-breaking-graph-slowv2.png -------------------------------------------------------------------------------- /workshop/content/images/circuit-graph.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/thoraxe/lab-ossm/0f633a3cb887cd12464e65dfa0d9c4459fb00573/workshop/content/images/circuit-graph.png -------------------------------------------------------------------------------- /workshop/content/images/delay.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/thoraxe/lab-ossm/0f633a3cb887cd12464e65dfa0d9c4459fb00573/workshop/content/images/delay.png -------------------------------------------------------------------------------- /workshop/content/images/failfast.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/thoraxe/lab-ossm/0f633a3cb887cd12464e65dfa0d9c4459fb00573/workshop/content/images/failfast.png -------------------------------------------------------------------------------- /workshop/content/images/kiali-graph-1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/thoraxe/lab-ossm/0f633a3cb887cd12464e65dfa0d9c4459fb00573/workshop/content/images/kiali-graph-1.png -------------------------------------------------------------------------------- /workshop/content/images/kiali-graph-2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/thoraxe/lab-ossm/0f633a3cb887cd12464e65dfa0d9c4459fb00573/workshop/content/images/kiali-graph-2.png -------------------------------------------------------------------------------- /workshop/content/images/kiali-tracing-1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/thoraxe/lab-ossm/0f633a3cb887cd12464e65dfa0d9c4459fb00573/workshop/content/images/kiali-tracing-1.png -------------------------------------------------------------------------------- /workshop/content/images/kiali-tracing-2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/thoraxe/lab-ossm/0f633a3cb887cd12464e65dfa0d9c4459fb00573/workshop/content/images/kiali-tracing-2.png -------------------------------------------------------------------------------- /workshop/content/images/kiali-tracing-3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/thoraxe/lab-ossm/0f633a3cb887cd12464e65dfa0d9c4459fb00573/workshop/content/images/kiali-tracing-3.png -------------------------------------------------------------------------------- /workshop/content/images/kiali-tracing-4.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/thoraxe/lab-ossm/0f633a3cb887cd12464e65dfa0d9c4459fb00573/workshop/content/images/kiali-tracing-4.png -------------------------------------------------------------------------------- /workshop/content/images/mtls_initial.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/thoraxe/lab-ossm/0f633a3cb887cd12464e65dfa0d9c4459fb00573/workshop/content/images/mtls_initial.png -------------------------------------------------------------------------------- /workshop/content/images/mtls_no_destination_rule.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/thoraxe/lab-ossm/0f633a3cb887cd12464e65dfa0d9c4459fb00573/workshop/content/images/mtls_no_destination_rule.png -------------------------------------------------------------------------------- /workshop/content/images/mtls_permissive.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/thoraxe/lab-ossm/0f633a3cb887cd12464e65dfa0d9c4459fb00573/workshop/content/images/mtls_permissive.png -------------------------------------------------------------------------------- /workshop/content/images/mtls_policy_and_rule.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/thoraxe/lab-ossm/0f633a3cb887cd12464e65dfa0d9c4459fb00573/workshop/content/images/mtls_policy_and_rule.png -------------------------------------------------------------------------------- /workshop/content/images/nocircuit-graph.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/thoraxe/lab-ossm/0f633a3cb887cd12464e65dfa0d9c4459fb00573/workshop/content/images/nocircuit-graph.png -------------------------------------------------------------------------------- /workshop/content/images/nocircuit.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/thoraxe/lab-ossm/0f633a3cb887cd12464e65dfa0d9c4459fb00573/workshop/content/images/nocircuit.png -------------------------------------------------------------------------------- /workshop/content/images/rate.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/thoraxe/lab-ossm/0f633a3cb887cd12464e65dfa0d9c4459fb00573/workshop/content/images/rate.png -------------------------------------------------------------------------------- /workshop/content/images/rbac-fail.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/thoraxe/lab-ossm/0f633a3cb887cd12464e65dfa0d9c4459fb00573/workshop/content/images/rbac-fail.png -------------------------------------------------------------------------------- /workshop/content/images/rbac-success.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/thoraxe/lab-ossm/0f633a3cb887cd12464e65dfa0d9c4459fb00573/workshop/content/images/rbac-success.png -------------------------------------------------------------------------------- /workshop/content/images/retry.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/thoraxe/lab-ossm/0f633a3cb887cd12464e65dfa0d9c4459fb00573/workshop/content/images/retry.png -------------------------------------------------------------------------------- /workshop/content/images/routing-graph-1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/thoraxe/lab-ossm/0f633a3cb887cd12464e65dfa0d9c4459fb00573/workshop/content/images/routing-graph-1.png -------------------------------------------------------------------------------- /workshop/content/images/routing-graph-2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/thoraxe/lab-ossm/0f633a3cb887cd12464e65dfa0d9c4459fb00573/workshop/content/images/routing-graph-2.png -------------------------------------------------------------------------------- /workshop/content/images/routing-graph-3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/thoraxe/lab-ossm/0f633a3cb887cd12464e65dfa0d9c4459fb00573/workshop/content/images/routing-graph-3.png -------------------------------------------------------------------------------- /workshop/content/images/timeout-v1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/thoraxe/lab-ossm/0f633a3cb887cd12464e65dfa0d9c4459fb00573/workshop/content/images/timeout-v1.png -------------------------------------------------------------------------------- /workshop/content/images/timeout-v2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/thoraxe/lab-ossm/0f633a3cb887cd12464e65dfa0d9c4459fb00573/workshop/content/images/timeout-v2.png -------------------------------------------------------------------------------- /workshop/content/images/timeout.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/thoraxe/lab-ossm/0f633a3cb887cd12464e65dfa0d9c4459fb00573/workshop/content/images/timeout.png -------------------------------------------------------------------------------- /workshop/content/images/whitelist_v2_fail.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/thoraxe/lab-ossm/0f633a3cb887cd12464e65dfa0d9c4459fb00573/workshop/content/images/whitelist_v2_fail.png -------------------------------------------------------------------------------- /workshop/content/intro.adoc: -------------------------------------------------------------------------------- 1 | ## Purpose 2 | 3 | As microservices-based applications become more prevalent, both the number of 4 | and complexity of their interactions increases. Up until now much of the 5 | burden of managing these complex microservices interactions has been placed 6 | on the application developer, with different or non-existent support for 7 | microservice concepts depending on language and framework. 8 | 9 | The service mesh concept pushes this responsibility to the infrastructure, 10 | with features for traffic management, distributed tracing and observability, 11 | policy enforcement, and service/identity security, freeing the developer to 12 | focus on business value. In this hands-on session you will learn how to apply 13 | some of these features to a simple polyglot microservices application running 14 | on top of OpenShift Container Platform using OpenShift Service Mesh, an open 15 | platform to connect, manage, and secure microservices. 16 | 17 | ## Background 18 | 19 | Red Hat OpenShift Service Mesh provides an easy way to create a network of 20 | deployed services with load balancing, service-to-service authentication, 21 | monitoring, and more, without requiring any changes in application code. 22 | OpenShift can automatically inject a special sidecar proxy throughout your 23 | environment to enable Service Mesh management for your application. This 24 | proxy intercepts all network communication between your microservices, and is 25 | configured and managed using Service Mesh’s control plane functionality -- 26 | not your application code! 27 | 28 | Kiali is an observability console designed to provide operational insight 29 | into the behavior and performance of the service mesh as a whole. 30 | 31 | Jaeger is a utility for capturing distributed tracing information of requests 32 | as they travel throughout the mesh. 33 | 34 | Prometheus and Grafana are used to capture metrics about the performance and 35 | behavior of the mesh. 36 | 37 | These components combined together make up the Red Hat OpenShift Service Mesh. 38 | 39 | ## Conventions 40 | You will see various code and command blocks throughout these exercises. Some of 41 | the command blocks can be executed directly while others will require modification, 42 | of the command before execution. If you see a command block with a red border 43 | (see below), the command will require slight modification. 44 | 45 | [source,none,role="copypaste copypaste-warning"] 46 | ---- 47 | some command to modify 48 | ---- 49 | 50 | If you hover over the command block above and left-click, it should 51 | automatically highlight all the text to make for easier copying. 52 | 53 | Most command blocks support executing with a click. Which terminal the 54 | command block executes in is pre-determined and will be indicated to you. For 55 | example, command blocks with looping scripts will be executed in the lower 56 | terminal like below: 57 | 58 | [source,bash,role="execute-2"] 59 | ---- 60 | for i in 1 2 3; do echo "hello $i"; done 61 | ---- 62 | 63 | For non-terminating loops, you will have to click the lower terminal and then 64 | press Ctrl+C to terminate the program. For example, try executing the next 65 | code block: 66 | 67 | [source,bash,role="execute-2"] 68 | ---- 69 | echo "A load generating script is running in the next step. Ctrl+C to stop" 70 | while :;do echo "this won't print" &> /dev/null ; done 71 | ---- 72 | 73 | For ordinary commands, we will be using the upper terminal: 74 | 75 | [source,bash,role="execute-1"] 76 | ---- 77 | echo "Hello World" 78 | ---- 79 | 80 | ## Getting Started 81 | The terminals are running in a container inside the same OpenShift cluster 82 | where you will be using the service mesh. That container has many utilities 83 | pre-installed, including the `oc` CLI. The Service Mesh control plane is 84 | already deployed with several application components in an OpenShift 85 | Container Platform 4 environment. 86 | 87 | [source,bash,role="execute-1"] 88 | ---- 89 | oc version 90 | ---- 91 | 92 | When you logged in earlier with your credentials, this lab guide and its 93 | terminals became logged in as you, too: 94 | 95 | [source,bash,role="execute-1"] 96 | ---- 97 | oc whoami 98 | ---- 99 | 100 | You can access the OpenShift web console by clicking on word *Console* on the 101 | toolbar above your terminal. To come back to the terminals, just click 102 | *Terminal*. 103 | 104 | ## Before Continuing 105 | While all of the exercises can stand alone and you can do them in any order, 106 | most of them involve looking at the Kiali observability interface and/or 107 | Jaeger's tracing interface. As such, please make sure to complete the 108 | Observability with Kiali module first before continuing to any others. We do 109 | not describe accessing Kiali or Jaeger anywhere else. 110 | -------------------------------------------------------------------------------- /workshop/content/kiali.adoc: -------------------------------------------------------------------------------- 1 | = Observing the Service Mesh using Kiali 2 | 3 | Applications deployed within the Service Mesh are able to take advantage of 4 | the built-in tracing and monitoring features present in the control plane. 5 | These features provide an invaluable insight into the organisation and 6 | operation of the application as well as the application's performance. 7 | 8 | == What we will learn in this module 9 | 10 | This module will provide a brief introduction to the Kiali observability 11 | component within the Service Mesh control plane. 12 | 13 | == Kiali 14 | 15 | link:http://kiali.io[Kiali] is a component which visualizes an application's 16 | topology, health, metrics and tracing information to provide a deeper 17 | understanding of the application's behaviour and performance. Within a single 18 | view it is easy to determine which services are communicating with each 19 | other, where the traffic is flowing, information about the current health of 20 | the service and other useful metrics. 21 | 22 | Kiali also integrates with Jaeger to provide access to distributed tracing 23 | information, allowing you to track an individual request as it flows through 24 | the system and identify potential issues. 25 | 26 | === How does Kiali work? 27 | 28 | Every pod deployed as part of the Service Mesh contains a proxy container 29 | responsible for intercepting the application traffic coming into and going 30 | out of the pod. The Service Mesh proxy is automatically created on deployment 31 | of the application and automatically captures information as part of each 32 | service invocation. Kiali uses this information to provide a visualization of 33 | the Service Mesh behaviour. The information includes metrics as well as 34 | distributed tracing information. 35 | 36 | === Opening the Kiali console 37 | 38 | To open the Kiali console get the link from the following command. Open it in 39 | a new tab. Make sure to use HTTPS to access. 40 | 41 | [source,bash,role="execute-1"] 42 | ---- 43 | echo https://$(oc get route -n %username%-smcp kiali -o 'jsonpath={.spec.host}') 44 | ---- 45 | 46 | When prompted to login use the same username and password you used to access OpenShift. 47 | 48 | === Generating load 49 | 50 | Before we take a look at the Kiali console lets first generate some load on 51 | the Service Mesh. Let's execute the following in terminal 2 (the lower terminal) 52 | 53 | [source,bash,role="execute-2"] 54 | ---- 55 | bash /opt/app-root/workshop/content/scripts/curl_customer_quiet.sh 56 | ---- 57 | 58 | === Kiali's Graph 59 | 60 | Within the Kiali UI select the _Graph_ option from the left hand navigation 61 | and then choose 62 | 63 | * Namespace: %username%-tutorial 64 | * Versioned app graph 65 | * Requests percentage 66 | * Last 1m 67 | * Every 10s 68 | 69 | image:images/kiali-graph-1.png[Kiali Graph showing configuration] 70 | 71 | You should now see the application graph showing traffic being created by our 72 | script generating load. The traffic will flow from the Service Mesh ingress 73 | gateway to the customer service, preference service and finally the 74 | recommendation service. The request percentage will show _100%_ on each edge 75 | except within the recommendation service where the traffic should be split 76 | evenly across all three versions. 77 | 78 | image:images/kiali-graph-2.png[Kiali Graph showing application traffic] 79 | 80 | === Kiali's View of Jaeger Distributed Tracing 81 | 82 | Jaeger is an end-to-end distributed tracing tool, providing insight into 83 | transactions, performance and latency, root cause analysis, and more. 84 | 85 | Kiali has the capability to expose a view of Jager Distributed Tracing. This 86 | involves the use of an `iframe`. As you saw when you logged into Kiali, you 87 | had to accept self-signed certificates and allow Kiali to use your user 88 | (`%username%`) permissions to talk to OpenShift. Becuase Kiali is exposing 89 | Jaeger via an `iframe`, you first need to visit Jaeger to accept the 90 | self-signed certificates and grant it permissions. 91 | 92 | [source,bash,role="execute-1"] 93 | ---- 94 | echo https://$(oc get route -n %username%-smcp jaeger -o 'jsonpath={.spec.host}') 95 | ---- 96 | 97 | Open this URL in another browser tab, accept all certificates and, when 98 | prompted, login with the same user and password that you used to login to 99 | OpenShift. Once you are done, you can return to the browser tab where you 100 | have Kiali open. 101 | 102 | Within the Kiali UI select the _Distributed Tracing_ option from the left 103 | hand navigation and then choose 104 | 105 | * Namespace: %username%-tutorial 106 | * Service: %username%-tutorial -> customer 107 | 108 | and finally press the _Search_ button. 109 | 110 | image:images/kiali-tracing-1.png[Kiali Tracing showing configuration] 111 | 112 | You should now see a list of the 20 most recent invocations of the customer service. 113 | 114 | image:images/kiali-tracing-2.png[Kiali Tracing showing invocations] 115 | 116 | Select one of the invocations to see more information. The graph should now 117 | show a hierarchy of the invocations with those spans highlighted in _red_ 118 | automatically captured by the Service Mesh proxy, both client and server 119 | side, and the remainder provided by the application. 120 | 121 | image:images/kiali-tracing-3.png[Kiali Tracing showing detailed invocation] 122 | 123 | Each span can be expanded to show more contextual information captured as part of the tracing. 124 | 125 | === Cleaning up 126 | 127 | Click into the lower terminal that is still silently executing the script we 128 | used to generate load. Press the ctrl+c keys to terminate the script. 129 | 130 | == What we learned in this module 131 | 132 | Kiali is a very useful tool for visualizing the behaviour of your Service 133 | Mesh. Through one UI you not only have a consistent view of your service 134 | interactions but you can see more detailed information about those 135 | invocations helping you to understand and identify potential issues which may 136 | arise. 137 | 138 | With an understanding of Kiali you should now be able to make use of its 139 | capabilities within the following modules to visualize the changes in 140 | behaviour we will make through the resources within the Service Mesh 141 | framework. Please keep your Kiali UI open as you work through the following 142 | modules. 143 | 144 | Kiali provides additional capabilities beyond what we have explored in this 145 | module, for more information refer to the link:http://kiali.io[Kiali 146 | website]. 147 | -------------------------------------------------------------------------------- /workshop/content/mtls.adoc: -------------------------------------------------------------------------------- 1 | = Mutual TLS 2 | 3 | Mutual authentication or two-way authentication refers to two parties 4 | authenticating each other at the same time, being a default mode of 5 | authentication in some protocols (IKE, SSH) and optional in others (TLS). 6 | 7 | With Red Hat OpenShift Service Mesh, Mutual TLS can be used without either 8 | application/service knowing that it is happening. The TLS is handled entirely 9 | by the service mesh infrastructure and between the two sidecar proxies. 10 | 11 | == What we will learn in this module 12 | This module will provide instruction on how to enable Mutual TLS to secure 13 | communication between services in the mesh. 14 | 15 | Before continuing, make sure you only have the `customer` _Virtual Service_ 16 | and the `recommendation` _Destination Rule_: 17 | 18 | [source,bash,role="execute-1"] 19 | ---- 20 | oc get virtualservice,destinationrule -n %username%-tutorial 21 | ---- 22 | 23 | You will see something like: 24 | 25 | [source,bash] 26 | ---- 27 | NAME GATEWAYS HOSTS AGE 28 | virtualservice.networking.istio.io/customer [customer-gateway] [*] 60s 29 | 30 | NAME HOST AGE 31 | destinationrule.networking.istio.io/recommendation recommendation 38m 32 | ---- 33 | 34 | If you had accidentally deleted the `DestinationRule`, you can recreate it with the following command: 35 | 36 | [source,bash,role="execute-1"] 37 | ---- 38 | oc apply -f /opt/app-root/workshop/content/src/istiofiles/destination-rule.yml -n %username%-tutorial 39 | ---- 40 | 41 | [#enablemtls] 42 | == Enabling Mutual TLS 43 | For this example, we will take advantage of a pod that is not under the 44 | control of the service mesh. 45 | 46 | Start by generating load in the background: 47 | 48 | [source,bash,role="execute-2"] 49 | ---- 50 | bash /opt/app-root/workshop/content/scripts/curl_customer_preference_quiet.sh 51 | ---- 52 | 53 | Examine the traffic between the "external" pod and the customer services and 54 | the preference service by running the load once in the upper terminal: 55 | 56 | [source,bash,role="execute-1"] 57 | ---- 58 | bash /opt/app-root/workshop/content/scripts/curl_customer_preference.sh 59 | ---- 60 | 61 | _Kiali’s Graph_ 62 | 63 | Within the Kiali UI select the Graph option from the left hand navigation and 64 | then choose: 65 | 66 | * Namespace: %username%-tutorial 67 | * Versioned app graph 68 | * Requests percentage 69 | * Last 1m 70 | * Every 10s 71 | * Security checked under display 72 | 73 | You will notice that there are requests originating from "unknown". That is 74 | the pod inside which the `curl` command was executed by the script. The 75 | script also executes a `curl` from within the `customer` pod. 76 | 77 | image::images/mtls_initial.png[] 78 | 79 | Now configure preference to use mutual TLS policy. 80 | 81 | [source,bash,role="execute-1"] 82 | ---- 83 | oc apply -f /opt/app-root/workshop/content/src/istiofiles/authentication-enable-tls.yml -n %username%-tutorial 84 | ---- 85 | 86 | The Policy just created can be seen below: 87 | 88 | [source, yaml] 89 | ---- 90 | apiVersion: "authentication.istio.io/v1alpha1" 91 | kind: "Policy" 92 | metadata: 93 | name: "preference-mutualtls" 94 | spec: 95 | targets: 96 | - name: preference 97 | peers: 98 | - mtls: 99 | mode: STRICT 100 | ---- 101 | 102 | *Note:* It may take some time before the mesh begins to enforce the policy. If 103 | you do not see the expected behaviors, wait a few more moments and then try 104 | again. Patience is a virtue. 105 | 106 | Try running the load once in the upper terminal: 107 | 108 | [source,bash,role="execute-1"] 109 | ---- 110 | bash /opt/app-root/workshop/content/scripts/curl_customer_preference.sh 111 | ---- 112 | 113 | This time, we can see that the curl from the curl pod failed with exit code 114 | 56. This is because preference is now requiring encrypted communication over 115 | mutual TLS (`STRICT`) via a _policy enforcement_, but the curl pod (which is 116 | outside the mesh) is not attempting to use mututal TLS. While the customer 117 | pod is inside the mesh, there are no rules that tell the customer pod's proxy 118 | to attempt to use mutual TLS. Thus, the proxy simply blocks the connection 119 | attempt completely (503 Service Unavailable) because the _policy_ says we 120 | should be using mutual TLS (and we're not). 121 | 122 | Now, create a destination rule to make communication to customer use mutual 123 | TLS and run the curl again: 124 | 125 | [source,bash,role="execute-1"] 126 | ---- 127 | oc apply -f /opt/app-root/workshop/content/src/istiofiles/destination-rule-tls.yml -n %username%-tutorial 128 | ---- 129 | 130 | The yaml just created can be seen below: 131 | 132 | [source, yaml] 133 | ---- 134 | apiVersion: "networking.istio.io/v1alpha3" 135 | kind: "DestinationRule" 136 | metadata: 137 | name: "preference-destination-rule" 138 | spec: 139 | host: "preference" 140 | trafficPolicy: 141 | tls: 142 | mode: ISTIO_MUTUAL 143 | ---- 144 | 145 | And now try running the load again in the upper terminal: 146 | 147 | [source,bash,role="execute-1"] 148 | ---- 149 | bash /opt/app-root/workshop/content/scripts/curl_customer_preference.sh 150 | ---- 151 | 152 | The curl pod will again show exit code 56 because it is still being prevented 153 | from communicating with the mesh. 154 | 155 | Because the `DestinationRule` for the `preference` host now specifies that 156 | the traffic policy should be `ISTIO_MUTUAL` (TLS), the traffic exiting the 157 | customer pod (with a destination of `preference`) gets mutual TLS enabled. 158 | Since the preference target has the `STRICT` enforcement of mutual TLS, and 159 | we are using mutual TLS, the traffic is allowed. 160 | 161 | Looking at the Kiali graph, a lock is now present on the graph of 162 | communication between customer and preference, indicating that this 163 | communication is secured via mTLS. 164 | 165 | image::images/mtls_policy_and_rule.png[] 166 | 167 | Lastly, note that the `curl` command that was executed was showing headers 168 | indicating `HTTP`, but we also know the communication was enforced with 169 | mutual TLS. What happened? Envoy (the sidecar proxy) is intercepting any 170 | communication leaving the pod, and Envoy is enforcing the TLS connection 171 | between the source and the destination. In this way the services don't even 172 | have to be TLS aware themselves! The mesh can "give" us TLS even if our 173 | services can't. 174 | 175 | Lastly, you'll notice that the Kiali graph no longer shows the traffic coming 176 | from "unknown". This is because the traffic is dropped before leaving the 177 | pod, so it never gets counted by the metrics. 178 | 179 | [#mtlsmigration] 180 | == mTLS migration 181 | 182 | Mutual TLS in OpenShift Service Mesh provides the ability to migrate to mTLS 183 | gradually rather than forcing all services to migrate to mTLS at once. Lets 184 | try that now. 185 | 186 | First, delete the policy we created above. 187 | 188 | [source,bash,role="execute-1"] 189 | ---- 190 | oc delete policy -n %username%-tutorial preference-mutualtls 191 | ---- 192 | 193 | Now create a policy using permissive mode. 194 | 195 | [source,bash,role="execute-1"] 196 | ---- 197 | oc apply -f /opt/app-root/workshop/content/src/istiofiles/policy-permissive-tls.yml -n %username%-tutorial 198 | ---- 199 | 200 | The contents of the file are displayed below: 201 | 202 | [source,yaml] 203 | ---- 204 | apiVersion: "authentication.istio.io/v1alpha1" 205 | kind: "Policy" 206 | metadata: 207 | name: "preference-mutualtls" 208 | spec: 209 | targets: 210 | - name: preference 211 | peers: 212 | - mtls: 213 | mode: PERMISSIVE 214 | ---- 215 | 216 | If we try our curl commands again, we notice that this time they both pass: 217 | 218 | [source,bash,role="execute-1"] 219 | ---- 220 | bash /opt/app-root/workshop/content/scripts/curl_customer_preference.sh 221 | ---- 222 | 223 | In Kiali, we can see that the lock is still shown, indicating the presence of 224 | mTLS. We see the curl pod labeled as unknown since it's not part of the mesh, 225 | and we can see that both customer and curl are succesful. In this way, mutual 226 | TLS is used when rules suggest it should be used, but, if no matching rule 227 | exists, non-mTLS is _permitted_. 228 | 229 | image::images/mtls_permissive.png[] 230 | 231 | *NOTE*: You may see errors in the communication between the `customer` and 232 | `preference` services. This is due to a known issue being tracked here: 233 | https://issues.jboss.org/browse/MAISTRA-1000 234 | 235 | [#cleanup] 236 | == Cleanup 237 | 238 | To cleanup, delete both the policy and destination rule that we created. 239 | 240 | [source,bash,role="execute-1"] 241 | ---- 242 | oc delete policy -n %username%-tutorial preference-mutualtls 243 | oc delete destinationrule -n %username%-tutorial preference-destination-rule 244 | ---- 245 | 246 | You can also stop the load generating script by clicking the lower terminal 247 | and pressing control+c. -------------------------------------------------------------------------------- /workshop/content/rate-limiting.adoc: -------------------------------------------------------------------------------- 1 | = Rate-Limiting/Policy 2 | 3 | Istio provides policy features to enforce rate limits on traffic to a 4 | destination, allowing you to mitigate naughty, misbehaving or faulty 5 | application communication patterns transparently to either side. 6 | 7 | == What we will learn in this module 8 | This module will provide instruction on how to limit the amount of traffic to 9 | a particular service in terms of requests per time period. 10 | 11 | == Before Starting 12 | You only need the `customer` Virtual Service and Gateway, but if you have the 13 | `recommendation` Destination Rule from other exercises, that's OK: 14 | 15 | [source,bash,role="execute-1"] 16 | ---- 17 | oc -n %username%-tutorial get istio-io 18 | ---- 19 | 20 | And you should see something like the following: 21 | 22 | ---- 23 | NAME AGE 24 | gateway.networking.istio.io/customer-gateway 3h16m 25 | 26 | NAME GATEWAYS HOSTS AGE 27 | virtualservice.networking.istio.io/customer [customer-gateway] [*] 3h16m 28 | 29 | NAME HOST AGE 30 | destinationrule.networking.istio.io/recommendation recommendation 36m 31 | ---- 32 | 33 | If you have any scripts running in the bottom terminal, make sure to click 34 | there and then press ctrl+c to terminate them. 35 | 36 | [#ratelimiting] 37 | == Rate Limiting 38 | 39 | *NOTE*: The Rate Limiting rules take some time to be applied and 40 | reflected. Be patient here! 41 | 42 | First, make sure that you have a `DestinationRule` for the `recommendation` service: 43 | 44 | [source,bash,role="execute-1"] 45 | ---- 46 | oc apply -f /opt/app-root/workshop/content/src/istiofiles/destination-rule.yml -n %username%-tutorial 47 | ---- 48 | 49 | The following YAML will limit the number of concurrent requests hitting _v2_ of the `recommendation` service: 50 | 51 | [source,bash,role="execute-1"] 52 | ---- 53 | sed -e "s/USERNAME/$JUPYTERHUB_USER/" /opt/app-root/workshop/content/src/istiofiles/recommendation_rate_limit.yml | oc apply -n %username%-smcp -f - 54 | ---- 55 | 56 | The YAML looks like the following: 57 | 58 | [source,yaml,subs="+macros,+attributes"] 59 | ---- 60 | --- 61 | apiVersion: config.istio.io/v1alpha2 62 | kind: handler 63 | metadata: 64 | name: quotahandler 65 | namespace: %username%-smcp 66 | spec: 67 | compiledAdapter: memquota 68 | params: 69 | quotas: 70 | - name: requestcountquota.instance.%username%-smcp 71 | maxAmount: 500 72 | validDuration: 1s 73 | # The first matching override is applied. 74 | # A requestcount instance is checked against override dimensions. 75 | overrides: 76 | # The following override applies to 'recommendation' regardless 77 | # of the source. 78 | - dimensions: 79 | destination: recommendation 80 | maxAmount: 1 81 | validDuration: 5s 82 | --- 83 | apiVersion: config.istio.io/v1alpha2 84 | kind: instance 85 | metadata: 86 | name: requestcountquota 87 | namespace: %username%-smcp 88 | spec: 89 | compiledTemplate: quota 90 | params: 91 | dimensions: 92 | source: request.headers["x-forwarded-for"] | "unknown" 93 | destination: destination.labels["app"] | destination.service.name | "unknown" 94 | destinationVersion: destination.labels["version"] | "unknown" 95 | --- 96 | apiVersion: config.istio.io/v1alpha2 97 | kind: QuotaSpec 98 | metadata: 99 | name: request-count 100 | namespace: %username%-smcp 101 | spec: 102 | rules: 103 | - quotas: 104 | - charge: 1 105 | quota: requestcountquota 106 | --- 107 | apiVersion: config.istio.io/v1alpha2 108 | kind: QuotaSpecBinding 109 | metadata: 110 | name: request-count 111 | namespace: %username%-smcp 112 | spec: 113 | quotaSpecs: 114 | - name: request-count 115 | namespace: %username%-smcp 116 | services: 117 | - name: recommendation 118 | namespace: %username%-tutorial 119 | # - service: '*' # Uncomment this to bind *all* services to request-count 120 | --- 121 | apiVersion: config.istio.io/v1alpha2 122 | kind: rule 123 | metadata: 124 | name: quota 125 | namespace: %username%-smcp 126 | spec: 127 | # quota only applies if you are not logged in. 128 | # match: match(request.headers["cookie"], "user=*") == false 129 | actions: 130 | - handler: quotahandler 131 | instances: 132 | - requestcountquota 133 | ---- 134 | 135 | Note that the memquota resource `handler` defines a rate limit of 4 requests 136 | per 5 seconds for the `recommendation` destination. Next, deploy the rate 137 | limiting resources. 138 | 139 | You will see something like: 140 | 141 | ---- 142 | handler.config.istio.io/quotahandler created 143 | instance.config.istio.io/requestcountquota created 144 | quotaspec.config.istio.io/request-count created 145 | quotaspecbinding.config.istio.io/request-count created 146 | rule.config.istio.io/quota created 147 | ---- 148 | 149 | Throw some requests at customer by executing the following several times (at 150 | least 3) in succession: 151 | 152 | [source,bash,role="execute-1"] 153 | ---- 154 | export INGRESS_GATEWAY=$(oc get route -n %username%-smcp istio-ingressgateway -o 'jsonpath={.spec.host}') 155 | for x in $(seq 1 10); do curl http://${INGRESS_GATEWAY}; done 156 | ---- 157 | 158 | You should see some 429 errors indicating the quota has been exhausted. 159 | 160 | ---- 161 | customer => preference => recommendation v2 from '74f48f4cbc-j7rfm': 2944 162 | customer => preference => recommendation v3 from '588747fd55-m8mj9': 2962 163 | customer => preference => recommendation v1 from '7f8755bb79-vjwq2': 2960 164 | customer => preference => recommendation v3 from '588747fd55-m8mj9': 2963 165 | customer => preference => recommendation v1 from '7f8755bb79-vjwq2': 2961 166 | customer => preference => recommendation v2 from '74f48f4cbc-j7rfm': 2945 167 | customer => preference => recommendation v3 from '588747fd55-m8mj9': 2964 168 | customer => preference => recommendation v2 from '74f48f4cbc-j7rfm': 2946 169 | customer => preference => recommendation v3 from '588747fd55-m8mj9': 2965 170 | customer => preference => recommendation v2 from '74f48f4cbc-j7rfm': 2947 171 | customer => Error: 503 - preference => Error: 429 - RESOURCE_EXHAUSTED:Quota is exhausted for: requestcount 172 | customer => preference => recommendation v1 from '7f8755bb79-vjwq2': 2962 173 | customer => Error: 503 - preference => Error: 429 - RESOURCE_EXHAUSTED:Quota is exhausted for: requestcount 174 | customer => preference => recommendation v1 from '7f8755bb79-vjwq2': 2963 175 | customer => preference => recommendation v1 from '7f8755bb79-vjwq2': 2964 176 | customer => preference => recommendation v1 from '7f8755bb79-vjwq2': 2965 177 | customer => Error: 503 - preference => Error: 429 - RESOURCE_EXHAUSTED:Quota is exhausted for: requestcount 178 | ---- 179 | 180 | === Kiali's Graph 181 | 182 | Within the Kiali UI select the *Graph* option from the left hand navigation 183 | and then choose 184 | 185 | * Namespace: %username%-tutorial 186 | * Versioned app graph 187 | * Requests percentage 188 | * Last 1m 189 | * Every 10s 190 | 191 | [#img-503] 192 | .Kiali Graph Showing Rate Limited Failures 193 | image::images/rate.png[] 194 | 195 | Note the rate limited failure rate from preference to recommendation. 196 | 197 | === Clean up 198 | 199 | [source,bash,role="execute-1"] 200 | ---- 201 | sed -e "s/USERNAME/$JUPYTERHUB_USER/" /opt/app-root/workshop/content/src/istiofiles/recommendation_rate_limit.yml | oc delete -n %username%-smcp -f - 202 | ---- 203 | 204 | You will see something like: 205 | 206 | ---- 207 | handler.config.istio.io "quotahandler" deleted 208 | instance.config.istio.io "requestcountquota" deleted 209 | quotaspec.config.istio.io "request-count" deleted 210 | quotaspecbinding.config.istio.io "request-count" deleted 211 | rule.config.istio.io "quota" deleted 212 | ---- 213 | -------------------------------------------------------------------------------- /workshop/content/routing.adoc: -------------------------------------------------------------------------------- 1 | = Routing within the Service Mesh 2 | 3 | One of the advantages of using a Service Mesh is the ability to modify the 4 | behaviour of your application's traffic without requiring code changes. This 5 | change in behaviour is handled declaratively through the use of the Service 6 | Mesh custom resources. 7 | 8 | == What we will learn in this module 9 | 10 | This module will provide an introduction to traffic management within the 11 | Service Mesh, demonstrating some of these capabilities through the use of our 12 | existing example and the visualisation present in the Kiali Console. 13 | 14 | === Before we begin 15 | 16 | Before starting this module you should ensure you have access to the Kiali 17 | Console and have selected the Graph tab as discussed previously. This 18 | visualisation will be used to highlight the change in behaviour as we work 19 | through this module. 20 | 21 | We also need to check the current Service Mesh configuration is as expected. 22 | To check the configuration run the following command: 23 | 24 | // TODO: figure out what the new version of this is 25 | [source,bash,role="execute-1"] 26 | ---- 27 | oc get istio-io -n %username%-tutorial 28 | ---- 29 | 30 | and check for output similar to the following: 31 | 32 | ---- 33 | NAME GATEWAYS HOSTS AGE 34 | virtualservice.networking.istio.io/customer [customer-gateway] [*] 9m34s 35 | 36 | NAME AGE 37 | gateway.networking.istio.io/customer-gateway 9m35s 38 | ---- 39 | 40 | === How does traffic management work within the Service Mesh? 41 | 42 | Every pod deployed as part of the Service Mesh contains a proxy container 43 | responsible for intercepting the application traffic coming into and going 44 | out of the pod. When the application invokes a service the client side proxy 45 | will intercept the invocation and determine which server side endpoint will 46 | be invoked based on the declarative rules in force at the time of the 47 | invocation. The default behaviour is to distribute requests to each endpoint 48 | of a service using a _Round Robin_ load balancer. In our Recommendation 49 | service requests will be distributed across the v1, v2 and v3 endpoints. 50 | 51 | When configuring traffic management rules there are two resources which need 52 | to be configured, these are: 53 | 54 | * The _Virtual Service_ specifying the routing rules to apply when the host is addressed. These routing rules can include 55 | ** Routing to specific destinations 56 | ** Matching on a transport type 57 | ** Matching on specific transport attributes such as headers or path 58 | * The _Destination Rule_ specifying policies to be applied to the destination such as 59 | ** Load balancing 60 | ** Outlier detection 61 | ** Transport Encryption (TLS) 62 | ** Connection Pools 63 | 64 | We will cover some of these details as we work through this module. 65 | 66 | === Generating load 67 | 68 | Before we work through some of the routing scenarios we need to first 69 | generate load on the services. From within a terminal enter the following 70 | command 71 | 72 | [source,bash,role="execute-2"] 73 | ---- 74 | bash /opt/app-root/workshop/content/scripts/curl_customer_quiet.sh 75 | ---- 76 | 77 | We will leave this script running for the remainder of this module. Switch 78 | over to the Kiali Console's _Graph_ section (like in the previous module) and 79 | make sure you are seeing traffic split roughly _33%_ to each of the 80 | recommendation endpoints, if you do not see any change then press the 81 | _refresh_ button in the top right of the Kiali Console. 82 | 83 | image:images/kiali-graph-2.png[Kiali showing traffic distributed across three endpoints] 84 | 85 | == Weighted Routing with the Service Mesh 86 | 87 | In this example we will modify the default behaviour for distributing 88 | requests between the endpoints. Before we can do this we need to define our 89 | _Destination Rule_ and specify the endpoint subsets we would like to use. For 90 | our demonstration we will use the version labels on each endpoint and name 91 | the subsets version-v1, version-v2 and version-v3. 92 | 93 | [source,yaml] 94 | ---- 95 | apiVersion: networking.istio.io/v1alpha3 96 | kind: DestinationRule 97 | metadata: 98 | name: recommendation 99 | spec: 100 | host: recommendation 101 | subsets: 102 | - labels: 103 | version: v1 104 | name: v1 105 | - labels: 106 | version: v2 107 | name: v2 108 | - labels: 109 | version: v3 110 | name: v3 111 | ---- 112 | 113 | To instantiate this `DestinationRule` you can execute the following: 114 | 115 | [source,bash,role="execute-1"] 116 | ---- 117 | oc apply -f /opt/app-root/workshop/content/src/istiofiles/destination-rule.yml -n %username%-tutorial 118 | ---- 119 | 120 | NOTE: We will be using this DestinationRule for the remainder of the examples 121 | within this module 122 | 123 | With the subsets now defined in the `DestinationRule` we can take a look at a 124 | _Virtual Service_ and how to specify different weighting across the subsets. 125 | This feature will allow you to better control the distribution of all 126 | requests across the endpoints, for example when deploying a new version of a 127 | service where you may only wish to send a small percentage of the requests to 128 | that service to see how it reacts to live traffic. 129 | 130 | In this example we will start by specifying _80%_ of traffic to the v1 131 | endpoint and _20%_ of traffic to the v2 endpoint. 132 | 133 | [source,yaml] 134 | ---- 135 | apiVersion: networking.istio.io/v1alpha3 136 | kind: VirtualService 137 | metadata: 138 | name: recommendation 139 | spec: 140 | hosts: 141 | - recommendation 142 | http: 143 | - route: 144 | - destination: 145 | host: recommendation 146 | subset: v1 147 | weight: 80 148 | - destination: 149 | host: recommendation 150 | subset: v2 151 | weight: 20 152 | ---- 153 | 154 | === Deploying the Weighted Routing configuration 155 | 156 | To deploy this configuration into the Service Mesh, execute the following command: 157 | 158 | [source,bash,role="execute-1"] 159 | ---- 160 | oc apply -f /opt/app-root/workshop/content/src/istiofiles/virtual-service-80-20.yml -n %username%-tutorial 161 | ---- 162 | 163 | Switch over to the Kiali console to watch the traffic shifting from the 164 | original distribution to roughly _80%_ v1 and _20%_ v2. 165 | 166 | image:images/routing-graph-2.png[Kiali showing traffic distributed 80/20 across v1 and v2 endpoints] 167 | 168 | === Modifying the Weighted Routing configuration 169 | 170 | The weighting can be modified dynamically to further shift traffic. For 171 | example now we know v2 is working we have decided to shift more traffic to 172 | that service 173 | 174 | Switch to the terminal and execute the following command: 175 | 176 | [source,bash,role="execute-1"] 177 | ---- 178 | oc apply -f /opt/app-root/workshop/content/src/istiofiles/virtual-service-20-80.yml -n %username%-tutorial 179 | ---- 180 | 181 | This `VirtualService` is almost identical to the previous one, except that 182 | the target percentages are reversed (V2 gets 80%). Switch back to the Kiali 183 | console and watch the traffic shift towards to v2 service. 184 | 185 | === Cleaning up 186 | 187 | Switch to the terminal and execute the following command 188 | 189 | [source,bash,role="execute-1"] 190 | ---- 191 | oc delete virtualservice recommendation -n %username%-tutorial 192 | ---- 193 | 194 | The traffic should now return to the default distribution with roughly 33% 195 | going to each endpoint. 196 | 197 | == Canary Releases with the Service Mesh 198 | 199 | A canary release involves rolling out a small deployment of new code and then 200 | routing specific users to it in an attempt to validate that everything is 201 | working as expected. In the previous example we modified the default 202 | behaviour for distributing requests between the endpoints so we could send 203 | traffic to particular endpoints based on weighting. In this example we will 204 | modify the behaviour to be more selective, using characteristics of the 205 | individual request to determine which endpoint should receive the request. In 206 | this way we can ensure that only a small subset of specific users reach the 207 | newly deployed code - a canary release. 208 | 209 | As with the last example we need a _Destination Rule_ (already created) and a 210 | _Virtual Service_. We will use the same Destination Rule as in the previous 211 | example to define the individual subsets and will create a new _Virtual 212 | Service_ to identify those requests destined for version v2. 213 | 214 | For the purpose of this example we will assume our application includes a 215 | header identifying the location of the caller. We will use this header to 216 | send everyone from the _Boston_ office to endpoint v2 while sending the 217 | remaining requests to endpoint v1. 218 | 219 | The _Virtual Service_ for this configuration is as follows 220 | 221 | [source,yaml] 222 | ---- 223 | apiVersion: networking.istio.io/v1alpha3 224 | kind: VirtualService 225 | metadata: 226 | name: recommendation 227 | spec: 228 | hosts: 229 | - recommendation 230 | http: 231 | - match: 232 | - headers: 233 | user-location: 234 | exact: Boston 235 | route: 236 | - destination: 237 | host: recommendation 238 | subset: v2 239 | - route: 240 | - destination: 241 | host: recommendation 242 | subset: v1 243 | ---- 244 | 245 | === Deploying the Canary Release configuration 246 | 247 | To deploy this configuration into the Service Mesh switch to a terminal and 248 | execute the following command: 249 | 250 | [source,bash,role="execute-1"] 251 | ---- 252 | oc apply -f /opt/app-root/workshop/content/src/istiofiles/routing-canary.yaml -n %username%-tutorial 253 | ---- 254 | 255 | Switch back to the terminal running the load script and press ctrl+c to break out of it. Then, run a script that doesn't suppress output: 256 | 257 | [source,bash,role="execute-2"] 258 | ---- 259 | bash /opt/app-root/workshop/content/scripts/curl_customer.sh 260 | ---- 261 | 262 | You will notice the responses are only coming from the v1 endpoint and we are 263 | not seeing replies from the v2 or v3 endpoints. This is the behaviour for all 264 | requests which are not marked as coming from the Boston office as we defined 265 | in our `VirtualService`. 266 | 267 | === Verifying the Canary Release configuration 268 | 269 | To see the effect of the Canary Release routing we need to craft a request 270 | with the appropriate header indicating the request is coming from the Boston 271 | office. You can press ctrl-c in the lower terminal to stop the script before 272 | continuing. The following command will execute in the top terminal: 273 | 274 | [source,bash,role="execute-1"] 275 | ---- 276 | export INGRESS_GATEWAY=$(oc get route -n %username%-smcp istio-ingressgateway -o 'jsonpath={.spec.host}') 277 | curl -H "user-location: Boston" http://${INGRESS_GATEWAY}/ 278 | ---- 279 | 280 | Note the response from the above command is returned from the v2 endpoint. 281 | You can try different values for the header and note the responses all come 282 | from the v1 endpoint. 283 | 284 | === Cleaning up 285 | 286 | Switch to the terminal and execute the following command: 287 | 288 | [source,bash,role="execute-1"] 289 | ---- 290 | oc delete virtualservice recommendation -n %username%-tutorial 291 | ---- 292 | 293 | The traffic should now return to the default distribution with roughly 33% 294 | going to each endpoint. 295 | 296 | == Mirroring Traffic with the Service Mesh 297 | 298 | In this example we will modify the default behaviour for distributing 299 | requests between the endpoints to send all traffic to the v2 endpoint and 300 | then use the Service Mesh's routing capabilities to mirror the traffic to the 301 | v3 endpoint. 302 | 303 | Traffic mirroring is useful when you wish to test a new version of a service 304 | with live traffic while isolating the service client from the responses 305 | returned by the new endpoint. 306 | 307 | Traffic mirroring works by sending the request to the original endpoint, in 308 | our example v2, while also sending a copy of the request to another endpoint, 309 | in our example v3. The responses returned to the client will come from the 310 | original endpoint (v2) whereas responses from the mirror endpoint (v3) will 311 | be ignored. 312 | 313 | As with the last example we need to define two resources, the _Destination 314 | Rule_ and the _Virtual Service_. We will use the same Destination Rule as in 315 | the previous examples to define the individual subsets and will create a new 316 | Virtual Service to set the v2 endpoint as default and mirror traffic to the 317 | v3 endpoint. 318 | 319 | The _Virtual Service_ for this configuration is as follows: 320 | 321 | [source,yaml] 322 | ---- 323 | apiVersion: networking.istio.io/v1alpha3 324 | kind: VirtualService 325 | metadata: 326 | name: recommendation 327 | spec: 328 | hosts: 329 | - recommendation 330 | http: 331 | - route: 332 | - destination: 333 | host: recommendation 334 | subset: v2 335 | mirror: 336 | host: recommendation 337 | subset: v3 338 | ---- 339 | 340 | === Before we start 341 | 342 | Before deploying this configuration we need to restart the silent load 343 | script. Make sure that you are not running any other scripts in the lower 344 | terminal by pressing ctrl-c, then, you will execute the following: 345 | 346 | [source,bash,role="execute-2"] 347 | ---- 348 | bash /opt/app-root/workshop/content/scripts/curl_customer_long.sh 349 | ---- 350 | 351 | Now on Kiali, 352 | 353 | - Go to Workloads section. 354 | - Choose Namespace: %username%-tutorial 355 | - Select "recommendation-v3" deployment 356 | - Click on logs 357 | - Scroll to the bottom 358 | 359 | Notice the v3 endpoint is responding to a request every three seconds, this 360 | corresponds to the request from the load script seeing the v3 responses. Keep 361 | both scripts running while we walk through this example. 362 | 363 | === Deploying the Mirroring Traffic configuration 364 | 365 | To deploy this configuration into the Service Mesh switch to a terminal and 366 | execute the following command: 367 | 368 | [source,bash,role="execute-1"] 369 | ---- 370 | oc apply -f /opt/app-root/workshop/content/src/istiofiles/routing-mirroring.yaml -n %username%-tutorial 371 | ---- 372 | 373 | Look at the terminal running the load script and you will notice the 374 | responses are only coming from the v2 endpoint with no responses coming from 375 | the v1 or v3 endpoints. 376 | 377 | If you look at the Kiali logs for the `recommendation-v3` deployment you will 378 | now notice a request coming roughly every second. 379 | 380 | Finally switch to the Kiali console and notice all the traffic in the _Graph_ 381 | tab has shifted across to the v2 endpoint. Kiali shows only the normal 382 | traffic flow for the application and not the mirrored traffic. 383 | 384 | image:images/routing-graph-3.png[Kiali showing traffic to the v2 endpoint with no mirrored traffic visible] 385 | 386 | === Cleaning up 387 | 388 | Switch back to the terminal monitoring the v3 console and press the ctrl+c 389 | keys to terminate the script. 390 | 391 | Then execute the following command: 392 | 393 | [source,bash,role="execute-1"] 394 | ---- 395 | oc delete virtualservice recommendation -n %username%-tutorial 396 | ---- 397 | 398 | The traffic should now return to the default distribution with roughly 33% 399 | going to each endpoint. 400 | 401 | == What we learned in this module 402 | 403 | In this module we learned how to manage the traffic in our application 404 | through the declaration of routing rules deployed as Service Mesh 405 | _Destination Rule_ and _Virtual Service_ resources. This change in routing 406 | behaviour was managed without any modifications to the application's code and 407 | without the application being aware these changes were occurring. 408 | 409 | We learned: 410 | 411 | * how to distribute requests across a number of services using weighting 412 | * how to distribute requests based on specific characteristics of the incoming request 413 | * how to mirror traffic from one endpoint to another. 414 | 415 | The Service Mesh traffic management capabilities support the declaration of 416 | more complex routing behaviour. This module is designed to provide only a 417 | small taste of what is possible. 418 | -------------------------------------------------------------------------------- /workshop/content/scripts/build-images.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | MYDIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )" 4 | MYHOME=${MYDIR}/.. 5 | 6 | TAG_PREFIX=${TAG_PREFIX:-example} 7 | 8 | function fail() { 9 | echo $1 10 | exit 1 11 | } 12 | 13 | for project in customer preference recommendation ; do 14 | mvn -f ${MYHOME}/src/${project} clean package -DskipTests && \ 15 | docker build -t ${TAG_PREFIX}/${project}:v1 ${MYHOME}/src/${project} || \ 16 | fail "build of $project failed" 17 | done 18 | 19 | for version in v2 v3 ; do 20 | docker build -t ${TAG_PREFIX}/${project}:${version} ${MYHOME}/src/${project} || \ 21 | fail "build of $project failed" 22 | done 23 | -------------------------------------------------------------------------------- /workshop/content/scripts/curl_customer.sh: -------------------------------------------------------------------------------- 1 | export INGRESS_GATEWAY=$(oc get route istio-ingressgateway -n $JUPYTERHUB_USER-smcp -o 'jsonpath={.spec.host}') 2 | while :; do sleep 0.2; curl http://${INGRESS_GATEWAY} ; done 3 | -------------------------------------------------------------------------------- /workshop/content/scripts/curl_customer_long.sh: -------------------------------------------------------------------------------- 1 | export INGRESS_GATEWAY=$(oc get route istio-ingressgateway -n $JUPYTERHUB_USER-smcp -o 'jsonpath={.spec.host}') 2 | while :; do sleep 1; curl http://${INGRESS_GATEWAY} ; done -------------------------------------------------------------------------------- /workshop/content/scripts/curl_customer_preference.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | export CURL_POD=$(oc get pods -n $JUPYTERHUB_USER-tutorial -l app=curl | grep curl | awk '{ print $1}' ) 4 | export CUSTOMER_POD=$(oc get pods -n $JUPYTERHUB_USER-tutorial -l app=customer | grep customer | awk '{ print $1}' ) 5 | 6 | echo "Curl from outside mesh" 7 | oc exec -n $JUPYTERHUB_USER-tutorial $CURL_POD -- curl -sv http://preference:8080 8 | echo; echo; 9 | echo "Curl from inside mesh" 10 | oc exec -n $JUPYTERHUB_USER-tutorial $CUSTOMER_POD -c customer -- curl -sv http://preference:8080 11 | 12 | -------------------------------------------------------------------------------- /workshop/content/scripts/curl_customer_preference_quiet.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | export CURL_POD=$(oc get pods -n $JUPYTERHUB_USER-tutorial -l app=curl | grep curl | awk '{ print $1}' ) 4 | export CUSTOMER_POD=$(oc get pods -n $JUPYTERHUB_USER-tutorial -l app=customer | grep customer | awk '{ print $1}' ) 5 | 6 | echo "A load generating script is running in the next step. Ctrl+C to stop" 7 | 8 | while :; do 9 | 10 | # echo "Executing curl in curl pod" 11 | oc exec -n $JUPYTERHUB_USER-tutorial $CURL_POD -- curl -s http://preference:8080 > /dev/null 12 | sleep 0.5 13 | 14 | # echo "Executing curl in customer pod" 15 | oc exec -n $JUPYTERHUB_USER-tutorial $CUSTOMER_POD -c customer -- curl -s http://preference:8080 > /dev/null 16 | 17 | sleep 0.5 18 | 19 | done 20 | 21 | -------------------------------------------------------------------------------- /workshop/content/scripts/curl_customer_quiet.sh: -------------------------------------------------------------------------------- 1 | export INGRESS_GATEWAY=$(oc get route istio-ingressgateway -n $JUPYTERHUB_USER-smcp -o 'jsonpath={.spec.host}') 2 | 3 | echo "A load generating script is running in the next step. Ctrl+C to stop" 4 | 5 | while :; do sleep 0.2; curl http://${INGRESS_GATEWAY} &> /dev/null ; done 6 | -------------------------------------------------------------------------------- /workshop/content/scripts/curl_customer_token.sh: -------------------------------------------------------------------------------- 1 | export INGRESS_GATEWAY=$(oc get route -n istio-system istio-ingressgateway -o 'jsonpath={.spec.host}') 2 | TOKEN=$(curl https://raw.githubusercontent.com/istio/istio/release-1.1/security/tools/jwt/samples/demo.jwt -s) 3 | while :; do curl --header "Authorization: Bearer $TOKEN" $INGRESS_GATEWAY -s ; done 4 | -------------------------------------------------------------------------------- /workshop/content/scripts/curl_customer_token_quiet.sh: -------------------------------------------------------------------------------- 1 | export INGRESS_GATEWAY=$(oc get route -n istio-system istio-ingressgateway -o 'jsonpath={.spec.host}') 2 | TOKEN=$(curl https://raw.githubusercontent.com/istio/istio/release-1.1/security/tools/jwt/samples/demo.jwt -s) 3 | 4 | echo "A load generating script is running in the next step. Ctrl+C to stop" 5 | 6 | while :; do curl --header "Authorization: Bearer $TOKEN" $INGRESS_GATEWAY -s &> /dev/null ; done 7 | -------------------------------------------------------------------------------- /workshop/content/scripts/curl_recommendation_args.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # takes an argument like v1, v2, or v3 4 | # finds the pod IP of the recommendation version 5 | # curls the found IP with verbose output 6 | 7 | export REC_IP=`oc get pod -n ${JUPYTERHUB_USER}-tutorial -l "app=recommendation,version=v2" -o jsonpath='{.items[0].status.podIP}'` 8 | oc exec -n ${JUPYTERHUB_USER}-tutorial $(oc get pod -n ${JUPYTERHUB_USER}-tutorial -l app=curl -o name | cut -f2 -d/) -- curl -sv $REC_IP:8080 -------------------------------------------------------------------------------- /workshop/content/scripts/deploy.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | # 3 | # Deploy services to OpenShift/Istio 4 | # Assumes you are oc-login'd and istio is installed and istioctl available at $ISTIO_HOME 5 | # 6 | MYHOME="$( cd "$( dirname "${BASH_SOURCE[0]}" )/.." && pwd -P)" 7 | DEPLOYMENT_DIR="${MYHOME}/src/deployments" 8 | 9 | # name of project in which we are working 10 | PROJECT=${PROJECT:-istio-tutorial} 11 | 12 | oc new-project ${PROJECT} || exit 1 13 | oc adm policy add-scc-to-user privileged -z default -n ${PROJECT} 14 | 15 | # deploy customer 16 | oc create -n ${PROJECT} -f ${DEPLOYMENT_DIR}/customer.yaml 17 | 18 | # deploy preferences 19 | oc create -n ${PROJECT} -f ${DEPLOYMENT_DIR}/preference.yaml 20 | 21 | # deploy recommendation 22 | oc create -n ${PROJECT} -f ${DEPLOYMENT_DIR}/recommendation.yaml 23 | 24 | # deploy gateway 25 | oc create -n ${PROJECT} -f ${DEPLOYMENT_DIR}/gateway.yaml 26 | 27 | # deploy curl 28 | oc create -n ${PROJECT} -f ${DEPLOYMENT_DIR}/curl.yaml 29 | -------------------------------------------------------------------------------- /workshop/content/scripts/loadtest.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | export INGRESS_GATEWAY=$(oc get route -n istio-system istio-ingressgateway -o 'jsonpath={.spec.host}') 4 | 5 | function curl_gateway(){ 6 | j=0 7 | while [ $j -lt 50 ]; do 8 | curl http://${INGRESS_GATEWAY}/ 9 | j=$[$j+1] 10 | done 11 | } 12 | 13 | i=0 14 | while [ $i -lt 20 ]; do 15 | curl_gateway & 16 | i=$[$i+1] 17 | done 18 | 19 | -------------------------------------------------------------------------------- /workshop/content/scripts/loadtest_quiet.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | export INGRESS_GATEWAY=$(oc get route -n $JUPYTERHUB_USER-smcp istio-ingressgateway -o 'jsonpath={.spec.host}') 4 | 5 | function curl_gateway(){ 6 | j=0 7 | while [ $j -lt 50 ]; do 8 | curl http://${INGRESS_GATEWAY}/ &> /dev/null 9 | j=$[$j+1] 10 | done 11 | } 12 | 13 | i=0 14 | while [ $i -lt 20 ]; do 15 | curl_gateway & 16 | i=$[$i+1] 17 | done 18 | 19 | -------------------------------------------------------------------------------- /workshop/content/scripts/retries.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | export RECO_POD=$(oc get pod -n $JUPYTERHUB_USER-tutorial -l 'app=recommendation,version=v2' -o jsonpath='{..metadata.name}') 4 | export RECO_IP=$(oc get pod -n $JUPYTERHUB_USER-tutorial $RECO_POD -o jsonpath='{.status.podIP'}) 5 | oc exec -n $JUPYTERHUB_USER-tutorial -c recommendation $RECO_POD -- curl -s $RECO_IP:8080/$1 6 | -------------------------------------------------------------------------------- /workshop/content/scripts/setup.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | OC_VERSION="v3.9.0-alpha.3" 4 | GIT_STRING="-78ddc10" 5 | 6 | #subscription-manager register --username openshift-enterprise 7 | #subscription-manager attach --pool 8a85f98b621f8a6b01621fac0f790155 8 | #subscription-manager repos --disable="*" 9 | #subscription-manager repos \ 10 | # --enable="rhel-7-server-rpms" \ 11 | # --enable="rhel-7-server-extras-rpms" \ 12 | # --enable="rhel-7-server-ose-3.7-rpms" \ 13 | # --enable="rhel-7-fast-datapath-rpms" 14 | #yum -y install vim-enhanced docker wget git net-tools bind-utils iptables-services bridge-utils bash-completion kexec-tools sos psacct 15 | #systemctl enable docker 16 | #systemctl start docker 17 | #cd 18 | wget https://github.com/openshift/origin/releases/download/$OC_VERSION/openshift-origin-server-$OC_VERSION$GIT_STRING-linux-64bit.tar.gz 19 | tar -xzvf openshift-origin-server-$OC_VERSION$GIT_STRING-linux-64bit.tar.gz 20 | #mv openshift-origin-server-v3.9.0-alpha.3-78ddc10-linux-64bit/oc /usr/bin 21 | #restorecon -rv /usr/bin 22 | 23 | -------------------------------------------------------------------------------- /workshop/content/setup-environment.adoc: -------------------------------------------------------------------------------- 1 | Include in this page any steps which should be run by a user to check that the workshop environment is setup correctly. Exactly what you provide in this step will depend on whether the workshop is designed to be deployed in a specific way. 2 | 3 | It is a good idea in this page to provide at least one sample command to run which is marked with the `execute` annotation so anyone doing the workshop understands they can click on marked commands to run them. For example: 4 | 5 | [source,bash,role=execute] 6 | ---- 7 | date 8 | ---- 9 | 10 | Did you type the command in yourself? If you did, click on the command instead and you will find that it is executed for you. You can click on any command which has the ++++++ icon shown to the right of it, and it will be copied to the interactive terminal and run. If you would rather make a copy of the command so you can paste it to another window, hold down the shift key when you click on the command. 11 | -------------------------------------------------------------------------------- /workshop/content/src/customer/.dockerignore: -------------------------------------------------------------------------------- 1 | * 2 | !target/*-runner 3 | !target/*-runner.jar 4 | !target/lib/* -------------------------------------------------------------------------------- /workshop/content/src/customer/.gitignore: -------------------------------------------------------------------------------- 1 | target/ 2 | !.mvn/wrapper/maven-wrapper.jar 3 | 4 | ### STS ### 5 | .apt_generated 6 | .classpath 7 | .factorypath 8 | .project 9 | .settings 10 | .springBeans 11 | 12 | ### IntelliJ IDEA ### 13 | .idea 14 | *.iws 15 | *.iml 16 | *.ipr 17 | 18 | ### NetBeans ### 19 | nbproject/private/ 20 | build/ 21 | nbbuild/ 22 | dist/ 23 | nbdist/ 24 | .nb-gradle/ -------------------------------------------------------------------------------- /workshop/content/src/customer/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM fabric8/java-jboss-openjdk8-jdk:1.5.4 2 | ENV JAVA_OPTIONS=-Dquarkus.http.host=0.0.0.0 3 | ENV JAEGER_SERVICE_NAME=customer\ 4 | JAEGER_ENDPOINT=http://jaeger-collector.istio-system.svc:14268/api/traces\ 5 | JAEGER_PROPAGATION=b3\ 6 | JAEGER_SAMPLER_TYPE=const\ 7 | JAEGER_SAMPLER_PARAM=1 8 | EXPOSE 8080 8778 9779 9 | COPY target/lib/* /deployments/lib/ 10 | COPY target/*-runner.jar /deployments/app.jar 11 | ENTRYPOINT [ "/deployments/run-java.sh" ] -------------------------------------------------------------------------------- /workshop/content/src/customer/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 4.0.0 5 | com.redhat.developer.demos.customer.rest 6 | customer 7 | 1.0-SNAPSHOT 8 | 9 | 2.22.0 10 | 0.12.0 11 | UTF-8 12 | 1.8 13 | 1.8 14 | 15 | 16 | 17 | 18 | io.quarkus 19 | quarkus-bom 20 | ${quarkus.version} 21 | pom 22 | import 23 | 24 | 25 | 26 | 27 | 28 | io.quarkus 29 | quarkus-resteasy 30 | 31 | 32 | io.quarkus 33 | quarkus-junit5 34 | test 35 | 36 | 37 | io.rest-assured 38 | rest-assured 39 | test 40 | 41 | 42 | io.quarkus 43 | quarkus-smallrye-rest-client 44 | 45 | 46 | io.quarkus 47 | quarkus-smallrye-opentracing 48 | 49 | 50 | io.quarkus 51 | quarkus-smallrye-health 52 | 53 | 54 | io.quarkus 55 | quarkus-smallrye-metrics 56 | 57 | 58 | 59 | 60 | 61 | io.quarkus 62 | quarkus-maven-plugin 63 | ${quarkus.version} 64 | 65 | 66 | 67 | build 68 | 69 | 70 | 71 | 72 | 73 | maven-surefire-plugin 74 | ${surefire-plugin.version} 75 | 76 | 77 | org.jboss.logmanager.LogManager 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | native 86 | 87 | 88 | native 89 | 90 | 91 | 92 | 93 | 94 | io.quarkus 95 | quarkus-maven-plugin 96 | ${quarkus.version} 97 | 98 | 99 | 100 | native-image 101 | 102 | 103 | true 104 | 105 | 106 | 107 | 108 | 109 | maven-failsafe-plugin 110 | ${surefire-plugin.version} 111 | 112 | 113 | 114 | integration-test 115 | verify 116 | 117 | 118 | 119 | ${project.build.directory}/${project.build.finalName}-runner 120 | 121 | 122 | 123 | 124 | 125 | 126 | 127 | 128 | 129 | 130 | -------------------------------------------------------------------------------- /workshop/content/src/customer/src/main/docker/Dockerfile.jvm: -------------------------------------------------------------------------------- 1 | #### 2 | # This Dockerfile is used in order to build a container that runs the Quarkus application in JVM mode 3 | # 4 | # Before building the docker image run: 5 | # 6 | # mvn package 7 | # 8 | # Then, build the image with: 9 | # 10 | # docker build -f src/main/docker/Dockerfile.jvm -t quarkus/customer-jvm . 11 | # 12 | # Then run the container using: 13 | # 14 | # docker run -i --rm -p 8080:8080 quarkus/customer-jvm 15 | # 16 | ### 17 | FROM fabric8/java-jboss-openjdk8-jdk 18 | ENV JAVA_OPTIONS=-Dquarkus.http.host=0.0.0.0 19 | COPY target/lib/* /deployments/lib/ 20 | COPY target/*-runner.jar /deployments/app.jar 21 | ENTRYPOINT [ "/deployments/run-java.sh" ] -------------------------------------------------------------------------------- /workshop/content/src/customer/src/main/docker/Dockerfile.native: -------------------------------------------------------------------------------- 1 | #### 2 | # This Dockerfile is used in order to build a container that runs the Quarkus application in native (no JVM) mode 3 | # 4 | # Before building the docker image run: 5 | # 6 | # mvn package -Pnative -Dnative-image.docker-build=true 7 | # 8 | # Then, build the image with: 9 | # 10 | # docker build -f src/main/docker/Dockerfile.native -t quarkus/customer . 11 | # 12 | # Then run the container using: 13 | # 14 | # docker run -i --rm -p 8080:8080 quarkus/customer 15 | # 16 | ### 17 | FROM registry.fedoraproject.org/fedora-minimal 18 | WORKDIR /work/ 19 | COPY target/*-runner /work/customer 20 | RUN chmod 775 /work 21 | EXPOSE 8080 22 | CMD ["./customer", "-Xmx8m", "-Xmn8m", "-Xms8m"] 23 | # CMD ["./customer", "-Dquarkus.http.host=0.0.0.0"] 24 | -------------------------------------------------------------------------------- /workshop/content/src/customer/src/main/java/com/redhat/developer/demos/customer/rest/BaggageHeadersFactory.java: -------------------------------------------------------------------------------- 1 | package com.redhat.developer.demos.customer.rest; 2 | 3 | import org.eclipse.microprofile.rest.client.ext.ClientHeadersFactory; 4 | 5 | import javax.ws.rs.core.MultivaluedHashMap; 6 | import javax.ws.rs.core.MultivaluedMap; 7 | import io.quarkus.runtime.annotations.RegisterForReflection; 8 | 9 | @RegisterForReflection 10 | public class BaggageHeadersFactory implements ClientHeadersFactory { 11 | 12 | 13 | @Override 14 | public MultivaluedMap update(MultivaluedMap incomingHeaders, MultivaluedMap clientOutgoingHeaders) { 15 | MultivaluedHashMap headers = new MultivaluedHashMap<>(); 16 | String userAgent = incomingHeaders.getFirst("user-agent"); 17 | headers.putSingle("baggage-user-agent", userAgent); 18 | String userLocation = incomingHeaders.getFirst("user-location"); 19 | headers.putSingle("user-location", userLocation); 20 | return headers; 21 | } 22 | 23 | } 24 | -------------------------------------------------------------------------------- /workshop/content/src/customer/src/main/java/com/redhat/developer/demos/customer/rest/CustomerResource.java: -------------------------------------------------------------------------------- 1 | package com.redhat.developer.demos.customer.rest; 2 | 3 | import org.eclipse.microprofile.rest.client.inject.RestClient; 4 | import org.slf4j.Logger; 5 | import org.slf4j.LoggerFactory; 6 | 7 | import javax.inject.Inject; 8 | import javax.ws.rs.*; 9 | import javax.ws.rs.core.MediaType; 10 | import javax.ws.rs.core.Response; 11 | 12 | @Path("/") 13 | public class CustomerResource { 14 | 15 | private static final String RESPONSE_STRING_FORMAT = "customer => %s\n"; 16 | 17 | private final Logger logger = LoggerFactory.getLogger(getClass()); 18 | 19 | @Inject 20 | @RestClient 21 | PreferenceService preferenceService; 22 | 23 | @GET 24 | @Produces(MediaType.TEXT_PLAIN) 25 | public Response getCustomer() { 26 | try { 27 | String response = preferenceService.getPreference().trim(); 28 | return Response.ok(String.format(RESPONSE_STRING_FORMAT, response)).build(); 29 | } catch (WebApplicationException ex) { 30 | Response response = ex.getResponse(); 31 | logger.warn("Non HTTP 20x trying to get the response from preference service: " + response.getStatus()); 32 | return Response 33 | .status(Response.Status.SERVICE_UNAVAILABLE) 34 | .entity(String.format(RESPONSE_STRING_FORMAT, 35 | String.format("Error: %d - %s", response.getStatus(), response.readEntity(String.class))) 36 | ) 37 | .build(); 38 | } catch (ProcessingException ex) { 39 | logger.warn("Exception trying to get the response from preference service.", ex); 40 | return Response 41 | .status(Response.Status.SERVICE_UNAVAILABLE) 42 | .entity(String.format(RESPONSE_STRING_FORMAT, ex.getCause().getClass().getSimpleName() + ": " + ex.getCause().getMessage())) 43 | .build(); 44 | } 45 | } 46 | 47 | } -------------------------------------------------------------------------------- /workshop/content/src/customer/src/main/java/com/redhat/developer/demos/customer/rest/PreferenceService.java: -------------------------------------------------------------------------------- 1 | package com.redhat.developer.demos.customer.rest; 2 | 3 | import org.eclipse.microprofile.rest.client.annotation.RegisterClientHeaders; 4 | import org.eclipse.microprofile.rest.client.inject.RegisterRestClient; 5 | 6 | import javax.ws.rs.GET; 7 | import javax.ws.rs.HeaderParam; 8 | import javax.ws.rs.Path; 9 | import javax.ws.rs.Produces; 10 | import javax.ws.rs.core.Response; 11 | 12 | @RegisterClientHeaders(BaggageHeadersFactory.class) 13 | @RegisterRestClient 14 | public interface PreferenceService { 15 | 16 | @Path("/") 17 | @GET 18 | @Produces("text/plain") 19 | public String getPreference(); 20 | 21 | } 22 | -------------------------------------------------------------------------------- /workshop/content/src/customer/src/main/resources/application.properties: -------------------------------------------------------------------------------- 1 | com.redhat.developer.demos.customer.rest.PreferenceService/mp-rest/url=http://preference:8080 -------------------------------------------------------------------------------- /workshop/content/src/customer/src/test/java/com/redhat/developer/demos/customer/rest/CustomerResourceTest.java: -------------------------------------------------------------------------------- 1 | package com.redhat.developer.demos.customer.rest; 2 | 3 | import io.quarkus.test.junit.QuarkusTest; 4 | import org.junit.jupiter.api.Test; 5 | 6 | import static io.restassured.RestAssured.given; 7 | import static org.hamcrest.CoreMatchers.is; 8 | import static org.hamcrest.CoreMatchers.startsWith; 9 | 10 | @QuarkusTest 11 | public class CustomerResourceTest { 12 | 13 | @Test 14 | public void testHelloEndpoint() { 15 | given() 16 | .when().get("/") 17 | .then() 18 | .statusCode(503) 19 | .body(startsWith("customer =>")); 20 | } 21 | 22 | } -------------------------------------------------------------------------------- /workshop/content/src/customer/src/test/java/com/redhat/developer/demos/customer/rest/NativeCustomerResourceIT.java: -------------------------------------------------------------------------------- 1 | package com.redhat.developer.demos.customer.rest; 2 | 3 | import io.quarkus.test.junit.SubstrateTest; 4 | 5 | @SubstrateTest 6 | public class NativeCustomerResourceIT extends CustomerResourceTest { 7 | 8 | // Execute the same tests but in native mode. 9 | } -------------------------------------------------------------------------------- /workshop/content/src/deployments/curl.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: extensions/v1beta1 2 | kind: Deployment 3 | metadata: 4 | labels: 5 | app: curl 6 | version: v1 7 | name: curl 8 | spec: 9 | replicas: 1 10 | selector: 11 | matchLabels: 12 | app: curl 13 | version: v1 14 | template: 15 | metadata: 16 | labels: 17 | app: curl 18 | version: v1 19 | annotations: 20 | sidecar.istio.io/proxyCPU: "500m" 21 | sidecar.istio.io/proxyMemory: 400Mi 22 | spec: 23 | containers: 24 | - image: quay.io/maistra_demos/curl:latest 25 | command: ["/bin/sleep", "3650d"] 26 | imagePullPolicy: Always 27 | name: curl 28 | -------------------------------------------------------------------------------- /workshop/content/src/deployments/customer.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: extensions/v1beta1 2 | kind: Deployment 3 | metadata: 4 | labels: 5 | app: customer 6 | version: v1 7 | name: customer-v1 8 | spec: 9 | replicas: 1 10 | selector: 11 | matchLabels: 12 | app: customer 13 | version: v1 14 | template: 15 | metadata: 16 | labels: 17 | app: customer 18 | version: v1 19 | annotations: 20 | sidecar.istio.io/inject: "true" 21 | sidecar.istio.io/proxyCPU: "500m" 22 | sidecar.istio.io/proxyMemory: 400Mi 23 | spec: 24 | containers: 25 | - env: 26 | - name: JAVA_OPTIONS 27 | value: -Xms128m -Xmx256m -Djava.net.preferIPv4Stack=true -Djava.security.egd=file:///dev/./urandom 28 | image: quay.io/maistra_demos/customer:v1 29 | imagePullPolicy: Always 30 | name: customer 31 | ports: 32 | - containerPort: 8080 33 | name: http 34 | protocol: TCP 35 | --- 36 | apiVersion: v1 37 | kind: Service 38 | metadata: 39 | name: customer 40 | labels: 41 | app: customer 42 | spec: 43 | ports: 44 | - name: http 45 | port: 8080 46 | selector: 47 | app: customer 48 | -------------------------------------------------------------------------------- /workshop/content/src/deployments/gateway.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: networking.istio.io/v1alpha3 2 | kind: Gateway 3 | metadata: 4 | name: customer-gateway 5 | spec: 6 | selector: 7 | istio: ingressgateway # use istio default controller 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: customer 20 | spec: 21 | hosts: 22 | - "*" 23 | gateways: 24 | - customer-gateway 25 | http: 26 | - match: 27 | - uri: 28 | exact: / 29 | route: 30 | - destination: 31 | host: customer 32 | port: 33 | number: 8080 34 | -------------------------------------------------------------------------------- /workshop/content/src/deployments/preference.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: extensions/v1beta1 2 | kind: Deployment 3 | metadata: 4 | labels: 5 | app: preference 6 | version: v1 7 | name: preference-v1 8 | spec: 9 | replicas: 1 10 | selector: 11 | matchLabels: 12 | app: preference 13 | version: v1 14 | template: 15 | metadata: 16 | labels: 17 | app: preference 18 | version: v1 19 | annotations: 20 | sidecar.istio.io/inject: "true" 21 | sidecar.istio.io/proxyCPU: "500m" 22 | sidecar.istio.io/proxyMemory: 400Mi 23 | spec: 24 | containers: 25 | - env: 26 | - name: JAVA_OPTIONS 27 | value: -Xms128m -Xmx256m -Djava.net.preferIPv4Stack=true -Djava.security.egd=file:///dev/./urandom 28 | image: quay.io/maistra_demos/preference:v1 29 | imagePullPolicy: Always 30 | name: preference 31 | ports: 32 | - containerPort: 8080 33 | name: http 34 | protocol: TCP 35 | --- 36 | apiVersion: v1 37 | kind: Service 38 | metadata: 39 | name: preference 40 | labels: 41 | app: preference 42 | spec: 43 | ports: 44 | - name: http 45 | port: 8080 46 | selector: 47 | app: preference 48 | -------------------------------------------------------------------------------- /workshop/content/src/deployments/recommendation.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: extensions/v1beta1 2 | kind: Deployment 3 | metadata: 4 | labels: 5 | app: recommendation 6 | version: v1 7 | name: recommendation-v1 8 | spec: 9 | replicas: 1 10 | selector: 11 | matchLabels: 12 | app: recommendation 13 | version: v1 14 | template: 15 | metadata: 16 | labels: 17 | app: recommendation 18 | version: v1 19 | annotations: 20 | sidecar.istio.io/inject: "true" 21 | sidecar.istio.io/proxyCPU: "500m" 22 | sidecar.istio.io/proxyMemory: 400Mi 23 | spec: 24 | containers: 25 | - env: 26 | - name: JAVA_OPTIONS 27 | value: -Xmx256m 28 | image: quay.io/maistra_demos/recommendation:v1 29 | imagePullPolicy: Always 30 | name: recommendation 31 | ports: 32 | - containerPort: 8080 33 | name: http 34 | protocol: TCP 35 | --- 36 | apiVersion: extensions/v1beta1 37 | kind: Deployment 38 | metadata: 39 | labels: 40 | app: recommendation 41 | version: v2 42 | name: recommendation-v2 43 | spec: 44 | replicas: 1 45 | selector: 46 | matchLabels: 47 | app: recommendation 48 | version: v2 49 | template: 50 | metadata: 51 | labels: 52 | app: recommendation 53 | version: v2 54 | annotations: 55 | sidecar.istio.io/inject: "true" 56 | sidecar.istio.io/proxyCPU: "500m" 57 | sidecar.istio.io/proxyMemory: 400Mi 58 | spec: 59 | containers: 60 | - env: 61 | - name: JAVA_OPTIONS 62 | value: -Xmx256m 63 | image: quay.io/maistra_demos/recommendation:v2 64 | imagePullPolicy: Always 65 | name: recommendation 66 | ports: 67 | - containerPort: 8080 68 | name: http 69 | protocol: TCP 70 | --- 71 | apiVersion: extensions/v1beta1 72 | kind: Deployment 73 | metadata: 74 | labels: 75 | app: recommendation 76 | version: v3 77 | name: recommendation-v3 78 | spec: 79 | replicas: 1 80 | selector: 81 | matchLabels: 82 | app: recommendation 83 | version: v3 84 | template: 85 | metadata: 86 | labels: 87 | app: recommendation 88 | version: v3 89 | annotations: 90 | sidecar.istio.io/inject: "true" 91 | sidecar.istio.io/proxyCPU: "500m" 92 | sidecar.istio.io/proxyMemory: 400Mi 93 | spec: 94 | containers: 95 | - env: 96 | - name: JAVA_OPTIONS 97 | value: -Xmx256m 98 | image: quay.io/maistra_demos/recommendation:v3 99 | imagePullPolicy: Always 100 | name: recommendation 101 | ports: 102 | - containerPort: 8080 103 | name: http 104 | protocol: TCP 105 | --- 106 | apiVersion: v1 107 | kind: Service 108 | metadata: 109 | name: recommendation 110 | labels: 111 | app: recommendation 112 | spec: 113 | ports: 114 | - name: http 115 | port: 8080 116 | selector: 117 | app: recommendation 118 | -------------------------------------------------------------------------------- /workshop/content/src/istiofiles/acl-blacklist.yml: -------------------------------------------------------------------------------- 1 | apiVersion: "config.istio.io/v1alpha2" 2 | kind: handler 3 | metadata: 4 | name: denycustomerhandler 5 | spec: 6 | compiledAdapter: denier 7 | params: 8 | status: 9 | code: 7 10 | message: Not allowed 11 | --- 12 | apiVersion: "config.istio.io/v1alpha2" 13 | kind: instance 14 | metadata: 15 | name: denycustomerrequests 16 | spec: 17 | compiledTemplate: checknothing 18 | --- 19 | apiVersion: "config.istio.io/v1alpha2" 20 | kind: rule 21 | metadata: 22 | name: denycustomer 23 | spec: 24 | match: source.labels["app"]=="preference" && destination.labels["app"] == "recommendation" && destination.labels["version"] == "v3" 25 | actions: 26 | - handler: denycustomerhandler 27 | instances: [ denycustomerrequests ] 28 | 29 | -------------------------------------------------------------------------------- /workshop/content/src/istiofiles/acl-whitelist.yml: -------------------------------------------------------------------------------- 1 | apiVersion: "config.istio.io/v1alpha2" 2 | kind: handler 3 | metadata: 4 | name: preferencewhitelist 5 | spec: 6 | compiledAdapter: listchecker 7 | params: 8 | overrides: ["v1", "v3"] 9 | blacklist: false 10 | --- 11 | apiVersion: "config.istio.io/v1alpha2" 12 | kind: instance 13 | metadata: 14 | name: preferencesource 15 | spec: 16 | compiledTemplate: listentry 17 | params: 18 | value: destination.labels["version"] 19 | --- 20 | apiVersion: "config.istio.io/v1alpha2" 21 | kind: rule 22 | metadata: 23 | name: checktorecommendation 24 | spec: 25 | match: destination.labels["app"] == "recommendation" 26 | actions: 27 | - handler: preferencewhitelist 28 | instances: [ preferencesource ] 29 | -------------------------------------------------------------------------------- /workshop/content/src/istiofiles/authentication-enable-tls.yml: -------------------------------------------------------------------------------- 1 | apiVersion: "authentication.istio.io/v1alpha1" 2 | kind: "Policy" 3 | metadata: 4 | name: "preference-mutualtls" 5 | spec: 6 | targets: 7 | - name: preference 8 | peers: 9 | - mtls: 10 | mode: STRICT -------------------------------------------------------------------------------- /workshop/content/src/istiofiles/authorization-enable-rbac.yml: -------------------------------------------------------------------------------- 1 | apiVersion: "rbac.istio.io/v1alpha1" 2 | kind: RbacConfig 3 | metadata: 4 | name: default 5 | spec: 6 | mode: 'ON_WITH_INCLUSION' 7 | inclusion: 8 | namespaces: ["CHANGEIT"] 9 | -------------------------------------------------------------------------------- /workshop/content/src/istiofiles/controlplane.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: istio.openshift.com/v1alpha3 2 | kind: ControlPlane 3 | metadata: 4 | name: basic-install 5 | spec: 6 | istio: 7 | global: 8 | proxy: 9 | resources: 10 | requests: 11 | cpu: 100m 12 | memory: 128Mi 13 | limits: 14 | cpu: 500m 15 | memory: 128Mi 16 | disablePolicyChecks: false 17 | gateways: 18 | istio-egressgateway: 19 | autoscaleEnabled: false 20 | istio-ingressgateway: 21 | autoscaleEnabled: false 22 | ior_enabled: false 23 | 24 | mixer: 25 | policy: 26 | autoscaleEnabled: false 27 | 28 | telemetry: 29 | autoscaleEnabled: false 30 | resources: 31 | requests: 32 | cpu: 100m 33 | memory: 1G 34 | limits: 35 | cpu: 500m 36 | memory: 4G 37 | 38 | pilot: 39 | autoscaleEnabled: false 40 | traceSampling: 100.0 41 | 42 | kiali: 43 | enabled: true 44 | dashboard: 45 | user: admin 46 | passphrase: admin 47 | tracing: 48 | enabled: true -------------------------------------------------------------------------------- /workshop/content/src/istiofiles/destination-rule-recommendation_cb_policy_version_v2.yml: -------------------------------------------------------------------------------- 1 | apiVersion: networking.istio.io/v1alpha3 2 | kind: DestinationRule 3 | metadata: 4 | name: recommendation 5 | spec: 6 | host: recommendation 7 | subsets: 8 | - name: v1 9 | labels: 10 | version: v1 11 | - name: v2 12 | labels: 13 | version: v2 14 | trafficPolicy: 15 | connectionPool: 16 | http: 17 | http1MaxPendingRequests: 1 18 | maxRequestsPerConnection: 1 19 | tcp: 20 | maxConnections: 1 21 | outlierDetection: 22 | baseEjectionTime: 120.000s 23 | consecutiveErrors: 1 24 | interval: 1.000s 25 | maxEjectionPercent: 100 26 | - name: v3 27 | labels: 28 | version: v3 29 | -------------------------------------------------------------------------------- /workshop/content/src/istiofiles/destination-rule-tls.yml: -------------------------------------------------------------------------------- 1 | apiVersion: "networking.istio.io/v1alpha3" 2 | kind: "DestinationRule" 3 | metadata: 4 | name: "preference-destination-rule" 5 | spec: 6 | host: "preference" 7 | trafficPolicy: 8 | tls: 9 | mode: ISTIO_MUTUAL -------------------------------------------------------------------------------- /workshop/content/src/istiofiles/destination-rule.yml: -------------------------------------------------------------------------------- 1 | apiVersion: networking.istio.io/v1alpha3 2 | kind: DestinationRule 3 | metadata: 4 | name: recommendation 5 | spec: 6 | host: recommendation 7 | subsets: 8 | - labels: 9 | version: v1 10 | name: v1 11 | - labels: 12 | version: v2 13 | name: v2 14 | - labels: 15 | version: v3 16 | name: v3 17 | -------------------------------------------------------------------------------- /workshop/content/src/istiofiles/namespace-rbac-policy.yml: -------------------------------------------------------------------------------- 1 | apiVersion: "rbac.istio.io/v1alpha1" 2 | kind: ServiceRole 3 | metadata: 4 | name: service-viewer 5 | spec: 6 | rules: 7 | - services: ["*"] 8 | methods: ["GET"] 9 | constraints: 10 | - key: "destination.labels[app]" 11 | values: ["customer", "recommendation", "preference"] 12 | --- 13 | apiVersion: "rbac.istio.io/v1alpha1" 14 | kind: ServiceRoleBinding 15 | metadata: 16 | name: bind-service-viewer 17 | spec: 18 | subjects: 19 | - user: "*" 20 | roleRef: 21 | kind: ServiceRole 22 | name: "service-viewer" 23 | -------------------------------------------------------------------------------- /workshop/content/src/istiofiles/policy-jwt.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: "authentication.istio.io/v1alpha1" 2 | kind: "Policy" 3 | metadata: 4 | name: "jwt-example" 5 | spec: 6 | targets: 7 | - name: customer 8 | origins: 9 | - jwt: 10 | issuer: "testing@secure.istio.io" 11 | jwksUri: "https://raw.githubusercontent.com/istio/istio/release-1.1/security/tools/jwt/samples/jwks.json" 12 | principalBinding: USE_ORIGIN 13 | --- 14 | -------------------------------------------------------------------------------- /workshop/content/src/istiofiles/policy-permissive-tls.yml: -------------------------------------------------------------------------------- 1 | apiVersion: "authentication.istio.io/v1alpha1" 2 | kind: "Policy" 3 | metadata: 4 | name: "preference-mutualtls" 5 | spec: 6 | targets: 7 | - name: preference 8 | peers: 9 | - mtls: 10 | mode: PERMISSIVE -------------------------------------------------------------------------------- /workshop/content/src/istiofiles/recommendation_rate_limit.yml: -------------------------------------------------------------------------------- 1 | --- 2 | apiVersion: config.istio.io/v1alpha2 3 | kind: handler 4 | metadata: 5 | name: quotahandler 6 | namespace: USERNAME-smcp 7 | spec: 8 | compiledAdapter: memquota 9 | params: 10 | quotas: 11 | - name: requestcountquota.instance.USERNAME-smcp 12 | maxAmount: 500 13 | validDuration: 1s 14 | # The first matching override is applied. 15 | # A requestcount instance is checked against override dimensions. 16 | overrides: 17 | # The following override applies to 'recommendation' regardless 18 | # of the source. 19 | - dimensions: 20 | destination: recommendation 21 | maxAmount: 1 22 | validDuration: 5s 23 | --- 24 | apiVersion: config.istio.io/v1alpha2 25 | kind: instance 26 | metadata: 27 | name: requestcountquota 28 | namespace: USERNAME-smcp 29 | spec: 30 | compiledTemplate: quota 31 | params: 32 | dimensions: 33 | source: request.headers["x-forwarded-for"] | "unknown" 34 | destination: destination.labels["app"] | destination.service.name | "unknown" 35 | destinationVersion: destination.labels["version"] | "unknown" 36 | --- 37 | apiVersion: config.istio.io/v1alpha2 38 | kind: QuotaSpec 39 | metadata: 40 | name: request-count 41 | namespace: USERNAME-smcp 42 | spec: 43 | rules: 44 | - quotas: 45 | - charge: 1 46 | quota: requestcountquota 47 | --- 48 | apiVersion: config.istio.io/v1alpha2 49 | kind: QuotaSpecBinding 50 | metadata: 51 | name: request-count 52 | namespace: USERNAME-smcp 53 | spec: 54 | quotaSpecs: 55 | - name: request-count 56 | namespace: USERNAME-smcp 57 | services: 58 | - name: recommendation 59 | namespace: USERNAME-tutorial 60 | # - service: '*' # Uncomment this to bind *all* services to request-count 61 | --- 62 | apiVersion: config.istio.io/v1alpha2 63 | kind: rule 64 | metadata: 65 | name: quota 66 | namespace: USERNAME-smcp 67 | spec: 68 | # quota only applies if you are not logged in. 69 | # match: match(request.headers["cookie"], "user=*") == false 70 | actions: 71 | - handler: quotahandler 72 | instances: 73 | - requestcountquota 74 | -------------------------------------------------------------------------------- /workshop/content/src/istiofiles/recommendation_requestcount.yml: -------------------------------------------------------------------------------- 1 | apiVersion: "config.istio.io/v1alpha2" 2 | kind: metric 3 | metadata: 4 | name: recommendationrequestcount 5 | namespace: istio-system 6 | spec: 7 | value: "1" 8 | dimensions: 9 | source: source.service | "unknown" 10 | destination: destination.service | "unknown" 11 | version: destination.labels["version"] | "unknown" 12 | user_agent: request.headers["user-agent"] | "unknown" 13 | monitored_resource_type: '"UNSPECIFIED"' 14 | --- 15 | apiVersion: "config.istio.io/v1alpha2" 16 | kind: prometheus 17 | metadata: 18 | name: recommendationrequestcounthandler 19 | namespace: istio-system 20 | spec: 21 | metrics: 22 | - name: recommendation_request_count 23 | instance_name: recommendationrequestcount.metric.istio-system 24 | kind: COUNTER 25 | label_names: 26 | - source 27 | - destination 28 | - user_agent 29 | - version 30 | --- 31 | apiVersion: "config.istio.io/v1alpha2" 32 | kind: rule 33 | metadata: 34 | name: recommendationrequestcountprom 35 | namespace: istio-system 36 | spec: 37 | match: destination.service == "recommendation.istio-lab.svc.cluster.local" 38 | actions: 39 | - handler: recommendationrequestcounthandler.prometheus 40 | instances: 41 | - recommendationrequestcount.metric 42 | -------------------------------------------------------------------------------- /workshop/content/src/istiofiles/routing-canary.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: networking.istio.io/v1alpha3 2 | kind: VirtualService 3 | metadata: 4 | name: recommendation 5 | spec: 6 | hosts: 7 | - recommendation 8 | http: 9 | - match: 10 | - headers: 11 | user-location: 12 | exact: Boston 13 | route: 14 | - destination: 15 | host: recommendation 16 | subset: v2 17 | - route: 18 | - destination: 19 | host: recommendation 20 | subset: v1 21 | -------------------------------------------------------------------------------- /workshop/content/src/istiofiles/routing-mirroring.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: networking.istio.io/v1alpha3 2 | kind: VirtualService 3 | metadata: 4 | name: recommendation 5 | spec: 6 | hosts: 7 | - recommendation 8 | http: 9 | - route: 10 | - destination: 11 | host: recommendation 12 | subset: v2 13 | mirror: 14 | host: recommendation 15 | subset: v3 16 | -------------------------------------------------------------------------------- /workshop/content/src/istiofiles/routing-weighted.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: networking.istio.io/v1alpha3 2 | kind: DestinationRule 3 | metadata: 4 | name: recommendation 5 | spec: 6 | host: recommendation 7 | subsets: 8 | - labels: 9 | version: v1 10 | name: version-v1 11 | - labels: 12 | version: v2 13 | name: version-v2 14 | - labels: 15 | version: v3 16 | name: version-v3 17 | --- 18 | apiVersion: networking.istio.io/v1alpha3 19 | kind: VirtualService 20 | metadata: 21 | name: recommendation 22 | spec: 23 | hosts: 24 | - recommendation 25 | http: 26 | - route: 27 | - destination: 28 | host: recommendation 29 | subset: version-v1 30 | weight: 80 31 | - destination: 32 | host: recommendation 33 | subset: version-v2 34 | weight: 20 35 | -------------------------------------------------------------------------------- /workshop/content/src/istiofiles/virtual-service-20-80.yml: -------------------------------------------------------------------------------- 1 | apiVersion: networking.istio.io/v1alpha3 2 | kind: VirtualService 3 | metadata: 4 | name: recommendation 5 | spec: 6 | hosts: 7 | - recommendation 8 | http: 9 | - route: 10 | - destination: 11 | host: recommendation 12 | subset: v1 13 | weight: 20 14 | - destination: 15 | host: recommendation 16 | subset: v2 17 | weight: 80 -------------------------------------------------------------------------------- /workshop/content/src/istiofiles/virtual-service-80-20.yml: -------------------------------------------------------------------------------- 1 | apiVersion: networking.istio.io/v1alpha3 2 | kind: VirtualService 3 | metadata: 4 | name: recommendation 5 | spec: 6 | hosts: 7 | - recommendation 8 | http: 9 | - route: 10 | - destination: 11 | host: recommendation 12 | subset: v1 13 | weight: 80 14 | - destination: 15 | host: recommendation 16 | subset: v2 17 | weight: 20 -------------------------------------------------------------------------------- /workshop/content/src/istiofiles/virtual-service-recommendation-503.yml: -------------------------------------------------------------------------------- 1 | apiVersion: networking.istio.io/v1alpha3 2 | kind: DestinationRule 3 | metadata: 4 | name: recommendation 5 | spec: 6 | host: recommendation 7 | subsets: 8 | - labels: 9 | version: v1 10 | name: v1 11 | - labels: 12 | version: v2 13 | name: v2 14 | - labels: 15 | version: v3 16 | name: v3 17 | --- 18 | apiVersion: networking.istio.io/v1alpha3 19 | kind: VirtualService 20 | metadata: 21 | name: recommendation 22 | spec: 23 | hosts: 24 | - recommendation 25 | http: 26 | - route: 27 | - destination: 28 | host: recommendation 29 | subset: v1 30 | weight: 34 31 | - destination: 32 | host: recommendation 33 | subset: v2 34 | weight: 33 35 | - destination: 36 | host: recommendation 37 | subset: v3 38 | weight: 33 39 | fault: 40 | abort: 41 | percentage: 42 | value: 50 43 | httpStatus: 503 44 | --- 45 | -------------------------------------------------------------------------------- /workshop/content/src/istiofiles/virtual-service-recommendation-delay.yml: -------------------------------------------------------------------------------- 1 | apiVersion: networking.istio.io/v1alpha3 2 | kind: DestinationRule 3 | metadata: 4 | name: recommendation 5 | spec: 6 | host: recommendation 7 | subsets: 8 | - labels: 9 | version: v1 10 | name: v1 11 | - labels: 12 | version: v2 13 | name: v2 14 | - labels: 15 | version: v3 16 | name: v3 17 | --- 18 | apiVersion: networking.istio.io/v1alpha3 19 | kind: VirtualService 20 | metadata: 21 | name: recommendation 22 | spec: 23 | hosts: 24 | - recommendation 25 | http: 26 | - route: 27 | - destination: 28 | host: recommendation 29 | subset: v1 30 | weight: 34 31 | - destination: 32 | host: recommendation 33 | subset: v2 34 | weight: 33 35 | - destination: 36 | host: recommendation 37 | subset: v3 38 | weight: 33 39 | fault: 40 | delay: 41 | fixedDelay: 7.000s 42 | percent: 50 43 | --- 44 | -------------------------------------------------------------------------------- /workshop/content/src/istiofiles/virtual-service-recommendation-split.yml: -------------------------------------------------------------------------------- 1 | apiVersion: networking.istio.io/v1alpha3 2 | kind: VirtualService 3 | metadata: 4 | name: recommendation 5 | spec: 6 | hosts: 7 | - recommendation 8 | http: 9 | - route: 10 | - destination: 11 | host: recommendation 12 | subset: v1 13 | weight: 34 14 | - destination: 15 | host: recommendation 16 | subset: v2 17 | weight: 33 18 | - destination: 19 | host: recommendation 20 | subset: v3 21 | weight: 33 22 | --- 23 | -------------------------------------------------------------------------------- /workshop/content/src/istiofiles/virtual-service-recommendation-timeout.yml: -------------------------------------------------------------------------------- 1 | apiVersion: networking.istio.io/v1alpha3 2 | kind: VirtualService 3 | metadata: 4 | name: recommendation 5 | spec: 6 | hosts: 7 | - recommendation 8 | http: 9 | - route: 10 | - destination: 11 | host: recommendation 12 | timeout: 1.000s 13 | -------------------------------------------------------------------------------- /workshop/content/src/preference/.dockerignore: -------------------------------------------------------------------------------- 1 | * 2 | !target/*-runner 3 | !target/*-runner.jar 4 | !target/lib/* -------------------------------------------------------------------------------- /workshop/content/src/preference/.gitignore: -------------------------------------------------------------------------------- 1 | target/ 2 | !.mvn/wrapper/maven-wrapper.jar 3 | 4 | ### STS ### 5 | .apt_generated 6 | .classpath 7 | .factorypath 8 | .project 9 | .settings 10 | .springBeans 11 | 12 | ### IntelliJ IDEA ### 13 | .idea 14 | *.iws 15 | *.iml 16 | *.ipr 17 | 18 | ### NetBeans ### 19 | nbproject/private/ 20 | build/ 21 | nbbuild/ 22 | dist/ 23 | nbdist/ 24 | .nb-gradle/ -------------------------------------------------------------------------------- /workshop/content/src/preference/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM fabric8/java-jboss-openjdk8-jdk:1.5.4 2 | ENV JAVA_OPTIONS=-Dquarkus.http.host=0.0.0.0 3 | ENV JAEGER_SERVICE_NAME=preference \ 4 | JAEGER_ENDPOINT=http://jaeger-collector.istio-system.svc:14268/api/traces \ 5 | JAEGER_PROPAGATION=b3 \ 6 | JAEGER_SAMPLER_TYPE=const \ 7 | JAEGER_SAMPLER_PARAM=1 8 | EXPOSE 8080 8778 9779 9 | COPY target/lib/* /deployments/lib/ 10 | COPY target/*-runner.jar /deployments/app.jar 11 | ENTRYPOINT [ "/deployments/run-java.sh" ] -------------------------------------------------------------------------------- /workshop/content/src/preference/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 4.0.0 5 | com.redhat.developer.demos.preference.rest 6 | preference 7 | 1.0-SNAPSHOT 8 | 9 | 2.22.0 10 | 0.12.0 11 | UTF-8 12 | 1.8 13 | 1.8 14 | 15 | 16 | 17 | 18 | io.quarkus 19 | quarkus-bom 20 | ${quarkus.version} 21 | pom 22 | import 23 | 24 | 25 | 26 | 27 | 28 | io.quarkus 29 | quarkus-resteasy 30 | 31 | 32 | io.quarkus 33 | quarkus-junit5 34 | test 35 | 36 | 37 | io.rest-assured 38 | rest-assured 39 | test 40 | 41 | 42 | io.quarkus 43 | quarkus-smallrye-rest-client 44 | 45 | 46 | io.quarkus 47 | quarkus-smallrye-opentracing 48 | 49 | 50 | io.quarkus 51 | quarkus-smallrye-health 52 | 53 | 54 | io.quarkus 55 | quarkus-smallrye-metrics 56 | 57 | 58 | 59 | 60 | 61 | io.quarkus 62 | quarkus-maven-plugin 63 | ${quarkus.version} 64 | 65 | 66 | 67 | build 68 | 69 | 70 | 71 | 72 | 73 | maven-surefire-plugin 74 | ${surefire-plugin.version} 75 | 76 | 77 | org.jboss.logmanager.LogManager 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | native 86 | 87 | 88 | native 89 | 90 | 91 | 92 | 93 | 94 | io.quarkus 95 | quarkus-maven-plugin 96 | ${quarkus.version} 97 | 98 | 99 | 100 | native-image 101 | 102 | 103 | true 104 | 105 | 106 | 107 | 108 | 109 | maven-failsafe-plugin 110 | ${surefire-plugin.version} 111 | 112 | 113 | 114 | integration-test 115 | verify 116 | 117 | 118 | 119 | ${project.build.directory}/${project.build.finalName}-runner 120 | 121 | 122 | 123 | 124 | 125 | 126 | 127 | 128 | 129 | 130 | -------------------------------------------------------------------------------- /workshop/content/src/preference/src/main/docker/Dockerfile.jvm: -------------------------------------------------------------------------------- 1 | #### 2 | # This Dockerfile is used in order to build a container that runs the Quarkus application in JVM mode 3 | # 4 | # Before building the docker image run: 5 | # 6 | # mvn package 7 | # 8 | # Then, build the image with: 9 | # 10 | # docker build -f src/main/docker/Dockerfile.jvm -t quarkus/preference-jvm . 11 | # 12 | # Then run the container using: 13 | # 14 | # docker run -i --rm -p 8080:8080 quarkus/preference-jvm 15 | # 16 | ### 17 | FROM fabric8/java-jboss-openjdk8-jdk 18 | ENV JAVA_OPTIONS=-Dquarkus.http.host=0.0.0.0 19 | COPY target/lib/* /deployments/lib/ 20 | COPY target/*-runner.jar /deployments/app.jar 21 | ENTRYPOINT [ "/deployments/run-java.sh" ] -------------------------------------------------------------------------------- /workshop/content/src/preference/src/main/docker/Dockerfile.native: -------------------------------------------------------------------------------- 1 | #### 2 | # This Dockerfile is used in order to build a container that runs the Quarkus application in native (no JVM) mode 3 | # 4 | # Before building the docker image run: 5 | # 6 | # mvn package -Pnative -Dnative-image.docker-build=true 7 | # 8 | # Then, build the image with: 9 | # 10 | # docker build -f src/main/docker/Dockerfile.native -t quarkus/preference . 11 | # 12 | # Then run the container using: 13 | # 14 | # docker run -i --rm -p 8080:8080 quarkus/preference 15 | # 16 | ### 17 | FROM registry.fedoraproject.org/fedora-minimal 18 | WORKDIR /work/ 19 | COPY target/*-runner /work/preference 20 | RUN chmod 775 /work 21 | EXPOSE 8080 22 | CMD ["./preference", "-Xmx8m", "-Xmn8m", "-Xms8m"] 23 | # CMD ["./preference", "-Dquarkus.http.host=0.0.0.0"] -------------------------------------------------------------------------------- /workshop/content/src/preference/src/main/java/com/redhat/developer/demos/customer/rest/PreferenceResource.java: -------------------------------------------------------------------------------- 1 | package com.redhat.developer.demos.customer.rest; 2 | 3 | import org.eclipse.microprofile.rest.client.inject.RestClient; 4 | import org.slf4j.Logger; 5 | import org.slf4j.LoggerFactory; 6 | 7 | import javax.inject.Inject; 8 | import javax.ws.rs.*; 9 | import javax.ws.rs.core.MediaType; 10 | import javax.ws.rs.core.Response; 11 | 12 | @Path("/") 13 | public class PreferenceResource { 14 | 15 | private static final String RESPONSE_STRING_FORMAT = "preference => %s\n"; 16 | 17 | private final Logger logger = LoggerFactory.getLogger(getClass()); 18 | 19 | @Inject 20 | @RestClient 21 | RecommendationService recommendationService; 22 | 23 | @GET 24 | @Produces(MediaType.TEXT_PLAIN) 25 | public Response getCustomer() { 26 | try { 27 | String response = recommendationService.getPreference(); 28 | return Response.ok(String.format(RESPONSE_STRING_FORMAT, response)).build(); 29 | } catch (WebApplicationException ex) { 30 | Response response = ex.getResponse(); 31 | logger.warn("Non HTTP 20x trying to get the response from recommendation service: " + response.getStatus()); 32 | return Response 33 | .status(Response.Status.SERVICE_UNAVAILABLE) 34 | .entity(String.format(RESPONSE_STRING_FORMAT, 35 | String.format("Error: %d - %s", response.getStatus(), response.readEntity(String.class))) 36 | ) 37 | .build(); 38 | } catch (ProcessingException ex) { 39 | logger.warn("Exception trying to get the response from recommendation service.", ex); 40 | return Response 41 | .status(Response.Status.SERVICE_UNAVAILABLE) 42 | .entity(String.format(RESPONSE_STRING_FORMAT, ex.getCause().getClass().getSimpleName() + ": " + ex.getCause().getMessage())) 43 | .build(); 44 | } 45 | } 46 | 47 | } -------------------------------------------------------------------------------- /workshop/content/src/preference/src/main/java/com/redhat/developer/demos/customer/rest/RecommendationService.java: -------------------------------------------------------------------------------- 1 | package com.redhat.developer.demos.customer.rest; 2 | 3 | import org.eclipse.microprofile.rest.client.annotation.RegisterClientHeaders; 4 | import org.eclipse.microprofile.rest.client.inject.RegisterRestClient; 5 | 6 | import javax.ws.rs.GET; 7 | import javax.ws.rs.HeaderParam; 8 | import javax.ws.rs.Path; 9 | import javax.ws.rs.Produces; 10 | import javax.ws.rs.core.Response; 11 | 12 | @RegisterClientHeaders 13 | @RegisterRestClient 14 | public interface RecommendationService { 15 | 16 | @Path("/") 17 | @GET 18 | @Produces("text/plain") 19 | public String getPreference(); 20 | 21 | } 22 | -------------------------------------------------------------------------------- /workshop/content/src/preference/src/main/resources/application.properties: -------------------------------------------------------------------------------- 1 | org.eclipse.microprofile.rest.client.propagateHeaders=baggage-user-agent,user-location 2 | com.redhat.developer.demos.customer.rest.RecommendationService/mp-rest/url=http://recommendation:8080 3 | -------------------------------------------------------------------------------- /workshop/content/src/preference/src/test/java/com/redhat/developer/demos/customer/rest/NativePreferenceResourceIT.java: -------------------------------------------------------------------------------- 1 | package com.redhat.developer.demos.customer.rest; 2 | 3 | import io.quarkus.test.junit.SubstrateTest; 4 | 5 | @SubstrateTest 6 | public class NativePreferenceResourceIT extends PreferenceResourceTest { 7 | 8 | // Execute the same tests but in native mode. 9 | } -------------------------------------------------------------------------------- /workshop/content/src/preference/src/test/java/com/redhat/developer/demos/customer/rest/PreferenceResourceTest.java: -------------------------------------------------------------------------------- 1 | package com.redhat.developer.demos.customer.rest; 2 | 3 | import io.quarkus.test.junit.QuarkusTest; 4 | import org.junit.jupiter.api.Test; 5 | 6 | import static io.restassured.RestAssured.given; 7 | import static org.hamcrest.CoreMatchers.is; 8 | import static org.hamcrest.CoreMatchers.startsWith; 9 | 10 | @QuarkusTest 11 | public class PreferenceResourceTest { 12 | 13 | @Test 14 | public void testHelloEndpoint() { 15 | given() 16 | .when().get("/") 17 | .then() 18 | .statusCode(503) 19 | .body(startsWith("preference =>")); 20 | } 21 | 22 | } -------------------------------------------------------------------------------- /workshop/content/src/recommendation/.dockerignore: -------------------------------------------------------------------------------- 1 | * 2 | !target/*-runner 3 | !target/*-runner.jar 4 | !target/lib/* -------------------------------------------------------------------------------- /workshop/content/src/recommendation/.gitignore: -------------------------------------------------------------------------------- 1 | target/ 2 | !.mvn/wrapper/maven-wrapper.jar 3 | 4 | ### STS ### 5 | .apt_generated 6 | .classpath 7 | .factorypath 8 | .project 9 | .settings 10 | .springBeans 11 | 12 | ### IntelliJ IDEA ### 13 | .idea 14 | *.iws 15 | *.iml 16 | *.ipr 17 | 18 | ### NetBeans ### 19 | nbproject/private/ 20 | build/ 21 | nbbuild/ 22 | dist/ 23 | nbdist/ 24 | .nb-gradle/ -------------------------------------------------------------------------------- /workshop/content/src/recommendation/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM fabric8/java-jboss-openjdk8-jdk:1.5.4 2 | ENV JAVA_OPTIONS=-Dquarkus.http.host=0.0.0.0 3 | ENV JAEGER_SERVICE_NAME=recommendation \ 4 | JAEGER_ENDPOINT=http://jaeger-collector.istio-system.svc:14268/api/traces \ 5 | JAEGER_PROPAGATION=b3 \ 6 | JAEGER_SAMPLER_TYPE=const \ 7 | JAEGER_SAMPLER_PARAM=1 8 | EXPOSE 8080 8778 9779 9 | COPY target/lib/* /deployments/lib/ 10 | COPY target/*-runner.jar /deployments/app.jar 11 | ENTRYPOINT [ "/deployments/run-java.sh" ] -------------------------------------------------------------------------------- /workshop/content/src/recommendation/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 4.0.0 5 | com.redhat.developers 6 | recommendation 7 | 1.0-SNAPSHOT 8 | 9 | 2.22.0 10 | 0.12.0 11 | UTF-8 12 | 1.8 13 | 1.8 14 | 15 | 16 | 17 | 18 | io.quarkus 19 | quarkus-bom 20 | ${quarkus.version} 21 | pom 22 | import 23 | 24 | 25 | 26 | 27 | 28 | io.quarkus 29 | quarkus-resteasy 30 | 31 | 32 | io.quarkus 33 | quarkus-smallrye-rest-client 34 | 35 | 36 | io.quarkus 37 | quarkus-arc 38 | 39 | 40 | io.quarkus 41 | quarkus-junit5 42 | 43 | 44 | io.rest-assured 45 | rest-assured 46 | test 47 | 48 | 49 | io.quarkus 50 | quarkus-smallrye-health 51 | 52 | 53 | io.quarkus 54 | quarkus-smallrye-metrics 55 | 56 | 57 | io.quarkus 58 | quarkus-smallrye-opentracing 59 | 60 | 61 | 62 | 63 | 64 | io.quarkus 65 | quarkus-maven-plugin 66 | ${quarkus.version} 67 | 68 | 69 | 70 | build 71 | 72 | 73 | 74 | 75 | 76 | maven-surefire-plugin 77 | ${surefire-plugin.version} 78 | 79 | 80 | org.jboss.logmanager.LogManager 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | native 89 | 90 | 91 | native 92 | 93 | 94 | 95 | 96 | 97 | io.quarkus 98 | quarkus-maven-plugin 99 | ${quarkus.version} 100 | 101 | 102 | 103 | native-image 104 | 105 | 106 | true 107 | 108 | 109 | 110 | 111 | 112 | maven-failsafe-plugin 113 | ${surefire-plugin.version} 114 | 115 | 116 | 117 | integration-test 118 | verify 119 | 120 | 121 | 122 | ${project.build.directory}/${project.build.finalName}-runner 123 | 124 | 125 | 126 | 127 | 128 | 129 | 130 | 131 | 132 | 133 | -------------------------------------------------------------------------------- /workshop/content/src/recommendation/src/main/docker/Dockerfile.jvm: -------------------------------------------------------------------------------- 1 | #### 2 | # This Dockerfile is used in order to build a container that runs the Quarkus application in JVM mode 3 | # 4 | # Before building the docker image run: 5 | # 6 | # mvn package 7 | # 8 | # Then, build the image with: 9 | # 10 | # docker build -f src/main/docker/Dockerfile.jvm -t quarkus/recommendation-jvm . 11 | # 12 | # Then run the container using: 13 | # 14 | # docker run -i --rm -p 8080:8080 quarkus/recommendation-jvm 15 | # 16 | ### 17 | FROM fabric8/java-jboss-openjdk8-jdk 18 | ENV JAVA_OPTIONS=-Dquarkus.http.host=0.0.0.0 19 | COPY target/lib/* /deployments/lib/ 20 | COPY target/*-runner.jar /deployments/app.jar 21 | ENTRYPOINT [ "/deployments/run-java.sh" ] -------------------------------------------------------------------------------- /workshop/content/src/recommendation/src/main/docker/Dockerfile.native: -------------------------------------------------------------------------------- 1 | #### 2 | # This Dockerfile is used in order to build a container that runs the Quarkus application in native (no JVM) mode 3 | # 4 | # Before building the docker image run: 5 | # 6 | # mvn package -Pnative -Dnative-image.docker-build=true 7 | # 8 | # Then, build the image with: 9 | # 10 | # docker build -f src/main/docker/Dockerfile.native -t quarkus/recommendation . 11 | # 12 | # Then run the container using: 13 | # 14 | # docker run -i --rm -p 8080:8080 quarkus/recommendation 15 | # 16 | ### 17 | FROM registry.fedoraproject.org/fedora-minimal 18 | WORKDIR /work/ 19 | COPY target/*-runner /work/recommendation 20 | RUN chmod 775 /work 21 | EXPOSE 8080 22 | CMD ["./recommendation", "-Xmx8m", "-Xmn8m", "-Xms8m"] 23 | # CMD ["./recommendation", "-Dquarkus.http.host=0.0.0.0"] -------------------------------------------------------------------------------- /workshop/content/src/recommendation/src/main/java/com/redhat/developer/demos/recommendation/rest/RecommendationResource.java: -------------------------------------------------------------------------------- 1 | package com.redhat.developer.demos.recommendation.rest; 2 | 3 | import java.io.ByteArrayInputStream; 4 | import java.io.InputStream; 5 | import javax.json.Json; 6 | import javax.json.JsonObject; 7 | import javax.ws.rs.client.Client; 8 | import javax.ws.rs.client.ClientBuilder; 9 | import org.slf4j.Logger; 10 | import org.slf4j.LoggerFactory; 11 | 12 | import javax.ws.rs.GET; 13 | import javax.ws.rs.Path; 14 | import javax.ws.rs.QueryParam; 15 | import javax.ws.rs.core.Response; 16 | 17 | @Path("/") 18 | public class RecommendationResource { 19 | 20 | private static final String RESPONSE_STRING_FORMAT = "recommendation %s from '%s': %d\n"; 21 | 22 | private static final String RESPONSE_STRING_NOW_FORMAT = "recommendation v3 %s from '%s': %d\n"; 23 | 24 | private final Logger logger = LoggerFactory.getLogger(getClass()); 25 | 26 | /** 27 | * Counter to help us see the lifecycle 28 | */ 29 | private int count = 0; 30 | 31 | /** 32 | * Flag for throwing a 503 when enabled 33 | */ 34 | private boolean misbehave = false; 35 | 36 | /** 37 | * Timeout value for forcing timeout if > 0 38 | */ 39 | private long timeout; 40 | 41 | private static final String HOSTNAME = parseContainerIdFromHostname( 42 | System.getenv().getOrDefault("HOSTNAME", "unknown")); 43 | private static final String VERSION = parseVersionFromHostname( 44 | System.getenv().getOrDefault("HOSTNAME", "unknown")); 45 | 46 | static String parseVersionFromHostname(String hostname) { 47 | return hostname.replaceAll("recommendation-", "").replaceAll("-.*", ""); 48 | } 49 | 50 | static String parseContainerIdFromHostname(String hostname) { 51 | return hostname.replaceAll("recommendation-v\\d+-", ""); 52 | } 53 | 54 | @GET 55 | public Response getRecommendations() { 56 | count++; 57 | logger.info(String.format("recommendation request from %s: %d", HOSTNAME, count)); 58 | 59 | if (timeout > 0) { 60 | sleep(timeout); 61 | } 62 | 63 | logger.debug("recommendation service ready to return"); 64 | if (misbehave) { 65 | return doMisbehavior(); 66 | } 67 | return Response.ok(String.format(RESPONSE_STRING_FORMAT, VERSION, HOSTNAME, count)).build(); 68 | // return Response.ok(String.format(RESPONSE_STRING_NOW_FORMAT, getNow(), HOSTNAME, count)).build(); 69 | } 70 | 71 | private void sleep(final long timeout) { 72 | try { 73 | Thread.sleep(timeout); 74 | } catch (InterruptedException e) { 75 | logger.info("Thread interrupted"); 76 | } 77 | } 78 | 79 | private Response doMisbehavior() { 80 | logger.debug(String.format("Misbehaving %d", count)); 81 | return Response.status(Response.Status.SERVICE_UNAVAILABLE) 82 | .entity(String.format("recommendation misbehavior from '%s'\n", HOSTNAME)).build(); 83 | } 84 | 85 | @GET 86 | @Path("/misbehave") 87 | public Response flagMisbehave() { 88 | this.misbehave = true; 89 | logger.debug("'misbehave' has been set to 'true'"); 90 | return Response.ok("Following requests to / will return a 503\n").build(); 91 | } 92 | 93 | @GET 94 | @Path("/behave") 95 | public Response flagBehave() { 96 | this.misbehave = false; 97 | logger.debug("'misbehave' has been set to 'false'"); 98 | return Response.ok("Following requests to / will return 200\n").build(); 99 | } 100 | 101 | @GET 102 | @Path("/timeout") 103 | public Response setTimeout(@QueryParam("timeout") int timeout) { 104 | this.timeout = timeout*1000L; 105 | logger.debug("'timeout' has been set to " + timeout + " seconds"); 106 | return Response.ok("Timeout has been set to " + timeout + " seconds\n").build(); 107 | } 108 | 109 | private String getNow() { 110 | final Client client = ClientBuilder.newClient(); 111 | final Response res = client.target("http://worldclockapi.com/api/json/cet/now").request().get(); 112 | final String jsonObject = res.readEntity(String.class); 113 | return Json.createReader(new ByteArrayInputStream(jsonObject.getBytes())).readObject().getString("currentDateTime"); 114 | } 115 | 116 | } 117 | -------------------------------------------------------------------------------- /workshop/content/src/recommendation/src/test/java/com/redhat/developer/demos/recommendation/rest/NativeRecommendationResourceIT.java: -------------------------------------------------------------------------------- 1 | package com.redhat.developer.demos.recommendation.rest; 2 | 3 | import io.quarkus.test.junit.SubstrateTest; 4 | 5 | @SubstrateTest 6 | public class NativeRecommendationResourceIT extends RecommendationResourceTest { 7 | 8 | // Execute the same tests but in native mode. 9 | } -------------------------------------------------------------------------------- /workshop/content/src/recommendation/src/test/java/com/redhat/developer/demos/recommendation/rest/RecommendationResourceTest.java: -------------------------------------------------------------------------------- 1 | package com.redhat.developer.demos.recommendation.rest; 2 | 3 | import io.quarkus.test.junit.QuarkusTest; 4 | import org.junit.jupiter.api.Test; 5 | 6 | import static io.restassured.RestAssured.given; 7 | import static org.hamcrest.CoreMatchers.startsWith; 8 | 9 | @QuarkusTest 10 | public class RecommendationResourceTest { 11 | 12 | @Test 13 | public void testHelloEndpoint() { 14 | given() 15 | .when().get("/") 16 | .then() 17 | .statusCode(200) 18 | .body(startsWith("recommendation")); 19 | } 20 | 21 | } -------------------------------------------------------------------------------- /workshop/content/workshop-overview.adoc: -------------------------------------------------------------------------------- 1 | This is the initial landing page for your workshop. Include in this page a description of what your workshop is about. 2 | -------------------------------------------------------------------------------- /workshop/content/workshop-summary.adoc: -------------------------------------------------------------------------------- 1 | This is the last page of the workshop. Include in this page a summary of the workshop and any links to resources relevant to the workshop. This ensures anyone doing the workshop has material they can research later to learn more. 2 | -------------------------------------------------------------------------------- /workshop/modules.yaml: -------------------------------------------------------------------------------- 1 | modules: 2 | intro: 3 | name: Workshop Overview 4 | exit_sign: Observability with Kiali 5 | kiali: 6 | name: Observing the Service Mesh using Kiali 7 | exit_sign: Routing 8 | routing: 9 | name: Routing within the Service Mesh 10 | exit_sign: Mutual TLS 11 | mtls: 12 | name: Mutual TLS 13 | exit_sign: Authentication 14 | authentication: 15 | name: Authentication (JWT and RBAC) 16 | exit_sign: ACLs 17 | acl: 18 | name: Whitelisting/Blacklisting 19 | exit_sign: Circuit Breaking 20 | circuit-breaking: 21 | name: Retries, Timeouts and Circuit Breaking 22 | exit_sign: Rate Limiting 23 | rate-limiting: 24 | name: Rate Limiting/Policy 25 | exit_sign: Fault Injection 26 | fault-injection: 27 | name: Fault Injection -------------------------------------------------------------------------------- /workshop/workshop.yaml: -------------------------------------------------------------------------------- 1 | name: Red Hat OpenShift Service Mesh in Action 2 | 3 | modules: 4 | activate: 5 | - intro 6 | - kiali 7 | - routing 8 | - mtls 9 | - authentication 10 | - acl 11 | - circuit-breaking 12 | - rate-limiting 13 | - fault-injection 14 | --------------------------------------------------------------------------------