├── .github ├── pull_request_template.md ├── semantic.yml └── workflows │ ├── pre-release.yml │ └── release.yml ├── .gitignore ├── .versionrc.json ├── CHANGELOG.md ├── CODEOWNERS ├── CONTRIBUTING.md ├── LICENSE ├── README.md ├── dynatrace-oneagent ├── deploy-dynatrace-oneagent-openshift.sh └── deploy-dynatrace-oneagent.sh ├── gh-actions-scripts └── post-changelog-actions.sh ├── istio-configuration └── configure-istio.sh ├── load-generation ├── bin │ ├── loadgenerator-linux │ ├── loadgenerator-mac │ └── loadgenerator-win.exe ├── build.sh ├── cartsloadgen │ ├── Dockerfile │ ├── build.sh │ ├── deploy │ │ ├── cartsloadgen-base.yaml │ │ ├── cartsloadgen-fast.yaml │ │ ├── cartsloadgen-faulty.yaml │ │ └── cartsloadgen-prod.yaml │ └── generate_traffic.sh └── main.go ├── onboarding-carts ├── argo │ ├── carts.tgz │ └── carts │ │ ├── Chart.yaml │ │ ├── templates │ │ ├── NOTES.txt │ │ ├── _helpers.tpl │ │ ├── carts-db-deployment.yaml │ │ ├── carts-db-service.yaml │ │ ├── keptn-hook.yaml │ │ ├── rollout.yaml │ │ └── service.yaml │ │ └── values.yaml ├── carts-db.tgz ├── carts-db │ ├── Chart.yaml │ ├── templates │ │ ├── carts-db-deployment.yaml │ │ └── carts-db-service.yaml │ └── values.yaml ├── carts.tgz ├── carts │ ├── Chart.yaml │ ├── templates │ │ ├── deployment.yaml │ │ └── service.yaml │ └── values.yaml ├── dynatrace │ └── dynatrace.conf.yaml ├── jmeter │ ├── basiccheck.jmx │ └── load.jmx ├── lighthouse-source-dynatrace.yaml ├── lighthouse-source-prometheus.yaml ├── manifests │ ├── manifest-carts-db.yaml │ └── manifest-carts.yaml ├── remediation.yaml ├── remediation_feature_toggle.yaml ├── shipyard-argo.yaml ├── shipyard-quality-gates.yaml ├── shipyard.yaml ├── sli-config-argo-prometheus.yaml ├── sli-config-dynatrace-no-deployment-tag.yaml ├── sli-config-dynatrace.yaml ├── sli-config-prometheus.yaml ├── slo-quality-gates.yaml ├── slo-self-healing-dynatrace.yaml └── slo-self-healing-prometheus.yaml ├── quickstart ├── .gitignore ├── README.md ├── automated-operations.sh ├── clean-project.sh ├── demo │ ├── helm │ │ ├── hardening_endpoints.yaml │ │ ├── helloservice.tgz │ │ ├── helloservice │ │ │ ├── Chart.yaml │ │ │ ├── templates │ │ │ │ ├── deployment.yaml │ │ │ │ └── service.yaml │ │ │ └── values.yaml │ │ └── production_endpoints.yaml │ ├── jmeter │ │ ├── jmeter.conf.yaml │ │ └── load.jmx │ ├── job │ │ ├── config.yaml │ │ └── job-executor.yaml │ ├── prometheus │ │ └── sli.yaml │ ├── remediation.yaml │ ├── shipyard.yaml │ └── slo.yaml ├── expose-keptn.sh └── multistage-delivery.sh ├── simplenodeservice ├── .gitignore ├── README.md ├── dynatrace │ ├── createCalculatedMetrics.sh │ ├── createLoadTestingDashboard.sh │ ├── createTestRequestAttributes.sh │ ├── createTestStepCalculatedMetrics.sh │ └── loadtestingdashboard.json ├── helpers │ └── gen_load.sh ├── images │ ├── 4buildoverview.png │ └── simplenodesersviceui.png ├── keptn │ ├── add_resources.sh │ ├── charts.tgz │ ├── charts │ │ ├── Chart.yaml │ │ ├── templates │ │ │ ├── deployment.yaml │ │ │ └── services.yaml │ │ └── values.yaml │ ├── dynatrace │ │ ├── dynatrace.conf.yaml │ │ ├── sli_basic.yaml │ │ └── sli_perftest.yaml │ ├── exposeBridge.sh │ ├── jmeter │ │ ├── basiccheck.jmx │ │ ├── jmeter.conf.yaml │ │ └── load.jmx │ ├── manifests │ │ ├── bridge.yaml │ │ └── gen │ │ │ └── readme.md │ ├── shipyard.yaml │ ├── shipyard_keptn07.yaml │ ├── slo_basic.yaml │ ├── slo_empty.yaml │ └── slo_perftest.yaml ├── keptnevents │ └── configchangeevent.json ├── quality-gate-only │ ├── dynatrace │ │ ├── dynatrace.conf.yaml │ │ ├── sli_basic.yaml │ │ └── sli_perftest.yaml │ ├── shipyard_qualitystageonly.yaml │ ├── slo_basic.yaml │ └── slo_perftest.yaml └── twoframes.html └── unleash-server ├── README.md ├── shipyard.yaml ├── unleash-db.tgz ├── unleash-db ├── Chart.yaml ├── templates │ ├── db-deployment.yaml │ └── db-service.yaml └── values.yaml ├── unleash.tgz └── unleash ├── Chart.yaml ├── templates ├── deployment.yaml └── service.yaml └── values.yaml /.github/pull_request_template.md: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | ## This PR 5 | 6 | 7 | - adds this new feature 8 | 9 | ### Related Issues 10 | 11 | 12 | Fixes #1234523 13 | 14 | ### Notes 15 | 16 | 17 | ### Follow-up Tasks 18 | 19 | 20 | 21 | ### How to test 22 | 23 | 24 | -------------------------------------------------------------------------------- /.github/semantic.yml: -------------------------------------------------------------------------------- 1 | titleOnly: true 2 | types: 3 | - feat # A new feature 4 | - fix # A bug fix 5 | - build # Changes that affect the build system or external dependencies 6 | - chore # Other changes that don't modify src or test files 7 | - ci # Changes to our CI configuration files and scripts 8 | - docs # Documentation only changes 9 | - perf # A code change that improves performance 10 | - refactor # A code change that neither fixes a bug nor adds a feature 11 | - revert # Reverts a previous commit 12 | - style # Changes that do not affect the meaning of the code 13 | - test # Adding missing tests or correcting existing tests 14 | -------------------------------------------------------------------------------- /.github/workflows/pre-release.yml: -------------------------------------------------------------------------------- 1 | name: Pre-Release 2 | on: 3 | workflow_dispatch: 4 | inputs: 5 | semver-type: 6 | description: 'Can be one of [major,minor,patch]. CAUTION: This will enforce a new pre-release with the specified semantic version type bumped.' 7 | required: false 8 | env: 9 | NODE_VERSION: 14 10 | KEPTN_BOT_NAME: "Keptn Bot" 11 | KEPTN_BOT_EMAIL: "keptn-bot <86361500+keptn-bot@users.noreply.github.com>" 12 | RELEASE_NOTES_FILE: "RELEASE-BODY.md" 13 | PRERELEASE_KEYWORD: "next" 14 | defaults: 15 | run: 16 | shell: bash 17 | jobs: 18 | pre-release: 19 | name: Pre-Release 20 | runs-on: ubuntu-20.04 21 | steps: 22 | - name: Check SemVer input 23 | env: 24 | SEMVER_TYPE: ${{ github.event.inputs.semver-type }} 25 | run: | 26 | if [[ ! -z "$SEMVER_TYPE" ]]; then 27 | echo "SemVer Type is defined. Checking for valid SemVer type..." 28 | if [[ "$SEMVER_TYPE" == "major" ]] || [[ "$SEMVER_TYPE" == "minor" ]] || [[ "$SEMVER_TYPE" == "patch" ]]; then 29 | echo "::notice::SemVer Type is correctly set to $SEMVER_TYPE! Continuing with this version bump..." 30 | else 31 | echo "::error::ERROR: Enforced SemVer does not match any of [major,minor,patch]!" 32 | echo "Exiting..." 33 | exit 1 34 | fi 35 | else 36 | echo "::notice::No SemVer type defined, continuing with auto generated version number..." 37 | fi 38 | 39 | - name: Checkout repo 40 | uses: actions/checkout@v2 41 | with: 42 | fetch-depth: 0 43 | token: ${{ secrets.KEPTN_BOT_TOKEN }} 44 | 45 | - name: Set up Node.js 46 | uses: actions/setup-node@v2 47 | with: 48 | node-version: ${{ env.NODE_VERSION }} 49 | 50 | - name: Configure Git 51 | env: 52 | KEPTN_BOT_NAME: ${{ env.KEPTN_BOT_NAME }} 53 | KEPTN_BOT_EMAIL: ${{ env.KEPTN_BOT_EMAIL }} 54 | run: | 55 | git config user.name "$KEPTN_BOT_NAME" 56 | git config user.email "$KEPTN_BOT_EMAIL" 57 | 58 | - name: Prepare GitHub Release Notes 59 | env: 60 | SEMVER_TYPE: ${{ github.event.inputs.semver-type }} 61 | run: | 62 | if [[ -z "$SEMVER_TYPE" ]]; then 63 | npx standard-version@^9.3.1 \ 64 | --prerelease "${{ env.PRERELEASE_KEYWORD }}" \ 65 | -i "${{ env.RELEASE_NOTES_FILE }}" \ 66 | --skip.commit \ 67 | --skip.tag \ 68 | --header "" \ 69 | --release-as "$SEMVER_TYPE" 70 | else 71 | npx standard-version@^9.3.1 \ 72 | --prerelease "${{ env.PRERELEASE_KEYWORD }}" \ 73 | -i "${{ env.RELEASE_NOTES_FILE }}" \ 74 | --skip.commit \ 75 | --skip.tag \ 76 | --header "" 77 | fi 78 | 79 | - name: Enhance Release Notes with Build Metadata 80 | run: | 81 | echo "#### Build Information" >> "${{ env.RELEASE_NOTES_FILE }}" 82 | echo "" >> "${{ env.RELEASE_NOTES_FILE }}" 83 | echo "**GitHub Actions Run:** $GITHUB_SERVER_URL/$GITHUB_REPOSITORY/actions/runs/$GITHUB_RUN_ID" >> "${{ env.RELEASE_NOTES_FILE }}" 84 | 85 | - name: Create pre-release package 86 | id: create-release-package 87 | env: 88 | SEMVER_TYPE: ${{ github.event.inputs.semver-type }} 89 | GITHUB_TOKEN: ${{ secrets.KEPTN_BOT_TOKEN }} 90 | run: | 91 | echo "🚀 Creating pre-release package now..." 92 | 93 | if [[ ! -z "$SEMVER_TYPE" ]]; then 94 | npx standard-version@^9.3.1 \ 95 | --release-as "$SEMVER_TYPE" \ 96 | --prerelease "${{ env.PRERELEASE_KEYWORD }}" \ 97 | --skip.commit \ 98 | --skip.changelog 99 | else 100 | npx standard-version@^9.3.1 \ 101 | --prerelease "${{ env.PRERELEASE_KEYWORD }}" \ 102 | --skip.commit \ 103 | --skip.changelog 104 | fi 105 | 106 | echo "::set-output name=tag-name::$(git describe --tags --abbrev=0)" 107 | echo "⚡️ Pushing changes to remote repository..." 108 | git push --follow-tags 109 | 110 | - name: Create GitHub Release 111 | env: 112 | GITHUB_TOKEN: ${{ secrets.KEPTN_BOT_TOKEN }} 113 | RELEASE_TAG: ${{ steps.create-release-package.outputs.tag-name }} 114 | run: | 115 | gh release create "$RELEASE_TAG" --prerelease --notes-file "${{ env.RELEASE_NOTES_FILE }}" --title "$RELEASE_TAG" 116 | -------------------------------------------------------------------------------- /.github/workflows/release.yml: -------------------------------------------------------------------------------- 1 | name: Release 2 | on: 3 | workflow_dispatch: 4 | inputs: 5 | semver-type: 6 | description: 'Can be one of [major,minor,patch]. CAUTION: This will enforce a new release with the specified semantic version type bumped.' 7 | required: false 8 | env: 9 | NODE_VERSION: 14 10 | KEPTN_BOT_NAME: "Keptn Bot" 11 | KEPTN_BOT_EMAIL: "keptn-bot <86361500+keptn-bot@users.noreply.github.com>" 12 | RELEASE_NOTES_FILE: "RELEASE-BODY.md" 13 | defaults: 14 | run: 15 | shell: bash 16 | jobs: 17 | release: 18 | name: "Release" 19 | runs-on: ubuntu-20.04 20 | steps: 21 | - name: Check SemVer input 22 | env: 23 | SEMVER_TYPE: ${{ github.event.inputs.semver-type }} 24 | run: | 25 | if [[ ! -z "$SEMVER_TYPE" ]]; then 26 | echo "SemVer Type is defined. Checking for valid SemVer type..." 27 | if [[ "$SEMVER_TYPE" == "major" ]] || [[ "$SEMVER_TYPE" == "minor" ]] || [[ "$SEMVER_TYPE" == "patch" ]]; then 28 | echo "::notice::SemVer Type is correctly set to $SEMVER_TYPE! Continuing with this version bump..." 29 | else 30 | echo "::error::ERROR: Enforced SemVer does not match any of [major,minor,patch]!" 31 | echo "Exiting..." 32 | exit 1 33 | fi 34 | else 35 | echo "::notice::No SemVer type defined, continuing with auto generated version number..." 36 | fi 37 | 38 | - name: Checkout repo 39 | uses: actions/checkout@v2 40 | with: 41 | fetch-depth: 0 42 | token: ${{ secrets.KEPTN_BOT_TOKEN }} 43 | 44 | - name: Set up Node.js 45 | uses: actions/setup-node@v2 46 | with: 47 | node-version: ${{ env.NODE_VERSION }} 48 | 49 | - name: Configure Git 50 | env: 51 | KEPTN_BOT_NAME: ${{ env.KEPTN_BOT_NAME }} 52 | KEPTN_BOT_EMAIL: ${{ env.KEPTN_BOT_EMAIL }} 53 | run: | 54 | git config user.name "$KEPTN_BOT_NAME" 55 | git config user.email "$KEPTN_BOT_EMAIL" 56 | 57 | - name: Prepare GitHub Release Notes 58 | env: 59 | SEMVER_TYPE: ${{ github.event.inputs.semver-type }} 60 | run: | 61 | # Delete pre-release tags to be able to generate a changelog from last 'real' release 62 | # This is a workaround for a known limitation of standard-version 63 | # Reference: https://github.com/conventional-changelog/standard-version/issues/203#issuecomment-872415140 64 | git tag -l | grep -vE '^(0|[1-9][0-9]*)\.(0|[1-9][0-9]*)\.(0|[1-9][0-9]*)$' | xargs git tag -d 65 | 66 | if [[ ! -z "$SEMVER_TYPE" ]]; then 67 | npx standard-version@^9.3.1 \ 68 | -i "${{ env.RELEASE_NOTES_FILE }}" \ 69 | --skip.commit \ 70 | --skip.tag \ 71 | --header "" \ 72 | --release-as "$SEMVER_TYPE" 73 | else 74 | npx standard-version@^9.3.1 \ 75 | -i "${{ env.RELEASE_NOTES_FILE }}" \ 76 | --skip.commit \ 77 | --skip.tag \ 78 | --header "" 79 | fi 80 | 81 | - name: Temporarily disable "include administrators" branch protection 82 | uses: benjefferies/branch-protection-bot@6d0ac2b2d9bfd39794b017f8241adb7da7f0ab98 # pin@1.0.7 83 | with: 84 | access_token: ${{ secrets.KEPTN_BOT_TOKEN }} 85 | branch: ${{ github.event.repository.default_branch }} 86 | enforce_admins: false 87 | 88 | - name: Create release package 89 | id: create-release-package 90 | env: 91 | SEMVER_TYPE: ${{ github.event.inputs.semver-type }} 92 | GITHUB_TOKEN: ${{ secrets.KEPTN_BOT_TOKEN }} 93 | run: | 94 | echo "🚀 Creating release package now..." 95 | 96 | if [[ ! -z "$SEMVER_TYPE" ]]; then 97 | npx standard-version@^9.3.1 \ 98 | --release-as "$SEMVER_TYPE" 99 | else 100 | npx standard-version@^9.3.1 101 | fi 102 | 103 | echo "::set-output name=tag-name::$(git describe --tags --abbrev=0)" 104 | 105 | echo "Fetching previously deleted old tags..." 106 | git fetch origin --tags -f 107 | echo "⚡️ Pushing changes to remote repository..." 108 | git push --follow-tags 109 | 110 | - name: Enable "include administrators" branch protection 111 | uses: benjefferies/branch-protection-bot@6d0ac2b2d9bfd39794b017f8241adb7da7f0ab98 # pin@1.0.7 112 | if: always() # Force to always run this step to ensure "include administrators" is always turned back on 113 | with: 114 | access_token: ${{ secrets.KEPTN_BOT_TOKEN }} 115 | branch: ${{ github.event.repository.default_branch }} 116 | enforce_admins: true 117 | 118 | - name: Create GitHub Release 119 | env: 120 | GITHUB_TOKEN: ${{ secrets.KEPTN_BOT_TOKEN }} 121 | RELEASE_TAG: ${{ steps.create-release-package.outputs.tag-name }} 122 | run: | 123 | gh release create "$RELEASE_TAG" --draft --notes-file "${{ env.RELEASE_NOTES_FILE }}" --title "$RELEASE_TAG" 124 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .vscode 2 | .idea 3 | simplenodeservice/dynatrace/curloutput.txt 4 | simplenodeservice/keptn/remediation.yaml 5 | -------------------------------------------------------------------------------- /.versionrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "preMajor": true, 3 | "scripts": { 4 | "postchangelog": "./gh-actions-scripts/post-changelog-actions.sh" 5 | }, 6 | "tagPrefix": "", 7 | "types": [ 8 | { 9 | "type": "feat", 10 | "section": "Features" 11 | }, 12 | { 13 | "type": "fix", 14 | "section": "Bug Fixes" 15 | }, 16 | { 17 | "type": "chore", 18 | "section": "Other" 19 | }, 20 | { 21 | "type": "docs", 22 | "section": "Docs" 23 | }, 24 | { 25 | "type": "perf", 26 | "section": "Performance" 27 | }, 28 | { 29 | "type": "build", 30 | "hidden": true 31 | }, 32 | { 33 | "type": "ci", 34 | "hidden": true 35 | }, 36 | { 37 | "type": "refactor", 38 | "section": "Refactoring" 39 | }, 40 | { 41 | "type": "revert", 42 | "hidden": true 43 | }, 44 | { 45 | "type": "style", 46 | "hidden": true 47 | }, 48 | { 49 | "type": "test", 50 | "hidden": true 51 | } 52 | ] 53 | } 54 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Changelog 2 | 3 | All notable changes to this project will be documented in this file. See [standard-version](https://github.com/conventional-changelog/standard-version) for commit guidelines. 4 | 5 | ## [0.15.0](https://github.com/keptn/examples/compare/0.14.0...0.15.0) (2022-05-06) 6 | 7 | ## [0.11.0](https://github.com/keptn/examples/compare/0.10.0...0.11.0) (2021-11-30) 8 | 9 | 10 | ### Features 11 | 12 | * adding quickstart resources ([#196](https://github.com/keptn/examples/issues/196)) ([43ed263](https://github.com/keptn/examples/commit/43ed263da79693462501aff90c9222a0a7a118be)) 13 | 14 | 15 | ### Bug Fixes 16 | 17 | * change SLO values for new podtatohead image ([#199](https://github.com/keptn/examples/issues/199)) ([40fbc4f](https://github.com/keptn/examples/commit/40fbc4ff4979068fba2f0d9482d92f8ea81bba7b)) 18 | * Set new release version of prometheus-service ([#198](https://github.com/keptn/examples/issues/198)) ([7e2106a](https://github.com/keptn/examples/commit/7e2106aa080d9a1867b4b20f76dff55d5b9550bf)) 19 | * Use elevated token for checkout during (pre)release ([#200](https://github.com/keptn/examples/issues/200)) ([3ce97e3](https://github.com/keptn/examples/commit/3ce97e3b26026be44c6801dabf62900e047c75db)) 20 | 21 | ## [0.10.0](https://github.com/keptn/examples/compare/0.9.0...0.10.0) (2021-10-04) 22 | 23 | 24 | ### Features 25 | 26 | * Update Dynatrace SLI definitions ([91b933a](https://github.com/keptn/examples/commit/91b933a5c5915790042f18f58381f4d2097e6666)) 27 | 28 | 29 | ### Bug Fixes 30 | 31 | * Remove v prefix in version tags for automated releases ([5a05bf0](https://github.com/keptn/examples/commit/5a05bf07524737af58c114c4fce5d057c70fdd6a)) 32 | 33 | ## [0.9.0](https://github.com/keptn/examples/compare/0.8.4...0.9.0) 34 | 35 | This release has been created to keep the version of the examples in sync with the Keptn release version. 36 | 37 | ## [0.8.4](https://github.com/keptn/examples/compare/0.8.3...0.8.4) 38 | 39 | ### Bug Fixes 40 | 41 | - Updated Istio Ingress API Version from `networking.k8s.io/v1beta1` to `networking.k8s.io/v1` #174 42 | 43 | ## [0.8.3](https://github.com/keptn/examples/compare/0.8.2...0.8.3) 44 | 45 | ### Features 46 | 47 | - Added SLO and shipyard files and for Dynatrace self healing example #165 48 | - Added time frame for evaluation of remediation action (part of [#4079](https://github.com/keptn/keptn/issues/4079)) 49 | 50 | ### Fixed issues 51 | 52 | - Improve output of `configure-istio.sh` script #157 53 | 54 | ## [0.8.2](https://github.com/keptn/examples/compare/0.8.1...0.8.2) 55 | 56 | This release has been created to keep the version of the examples in sync with the Keptn release version. 57 | 58 | ## [0.8.1](https://github.com/keptn/examples/compare/0.8.0...0.8.1) 59 | 60 | This release has been created to keep the version of the examples in sync with the Keptn release version. 61 | 62 | ## [0.8.0](https://github.com/keptn/examples/compare/0.7.3...0.8.0) 63 | 64 | Note: Update of License file [#151](https://github.com/keptn/examples/issues/151) 65 | 66 | ### Features 67 | 68 | - Update of Shipyard files according to new specification [#143](https://github.com/keptn/examples/issues/143) 69 | - Deleted obsolete installation scripts for Dynatrace OneAgent 70 | - Updated Istio configuration scripts 71 | 72 | ### Fixed issues 73 | 74 | - Fixed SLI Config for Prometheus [#152](https://github.com/keptn/examples/issues/152) 75 | 76 | 77 | ## [0.7.3](https://github.com/keptn/examples/compare/0.7.2...0.7.3) 78 | 79 | ### Bug Fixes 80 | 81 | - Set URL of Dynatrace OneAgent Operator in OpenShift script to: `release-0.8` instead of `master` 82 | 83 | ## 0.7.2 84 | 85 | ### Features 86 | 87 | - Added install script for the Dynatrace OneAgent on OpenShift 88 | -------------------------------------------------------------------------------- /CODEOWNERS: -------------------------------------------------------------------------------- 1 | ############################################################## 2 | # 3 | # List of approvers/reviewers for Keptn examples 4 | # 5 | ############################################################## 6 | # 7 | # Get in touch with us via the Keptn Community 8 | # https://github.com/keptn/community 9 | # 10 | # 11 | # Learn about CODEOWNERS file format: 12 | # https://help.github.com/en/articles/about-code-owners 13 | # 14 | 15 | # These owners will be the default owners for everything in the repo. 16 | * @AloisReitbauer @thisthat @oleg-nenashev 17 | unleash-server/* @jetzlstorfer @oleg-nenashev 18 | simplenodeservice/* @grabnerandi @oleg-nenashev 19 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Contributing Guide 2 | 3 | Please follow the contribution guidelines provided within the keptn/keptn repo: 4 | 5 | * https://github.com/keptn/keptn/blob/master/CODE_OF_CONDUCT.md 6 | * https://github.com/keptn/keptn/blob/master/CONTRIBUTING.md 7 | -------------------------------------------------------------------------------- /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 2019 The Keptn Authors 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 | # Keptn Examples 2 | 3 | ## Keptn V1 has reached end of life on December 22nd, 2023 and has been [replaced](https://github.com/keptn/lifecycle-toolkit). 4 | 5 | This directory contains and refers to examples in order to explore the functionality of Keptn. Please visit [keptn.sh](https://keptn.sh) for more information about Keptn. 6 | 7 | ## Maintained Examples 8 | 9 | Maintained examples are updated with every [Keptn release](https://github.com/keptn/examples/releases) to use the latest features, current guidelines and best practices, as well as to update command syntax, output, or changed prerequisites. 10 | 11 | 12 | 13 | ### Carts 14 | 15 | This example allows to demonstrate the [Keptn tutorials](https://tutorials.keptn.sh). 16 | 17 | You can find the source code of the carts microservice at https://github.com/keptn-sockshop/carts 18 | 19 | #### Load Generator for Carts 20 | 21 | The following commands will set up a basic load generator for the carts microservice that generates traffic in **all three stages**: 22 | 23 | * Basic (Background traffic) 24 | ```console 25 | kubectl apply -f https://raw.githubusercontent.com/keptn/examples/master/load-generation/cartsloadgen/deploy/cartsloadgen-base.yaml 26 | ``` 27 | * More traffic 28 | ```console 29 | kubectl apply -f https://raw.githubusercontent.com/keptn/examples/master/load-generation/cartsloadgen/deploy/cartsloadgen-fast.yaml 30 | ``` 31 | * Faulty item in cart (generates cpu usage) 32 | ```console 33 | kubectl apply -f https://raw.githubusercontent.com/keptn/examples/master/load-generation/cartsloadgen/deploy/cartsloadgen-faulty.yaml 34 | ``` 35 | 36 | ### Unleash 37 | 38 | This example allows to demonstrate the [Self-healing with Feature Flags tutorials](https://tutorials.keptn.sh). 39 | 40 | You can find the source of the unleash service at https://github.com/keptn-sockshop/unleash-server 41 | 42 | ### Simplenodeservice 43 | 44 | This example is used for some of the [Keptn tutorials](https://tutorials.keptn.sh). 45 | 46 | More information about this simple node.js based example application can be found here: [Simplenodeservice README](./simplenodeservice/README.md) 47 | 48 | ## License 49 | 50 | See [LICENSE](LICENSE). 51 | 52 | ## Contributing 53 | 54 | If you want to contribute, just create a PR on the master branch. 55 | 56 | Please also see [CONTRIBUTING.md](CONTRIBUTING.md) instructions on how to contribute. 57 | -------------------------------------------------------------------------------- /dynatrace-oneagent/deploy-dynatrace-oneagent-openshift.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | set -e 3 | 4 | if [ -z "$DT_TENANT" ]; then 5 | echo "Please supply a value for the environment variable DT_TENANT" 6 | exit 1 7 | fi 8 | 9 | if [ -z "$DT_API_TOKEN" ]; then 10 | echo "Please supply a value for the environment variable DT_API_TOKEN" 11 | exit 1 12 | fi 13 | 14 | if [ -z "$DT_PAAS_TOKEN" ]; then 15 | echo "Please supply a value for the environment variable DT_PAAS_TOKEN" 16 | exit 1 17 | fi 18 | 19 | function replace_value_in_yaml_file() { 20 | OLDVAL=$1; NEWVAL=$2; FILE=$3 21 | 22 | sed -i'.bak' -e "s#$OLDVAL#$NEWVAL#g" $FILE 23 | } 24 | 25 | function wait_for_deployment_in_namespace() { 26 | DEPLOYMENT=$1; NAMESPACE=$2; 27 | RETRY=0; RETRY_MAX=50; 28 | 29 | while [[ $RETRY -lt $RETRY_MAX ]]; do 30 | DEPLOYMENT_LIST=$(eval "kubectl get deployments -n ${NAMESPACE} | awk '/$DEPLOYMENT /'" | awk '{print $1}') # list of multiple deployments when starting with the same name 31 | if [[ -z "$DEPLOYMENT_LIST" ]]; then 32 | RETRY=$[$RETRY+1] 33 | echo "Retry: ${RETRY}/${RETRY_MAX} - Wait 10s for deployment ${DEPLOYMENT} in namespace ${NAMESPACE}" 34 | sleep 15 35 | else 36 | READY_REPLICAS=$(eval kubectl get deployments $DEPLOYMENT -n $NAMESPACE -o=jsonpath='{$.status.availableReplicas}') 37 | WANTED_REPLICAS=$(eval kubectl get deployments $DEPLOYMENT -n $NAMESPACE -o=jsonpath='{$.spec.replicas}') 38 | if [[ "$READY_REPLICAS" = "$WANTED_REPLICAS" ]]; then 39 | echo "Found deployment ${DEPLOYMENT} in namespace ${NAMESPACE}: ${DEPLOYMENT_LIST}" 40 | break 41 | else 42 | RETRY=$[$RETRY+1] 43 | echo "Retry: ${RETRY}/${RETRY_MAX} - Wait 15s for deployment ${DEPLOYMENT} in namespace ${NAMESPACE}" 44 | sleep 15 45 | fi 46 | fi 47 | done 48 | 49 | if [[ $RETRY == $RETRY_MAX ]]; then 50 | echo "Error: Could not find deployment ${DEPLOYMENT} in namespace ${NAMESPACE}" 51 | exit 1 52 | fi 53 | } 54 | 55 | function wait_for_daemonset_in_namespace() { 56 | DAEMONSET=$1; NAMESPACE=$2; 57 | RETRY=0; RETRY_MAX=50; 58 | 59 | while [[ $RETRY -lt $RETRY_MAX ]]; do 60 | DAEMONSET_LIST=$(eval "kubectl get daemonset -n ${NAMESPACE} | awk '/$DAEMONSET /'" | awk '{print $1}') 61 | if [[ -z "$DAEMONSET_LIST" ]]; then 62 | RETRY=$[$RETRY+1] 63 | echo "Retry: ${RETRY}/${RETRY_MAX} - Wait 15s for daemonset ${DAEMONSET} in namespace ${NAMESPACE}" 64 | sleep 15 65 | else 66 | READY_REPLICAS=$(eval kubectl get daemonset $DAEMONSET -n $NAMESPACE -o=jsonpath='{$.status.desiredNumberScheduled}') 67 | WANTED_REPLICAS=$(eval kubectl get daemonset $DAEMONSET -n $NAMESPACE -o=jsonpath='{$.status.numberAvailable}') 68 | if [[ "$READY_REPLICAS" = "$WANTED_REPLICAS" ]]; then 69 | echo "Found daemonset ${DAEMONSET} in namespace ${NAMESPACE}: ${DAEMONSET_LIST}" 70 | break 71 | else 72 | RETRY=$[$RETRY+1] 73 | echo "Retry: ${RETRY}/${RETRY_MAX} - Wait 15s for daemonset ${DAEMONSET} in namespace ${NAMESPACE}" 74 | sleep 15 75 | fi 76 | fi 77 | done 78 | 79 | if [[ $RETRY == $RETRY_MAX ]]; then 80 | echo "Error: Could not find daemonset ${DAEMONSET} in namespace ${NAMESPACE}" 81 | exit 1 82 | fi 83 | } 84 | 85 | 86 | kubectl create namespace dynatrace 87 | sleep 5 88 | 89 | kubectl apply -f https://github.com/Dynatrace/dynatrace-oneagent-operator/releases/download/v0.8.2/openshift.yaml 90 | 91 | echo "Waiting a little bit before we continue..." 92 | sleep 10 93 | echo "Continuing now!" 94 | 95 | kubectl -n dynatrace create secret generic oneagent --from-literal="apiToken=$DT_API_TOKEN" --from-literal="paasToken=$DT_PAAS_TOKEN" 96 | 97 | curl -o cr.yaml https://raw.githubusercontent.com/Dynatrace/dynatrace-oneagent-operator/release-0.8/deploy/cr.yaml 98 | 99 | URL=https://ENVIRONMENTID.live.dynatrace.com/api 100 | API_URL=https://${DT_TENANT}/api 101 | replace_value_in_yaml_file $URL $API_URL cr.yaml 102 | 103 | kubectl apply -f cr.yaml 104 | 105 | echo "Verifying Dynatrace oneagent installation" 106 | wait_for_deployment_in_namespace "dynatrace-oneagent-operator" "dynatrace" 107 | wait_for_deployment_in_namespace "dynatrace-oneagent-webhook" "dynatrace" 108 | wait_for_daemonset_in_namespace "oneagent" "dynatrace" 109 | 110 | rm cr.yaml -------------------------------------------------------------------------------- /dynatrace-oneagent/deploy-dynatrace-oneagent.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | set -e 3 | 4 | if [ -z "$DT_TENANT" ]; then 5 | echo "Please supply a value for the environment variable DT_TENANT" 6 | exit 1 7 | fi 8 | 9 | if [ -z "$DT_API_TOKEN" ]; then 10 | echo "Please supply a value for the environment variable DT_API_TOKEN" 11 | exit 1 12 | fi 13 | 14 | if [ -z "$DT_PAAS_TOKEN" ]; then 15 | echo "Please supply a value for the environment variable DT_PAAS_TOKEN" 16 | exit 1 17 | fi 18 | 19 | function replace_value_in_yaml_file() { 20 | OLDVAL=$1; NEWVAL=$2; FILE=$3 21 | 22 | sed -i'.bak' -e "s#$OLDVAL#$NEWVAL#g" $FILE 23 | } 24 | 25 | function wait_for_deployment_in_namespace() { 26 | DEPLOYMENT=$1; NAMESPACE=$2; 27 | RETRY=0; RETRY_MAX=50; 28 | 29 | while [[ $RETRY -lt $RETRY_MAX ]]; do 30 | DEPLOYMENT_LIST=$(eval "kubectl get deployments -n ${NAMESPACE} | awk '/$DEPLOYMENT /'" | awk '{print $1}') # list of multiple deployments when starting with the same name 31 | if [[ -z "$DEPLOYMENT_LIST" ]]; then 32 | RETRY=$[$RETRY+1] 33 | echo "Retry: ${RETRY}/${RETRY_MAX} - Wait 10s for deployment ${DEPLOYMENT} in namespace ${NAMESPACE}" 34 | sleep 15 35 | else 36 | READY_REPLICAS=$(eval kubectl get deployments $DEPLOYMENT -n $NAMESPACE -o=jsonpath='{$.status.availableReplicas}') 37 | WANTED_REPLICAS=$(eval kubectl get deployments $DEPLOYMENT -n $NAMESPACE -o=jsonpath='{$.spec.replicas}') 38 | if [[ "$READY_REPLICAS" = "$WANTED_REPLICAS" ]]; then 39 | echo "Found deployment ${DEPLOYMENT} in namespace ${NAMESPACE}: ${DEPLOYMENT_LIST}" 40 | break 41 | else 42 | RETRY=$[$RETRY+1] 43 | echo "Retry: ${RETRY}/${RETRY_MAX} - Wait 15s for deployment ${DEPLOYMENT} in namespace ${NAMESPACE}" 44 | sleep 15 45 | fi 46 | fi 47 | done 48 | 49 | if [[ $RETRY == $RETRY_MAX ]]; then 50 | echo "Error: Could not find deployment ${DEPLOYMENT} in namespace ${NAMESPACE}" 51 | exit 1 52 | fi 53 | } 54 | 55 | function wait_for_daemonset_in_namespace() { 56 | DAEMONSET=$1; NAMESPACE=$2; 57 | RETRY=0; RETRY_MAX=50; 58 | 59 | while [[ $RETRY -lt $RETRY_MAX ]]; do 60 | DAEMONSET_LIST=$(eval "kubectl get daemonset -n ${NAMESPACE} | awk '/$DAEMONSET /'" | awk '{print $1}') 61 | if [[ -z "$DAEMONSET_LIST" ]]; then 62 | RETRY=$[$RETRY+1] 63 | echo "Retry: ${RETRY}/${RETRY_MAX} - Wait 15s for daemonset ${DAEMONSET} in namespace ${NAMESPACE}" 64 | sleep 15 65 | else 66 | READY_REPLICAS=$(eval kubectl get daemonset $DAEMONSET -n $NAMESPACE -o=jsonpath='{$.status.desiredNumberScheduled}') 67 | WANTED_REPLICAS=$(eval kubectl get daemonset $DAEMONSET -n $NAMESPACE -o=jsonpath='{$.status.numberAvailable}') 68 | if [[ "$READY_REPLICAS" = "$WANTED_REPLICAS" ]]; then 69 | echo "Found daemonset ${DAEMONSET} in namespace ${NAMESPACE}: ${DAEMONSET_LIST}" 70 | break 71 | else 72 | RETRY=$[$RETRY+1] 73 | echo "Retry: ${RETRY}/${RETRY_MAX} - Wait 15s for daemonset ${DAEMONSET} in namespace ${NAMESPACE}" 74 | sleep 15 75 | fi 76 | fi 77 | done 78 | 79 | if [[ $RETRY == $RETRY_MAX ]]; then 80 | echo "Error: Could not find daemonset ${DAEMONSET} in namespace ${NAMESPACE}" 81 | exit 1 82 | fi 83 | } 84 | 85 | 86 | kubectl create namespace dynatrace 87 | sleep 5 88 | 89 | kubectl apply -f https://github.com/Dynatrace/dynatrace-oneagent-operator/releases/latest/download/kubernetes.yaml 90 | 91 | echo "Waiting a little bit before we continue..." 92 | sleep 10 93 | echo "Continuing now!" 94 | 95 | kubectl -n dynatrace create secret generic oneagent --from-literal="apiToken=$DT_API_TOKEN" --from-literal="paasToken=$DT_PAAS_TOKEN" 96 | 97 | curl -o cr.yaml https://raw.githubusercontent.com/Dynatrace/dynatrace-oneagent-operator/master/deploy/cr.yaml 98 | 99 | URL=https://ENVIRONMENTID.live.dynatrace.com/api 100 | API_URL=https://${DT_TENANT}/api 101 | replace_value_in_yaml_file $URL $API_URL cr.yaml 102 | 103 | kubectl apply -f cr.yaml 104 | 105 | echo "Verifying Dynatrace oneagent installation" 106 | wait_for_deployment_in_namespace "dynatrace-oneagent-operator" "dynatrace" 107 | wait_for_deployment_in_namespace "dynatrace-oneagent-webhook" "dynatrace" 108 | wait_for_daemonset_in_namespace "oneagent" "dynatrace" 109 | 110 | rm cr.yaml -------------------------------------------------------------------------------- /gh-actions-scripts/post-changelog-actions.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | echo "Removing sign-off messages from changelog..." 4 | for file in {CHANGELOG,RELEASE-BODY}.md; do 5 | if [ -f "$file" ]; then 6 | echo "Replacing content in $file" 7 | # Reference: https://stackoverflow.com/a/1252191 8 | sed -e ':a' -e 'N' -e '$!ba' -e 's/\nSigned-off-by: .* <.*@.*>\n/ /g' "$file" > tmp 9 | mv tmp "$file" 10 | else 11 | echo "Not replacing anything since $file does not exist." 12 | fi 13 | done 14 | -------------------------------------------------------------------------------- /istio-configuration/configure-istio.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | set -e 3 | 4 | echo "Configure Istio and Keptn" 5 | 6 | # Get Ingress gateway IP-Address 7 | export INGRESS_IP=$(kubectl -n istio-system get svc istio-ingressgateway -o jsonpath='{.status.loadBalancer.ingress[0].ip}') 8 | 9 | # Check if IP-Address is not empty or pending 10 | if [ -z "$INGRESS_IP" ] || [ "$INGRESS_IP" = "Pending" ] ; then 11 | echo "Could not determine the external IP address of istio-ingressgateway in namespace istio-system. Please make sure it is ready and has an external IP address:" 12 | echo " - kubectl -n istio-system get svc istio-ingressgateway" 13 | echo "" 14 | echo "Please consult the istio docs for more information: https://istio.io/latest/docs/tasks/traffic-management/ingress/ingress-control/#determining-the-ingress-ip-and-ports" 15 | exit 1 16 | fi 17 | 18 | echo "External IP for istio-ingressgateway is ${INGRESS_IP}, creating configmaps..." 19 | 20 | K8S_VERSION=$(kubectl version -ojson) 21 | K8S_VERSION_MINOR=$(echo "$K8S_VERSION" | grep 'minor' | tail -1 | sed 's/^.*: //' | sed 's/^"\(.*\)".*/\1/') 22 | 23 | if [[ "$K8S_VERSION_MINOR" < "19" ]] 24 | then 25 | echo "Detected Kubernetes version < 1.19" 26 | # Applying ingress-manifest 27 | kubectl apply -f - <= 3 { 40 | problem := os.Args[2] 41 | if problem == "cpu" { 42 | item = faultyitemid 43 | } 44 | } 45 | 46 | var numberOfThreads int 47 | numberOfThreads = 1 48 | if len(os.Args) >= 4 { 49 | numberOfThreadsInt64, err := strconv.ParseInt(os.Args[3], 10, 64) 50 | if err != nil { 51 | numberOfThreads = 1 52 | } else { 53 | numberOfThreads = int(numberOfThreadsInt64) 54 | } 55 | } 56 | 57 | itemData := map[string]interface{}{ 58 | "itemId": item, 59 | "unitPrice": "99.99", 60 | } 61 | b, _ := json.Marshal(itemData) 62 | url = url + "/carts/1/items" 63 | 64 | fmt.Println("Exit program with CTRL+C") 65 | fmt.Println() 66 | 67 | tr := &http.Transport{ 68 | DialContext: resolveXipIoWithContext, 69 | } 70 | c := &http.Client{Timeout: 3 * time.Second, Transport: tr} 71 | 72 | wg.Add(numberOfThreads) 73 | for i := 0; i < numberOfThreads; i++ { 74 | go doRequests(url, b, c) 75 | fmt.Println("Created new Thread") 76 | } 77 | wg.Wait() 78 | 79 | } 80 | 81 | func doRequests(url string, b []byte, c *http.Client) { 82 | defer wg.Done() 83 | for true { 84 | req, err := http.NewRequest("POST", url, bytes.NewBuffer(b)) 85 | if err != nil { 86 | log.Fatalln(err) 87 | } 88 | req.Header.Set("X-Custom-Header", "myvalue") 89 | req.Header.Set("Content-Type", "application/json") 90 | 91 | resp, err := c.Do(req) 92 | 93 | if err != nil { 94 | continue 95 | } 96 | 97 | if resp.StatusCode == 201 { 98 | var result map[string]interface{} 99 | json.NewDecoder(resp.Body).Decode(&result) 100 | log.Println(result) 101 | } 102 | resp.Body.Close() 103 | } 104 | } 105 | 106 | // resolveXipIo resolves a xip io address 107 | func resolveXipIoWithContext(ctx context.Context, network, addr string) (net.Conn, error) { 108 | dialer := &net.Dialer{ 109 | DualStack: true, 110 | } 111 | 112 | if strings.Contains(addr, ".xip.io") { 113 | 114 | regex := `\b(?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.){3}(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?).xip.io\b` 115 | re := regexp.MustCompile(regex) 116 | ipWithXipIo := re.FindString(addr) 117 | ip := ipWithXipIo[:len(ipWithXipIo)-len(".xip.io")] 118 | 119 | regex = `:\d+$` 120 | re = regexp.MustCompile(regex) 121 | port := re.FindString(addr) 122 | 123 | var newAddr string 124 | if port != "" { 125 | newAddr = ip + port 126 | } 127 | addr = newAddr 128 | } 129 | return dialer.DialContext(ctx, network, addr) 130 | } 131 | -------------------------------------------------------------------------------- /onboarding-carts/argo/carts.tgz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/keptn/examples/9ff28ef074d0ab7580aec06706994bb99a08b27f/onboarding-carts/argo/carts.tgz -------------------------------------------------------------------------------- /onboarding-carts/argo/carts/Chart.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | description: A Helm chart for service carts 3 | name: carts 4 | version: 0.1.0 5 | -------------------------------------------------------------------------------- /onboarding-carts/argo/carts/templates/NOTES.txt: -------------------------------------------------------------------------------- 1 | 1. Get the application URL by running these commands: 2 | {{- if .Values.ingress.enabled }} 3 | {{- range .Values.ingress.hosts }} 4 | http{{ if $.Values.ingress.tls }}s{{ end }}://{{ . }}{{ $.Values.ingress.path }} 5 | {{- end }} 6 | {{- else if contains "NodePort" .Values.service.type }} 7 | export NODE_PORT=$(kubectl get --namespace {{ .Release.Namespace }} -o jsonpath="{.spec.ports[0].nodePort}" services {{ template "carts.fullname" . }}) 8 | export NODE_IP=$(kubectl get nodes --namespace {{ .Release.Namespace }} -o jsonpath="{.items[0].status.addresses[0].address}") 9 | echo http://$NODE_IP:$NODE_PORT 10 | {{- else if contains "LoadBalancer" .Values.service.type }} 11 | NOTE: It may take a few minutes for the LoadBalancer IP to be available. 12 | You can watch the status of by running 'kubectl get svc -w {{ template "carts.fullname" . }}' 13 | export SERVICE_IP=$(kubectl get svc --namespace {{ .Release.Namespace }} {{ template "carts.fullname" . }} -o jsonpath='{.status.loadBalancer.ingress[0].ip}') 14 | echo http://$SERVICE_IP:{{ .Values.service.port }} 15 | {{- else if contains "ClusterIP" .Values.service.type }} 16 | export POD_NAME=$(kubectl get pods --namespace {{ .Release.Namespace }} -l "app={{ template "carts.name" . }},release={{ .Release.Name }}" -o jsonpath="{.items[0].metadata.name}") 17 | echo "Visit http://127.0.0.1:8080 to use your application" 18 | kubectl port-forward $POD_NAME 8080:80 19 | {{- end }} 20 | -------------------------------------------------------------------------------- /onboarding-carts/argo/carts/templates/_helpers.tpl: -------------------------------------------------------------------------------- 1 | {{/* vim: set filetype=mustache: */}} 2 | {{/* 3 | Expand the name of the chart. 4 | */}} 5 | {{- define "carts.name" -}} 6 | {{- default .Chart.Name .Values.nameOverride | trunc 63 | trimSuffix "-" -}} 7 | {{- end -}} 8 | 9 | {{/* 10 | Create a default fully qualified app name. 11 | We truncate at 63 chars because some Kubernetes name fields are limited to this (by the DNS naming spec). 12 | If release name contains chart name it will be used as a full name. 13 | */}} 14 | {{- define "carts.fullname" -}} 15 | {{- if .Values.fullnameOverride -}} 16 | {{- .Values.fullnameOverride | trunc 63 | trimSuffix "-" -}} 17 | {{- else -}} 18 | {{- $name := default .Chart.Name .Values.nameOverride -}} 19 | {{- if contains $name .Release.Name -}} 20 | {{- .Release.Name | trunc 63 | trimSuffix "-" -}} 21 | {{- else -}} 22 | {{- printf "%s-%s" .Release.Name $name | trunc 63 | trimSuffix "-" -}} 23 | {{- end -}} 24 | {{- end -}} 25 | {{- end -}} 26 | 27 | {{/* 28 | Create chart name and version as used by the chart label. 29 | */}} 30 | {{- define "carts.chart" -}} 31 | {{- printf "%s-%s" .Chart.Name .Chart.Version | replace "+" "_" | trunc 63 | trimSuffix "-" -}} 32 | {{- end -}} 33 | -------------------------------------------------------------------------------- /onboarding-carts/argo/carts/templates/carts-db-deployment.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | apiVersion: v1 3 | kind: PersistentVolumeClaim 4 | metadata: 5 | name: carts-db-mongodata 6 | spec: 7 | accessModes: 8 | - ReadWriteOnce 9 | resources: 10 | requests: 11 | storage: 100Mi 12 | status: {} 13 | --- 14 | apiVersion: apps/v1 15 | kind: Deployment 16 | metadata: 17 | name: carts-db 18 | labels: 19 | app: carts-db 20 | spec: 21 | replicas: 1 22 | strategy: 23 | type: Recreate 24 | selector: 25 | matchLabels: 26 | app: carts-db 27 | template: 28 | metadata: 29 | labels: 30 | app: carts-db 31 | deployment: carts-db 32 | spec: 33 | containers: 34 | - name: carts-db 35 | image: mongo 36 | imagePullPolicy: IfNotPresent 37 | ports: 38 | - containerPort: 27017 39 | resources: {} 40 | volumeMounts: 41 | - mountPath: /data/db 42 | name: carts-db-mongodata 43 | restartPolicy: Always 44 | volumes: 45 | - name: carts-db-mongodata 46 | persistentVolumeClaim: 47 | claimName: carts-db-mongodata 48 | -------------------------------------------------------------------------------- /onboarding-carts/argo/carts/templates/carts-db-service.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | apiVersion: v1 3 | kind: Service 4 | metadata: 5 | name: carts-db 6 | spec: 7 | ports: 8 | - name: carts-db 9 | port: 27017 10 | targetPort: 27017 11 | selector: 12 | app: carts-db -------------------------------------------------------------------------------- /onboarding-carts/argo/carts/templates/keptn-hook.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: batch/v1 2 | kind: Job 3 | metadata: 4 | generateName: app-keptn-notification- 5 | annotations: 6 | argocd.argoproj.io/hook: Sync 7 | argocd.argoproj.io/hook-delete-policy: HookSucceeded 8 | spec: 9 | template: 10 | spec: 11 | containers: 12 | - name: keptn-notification 13 | image: agrimmer/alpine-curl-uuid-kubectl:latest 14 | command: ["/bin/sh","-c"] 15 | args: ['while [[ $(kubectl get rollout {{ .Values.keptn.service }}-{{ .Values.keptn.stage }} -n {{ .Values.keptn.project }}-{{ .Values.keptn.stage }} -o "jsonpath={..status.conditions[?(@.type==\"Progressing\")].reason}") == "ReplicaSetUpdated" ]]; do echo "waiting for rollout" && sleep 1; done; UUID=$(uuidgen); now=$(TZ=UTC date "+%FT%T.00Z"); curl -X POST -H "Content-Type: application/cloudevents+json" -H "x-token: ${KEPTN_API_TOKEN}" --insecure -d "{\"contenttype\": \"application/json\", \"data\": { \"project\": \"{{ .Values.keptn.project }}\", \"service\": \"{{ .Values.keptn.service }}\", \"stage\": \"{{ .Values.keptn.stage }}\", \"deploymentURILocal\": \"http://{{ .Values.keptn.service }}-canary.{{ .Values.keptn.project }}-{{ .Values.keptn.stage }}\", \"deploymentstrategy\": \"blue_green_service\", \"teststrategy\": \"performance\"}, \"id\": \"${UUID}\", \"source\": \"argo\", \"specversion\": \"0.2\", \"time\": \"${now}\", \"type\": \"sh.keptn.events.deployment-finished\", \"shkeptncontext\": \"${UUID}\"}" ${KEPTN_API_URL}/v1/event'] 16 | env: 17 | - name: KEPTN_API_URL 18 | valueFrom: 19 | secretKeyRef: 20 | name: argo 21 | key: KEPTN_API_URL 22 | - name: KEPTN_API_TOKEN 23 | valueFrom: 24 | secretKeyRef: 25 | name: argo 26 | key: KEPTN_API_TOKEN 27 | restartPolicy: Never 28 | backoffLimit: 2 -------------------------------------------------------------------------------- /onboarding-carts/argo/carts/templates/rollout.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: argoproj.io/v1alpha1 2 | kind: Rollout 3 | metadata: 4 | name: {{ template "carts.fullname" . }} 5 | labels: 6 | app: {{ template "carts.name" . }} 7 | chart: {{ template "carts.chart" . }} 8 | release: {{ .Release.Name }} 9 | heritage: {{ .Release.Service }} 10 | spec: 11 | replicas: {{ .Values.replicaCount }} 12 | revisionHistoryLimit: 3 13 | selector: 14 | matchLabels: 15 | app: {{ template "carts.name" . }} 16 | release: {{ .Release.Name }} 17 | strategy: 18 | blueGreen: 19 | autoPromotionEnabled: false 20 | activeService: {{ template "carts.name" . }}-primary 21 | previewService: {{ template "carts.name" . }}-canary 22 | template: 23 | metadata: 24 | labels: 25 | app: {{ template "carts.name" . }} 26 | release: {{ .Release.Name }} 27 | spec: 28 | containers: 29 | - name: {{ .Chart.Name }} 30 | image: "{{ .Values.image.repository }}:{{ .Values.image.tag }}" 31 | imagePullPolicy: {{ .Values.image.pullPolicy }} 32 | ports: 33 | - name: http 34 | containerPort: 8080 35 | protocol: TCP 36 | env: 37 | - name: DT_CUSTOM_PROP 38 | value: "keptn_project={{ .Values.keptn.project }} keptn_service={{ .Values.keptn.service }} keptn_stage={{ .Values.keptn.stage }}" 39 | - name: POD_NAME 40 | valueFrom: 41 | fieldRef: 42 | fieldPath: "metadata.name" 43 | - name: DEPLOYMENT_NAME 44 | valueFrom: 45 | fieldRef: 46 | fieldPath: "metadata.labels['deployment']" 47 | - name: CONTAINER_IMAGE 48 | value: "{{ .Values.image }}" 49 | - name: KEPTN_PROJECT 50 | value: "{{ .Values.keptn.project }}" 51 | - name: KEPTN_STAGE 52 | value: "{{ .Values.keptn.stage }}" 53 | - name: KEPTN_SERVICE 54 | value: "{{ .Values.keptn.service }}" 55 | livenessProbe: 56 | httpGet: 57 | path: /health 58 | port: 8080 59 | initialDelaySeconds: 60 60 | periodSeconds: 10 61 | timeoutSeconds: 15 62 | readinessProbe: 63 | httpGet: 64 | path: /health 65 | port: 8080 66 | initialDelaySeconds: 60 67 | periodSeconds: 10 68 | timeoutSeconds: 15 69 | resources: 70 | limits: 71 | cpu: 1000m 72 | memory: 2048Mi 73 | requests: 74 | cpu: 500m 75 | memory: 1024Mi 76 | -------------------------------------------------------------------------------- /onboarding-carts/argo/carts/templates/service.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | apiVersion: v1 3 | kind: Service 4 | metadata: 5 | name: {{ template "carts.name" . }}-primary 6 | labels: 7 | app: {{ template "carts.name" . }} 8 | chart: {{ template "carts.chart" . }} 9 | release: {{ .Release.Name }} 10 | heritage: {{ .Release.Service }} 11 | spec: 12 | type: {{ .Values.service.type }} 13 | ports: 14 | - port: {{ .Values.service.port }} 15 | targetPort: 8080 16 | protocol: TCP 17 | name: http 18 | selector: 19 | app: {{ template "carts.name" . }} 20 | release: {{ .Release.Name }} 21 | --- 22 | apiVersion: v1 23 | kind: Service 24 | metadata: 25 | name: {{ template "carts.name" . }}-canary 26 | labels: 27 | app: {{ template "carts.name" . }} 28 | chart: {{ template "carts.chart" . }} 29 | release: {{ .Release.Name }} 30 | heritage: {{ .Release.Service }} 31 | spec: 32 | type: {{ .Values.service.type }} 33 | ports: 34 | - port: {{ .Values.service.port }} 35 | targetPort: 8080 36 | protocol: TCP 37 | name: http 38 | selector: 39 | app: {{ template "carts.name" . }} 40 | release: {{ .Release.Name }} 41 | -------------------------------------------------------------------------------- /onboarding-carts/argo/carts/values.yaml: -------------------------------------------------------------------------------- 1 | replicaCount: 1 2 | 3 | image: 4 | repository: docker.io/keptnexamples/carts 5 | tag: 0.13.1 6 | pullPolicy: Always 7 | 8 | service: 9 | type: LoadBalancer 10 | port: 80 11 | 12 | ingress: 13 | enabled: false 14 | annotations: {} 15 | # kubernetes.io/ingress.class: nginx 16 | # kubernetes.io/tls-acme: "true" 17 | path: / 18 | hosts: 19 | - chart-example.local 20 | tls: [] 21 | # - secretName: chart-example-tls 22 | # hosts: 23 | # - chart-example.local 24 | 25 | keptn: 26 | project: sockshop 27 | stage: production 28 | service: carts -------------------------------------------------------------------------------- /onboarding-carts/carts-db.tgz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/keptn/examples/9ff28ef074d0ab7580aec06706994bb99a08b27f/onboarding-carts/carts-db.tgz -------------------------------------------------------------------------------- /onboarding-carts/carts-db/Chart.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | description: A Helm chart for service carts-db 3 | name: carts-db 4 | version: 0.1.0 5 | -------------------------------------------------------------------------------- /onboarding-carts/carts-db/templates/carts-db-deployment.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | apiVersion: v1 3 | kind: PersistentVolumeClaim 4 | metadata: 5 | name: carts-db-mongodata 6 | spec: 7 | accessModes: 8 | - ReadWriteOnce 9 | resources: 10 | requests: 11 | storage: 1Gi 12 | status: {} 13 | --- 14 | apiVersion: apps/v1 15 | kind: Deployment 16 | metadata: 17 | name: carts-db 18 | labels: 19 | app: carts-db 20 | spec: 21 | replicas: {{ .Values.replicaCount }} 22 | strategy: 23 | type: Recreate 24 | selector: 25 | matchLabels: 26 | app: carts-db 27 | template: 28 | metadata: 29 | labels: 30 | app: carts-db 31 | deployment: carts-db 32 | app.kubernetes.io/name: {{ .Values.keptn.service }} 33 | app.kubernetes.io/instance: "{{ .Values.keptn.service }}-{{ .Values.keptn.deployment }}" 34 | app.kubernetes.io/component: database 35 | app.kubernetes.io/part-of: "{{ .Values.keptn.project }}" 36 | app.kubernetes.io/managed-by: Keptn 37 | app.kubernetes.io/version: {{ (split ":" .Values.image)._1 | default "latest" }} 38 | spec: 39 | containers: 40 | - name: carts-db 41 | image: {{ .Values.image }} 42 | imagePullPolicy: IfNotPresent 43 | ports: 44 | - containerPort: 27017 45 | resources: {} 46 | volumeMounts: 47 | - mountPath: /data/db 48 | name: carts-db-mongodata 49 | restartPolicy: Always 50 | volumes: 51 | - name: carts-db-mongodata 52 | persistentVolumeClaim: 53 | claimName: carts-db-mongodata 54 | -------------------------------------------------------------------------------- /onboarding-carts/carts-db/templates/carts-db-service.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | apiVersion: v1 3 | kind: Service 4 | metadata: 5 | name: carts-db 6 | spec: 7 | ports: 8 | - name: {{ .Values.service.name }} 9 | port: {{ .Values.service.externalPort }} 10 | targetPort: {{ .Values.service.internalPort }} 11 | selector: 12 | app: carts-db -------------------------------------------------------------------------------- /onboarding-carts/carts-db/values.yaml: -------------------------------------------------------------------------------- 1 | image: mongo 2 | service: 3 | externalPort: 27017 4 | internalPort: 27017 5 | name: carts-db 6 | replicaCount: 1 7 | -------------------------------------------------------------------------------- /onboarding-carts/carts.tgz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/keptn/examples/9ff28ef074d0ab7580aec06706994bb99a08b27f/onboarding-carts/carts.tgz -------------------------------------------------------------------------------- /onboarding-carts/carts/Chart.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | description: A Helm chart for service carts 3 | name: carts 4 | version: 0.1.0 5 | -------------------------------------------------------------------------------- /onboarding-carts/carts/templates/deployment.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | apiVersion: apps/v1 3 | kind: Deployment 4 | metadata: 5 | name: carts 6 | spec: 7 | replicas: {{ .Values.replicaCount }} 8 | strategy: 9 | rollingUpdate: 10 | maxUnavailable: 0 11 | type: RollingUpdate 12 | selector: 13 | matchLabels: 14 | app: carts 15 | template: 16 | metadata: 17 | labels: 18 | app: carts 19 | app.kubernetes.io/name: {{ .Values.keptn.service }} 20 | app.kubernetes.io/instance: "{{ .Values.keptn.service }}-{{ .Values.keptn.deployment }}" 21 | app.kubernetes.io/component: api 22 | app.kubernetes.io/part-of: "{{ .Values.keptn.project }}" 23 | app.kubernetes.io/managed-by: Keptn 24 | app.kubernetes.io/version: {{ (split ":" .Values.image)._1 | default "latest" }} 25 | spec: 26 | containers: 27 | - name: carts 28 | image: "{{ .Values.image }}" 29 | imagePullPolicy: IfNotPresent 30 | ports: 31 | - name: http 32 | protocol: TCP 33 | containerPort: 8080 34 | env: 35 | - name: DT_CUSTOM_PROP 36 | value: "version={{ .Chart.Version }} revision={{ .Release.Revision }} releasename={{ .Release.Name }} keptn_project={{ .Values.keptn.project }} keptn_service={{ .Values.keptn.service }} keptn_stage={{ .Values.keptn.stage }} keptn_deployment={{ .Values.keptn.deployment }}" 37 | - name: POD_NAME 38 | valueFrom: 39 | fieldRef: 40 | fieldPath: "metadata.name" 41 | - name: DEPLOYMENT_NAME 42 | valueFrom: 43 | fieldRef: 44 | fieldPath: "metadata.labels['deployment']" 45 | - name: CONTAINER_IMAGE 46 | value: "{{ .Values.image }}" 47 | - name: KEPTN_PROJECT 48 | value: "{{ .Chart.Name }}" 49 | - name: KEPTN_STAGE 50 | valueFrom: 51 | fieldRef: 52 | fieldPath: "metadata.namespace" 53 | - name: KEPTN_SERVICE 54 | value: "carts" 55 | - name: UNLEASH_SERVER_URL 56 | value: "http://unleash.unleash-dev/api" 57 | livenessProbe: 58 | httpGet: 59 | path: /health 60 | port: 8080 61 | initialDelaySeconds: 60 62 | periodSeconds: 10 63 | timeoutSeconds: 15 64 | readinessProbe: 65 | httpGet: 66 | path: /health 67 | port: 8080 68 | initialDelaySeconds: 60 69 | periodSeconds: 10 70 | timeoutSeconds: 15 71 | resources: 72 | limits: 73 | cpu: 1000m 74 | memory: 2048Mi 75 | requests: 76 | cpu: 500m 77 | memory: 1024Mi 78 | -------------------------------------------------------------------------------- /onboarding-carts/carts/templates/service.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | apiVersion: v1 3 | kind: Service 4 | metadata: 5 | name: carts 6 | spec: 7 | type: ClusterIP 8 | ports: 9 | - name: http 10 | port: 80 11 | protocol: TCP 12 | targetPort: 8080 13 | selector: 14 | app: carts 15 | -------------------------------------------------------------------------------- /onboarding-carts/carts/values.yaml: -------------------------------------------------------------------------------- 1 | image: docker.io/keptnexamples/carts:0.13.1 2 | replicaCount: 1 -------------------------------------------------------------------------------- /onboarding-carts/dynatrace/dynatrace.conf.yaml: -------------------------------------------------------------------------------- 1 | spec_version: '0.1.0' 2 | dtCreds: dynatrace -------------------------------------------------------------------------------- /onboarding-carts/jmeter/basiccheck.jmx: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | false 7 | false 8 | 9 | 10 | 11 | SERVER_URL 12 | carts.sockshop-staging.public.demo.keptn.sh 13 | = 14 | 15 | 16 | CHECK_PATH 17 | / 18 | = 19 | 20 | 21 | DT_LTN 22 | Default 23 | = 24 | 25 | 26 | DefaultThinkTime 27 | 250 28 | = 29 | 30 | 31 | SERVER_PORT 32 | 80 33 | = 34 | 35 | 36 | PROTOCOL 37 | http 38 | = 39 | 40 | 41 | VUCount 42 | 1 43 | = 44 | 45 | 46 | LoopCount 47 | 1 48 | = 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | continue 57 | 58 | false 59 | ${__P(LoopCount,${VUCount})} 60 | 61 | ${__P(VUCount,${VUCount})} 62 | 1 63 | 1444323045000 64 | 1444323045000 65 | false 66 | 67 | 68 | 69 | 70 | 71 | 72 | false 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | true 81 | 82 | 83 | import org.apache.jmeter.util.JMeterUtils; 84 | import org.apache.jmeter.protocol.http.control.HeaderManager; 85 | import java.io; 86 | import java.util; 87 | 88 | // ------------------------------------------------------------------------------------- 89 | // Generate the x-dynatrace-test header based on this best practic 90 | // -> https://www.dynatrace.com/support/help/integrations/test-automation-frameworks/how-do-i-integrate-dynatrace-into-my-load-testing-process/ 91 | // ------------------------------------------------------------------------------------- 92 | String LTN=JMeterUtils.getProperty("DT_LTN"); 93 | if((LTN == null) || (LTN.length() == 0)) { 94 | if(vars != null) { 95 | LTN = vars.get("DT_LTN"); 96 | } 97 | } 98 | if(LTN == null) LTN = "NoTestName"; 99 | 100 | String LSN = (bsh.args.length > 0) ? bsh.args[0] : "Test Scenario"; 101 | String TSN = sampler.getName(); 102 | String VU = ctx.getThreadGroup().getName() + ctx.getThreadNum(); 103 | String headerValue = "LSN="+ LSN + ";TSN=" + TSN + ";LTN=" + LTN + ";VU=" + VU + ";"; 104 | 105 | // ------------------------------------------- 106 | // Set header 107 | // ------------------------------------------- 108 | HeaderManager hm = sampler.getHeaderManager(); 109 | hm.removeHeaderNamed("x-dynatrace-test"); 110 | hm.add(new org.apache.jmeter.protocol.http.control.Header("x-dynatrace-test", headerValue)); 111 | 112 | 113 | 114 | 115 | 116 | 117 | ${__P(SERVER_URL,${SERVER_URL})} 118 | ${__P(SERVER_PORT,${SERVER_PORT})} 119 | ${__P(PROTOCOL,${PROTOCOL})} 120 | 121 | ${__P(CHECK_PATH,${CHECK_PATH})} 122 | GET 123 | true 124 | false 125 | true 126 | false 127 | 128 | 129 | 130 | 131 | 132 | 133 | 134 | 135 | 136 | 137 | 138 | {__P(ThinkTime,${DefaultThinkTime})} 139 | 140 | 141 | 142 | 143 | false 144 | 145 | saveConfig 146 | 147 | 148 | true 149 | true 150 | true 151 | 152 | true 153 | true 154 | true 155 | true 156 | false 157 | true 158 | true 159 | false 160 | false 161 | false 162 | false 163 | false 164 | false 165 | false 166 | false 167 | 0 168 | true 169 | true 170 | 171 | 172 | 173 | 174 | 175 | 176 | 177 | 178 | -------------------------------------------------------------------------------- /onboarding-carts/jmeter/load.jmx: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | false 7 | false 8 | 9 | 10 | 11 | SERVER_URL 12 | carts.sockshop-staging.public.demo.keptn.sh 13 | = 14 | 15 | 16 | DefaultThinkTime 17 | 250 18 | = 19 | 20 | 21 | DT_LTN 22 | Default 23 | = 24 | 25 | 26 | SERVER_PORT 27 | 80 28 | = 29 | 30 | 31 | PROTOCOL 32 | http 33 | = 34 | 35 | 36 | VUCount 37 | 1 38 | = 39 | 40 | 41 | LoopCount 42 | 1 43 | = 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | continue 52 | 53 | false 54 | ${__P(LoopCount,${LoopCount})} 55 | 56 | ${__P(VUCount,${VUCount})} 57 | 1 58 | 1536064517000 59 | 1536064517000 60 | false 61 | 62 | 63 | 64 | 65 | 66 | 67 | false 68 | 69 | 70 | 71 | 72 | 73 | Cache-Control 74 | no-cache 75 | 76 | 77 | Content-Type 78 | application/json 79 | 80 | 81 | json 82 | true 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | false 91 | import org.apache.jmeter.util.JMeterUtils; 92 | import org.apache.jmeter.protocol.http.control.HeaderManager; 93 | import java.io; 94 | import java.util; 95 | 96 | // ------------------------------------------------------------------------------------- 97 | // Generate the x-dynatrace-test header based on this best practic 98 | // -> https://www.dynatrace.com/support/help/integrations/test-automation-frameworks/how-do-i-integrate-dynatrace-into-my-load-testing-process/ 99 | // ------------------------------------------------------------------------------------- 100 | String LTN=JMeterUtils.getProperty("DT_LTN"); 101 | if((LTN == null) || (LTN.length() == 0)) { 102 | if(vars != null) { 103 | LTN = vars.get("DT_LTN"); 104 | } 105 | } 106 | if(LTN == null) LTN = "NoTestName"; 107 | 108 | String LSN = (bsh.args.length > 0) ? bsh.args[0] : "Test Scenario"; 109 | String TSN = sampler.getName(); 110 | String VU = ctx.getThreadGroup().getName() + ctx.getThreadNum(); 111 | String headerValue = "LSN="+ LSN + ";TSN=" + TSN + ";LTN=" + LTN + ";VU=" + VU + ";"; 112 | 113 | // ------------------------------------------- 114 | // Set header 115 | // ------------------------------------------- 116 | HeaderManager hm = sampler.getHeaderManager(); 117 | hm.removeHeaderNamed("x-dynatrace-test"); 118 | hm.add(new org.apache.jmeter.protocol.http.control.Header("x-dynatrace-test", headerValue)); 119 | 120 | 121 | 122 | true 123 | 124 | 125 | 126 | false 127 | { 128 | "itemId":"03fef6ac-1896-4ce8-bd69-b798f85c6e0b", 129 | "unitPrice":"99.99" 130 | } 131 | = 132 | 133 | 134 | 135 | ${__P(SERVER_URL,${SERVER_URL})} 136 | ${__P(SERVER_PORT,${SERVER_PORT})} 137 | ${__P(PROTOCOL,${PROTOCOL})} 138 | 139 | /carts/1/items 140 | POST 141 | true 142 | false 143 | true 144 | false 145 | true 146 | 147 | 148 | 149 | 150 | 151 | 152 | {__P(ThinkTime,${DefaultThinkTime})} 153 | 154 | 155 | 156 | 157 | false 158 | 159 | saveConfig 160 | 161 | 162 | true 163 | true 164 | true 165 | 166 | true 167 | true 168 | true 169 | true 170 | false 171 | true 172 | true 173 | false 174 | false 175 | false 176 | false 177 | false 178 | false 179 | false 180 | false 181 | 0 182 | true 183 | true 184 | 185 | 186 | 187 | 188 | 189 | 190 | 191 | 192 | -------------------------------------------------------------------------------- /onboarding-carts/lighthouse-source-dynatrace.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | data: 3 | sli-provider: dynatrace 4 | kind: ConfigMap 5 | metadata: 6 | name: lighthouse-config-sockshop 7 | namespace: keptn 8 | -------------------------------------------------------------------------------- /onboarding-carts/lighthouse-source-prometheus.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | data: 3 | sli-provider: prometheus 4 | kind: ConfigMap 5 | metadata: 6 | name: lighthouse-config-sockshop 7 | namespace: keptn 8 | -------------------------------------------------------------------------------- /onboarding-carts/manifests/manifest-carts-db.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | apiVersion: v1 3 | kind: PersistentVolumeClaim 4 | metadata: 5 | name: carts-db-mongodata 6 | namespace: sockshop-hardening 7 | spec: 8 | accessModes: 9 | - ReadWriteOnce 10 | resources: 11 | requests: 12 | storage: 100Mi 13 | status: {} 14 | --- 15 | apiVersion: apps/v1 16 | kind: Deployment 17 | metadata: 18 | name: carts-db 19 | namespace: sockshop-hardening 20 | spec: 21 | replicas: 1 22 | selector: 23 | matchLabels: 24 | app: carts-db 25 | strategy: 26 | type: Recreate 27 | template: 28 | metadata: 29 | labels: 30 | app: carts-db 31 | spec: 32 | containers: 33 | - image: mongo 34 | name: carts-db 35 | ports: 36 | - containerPort: 27017 37 | resources: {} 38 | volumeMounts: 39 | - mountPath: /data/db 40 | name: carts-db-mongodata 41 | restartPolicy: Always 42 | volumes: 43 | - name: carts-db-mongodata 44 | persistentVolumeClaim: 45 | claimName: carts-db-mongodata 46 | --- 47 | apiVersion: v1 48 | kind: Service 49 | metadata: 50 | name: carts-db 51 | namespace: sockshop-hardening 52 | spec: 53 | ports: 54 | - name: "27017" 55 | port: 27017 56 | targetPort: 27017 57 | selector: 58 | app: carts-db 59 | -------------------------------------------------------------------------------- /onboarding-carts/manifests/manifest-carts.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | apiVersion: apps/v1 3 | kind: Deployment 4 | metadata: 5 | name: carts 6 | namespace: sockshop-hardening 7 | spec: 8 | replicas: 1 9 | selector: 10 | matchLabels: 11 | app: carts 12 | template: 13 | metadata: 14 | labels: 15 | app: carts 16 | version: v1 17 | deployment: carts 18 | spec: 19 | containers: 20 | - name: carts 21 | image: docker.io/keptnexamples/carts:0.13.1 22 | env: 23 | - name: JAVA_OPTS 24 | value: -Xms128m -Xmx512m -XX:PermSize=128m -XX:MaxPermSize=128m -XX:+UseG1GC -Djava.security.egd=file:/dev/urandom 25 | - name: POD_NAME 26 | valueFrom: 27 | fieldRef: 28 | fieldPath: "metadata.name" 29 | - name: KUBERNETES_NAMESPACE 30 | valueFrom: 31 | fieldRef: 32 | fieldPath: "metadata.namespace" 33 | - name: DEPLOYMENT_NAME 34 | valueFrom: 35 | fieldRef: 36 | fieldPath: "metadata.labels['deployment']" 37 | - name: CONTAINER_IMAGE 38 | value: docker.io/keptnexamples/carts:0.13.1 39 | - name: DT_CUSTOM_PROP 40 | value: "keptn_stage=hardening keptn_project=sockshop keptn_service=carts" 41 | resources: 42 | limits: 43 | cpu: 500m 44 | memory: 1024Mi 45 | requests: 46 | cpu: 400m 47 | memory: 768Mi 48 | ports: 49 | - containerPort: 8080 50 | volumeMounts: 51 | - mountPath: /tmp 52 | name: tmp-volume 53 | livenessProbe: 54 | httpGet: 55 | path: /health 56 | port: 8080 57 | initialDelaySeconds: 60 58 | periodSeconds: 10 59 | timeoutSeconds: 15 60 | readinessProbe: 61 | httpGet: 62 | path: /health 63 | port: 8080 64 | initialDelaySeconds: 60 65 | periodSeconds: 10 66 | timeoutSeconds: 15 67 | volumes: 68 | - name: tmp-volume 69 | emptyDir: 70 | medium: Memory 71 | nodeSelector: 72 | beta.kubernetes.io/os: linux 73 | --- 74 | apiVersion: v1 75 | kind: Service 76 | metadata: 77 | name: carts 78 | labels: 79 | app: carts 80 | namespace: sockshop-hardening 81 | spec: 82 | ports: 83 | - name: http 84 | port: 80 85 | targetPort: 8080 86 | selector: 87 | app: carts 88 | type: LoadBalancer 89 | -------------------------------------------------------------------------------- /onboarding-carts/remediation.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: spec.keptn.sh/0.1.4 2 | kind: Remediation 3 | metadata: 4 | name: carts-remediation 5 | spec: 6 | remediations: 7 | - problemType: Response time degradation 8 | actionsOnOpen: 9 | - action: scaling 10 | name: scaling 11 | description: Scale up 12 | value: "1" 13 | - problemType: response_time_p90 14 | actionsOnOpen: 15 | - action: scaling 16 | name: scaling 17 | description: Scale up 18 | value: "1" 19 | -------------------------------------------------------------------------------- /onboarding-carts/remediation_feature_toggle.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: spec.keptn.sh/0.1.4 2 | kind: Remediation 3 | metadata: 4 | name: carts-remediation 5 | spec: 6 | remediations: 7 | - problemType: Response time degradation 8 | actionsOnOpen: 9 | - action: toggle-feature 10 | name: Toogle feature flag 11 | description: Toogle feature flag EnableItemCache to ON 12 | value: 13 | EnableItemCache: "on" 14 | - problemType: Failure rate increase 15 | actionsOnOpen: 16 | - action: toggle-feature 17 | name: Toogle feature flag 18 | description: Toogle feature flag EnablePromotion to OFF 19 | value: 20 | EnablePromotion: "off" 21 | -------------------------------------------------------------------------------- /onboarding-carts/shipyard-argo.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: "spec.keptn.sh/0.2.2" 2 | kind: "Shipyard" 3 | metadata: 4 | name: "shipyard-sockshop" 5 | spec: 6 | stages: 7 | - name: "production" 8 | sequences: 9 | - name: "artifact-delivery" 10 | tasks: 11 | - name: "deployment" 12 | properties: 13 | deploymentstrategy: "blue_green_service" 14 | - name: "test" 15 | properties: 16 | teststrategy: "performance" 17 | - name: "evaluation" 18 | - name: "release" 19 | -------------------------------------------------------------------------------- /onboarding-carts/shipyard-quality-gates.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: "spec.keptn.sh/0.2.2" 2 | kind: "Shipyard" 3 | metadata: 4 | name: "shipyard-quality-gates" 5 | spec: 6 | stages: 7 | - name: "hardening" 8 | -------------------------------------------------------------------------------- /onboarding-carts/shipyard.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: "spec.keptn.sh/0.2.2" 2 | kind: "Shipyard" 3 | metadata: 4 | name: "shipyard-sockshop" 5 | spec: 6 | stages: 7 | - name: "dev" 8 | sequences: 9 | - name: "delivery" 10 | tasks: 11 | - name: "deployment" 12 | properties: 13 | deploymentstrategy: "direct" 14 | - name: "test" 15 | properties: 16 | teststrategy: "functional" 17 | - name: "evaluation" 18 | - name: "release" 19 | - name: "delivery-direct" 20 | tasks: 21 | - name: "deployment" 22 | properties: 23 | deploymentstrategy: "direct" 24 | - name: "release" 25 | 26 | - name: "staging" 27 | sequences: 28 | - name: "delivery" 29 | triggeredOn: 30 | - event: "dev.delivery.finished" 31 | tasks: 32 | - name: "deployment" 33 | properties: 34 | deploymentstrategy: "blue_green_service" 35 | - name: "test" 36 | properties: 37 | teststrategy: "performance" 38 | - name: "evaluation" 39 | - name: "release" 40 | - name: "rollback" 41 | triggeredOn: 42 | - event: "staging.delivery.finished" 43 | selector: 44 | match: 45 | result: "fail" 46 | tasks: 47 | - name: "rollback" 48 | - name: "delivery-direct" 49 | triggeredOn: 50 | - event: "dev.delivery-direct.finished" 51 | tasks: 52 | - name: "deployment" 53 | properties: 54 | deploymentstrategy: "direct" 55 | - name: "release" 56 | 57 | - name: "production" 58 | sequences: 59 | - name: "delivery" 60 | triggeredOn: 61 | - event: "staging.delivery.finished" 62 | tasks: 63 | - name: "deployment" 64 | properties: 65 | deploymentstrategy: "blue_green_service" 66 | - name: "release" 67 | - name: "rollback" 68 | triggeredOn: 69 | - event: "production.delivery.finished" 70 | selector: 71 | match: 72 | result: "fail" 73 | tasks: 74 | - name: "rollback" 75 | - name: "delivery-direct" 76 | triggeredOn: 77 | - event: "staging.delivery-direct.finished" 78 | tasks: 79 | - name: "deployment" 80 | properties: 81 | deploymentstrategy: "direct" 82 | - name: "release" 83 | 84 | - name: "remediation" 85 | triggeredOn: 86 | - event: "production.remediation.finished" 87 | selector: 88 | match: 89 | evaluation.result: "fail" 90 | tasks: 91 | - name: "get-action" 92 | - name: "action" 93 | - name: "evaluation" 94 | triggeredAfter: "15m" 95 | properties: 96 | timeframe: "15m" 97 | -------------------------------------------------------------------------------- /onboarding-carts/sli-config-argo-prometheus.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | spec_version: '1.0' 3 | indicators: 4 | response_time_p50: histogram_quantile(0.5, sum by(le) (rate(http_response_time_milliseconds_bucket{handler="ItemsController.addToCart",job="$SERVICE-$PROJECT-$STAGE-canary"}[$DURATION_SECONDS]))) 5 | response_time_p90: histogram_quantile(0.9, sum by(le) (rate(http_response_time_milliseconds_bucket{handler="ItemsController.addToCart",job="$SERVICE-$PROJECT-$STAGE-canary"}[$DURATION_SECONDS]))) 6 | response_time_p95: histogram_quantile(0.95, sum by(le) (rate(http_response_time_milliseconds_bucket{handler="ItemsController.addToCart",job="$SERVICE-$PROJECT-$STAGE-canary"}[$DURATION_SECONDS]))) -------------------------------------------------------------------------------- /onboarding-carts/sli-config-dynatrace-no-deployment-tag.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | spec_version: '1.0' 3 | indicators: 4 | throughput: "metricSelector=builtin:service.requestCount.total:merge(\"dt.entity.service\"):sum&entitySelector=type(SERVICE),tag(keptn_project:$PROJECT),tag(keptn_stage:$STAGE),tag(keptn_service:$SERVICE)" 5 | error_rate: "metricSelector=builtin:service.errors.total.count:merge(\"dt.entity.service\"):avg&entitySelector=type(SERVICE),tag(keptn_project:$PROJECT),tag(keptn_stage:$STAGE),tag(keptn_service:$SERVICE)" 6 | response_time_p50: "metricSelector=builtin:service.response.time:merge(\"dt.entity.service\"):percentile(50)&entitySelector=type(SERVICE),tag(keptn_project:$PROJECT),tag(keptn_stage:$STAGE),tag(keptn_service:$SERVICE)" 7 | response_time_p90: "metricSelector=builtin:service.response.time:merge(\"dt.entity.service\"):percentile(90)&entitySelector=type(SERVICE),tag(keptn_project:$PROJECT),tag(keptn_stage:$STAGE),tag(keptn_service:$SERVICE)" 8 | response_time_p95: "metricSelector=builtin:service.response.time:merge(\"dt.entity.service\"):percentile(95)&entitySelector=type(SERVICE),tag(keptn_project:$PROJECT),tag(keptn_stage:$STAGE),tag(keptn_service:$SERVICE)" -------------------------------------------------------------------------------- /onboarding-carts/sli-config-dynatrace.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | spec_version: '1.0' 3 | indicators: 4 | throughput: "metricSelector=builtin:service.requestCount.total:merge(\"dt.entity.service\"):sum&entitySelector=type(SERVICE),tag(keptn_project:$PROJECT),tag(keptn_stage:$STAGE),tag(keptn_service:$SERVICE),tag(keptn_deployment:$DEPLOYMENT)" 5 | error_rate: "metricSelector=builtin:service.errors.total.count:merge(\"dt.entity.service\"):avg&entitySelector=type(SERVICE),tag(keptn_project:$PROJECT),tag(keptn_stage:$STAGE),tag(keptn_service:$SERVICE),tag(keptn_deployment:$DEPLOYMENT)" 6 | response_time_p50: "metricSelector=builtin:service.response.time:merge(\"dt.entity.service\"):percentile(50)&entitySelector=type(SERVICE),tag(keptn_project:$PROJECT),tag(keptn_stage:$STAGE),tag(keptn_service:$SERVICE),tag(keptn_deployment:$DEPLOYMENT)" 7 | response_time_p90: "metricSelector=builtin:service.response.time:merge(\"dt.entity.service\"):percentile(90)&entitySelector=type(SERVICE),tag(keptn_project:$PROJECT),tag(keptn_stage:$STAGE),tag(keptn_service:$SERVICE),tag(keptn_deployment:$DEPLOYMENT)" 8 | response_time_p95: "metricSelector=builtin:service.response.time:merge(\"dt.entity.service\"):percentile(95)&entitySelector=type(SERVICE),tag(keptn_project:$PROJECT),tag(keptn_stage:$STAGE),tag(keptn_service:$SERVICE),tag(keptn_deployment:$DEPLOYMENT)" -------------------------------------------------------------------------------- /onboarding-carts/sli-config-prometheus.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | spec_version: '1.0' 3 | indicators: 4 | response_time_p50: histogram_quantile(0.5, sum by(le) (rate(http_response_time_milliseconds_bucket{handler="ItemsController.addToCart",job="$SERVICE-$PROJECT-$STAGE-$DEPLOYMENT"}[$DURATION_SECONDS]))) 5 | response_time_p90: histogram_quantile(0.9, sum by(le) (rate(http_response_time_milliseconds_bucket{handler="ItemsController.addToCart",job="$SERVICE-$PROJECT-$STAGE-$DEPLOYMENT"}[$DURATION_SECONDS]))) 6 | response_time_p95: histogram_quantile(0.95, sum by(le) (rate(http_response_time_milliseconds_bucket{handler="ItemsController.addToCart",job="$SERVICE-$PROJECT-$STAGE-$DEPLOYMENT"}[$DURATION_SECONDS]))) -------------------------------------------------------------------------------- /onboarding-carts/slo-quality-gates.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | spec_version: "1.0" 3 | comparison: 4 | aggregate_function: "avg" 5 | compare_with: "single_result" 6 | include_result_with_score: "pass" 7 | number_of_comparison_results: 1 8 | filter: 9 | objectives: 10 | - sli: "response_time_p95" 11 | displayName: "Response time P95" 12 | key_sli: false 13 | pass: # pass if (relative change <= 10% AND absolute value is < 600ms) 14 | - criteria: 15 | - "<=+10%" # relative values require a prefixed sign (plus or minus) 16 | - "<600" # absolute values only require a logical operator 17 | warning: # if the response time is below 800ms, the result should be a warning 18 | - criteria: 19 | - "<=800" 20 | weight: 1 21 | total_score: 22 | pass: "90%" 23 | warning: "75%" -------------------------------------------------------------------------------- /onboarding-carts/slo-self-healing-dynatrace.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | spec_version: "1.0" 3 | comparison: 4 | aggregate_function: "avg" 5 | compare_with: "single_result" 6 | include_result_with_score: "pass" 7 | number_of_comparison_results: 1 8 | filter: 9 | objectives: 10 | - sli: "response_time_p90" 11 | displayName: "Response time P90" 12 | key_sli: false 13 | pass: # pass if (relative change <= 10% AND absolute value is < 1000) 14 | - criteria: 15 | - "<=+10%" # relative values require a prefixed sign (plus or minus) 16 | - "<1000" # absolute values only require a logical operator 17 | warning: # if the response time is below 1200ms, the result should be a warning 18 | - criteria: 19 | - "<=1200" 20 | weight: 1 21 | - sli: "problem_open" 22 | displayName: "Problem open" 23 | key_sli: true 24 | pass: 25 | - criteria: 26 | - "=0" 27 | weight: 1 28 | total_score: 29 | pass: "90%" 30 | warning: "40%" 31 | -------------------------------------------------------------------------------- /onboarding-carts/slo-self-healing-prometheus.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | spec_version: "1.0" 3 | comparison: 4 | aggregate_function: "avg" 5 | compare_with: "single_result" 6 | include_result_with_score: "pass" 7 | number_of_comparison_results: 1 8 | filter: 9 | objectives: 10 | - sli: "response_time_p90" 11 | displayName: "Response time P90" 12 | key_sli: false 13 | pass: # pass if (relative change <= 10% AND absolute value is < 1000) 14 | - criteria: 15 | - "<=+10%" # relative values require a prefixed sign (plus or minus) 16 | - "<1000" # absolute values only require a logical operator 17 | warning: # if the response time is below 1200ms, the result should be a warning 18 | - criteria: 19 | - "<=1200" 20 | weight: 1 21 | total_score: 22 | pass: "90%" 23 | warning: "40%" 24 | -------------------------------------------------------------------------------- /quickstart/.gitignore: -------------------------------------------------------------------------------- 1 | remediation_trigger.json 2 | -------------------------------------------------------------------------------- /quickstart/README.md: -------------------------------------------------------------------------------- 1 | # Quickstart 2 | 3 | All files needed for the quickstart guide on https://keptn.sh/docs/quickstart/ are maintained here. 4 | -------------------------------------------------------------------------------- /quickstart/automated-operations.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | set -e 3 | 4 | #source <(curl -s https://raw.githubusercontent.com/keptn/keptn/0.8.5/test/utils.sh) 5 | 6 | function print_headline() { 7 | HEADLINE=$1 8 | 9 | echo "" 10 | echo "---------------------------------------------------------------------" 11 | echo $HEADLINE 12 | echo "---------------------------------------------------------------------" 13 | echo "" 14 | } 15 | 16 | # wait for a deployment to be up and running 17 | function wait_for_deployment_in_namespace() { 18 | DEPLOYMENT=$1; NAMESPACE=$2; 19 | RETRY=0; RETRY_MAX=10; 20 | 21 | while [[ $RETRY -lt $RETRY_MAX ]]; do 22 | DEPLOYMENT_LIST=$(eval "kubectl get deployments -n ${NAMESPACE} | awk '/$DEPLOYMENT /'" | awk '{print $1}') # list of multiple deployments when starting with the same name 23 | if [[ -z "$DEPLOYMENT_LIST" ]]; then 24 | RETRY=$((RETRY+1)) 25 | echo "Retry: ${RETRY}/${RETRY_MAX} - Deployment not found - waiting 5s for deployment ${DEPLOYMENT} in namespace ${NAMESPACE}" 26 | sleep 5 27 | else 28 | READY_REPLICAS=$(eval kubectl get deployments "$DEPLOYMENT" -n "$NAMESPACE" -o=jsonpath='{$.status.availableReplicas}') 29 | WANTED_REPLICAS=$(eval kubectl get deployments "$DEPLOYMENT" -n "$NAMESPACE" -o=jsonpath='{$.spec.replicas}') 30 | UNAVAILABLE_REPLICAS=$(eval kubectl get deployments "$DEPLOYMENT" -n "$NAMESPACE" -o=jsonpath='{$.status.unavailableReplicas}') 31 | if [[ "$READY_REPLICAS" = "$WANTED_REPLICAS" && "$UNAVAILABLE_REPLICAS" = "" ]]; then 32 | echo "Found deployment ${DEPLOYMENT} in namespace ${NAMESPACE}: ${DEPLOYMENT_LIST}" 33 | break 34 | else 35 | RETRY=$((RETRY+1)) 36 | echo "Retry: ${RETRY}/${RETRY_MAX} - Unsufficient replicas for deployment - waiting 5s for deployment ${DEPLOYMENT} in namespace ${NAMESPACE}" 37 | sleep 5 38 | fi 39 | fi 40 | done 41 | 42 | if [[ $RETRY == "$RETRY_MAX" ]]; then 43 | print_error "Could not find deployment ${DEPLOYMENT} in namespace ${NAMESPACE}" 44 | exit 1 45 | fi 46 | } 47 | 48 | PROJECT="podtatohead" 49 | SERVICE="helloservice" 50 | 51 | print_headline "Preparation of Auto-remediation in Production" 52 | cd examples/quickstart 53 | 54 | echo "Adding SLIs for Prometheus" 55 | keptn add-resource --project=$PROJECT --stage=production --service=$SERVICE --resource=./demo/prometheus/sli.yaml --resourceUri=prometheus/sli.yaml 56 | echo "Adding SLO definition file for the quality gate" 57 | keptn add-resource --project=$PROJECT --stage=production --service=$SERVICE --resource=./demo/slo.yaml --resourceUri=slo.yaml 58 | echo "Adding Remediation Configuration" 59 | keptn add-resource --project=$PROJECT --stage=production --service=$SERVICE --resource=./demo/remediation.yaml --resourceUri=remediation.yaml 60 | 61 | jes_is_installed=$(kubectl -n keptn get deployment job-executor-service --ignore-not-found) 62 | if [[ $jes_is_installed == "" ]]; then 63 | print_headline "Deploy Job Executor" 64 | kubectl apply -f ./demo/job/job-executor.yaml 65 | else 66 | print_headline "Job Executor Already Deployed. Skipping Deployment. Will Add Config." 67 | fi 68 | 69 | keptn add-resource --project=$PROJECT --service=$SERVICE --stage=production --resource=./demo/job/config.yaml --resourceUri=job/config.yaml 70 | 71 | wait_for_deployment_in_namespace "job-executor-service" "keptn" 72 | 73 | print_headline "Simulate Alert (Problem)" 74 | echo -e "{\"type\": \"sh.keptn.event.production.remediation.triggered\",\"specversion\":\"1.0\",\"source\":\"https:\/\/github.com\/keptn-contrib\/prometheus-service\",\"id\": \"f2b878d3-03c0-4e8f-bc3f-454bc1b3d79d\", \"time\": \"2019-06-07T07:02:15.64489Z\", \"contenttype\": \"application\/json\", \"data\": {\"project\": \"podtatohead\",\"stage\": \"production\",\"service\": \"helloservice\",\"problem\": { \"problemTitle\": \"out_of_memory\",\"rootCause\": \"Response time degradation\"}}}" > remediation_trigger.json | keptn send event -f remediation_trigger.json 75 | 76 | print_headline "Have a look at the Keptn Bridge and explore the demo project" 77 | 78 | echo "You can simulate a new problem any time by executing the following command:" 79 | echo "keptn send event -f remediation_trigger.json" 80 | -------------------------------------------------------------------------------- /quickstart/clean-project.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | set -e 3 | 4 | keptn delete project podtatohead 5 | # rm -rf podtato-head 6 | kubectl delete deploy -n keptn prometheus-service --ignore-not-found=true 7 | kubectl delete deploy -n keptn prometheus-sli-service --ignore-not-found=true 8 | kubectl delete secret -n keptn prometheus-credentials-podtatohead --ignore-not-found=true 9 | kubectl delete ns monitoring --ignore-not-found=true 10 | kubectl delete ns podtatohead-hardening --ignore-not-found=true 11 | kubectl delete ns podtatohead-production --ignore-not-found=true 12 | 13 | 14 | -------------------------------------------------------------------------------- /quickstart/demo/helm/hardening_endpoints.yaml: -------------------------------------------------------------------------------- 1 | deploymentURIsLocal: 2 | - "http://helloservice.podtatohead-hardening:80" 3 | -------------------------------------------------------------------------------- /quickstart/demo/helm/helloservice.tgz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/keptn/examples/9ff28ef074d0ab7580aec06706994bb99a08b27f/quickstart/demo/helm/helloservice.tgz -------------------------------------------------------------------------------- /quickstart/demo/helm/helloservice/Chart.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v2 2 | appVersion: 1.16.0 3 | description: A Helm chart for Kubernetes 4 | name: podtatoserver 5 | type: application 6 | version: 0.1.0 7 | -------------------------------------------------------------------------------- /quickstart/demo/helm/helloservice/templates/deployment.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: apps/v1 2 | kind: Deployment 3 | metadata: 4 | name: helloservice 5 | namespace: {{ .Release.Namespace }} 6 | spec: 7 | replicas: {{ .Values.replicaCount }} 8 | selector: 9 | matchLabels: 10 | app: helloservice 11 | template: 12 | metadata: 13 | labels: 14 | app: helloservice 15 | spec: 16 | terminationGracePeriodSeconds: 5 17 | containers: 18 | - name: server 19 | image: {{ .Values.image}} 20 | imagePullPolicy: Always 21 | ports: 22 | - containerPort: 9000 23 | env: 24 | - name: PORT 25 | value: "9000" 26 | -------------------------------------------------------------------------------- /quickstart/demo/helm/helloservice/templates/service.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: Service 3 | metadata: 4 | name: helloservice 5 | namespace: {{ .Release.Namespace }} 6 | spec: 7 | selector: 8 | app: helloservice 9 | ports: 10 | - name: http 11 | port: 80 12 | protocol: TCP 13 | targetPort: 9000 14 | type: ClusterIP 15 | -------------------------------------------------------------------------------- /quickstart/demo/helm/helloservice/values.yaml: -------------------------------------------------------------------------------- 1 | image: ghcr.io/podtato-head/podtatoserver:v0.1.1 2 | replicaCount: 1 -------------------------------------------------------------------------------- /quickstart/demo/helm/production_endpoints.yaml: -------------------------------------------------------------------------------- 1 | deploymentURIsLocal: 2 | - "http://helloservice.podtatohead-production:80" 3 | -------------------------------------------------------------------------------- /quickstart/demo/jmeter/jmeter.conf.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | spec_version: '0.1.0' 3 | workloads: 4 | - teststrategy: performance 5 | vuser: 30 6 | loopcount: 150 7 | script: jmeter/load.jmx 8 | acceptederrorrate: 1.0 9 | - teststrategy: performance_light 10 | vuser: 50 11 | loopcount: 50 12 | script: jmeter/load.jmx 13 | acceptederrorrate: 1.0 14 | 15 | -------------------------------------------------------------------------------- /quickstart/demo/job/config.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v2 2 | actions: 3 | - name: "Execute pod restart" 4 | events: 5 | - name: "sh.keptn.event.action.triggered" 6 | jsonpath: 7 | property: "$.data.action.action" 8 | match: "restart" 9 | tasks: 10 | - name: "Restart pod" 11 | image: bitnami/kubectl:1.21 12 | workingDir: "/bin" 13 | cmd: 14 | - bash 15 | - '-c' 16 | - kubectl delete pods -l app=$(SERVICE) -n $(PROJECT)-$(STAGE) 17 | env: 18 | - name: PROJECT 19 | value: "$.data.project" 20 | valueFrom: event 21 | - name: STAGE 22 | value: "$.data.stage" 23 | valueFrom: event 24 | - name: SERVICE 25 | value: "$.data.service" 26 | valueFrom: event 27 | - name: "Show Pod" 28 | image: bitnami/kubectl:1.21 29 | workingDir: "/bin" 30 | cmd: 31 | - bash 32 | - '-c' 33 | - kubectl get pods -n $(PROJECT)-$(STAGE) 34 | env: 35 | - name: PROJECT 36 | value: "$.data.project" 37 | valueFrom: event 38 | - name: STAGE 39 | value: "$.data.stage" 40 | valueFrom: event 41 | - name: SERVICE 42 | value: "$.data.service" 43 | valueFrom: event 44 | -------------------------------------------------------------------------------- /quickstart/demo/job/job-executor.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | # Deployment of our job-executor-service 3 | apiVersion: apps/v1 4 | kind: Deployment 5 | metadata: 6 | name: job-executor-service 7 | namespace: keptn 8 | spec: 9 | selector: 10 | matchLabels: 11 | app.kubernetes.io/name: distributor 12 | app.kubernetes.io/instance: keptn 13 | replicas: 1 14 | template: 15 | metadata: 16 | labels: 17 | app.kubernetes.io/name: distributor 18 | app.kubernetes.io/instance: keptn 19 | app.kubernetes.io/part-of: keptn-keptn 20 | app.kubernetes.io/component: control-plane 21 | app.kubernetes.io/version: develop 22 | spec: 23 | containers: 24 | - name: job-executor-service 25 | image: keptncontrib/job-executor-service:0.1.7 26 | ports: 27 | - containerPort: 8080 28 | resources: 29 | limits: 30 | cpu: 1 31 | memory: 512Mi 32 | requests: 33 | cpu: 50m 34 | memory: 128Mi 35 | env: 36 | - name: INIT_CONTAINER_CONFIGURATION_SERVICE_API_ENDPOINT 37 | value: "http://configuration-service:8080" 38 | - name: CONFIGURATION_SERVICE 39 | value: 'http://configuration-service:8080' 40 | - name: JOB_NAMESPACE 41 | value: 'keptn' 42 | - name: INIT_CONTAINER_IMAGE 43 | value: 'keptncontrib/job-executor-service-initcontainer:0.1.7' 44 | - name: DEFAULT_RESOURCE_LIMITS_CPU 45 | value: "1" 46 | - name: DEFAULT_RESOURCE_LIMITS_MEMORY 47 | value: "512Mi" 48 | - name: DEFAULT_RESOURCE_REQUESTS_CPU 49 | value: "50m" 50 | - name: DEFAULT_RESOURCE_REQUESTS_MEMORY 51 | value: "128Mi" 52 | - name: ALWAYS_SEND_FINISHED_EVENT 53 | value: "false" 54 | - name: distributor 55 | image: keptn/distributor:0.13.2 56 | livenessProbe: 57 | httpGet: 58 | path: /health 59 | port: 10999 60 | initialDelaySeconds: 5 61 | periodSeconds: 5 62 | imagePullPolicy: Always 63 | ports: 64 | - containerPort: 8080 65 | resources: 66 | requests: 67 | memory: "16Mi" 68 | cpu: "25m" 69 | limits: 70 | memory: "32Mi" 71 | cpu: "100m" 72 | env: 73 | - name: PUBSUB_URL 74 | value: 'nats://keptn-nats-cluster' 75 | - name: PUBSUB_TOPIC 76 | value: 'sh.keptn.>' 77 | - name: PUBSUB_RECIPIENT 78 | value: '127.0.0.1' 79 | - name: VERSION 80 | valueFrom: 81 | fieldRef: 82 | apiVersion: v1 83 | fieldPath: 'metadata.labels[''app.kubernetes.io/version'']' 84 | - name: LOCATION 85 | valueFrom: 86 | fieldRef: 87 | fieldPath: metadata.labels['app.kubernetes.io/component'] 88 | - name: K8S_DEPLOYMENT_NAME 89 | valueFrom: 90 | fieldRef: 91 | apiVersion: v1 92 | fieldPath: 'metadata.labels[''app.kubernetes.io/name'']' 93 | - name: K8S_POD_NAME 94 | valueFrom: 95 | fieldRef: 96 | apiVersion: v1 97 | fieldPath: metadata.name 98 | - name: K8S_NAMESPACE 99 | valueFrom: 100 | fieldRef: 101 | apiVersion: v1 102 | fieldPath: metadata.namespace 103 | - name: K8S_NODE_NAME 104 | valueFrom: 105 | fieldRef: 106 | apiVersion: v1 107 | fieldPath: spec.nodeName 108 | serviceAccountName: job-executor-service 109 | --- 110 | # Expose job-executor-service via Port 8080 within the cluster 111 | apiVersion: v1 112 | kind: Service 113 | metadata: 114 | name: job-executor-service 115 | namespace: keptn 116 | labels: 117 | run: job-executor-service 118 | spec: 119 | ports: 120 | - port: 8080 121 | protocol: TCP 122 | selector: 123 | run: job-executor-service 124 | --- 125 | apiVersion: v1 126 | kind: ServiceAccount 127 | metadata: 128 | name: job-executor-service 129 | namespace: keptn 130 | --- 131 | # Role for accessing secrets in the namespace 132 | apiVersion: rbac.authorization.k8s.io/v1 133 | kind: Role 134 | metadata: 135 | name: job-executor-service 136 | namespace: keptn 137 | rules: 138 | - apiGroups: 139 | - "" 140 | resources: 141 | - "secrets" 142 | verbs: 143 | - "get" 144 | - "list" 145 | - "watch" 146 | - apiGroups: 147 | - "" 148 | resources: 149 | - "pods" 150 | - "pods/log" 151 | - "persistentvolumeclaims" 152 | - "jobs" 153 | verbs: 154 | - "*" 155 | - apiGroups: 156 | - "batch" 157 | - "extensions" 158 | resources: 159 | - "jobs" 160 | verbs: 161 | - "*" 162 | --- 163 | # Role for accessing pods in the podtatohead-production namespace 164 | apiVersion: rbac.authorization.k8s.io/v1 165 | kind: Role 166 | metadata: 167 | name: job-executor 168 | namespace: podtatohead-production 169 | rules: 170 | - apiGroups: 171 | - "" 172 | resources: 173 | - "pods" 174 | - "pods/log" 175 | - "persistentvolumeclaims" 176 | - "jobs" 177 | verbs: 178 | - "*" 179 | --- 180 | # Bind role for accessing secrets onto the job-executor-service service account 181 | apiVersion: rbac.authorization.k8s.io/v1 182 | kind: RoleBinding 183 | metadata: 184 | name: job-executor-service 185 | namespace: keptn 186 | roleRef: 187 | apiGroup: rbac.authorization.k8s.io 188 | kind: Role 189 | name: job-executor-service 190 | subjects: 191 | - kind: ServiceAccount 192 | name: job-executor-service 193 | namespace: keptn 194 | --- 195 | # Bind role for restarting pod 196 | apiVersion: rbac.authorization.k8s.io/v1 197 | kind: RoleBinding 198 | metadata: 199 | name: job-executor-service-tmp 200 | namespace: podtatohead-production 201 | roleRef: 202 | apiGroup: rbac.authorization.k8s.io 203 | kind: Role 204 | name: job-executor 205 | subjects: 206 | - kind: ServiceAccount 207 | name: default 208 | namespace: keptn 209 | -------------------------------------------------------------------------------- /quickstart/demo/prometheus/sli.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | spec_version: '1.0' 3 | indicators: 4 | http_response_time_seconds_main_page_sum: sum(rate(http_server_request_duration_seconds_sum{method="GET",route="/",status_code="200",job="$SERVICE-$PROJECT-$STAGE"}[$DURATION_SECONDS])/rate(http_server_request_duration_seconds_count{method="GET",route="/",status_code="200",job="$SERVICE-$PROJECT-$STAGE"}[$DURATION_SECONDS])) 5 | failing_request: promhttp_metric_handler_requests_total{code!="200",job="$SERVICE-$PROJECT-$STAGE"} 6 | http_requests_total_sucess: http_requests_total{status="success",job="$SERVICE-$PROJECT-$STAGE"} 7 | go_routines: go_goroutines{job="$SERVICE-$PROJECT-$STAGE"} 8 | request_throughput: sum(rate(http_requests_total{status="success",job="$SERVICE-$PROJECT-$STAGE"}[$DURATION_SECONDS])) 9 | -------------------------------------------------------------------------------- /quickstart/demo/remediation.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: spec.keptn.sh/0.1.4 2 | kind: Remediation 3 | metadata: 4 | name: carts-remediation 5 | spec: 6 | remediations: 7 | - problemType: out_of_memory 8 | actionsOnOpen: 9 | - action: restart 10 | name: restart 11 | description: Restart Pod 12 | - problemType: response_time_p90 13 | actionsOnOpen: 14 | - action: scaling 15 | name: scaling 16 | description: Scale up 17 | value: "1" 18 | -------------------------------------------------------------------------------- /quickstart/demo/shipyard.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: "spec.keptn.sh/0.2.0" 2 | kind: "Shipyard" 3 | metadata: 4 | name: "shipyard-sockshop" 5 | spec: 6 | stages: 7 | - name: "hardening" 8 | sequences: 9 | - name: "delivery" 10 | tasks: 11 | - name: "deployment" 12 | properties: 13 | deploymentstrategy: "user_managed" 14 | - name: "test" 15 | properties: 16 | teststrategy: "performance" 17 | - name: "evaluation" 18 | - name: "production" 19 | sequences: 20 | - name: "delivery" 21 | triggeredOn: 22 | - event: "hardening.delivery.finished" 23 | tasks: 24 | - name: "deployment" 25 | properties: 26 | deploymentstrategy: "user_managed" 27 | - name: "release" 28 | - name: "remediation" 29 | triggeredOn: 30 | - event: "production.remediation.finished" 31 | selector: 32 | match: 33 | evaluation.result: "fail" 34 | tasks: 35 | - name: "get-action" 36 | - name: "action" 37 | - name: "evaluation" 38 | triggeredAfter: "2m" 39 | properties: 40 | timeframe: "2m" 41 | -------------------------------------------------------------------------------- /quickstart/demo/slo.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | spec_version: '0.1.0' 3 | comparison: 4 | compare_with: "single_result" 5 | include_result_with_score: "pass" 6 | aggregate_function: avg 7 | objectives: 8 | - sli: http_response_time_seconds_main_page_sum 9 | displayName: "HTTP Response Time" 10 | pass: 11 | - criteria: 12 | - "<=0.4" 13 | warning: 14 | - criteria: 15 | - "<=0.1" 16 | - sli: request_throughput 17 | displayName: "Request Throughput" 18 | pass: 19 | - criteria: 20 | #- "<=+100%" 21 | - ">=-80%" 22 | - sli: go_routines 23 | displayName: "Go Routines" 24 | key_sli: true 25 | pass: 26 | - criteria: 27 | - "<=30" 28 | - sli: failing_request 29 | displayName: "Failing Requests" 30 | pass: 31 | - criteria: 32 | - "<10" 33 | total_score: 34 | pass: "90%" 35 | warning: "75%" 36 | -------------------------------------------------------------------------------- /quickstart/expose-keptn.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | set -e 3 | 4 | #source <(curl -s https://raw.githubusercontent.com/keptn/keptn/0.8.3/test/utils.sh) 5 | 6 | function print_headline() { 7 | HEADLINE=$1 8 | 9 | echo "" 10 | echo "---------------------------------------------------------------------" 11 | echo $HEADLINE 12 | echo "---------------------------------------------------------------------" 13 | echo "" 14 | } 15 | 16 | function print_error() { 17 | echo "::error file=${BASH_SOURCE[1]##*/},line=${BASH_LINENO[0]}::$(timestamp) ${*}" 18 | } 19 | 20 | function verify_test_step() { 21 | if [[ $1 != '0' ]]; then 22 | print_error "$2" 23 | print_error "Keptn test step failed" 24 | exit 1 25 | fi 26 | } 27 | 28 | # wait for a deployment to be up and running 29 | function wait_for_deployment_in_namespace() { 30 | DEPLOYMENT=$1; NAMESPACE=$2; 31 | RETRY=0; RETRY_MAX=20; 32 | 33 | while [[ $RETRY -lt $RETRY_MAX ]]; do 34 | DEPLOYMENT_LIST=$(eval "kubectl get deployments -n ${NAMESPACE} | awk '/$DEPLOYMENT /'" | awk '{print $1}') # list of multiple deployments when starting with the same name 35 | if [[ -z "$DEPLOYMENT_LIST" ]]; then 36 | RETRY=$((RETRY+1)) 37 | echo "Retry: ${RETRY}/${RETRY_MAX} - Deployment not found - waiting 15s for deployment ${DEPLOYMENT} in namespace ${NAMESPACE}" 38 | sleep 15 39 | else 40 | READY_REPLICAS=$(eval kubectl get deployments "$DEPLOYMENT" -n "$NAMESPACE" -o=jsonpath='{$.status.availableReplicas}') 41 | WANTED_REPLICAS=$(eval kubectl get deployments "$DEPLOYMENT" -n "$NAMESPACE" -o=jsonpath='{$.spec.replicas}') 42 | UNAVAILABLE_REPLICAS=$(eval kubectl get deployments "$DEPLOYMENT" -n "$NAMESPACE" -o=jsonpath='{$.status.unavailableReplicas}') 43 | if [[ "$READY_REPLICAS" = "$WANTED_REPLICAS" && "$UNAVAILABLE_REPLICAS" = "" ]]; then 44 | echo "Found deployment ${DEPLOYMENT} in namespace ${NAMESPACE}: ${DEPLOYMENT_LIST}" 45 | break 46 | else 47 | RETRY=$((RETRY+1)) 48 | echo "Retry: ${RETRY}/${RETRY_MAX} - Unsufficient replicas for deployment - waiting 15s for deployment ${DEPLOYMENT} in namespace ${NAMESPACE}" 49 | sleep 15 50 | fi 51 | fi 52 | done 53 | 54 | if [[ $RETRY == "$RETRY_MAX" ]]; then 55 | print_error "Could not find deployment ${DEPLOYMENT} in namespace ${NAMESPACE}" 56 | exit 1 57 | fi 58 | } 59 | 60 | # ingress settings 61 | INGRESS_PORT=$2 62 | INGRESS_IP=127.0.0.1 63 | 64 | if [ -z "$INGRESS_PORT" ]; then 65 | INGRESS_PORT=8082 66 | fi 67 | 68 | # retries for opening the bridge 69 | MAX_RETRIES=5 70 | SLEEP_TIME=5 71 | 72 | 73 | print_headline "Configuring Ingress for your local installation" 74 | 75 | K8S_VERSION=$(kubectl version -ojson) 76 | K8S_VERSION_MINOR=$(echo "$K8S_VERSION" | grep 'minor' | tail -1 | sed 's/^.*: //' | sed 's/^"\(.*\)".*/\1/') 77 | 78 | if [[ "$K8S_VERSION_MINOR" < "19" ]] 79 | then 80 | echo "Detected Kubernetes version < 1.19" 81 | # Applying ingress-manifest 82 | kubectl apply -f - < /dev/null 157 | then 158 | echo "Open command not found. Printing connection details instead" 159 | echo http://$INGRESS_IP.nip.io:$INGRESS_PORT/bridge 160 | break 161 | else 162 | "${OPEN[@]}" http://$INGRESS_IP.nip.io:$INGRESS_PORT/bridge 163 | break 164 | fi 165 | fi 166 | echo "Keptn bridge not yet available, waiting $SLEEP_TIME seconds and then trying again" 167 | retries=$[$retries +1] 168 | sleep $SLEEP_TIME 169 | http_code=$(curl -LI http://$INGRESS_IP.nip.io:$INGRESS_PORT/bridge -o /dev/null -w '%{http_code}\n' -s) 170 | done 171 | 172 | if [ $retries -ge $MAX_RETRIES ]; then 173 | echo "Bridge not yet available at http://$INGRESS_IP.nip.io:$INGRESS_PORT/bridge" 174 | echo "Please check the log for any errors that might have happened." 175 | else 176 | print_headline "Welcome aboard!" 177 | echo "Find the Keptn Bridge at http://$INGRESS_IP.nip.io:$INGRESS_PORT/bridge " 178 | echo "Find the Keptn API at http://$INGRESS_IP.nip.io:$INGRESS_PORT/api " 179 | echo "For more information please visit https://keptn.sh " 180 | echo "" 181 | fi 182 | -------------------------------------------------------------------------------- /quickstart/multistage-delivery.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | set -e 3 | 4 | PROJECT="podtatohead" 5 | SERVICE="helloservice" 6 | IMAGE="ghcr.io/podtato-head/podtatoserver" 7 | VERSION=v0.1.1 8 | SLOW_VERSION=v0.1.2 9 | 10 | #source <(curl -s https://raw.githubusercontent.com/keptn/keptn/0.8.5/test/utils.sh) 11 | 12 | function print_headline() { 13 | HEADLINE=$1 14 | 15 | echo "" 16 | echo "---------------------------------------------------------------------" 17 | echo $HEADLINE 18 | echo "---------------------------------------------------------------------" 19 | echo "" 20 | } 21 | 22 | function verify_helm_installation(){ 23 | if ! command -v helm &> /dev/null 24 | then 25 | echo "Could not find helm. Please install helm to proceed further." 26 | exit 27 | fi 28 | } 29 | 30 | function print_error() { 31 | echo "::error file=${BASH_SOURCE[1]##*/},line=${BASH_LINENO[0]}::$(timestamp) ${*}" 32 | } 33 | 34 | function verify_test_step() { 35 | if [[ $1 != '0' ]]; then 36 | print_error "$2" 37 | print_error "Keptn test step failed" 38 | exit 1 39 | fi 40 | } 41 | 42 | function verify_namespace_exists() { 43 | NAMESPACE=$1; 44 | 45 | NAMESPACE_LIST=$(eval "kubectl get namespaces -L istio-injection | grep ${NAMESPACE} | awk '/$NAMESPACE/'" | awk '{print $1}') 46 | 47 | if [[ -z "$NAMESPACE_LIST" ]]; then 48 | print_error "Could not find namespace ${NAMESPACE}" 49 | exit 2 50 | else 51 | echo "Found namespace ${NAMESPACE}" 52 | fi 53 | } 54 | 55 | # wait for a deployment to be up and running 56 | function wait_for_deployment_in_namespace() { 57 | DEPLOYMENT=$1; NAMESPACE=$2; 58 | RETRY=0; RETRY_MAX=10; 59 | 60 | while [[ $RETRY -lt $RETRY_MAX ]]; do 61 | DEPLOYMENT_LIST=$(eval "kubectl get deployments -n ${NAMESPACE} | awk '/$DEPLOYMENT /'" | awk '{print $1}') # list of multiple deployments when starting with the same name 62 | if [[ -z "$DEPLOYMENT_LIST" ]]; then 63 | RETRY=$((RETRY+1)) 64 | echo "Retry: ${RETRY}/${RETRY_MAX} - Deployment not found - waiting 15s for deployment ${DEPLOYMENT} in namespace ${NAMESPACE}" 65 | sleep 15 66 | else 67 | READY_REPLICAS=$(eval kubectl get deployments "$DEPLOYMENT" -n "$NAMESPACE" -o=jsonpath='{$.status.availableReplicas}') 68 | WANTED_REPLICAS=$(eval kubectl get deployments "$DEPLOYMENT" -n "$NAMESPACE" -o=jsonpath='{$.spec.replicas}') 69 | UNAVAILABLE_REPLICAS=$(eval kubectl get deployments "$DEPLOYMENT" -n "$NAMESPACE" -o=jsonpath='{$.status.unavailableReplicas}') 70 | if [[ "$READY_REPLICAS" = "$WANTED_REPLICAS" && "$UNAVAILABLE_REPLICAS" = "" ]]; then 71 | echo "Found deployment ${DEPLOYMENT} in namespace ${NAMESPACE}: ${DEPLOYMENT_LIST}" 72 | break 73 | else 74 | RETRY=$((RETRY+1)) 75 | echo "Retry: ${RETRY}/${RETRY_MAX} - Unsufficient replicas for deployment - waiting 15s for deployment ${DEPLOYMENT} in namespace ${NAMESPACE}" 76 | sleep 15 77 | fi 78 | fi 79 | done 80 | 81 | if [[ $RETRY == "$RETRY_MAX" ]]; then 82 | print_error "Could not find deployment ${DEPLOYMENT} in namespace ${NAMESPACE}" 83 | exit 1 84 | fi 85 | } 86 | 87 | 88 | INGRESS_PORT=$1 89 | INGRESS_IP=127.0.0.1 90 | 91 | if [ -z "$INGRESS_PORT" ]; then 92 | INGRESS_PORT=8082 93 | fi 94 | 95 | 96 | verify_helm_installation 97 | 98 | print_headline "Downloading demo resources" 99 | echo "This will create a local folder ./examples" 100 | echo "git clone https://github.com/keptn/examples --single-branch" 101 | 102 | DIR="./examples" 103 | BOLD='\033[1m' 104 | NORMAL='\033[0m' 105 | if [ -d "$DIR" ]; then 106 | # Example directory already exists 107 | echo -e "\n${BOLD}Example directory already exists in folder. Skipping download!${NORMAL}" 108 | else 109 | # Example Directory does not exist 110 | git clone https://github.com/keptn/examples --single-branch 111 | fi 112 | 113 | cd examples/quickstart 114 | 115 | print_headline "Create a Keptn project" 116 | echo "keptn create project $PROJECT --shipyard=./demo/shipyard.yaml" 117 | keptn create project $PROJECT --shipyard=./demo/shipyard.yaml 118 | verify_test_step $? "keptn create project command failed." 119 | 120 | print_headline "Create a Keptn service" 121 | echo "keptn create service $SERVICE --project=${PROJECT} " 122 | keptn create service $SERVICE --project="${PROJECT}" 123 | 124 | print_headline "Add Helm chart for $SERVICE" 125 | keptn add-resource --project=$PROJECT --service=$SERVICE --all-stages --resource=./demo/helm/helloservice.tgz --resourceUri=helm/helloservice.tgz 126 | 127 | print_headline "Add endpoints file" 128 | keptn add-resource --project=$PROJECT --service=$SERVICE --stage=hardening --resource=./demo/helm/hardening_endpoints.yaml --resourceUri=helm/endpoints.yaml 129 | keptn add-resource --project=$PROJECT --service=$SERVICE --stage=production --resource=./demo/helm/production_endpoints.yaml --resourceUri=helm/endpoints.yaml 130 | 131 | # adding quality gates 132 | print_headline "Installing Prometheus" 133 | kubectl create ns monitoring 134 | helm repo add prometheus-community https://prometheus-community.github.io/helm-charts 135 | helm install prometheus prometheus-community/prometheus --namespace monitoring --wait 136 | 137 | verify_test_step $? "Install prometheus failed" 138 | 139 | K8S_VERSION=$(kubectl version -ojson) 140 | K8S_VERSION_MINOR=$(echo "$K8S_VERSION" | grep 'minor' | tail -1 | sed 's/^.*: //' | sed 's/^"\(.*\)".*/\1/') 141 | 142 | if [[ "$K8S_VERSION_MINOR" < "19" ]] 143 | then 144 | echo "Detected Kubernetes version < 1.19" 145 | # Applying ingress-manifest 146 | kubectl apply -f - < /dev/null 242 | then 243 | echo http://$INGRESS_IP.nip.io:$INGRESS_PORT/bridge/project/podtatohead/sequence 244 | else 245 | "${OPEN[@]}" http://$INGRESS_IP.nip.io:$INGRESS_PORT/bridge/project/podtatohead/sequence 246 | fi 247 | 248 | 249 | print_headline "Have a look at the Keptn Bridge and explore the demo project" 250 | echo "You can run a new delivery sequence with the following command" 251 | echo "keptn trigger delivery --project=$PROJECT --service=$SERVICE --image=$IMAGE --tag=$VERSION" 252 | 253 | print_headline "Multi-stage delviery demo with SLO-based quality gates has been successfully set up" 254 | echo "If you want to connect the demo to a Git upstream to learn how Keptn manages the resources please execute" 255 | echo " keptn update project podtatohead --git-user=GIT_USER --git-token=GIT_TOKEN --git-remote-url=GIT_REMOTE_URL " 256 | echo "with your git user, token, and remote url." 257 | echo "Learn more at https://keptn.sh/docs/0.9.x/manage/git_upstream/ " 258 | 259 | echo "You can run a new delivery sequence with the following command" 260 | echo "keptn trigger delivery --project=$PROJECT --service=$SERVICE --image=$IMAGE --tag=$VERSION" 261 | echo "or by deploying a slow version that will not pass the quality gate" 262 | echo "keptn trigger delivery --project=$PROJECT --service=$SERVICE --image=$IMAGE --tag=$SLOW_VERSION" 263 | -------------------------------------------------------------------------------- /simplenodeservice/.gitignore: -------------------------------------------------------------------------------- 1 | npm-debug.log 2 | serviceoutput.log 3 | helpers/loadtest.log 4 | helpers/nul 5 | nul 6 | loadtest.log 7 | -------------------------------------------------------------------------------- /simplenodeservice/README.md: -------------------------------------------------------------------------------- 1 | # Simplenodeservice: Node.js based sample app for Keptn 2 | 3 | This sample app is a modified version of the Node.js sample app from the [AWS Elastic Beanstalk Tutorial](https://docs.aws.amazon.com/elasticbeanstalk/latest/dg/nodejs-getstarted.html) 4 | 5 | I mainly use it to demonstrate continuous delivery, automated quality gates and self-healing of the Open Source project [Keptn](www.keptn.sh) as well as the monitoring capabilities of [Dynatrace](www.dynatrace.com) 6 | 7 | ## Extended Feature Set 8 | I've modified and extended it with a couple of additional API calls such as: 9 | * echo a string 10 | * invoke a server-side URL and return the byte size 11 | * "login" with a username 12 | * get the currently running version 13 | 14 | ![](./images/simplenodesersviceui.png) 15 | 16 | ## 4 Builds with different behavior 17 | 18 | I've also built-in an option to slow down server-side code execution or to simulate failed requests. 19 | The app also comes with 4 built-in "build number" behaviors - meaning - if you launch the app and tell it to run as Build 1, 2, 3 or 4 it shows slightly different behavior. You can also launch the application in Production or Non-Production Mode: 20 | 21 | | Build | Behavior | Dockerhub Image | 22 | | ----- | --------- | ------------ | 23 | | 1 | Everything good | grabnerandi/simplenodeservice:1.0.0 | 24 | |2|50% Failure Rate of /api/invoke| grabnerandi/simplenodeservice:2.0.0 | 25 | |3|Everything good| grabnerandi/simplenodeservice:3.0.0 | 26 | |4|20% Failure Rate of /api/invoke and twice as slow when running in production mode| grabnerandi/simplenodeservice:4.0.0 | 27 | 28 | Every build shows the build number and has its own color: 29 | ![](./images/4buildoverview.png) 30 | 31 | ### Switching builds without redeploy 32 | 33 | Every build is based on the same source code and the actual "changed behavior" is enforced at runtime by checking which build number it is supposed to run. 34 | When launching the 4 different versions each version in fact runs the same code but is just launched with a special environment variable that tells it which build it defaults to. 35 | You can therefore define the build during startup 36 | ``` 37 | export BUILD_NUMBER=2 38 | run simplenodeservice 39 | ``` 40 | 41 | or you can change it at runtime by calling a special URL 42 | ``` 43 | http://yoursimplenodeurl/api/version?newBuildNumber=2 44 | ``` 45 | 46 | ## How to run it 47 | 48 | There are different options on how to run / deploy that app. By default the port that is used is port 8080. You can however change that specifying a different value in the PORT environment variable. There are a couple of other environment variables that are important for you as they will be reflected in the UI and also decide whether the app believes it runs "in Production" or not. 49 | 50 | | Env variable | Example values | Description | 51 | | ------------ | -------------- | --------------- | 52 | | PORT | 8080 | port it binds to: default is 8080 | 53 | | DEPLOYMENT_GROUP_NAME | Production | will tell the app whether it runs in production or somewhere else | 54 | | NAMESPACE | Dev | This is used for display purposes in the UI | 55 | 56 | 57 | | Run where | How | Description | 58 | | --------- | --- | --------------------------------| 59 | | Local | npm start | Clone the source code repo from [simplenodeservice](https://github.com/grabnerandi/simplenodeservice) | 60 | | Docker | docker run -p 8080:8080 grabnerandi/simplenodeservice:1.0.0 | will expose the app on local port 8080 61 | | k8s | https://github.com/grabnerandi/keptn-qualitygate-examples | Follow the instructions there | 62 | 63 | ## How to run load against it 64 | 65 | If you want to simulate some load you can either create your own load testing scripts or use browser plugins that will for instance reload the webpage to simulate load. 66 | This example also comes with a very simply gen_load.sh bash script which uses curl to simulate load against the 4 major use cases that this app supports: Homepage, Echo, Version & Invoke. The script accepts two parameters: URL and Environment allowing you to set the URL of your app and either define "Production" or "non-production". The difference here is that "non-production" will also send the x-dynatrace-test HTTP Header which is used by Dynatrace for load testing integrations. 67 | 68 | Here two examples to launch that script: 69 | ``` 70 | ./gen_load.sh http://localhost:8080 Production 71 | 72 | ./gen_load.sh http://yoururl:8080 Staging 73 | ``` 74 | 75 | Once the script runs you can either abort it or you can create an empty file with the name endloadtest.txt. Once this file exists the script will also stop running! 76 | 77 | 78 | ## Using it with Keptn 0.7+ 79 | 80 | Here are the instructions on how to use Keptn to deploy and manage this application 81 | 82 | ### Tips for setting up Dynatrace Integration 83 | 84 | kubectl -n keptn create secret generic dynatrace --from-literal="DT_TENANT=$DT_TENANT" --from-literal="DT_API_TOKEN=$DT_API_TOKEN" --from-literal="DT_PAAS_TOKEN=$DT_PAAS_TOKEN" --from-literal="KEPTN_API_URL=http://$(kubectl -n keptn get ingress api-keptn-ingress -ojsonpath={.spec.rules[0].host})/api" --from-literal="KEPTN_API_TOKEN=$(kubectl get secret keptn-api-token -n keptn -ojsonpath={.data.keptn-api-token} | base64 --decode)" --from-literal="KEPTN_BRIDGE_URL=http://$(kubectl -n keptn get ingress api-keptn-ingress -ojsonpath={.spec.rules[0].host})/bridge" 85 | 86 | ### 1: Create Project & Upload Files 87 | 88 | The Keptn folder contains a lot of supporting resource files such as the Helm Charts, SLIs, SLOs or test scripts. Please navigate into that directory before executing these commands: 89 | 90 | ``` 91 | keptn create project keptn07project --shipyard=./shipyard_keptn07.yaml 92 | keptn onboard service simplenode --project=keptn07project --chart=./charts 93 | 94 | // define an empty SLO.yaml so that Keptn will look for a dashboard SLO 95 | keptn add-resource --project=keptn07project --stage=staging --service=simplenode --resource=slo_empty.yaml --resourceUri=slo.yaml 96 | keptn add-resource --project=keptn07project --stage=prod --service=simplenode --resource=slo_empty.yaml --resourceUri=slo.yaml 97 | 98 | // Upload Test Scripts for dev, staging & prod 99 | keptn add-resource --project=keptn07project --stage=dev --service=simplenode --resource=jmeter/basiccheck.jmx --resourceUri=jmeter/basiccheck.jmx 100 | keptn add-resource --project=keptn07project --stage=dev --service=simplenode --resource=jmeter/load.jmx --resourceUri=jmeter/load.jmx 101 | keptn add-resource --project=keptn07project --stage=dev --service=simplenode --resource=jmeter/jmeter.conf.yaml --resourceUri=jmeter/jmeter.conf.yaml 102 | 103 | keptn add-resource --project=keptn07project --stage=staging --service=simplenode --resource=jmeter/basiccheck.jmx --resourceUri=jmeter/basiccheck.jmx 104 | keptn add-resource --project=keptn07project --stage=staging --service=simplenode --resource=jmeter/load.jmx --resourceUri=jmeter/load.jmx 105 | keptn add-resource --project=keptn07project --stage=staging --service=simplenode --resource=jmeter/jmeter.conf.yaml --resourceUri=jmeter/jmeter.conf.yaml 106 | 107 | keptn add-resource --project=keptn07project --stage=prod --service=simplenode --resource=jmeter/basiccheck.jmx --resourceUri=jmeter/basiccheck.jmx 108 | keptn add-resource --project=keptn07project --stage=prod --service=simplenode --resource=jmeter/load.jmx --resourceUri=jmeter/load.jmx 109 | keptn add-resource --project=keptn07project --stage=prod --service=simplenode --resource=jmeter/jmeter.conf.yaml --resourceUri=jmeter/jmeter.conf.yaml 110 | 111 | ``` 112 | 113 | ### 2: Deploy the app 114 | 115 | ``` 116 | keptn send event new-artifact --project=keptn07project --service=simplenode --image=docker.io/grabnerandi/simplenodeservice --tag=1.0.0 117 | keptn send event new-artifact --project=keptn07project --service=simplenode --image=docker.io/grabnerandi/simplenodeservice --tag=2.0.0 118 | keptn send event new-artifact --project=keptn07project --service=simplenode --image=docker.io/grabnerandi/simplenodeservice --tag=3.0.0 119 | keptn send event new-artifact --project=keptn07project --service=simplenode --image=docker.io/grabnerandi/simplenodeservice --tag=4.0.0 120 | ``` 121 | 122 | ### 3: Deploy the app via events including labels 123 | 124 | To add labels to a new-artifact we can use keptn send event with the event details in the event.json. Before executing the following command make sure to edit your configchangeevent.json and change the version to 1.0.0, 2.0.0, 3.0.0 or 4.0.0. Also update the BuildId to reflect the BuildId from the CI tool e.g: Build101, Build102, Build103, Build104 125 | 126 | ``` 127 | keptn send event --file=configchangeevent.json 128 | ``` -------------------------------------------------------------------------------- /simplenodeservice/dynatrace/createCalculatedMetrics.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # Usage: 4 | # ./createCalculatedMetrics.sh CONTEXTLESS keptn-project simpleproject 5 | if [[ -z "$DT_TENANT" || -z "$DT_API_TOKEN" ]]; then 6 | echo "DT_TENANT & DT_API_TOKEN MUST BE SET!!" 7 | exit 1 8 | fi 9 | CONDITION_CONTEXT=$1 10 | CONDITION_KEY=$2 11 | CONDITION_VALUE=$3 12 | 13 | if [[ -z "$CONDITION_KEY" && -z "$CONDITION_VALUE" ]]; then 14 | echo "You have to at least specify a Tag Key or Value as a filter:" 15 | echo "Usage: ./createCalculatedMetrics.sh CONTEXTLESS keptn_project simpleproject" 16 | exit 1 17 | fi 18 | 19 | echo "=============================================================" 20 | echo "About to create 3 service metrics with condition [$1]$2:$3 on Dynatrace Tenant: $DT_TENANT!" 21 | echo "=============================================================" 22 | echo "Usage: ./createCalculatedMetric CONTEXT KEY VALUE" 23 | read -rsp $'Press ctrl-c to abort. Press any key to continue...\n' -n1 key 24 | 25 | #################################################################################################################### 26 | ## createCalculatedMetric(METRICKEY, METRICNAME, BASEMETRIC, UNIT, CONTEXT, KEY, VALUE, DIMENSIONNAME, DIMENSIONDEF DIMENSIONAGGR) 27 | #################################################################################################################### 28 | # Example: createCalculatedMetric("calc:service.topurlresponsetime", "Top URL Response Time", "RESPONSE_TIME", "CONTEXTLESS", "keptn_project", "simpleproject", "URL", "{URL}" "COUNT") 29 | # Full List of possible BASEMETRICS: CPU_TIME, DATABASE_CHILD_CALL_COUNT, DATABASE_CHILD_CALL_TIME, EXCEPTION_COUNT, FAILED_REQUEST_COUNT, FAILED_REQUEST_COUNT_CLIENT, FAILURE_RATE, FAILURE_RATE_CLIENT, HTTP_4XX_ERROR_COUNT, HTTP_4XX_ERROR_COUNT_CLIENT, HTTP_5XX_ERROR_COUNT, HTTP_5XX_ERROR_COUNT_CLIENT, IO_TIME, LOCK_TIME, NON_DATABASE_CHILD_CALL_COUNT, NON_DATABASE_CHILD_CALL_TIME, REQUEST_ATTRIBUTE, REQUEST_COUNT, RESPONSE_TIME, RESPONSE_TIME_CLIENT, SUCCESSFUL_REQUEST_COUNT, SUCCESSFUL_REQUEST_COUNT_CLIENT, TOTAL_PROCESSING_TIME, WAIT_TIME 30 | function createCalculatedMetric() { 31 | METRICKEY=$1 32 | METRICNAME=$2 33 | BASEMETRIC=$3 34 | METRICUNIT=$4 35 | CONDITION_CONTEXT=$5 36 | CONDITION_KEY=$6 37 | CONDITION_VALUE=$7 38 | DIMENSION_NAME=$8 39 | DIMENSION_DEFINTION=$9 40 | DIMENSION_AGGREGATE=${10} 41 | 42 | PAYLOAD='{ 43 | "tsmMetricKey": "'$METRICKEY'", 44 | "name": "'$METRICNAME'", 45 | "enabled": true, 46 | "metricDefinition": { 47 | "metric": "'$BASEMETRIC'", 48 | "requestAttribute": null 49 | }, 50 | "unit": "'$METRICUNIT'", 51 | "unitDisplayName": "", 52 | "conditions": [ 53 | { 54 | "attribute": "SERVICE_TAG", 55 | "comparisonInfo": { 56 | "type": "TAG", 57 | "comparison": "TAG_KEY_EQUALS", 58 | "value": { 59 | "context": "'$CONDITION_CONTEXT'", 60 | "key": "'$CONDITION_KEY'", 61 | "value": "'$CONDITION_VALUE'" 62 | }, 63 | "negate": false 64 | } 65 | } 66 | ], 67 | "dimensionDefinition": { 68 | "name": "'$DIMENSION_NAME'", 69 | "dimension": "'$DIMENSION_DEFINTION'", 70 | "placeholders": [], 71 | "topX": 10, 72 | "topXDirection": "DESCENDING", 73 | "topXAggregation": "'$DIMENSION_AGGREGATE'" 74 | } 75 | }' 76 | 77 | echo "Creating Metric $METRICNAME($METRICKEY)" 78 | echo "PUT https://$DT_TENANT/api/config/v1/calculatedMetrics/service/$METRICKEY" 79 | echo "$PAYLOAD" 80 | curl -X PUT \ 81 | "https://$DT_TENANT/api/config/v1/calculatedMetrics/service/$METRICKEY" \ 82 | -H 'accept: application/json; charset=utf-8' \ 83 | -H "Authorization: Api-Token $DT_API_TOKEN" \ 84 | -H 'Content-Type: application/json; charset=utf-8' \ 85 | -d "$PAYLOAD" \ 86 | -o curloutput.txt 87 | 88 | cat curloutput.txt 89 | } 90 | 91 | ## Creates a Calculated Service Metrics "Top URL Response Time"" 92 | ## Metrics Id: calc:service.topurlresponsetime 93 | ## Base Metric: Response Time (RESPONSE_TIME) 94 | ## Dimension: URL 95 | ## Condition: service tag [$TAG_CONTEXT]$TAG_KEY:TAG_VALUE 96 | createCalculatedMetric "calc:service.topurlresponsetime" "Top URL Response Time" "RESPONSE_TIME" "MICRO_SECOND" "$CONDITION_CONTEXT" "$CONDITION_KEY" "$CONDITION_VALUE" "URL" "{URL:Path}" "SUM" 97 | 98 | 99 | ## Creates a Calculated Service Metrics "Top URL Service Calls" 100 | ## Metrics Id: calc:service.topurlservicecalls 101 | ## Base Metric: Number of calls to other services (NON_DATABASE_CHILD_CALL_COUNT) 102 | ## Dimension: URL 103 | ## Condition: service tag [$TAG_CONTEXT]$TAG_KEY:TAG_VALUE 104 | createCalculatedMetric "calc:service.topurlservicecalls" "Top URL Service Calls" "NON_DATABASE_CHILD_CALL_COUNT" "COUNT" "$CONDITION_CONTEXT" "$CONDITION_KEY" "$CONDITION_VALUE" "URL" "{URL:Path}" "SINGLE_VALUE" 105 | 106 | ## Creates a Calculated Service Metrics "Top URL Service Calls" 107 | ## Metrics Id: calc:service.topurlservicecalls 108 | ## Base Metric: Number of calls to other services (NON_DATABASE_CHILD_CALL_COUNT) 109 | ## Dimension: URL 110 | ## Condition: service tag [$TAG_CONTEXT]$TAG_KEY:TAG_VALUE 111 | createCalculatedMetric "calc:service.topurldbcalls" "Top URL DB Calls" "DATABASE_CHILD_CALL_COUNT" "COUNT" "$CONDITION_CONTEXT" "$CONDITION_KEY" "$CONDITION_VALUE" "URL" "{URL:Path}" "SINGLE_VALUE" -------------------------------------------------------------------------------- /simplenodeservice/dynatrace/createTestRequestAttributes.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # Usage: 4 | # ./createTestRequestAttributes.sh 5 | 6 | if [[ -z "$DT_TENANT" || -z "$DT_API_TOKEN" ]]; then 7 | echo "DT_TENANT & DT_API_TOKEN MUST BE SET!!" 8 | exit 1 9 | fi 10 | 11 | echo "=============================================================" 12 | echo "About to create 3 Request Attributes (LTN, LSN, LTN) for better Test Integrations on Dynatrace Tenant: $DT_TENANT!" 13 | echo "If these Request Attributes already exists they WONT be overwritten!" 14 | echo "For more information about Load Testing Integration with Dynatrace check out the doc!" 15 | echo "=============================================================" 16 | echo "Usage: ./createTestRequestAttributes" 17 | read -rsp $'Press ctrl-c to abort. Press any key to continue...\n' -n1 key 18 | 19 | #################################################################################################################### 20 | ## createRequestAttribute(ATTRIBUTENAME, ATTRIBUTEPREFIX, HEADERNAME) 21 | #################################################################################################################### 22 | # Example: createRequestAttribute "TSN", "TSN", "x-dynatrace-test") 23 | function createRequestAttribute() { 24 | ATTRIBUTENAME=$1 25 | ATTRIBUTEPREFIX=$2 26 | HEADERNAME=$3 27 | PAYLOAD='{ 28 | "name": "'$ATTRIBUTENAME'", 29 | "enabled": true, 30 | "dataType": "STRING", 31 | "dataSources": [ 32 | { 33 | "enabled": true, 34 | "source": "REQUEST_HEADER", 35 | "valueProcessing": { 36 | "splitAt": "", 37 | "trim": false, 38 | "extractSubstring": { 39 | "position": "BETWEEN", 40 | "delimiter": "'$ATTRIBUTEPREFIX'=", 41 | "endDelimiter": ";" 42 | } 43 | }, 44 | "parameterName": "'$HEADERNAME'", 45 | "capturingAndStorageLocation": "CAPTURE_AND_STORE_ON_SERVER" 46 | } 47 | ], 48 | "normalization": "ORIGINAL", 49 | "aggregation": "FIRST", 50 | "confidential": false, 51 | "skipPersonalDataMasking": false 52 | }' 53 | 54 | echo "" 55 | echo "Creating Request Attribute $ATTRIBUTENAME. Parsing $ATTRIBUTEPREFIX=??; from $HEADERNAME)" 56 | echo "POST https://$DT_TENANT/api/config/v1/service/requestAttributes" 57 | echo "$PAYLOAD" 58 | curl -X POST \ 59 | "https://$DT_TENANT/api/config/v1/service/requestAttributes" \ 60 | -H 'accept: application/json; charset=utf-8' \ 61 | -H "Authorization: Api-Token $DT_API_TOKEN" \ 62 | -H 'Content-Type: application/json; charset=utf-8' \ 63 | -d "$PAYLOAD" \ 64 | -o curloutput.txt 65 | cat curloutput.txt 66 | echo "" 67 | } 68 | 69 | 70 | ########################################################################### 71 | # Create the 3 Request Attributes to parse out TSN (Test Step), LSN (Load Script Name), LTN (Load Test NAme) 72 | ########################################################################### 73 | createRequestAttribute "TSN" "TSN" "x-dynatrace-test" 74 | createRequestAttribute "LTN" "LTN" "x-dynatrace-test" 75 | createRequestAttribute "LSN" "LSN" "x-dynatrace-test" 76 | -------------------------------------------------------------------------------- /simplenodeservice/dynatrace/createTestStepCalculatedMetrics.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # Usage: 4 | # ./createTestStepCalculatedMetrics.sh CONTEXTLESS keptn_project simpleproject 5 | if [[ -z "$DT_TENANT" || -z "$DT_API_TOKEN" ]]; then 6 | echo "DT_TENANT & DT_API_TOKEN MUST BE SET!!" 7 | exit 1 8 | fi 9 | CONDITION_CONTEXT=$1 10 | CONDITION_KEY=$2 11 | CONDITION_VALUE=$3 12 | 13 | if [[ -z "$CONDITION_KEY" && -z "$CONDITION_VALUE" ]]; then 14 | echo "You have to at least specify a Tag Key or Value as a filter:" 15 | echo "Usage: ./createTestStepCalculatedMetrics.sh TAGCONTEXT TAGKEY [TAGVALUE]" 16 | echo "Example #1 for Keptn projects: ./createTestStepCalculatedMetrics.sh CONTEXTLESS keptn_project" 17 | echo "Example #2 for Env-Tags: ./createTestStepCalculatedMetrics.sh ENVIRONMENT AppUnderTest" 18 | exit 1 19 | fi 20 | 21 | echo "=============================================================" 22 | echo "About to create 6 calculated service metrics for Test Tool Integrations on Dynatrace Tenant: $DT_TENANT!" 23 | echo "These metrics will filter on service tag [$1]$2:$3 and Request Attribute TSN exists" 24 | echo "=============================================================" 25 | echo "Usage: ./createTestStepCalculatedMetrics CONTEXT KEY VALUE" 26 | read -rsp $'Press ctrl-c to abort. Press any key to continue...\n' -n1 key 27 | 28 | #################################################################################################################### 29 | ## createCalculatedTestMetric(METRICKEY, METRICNAME, BASEMETRIC, METRICUNIT, DIMENSION_NAME, DIMENSION_PATTERN, DIMENSION_AGGREGATE) 30 | #################################################################################################################### 31 | # Example: createCalculatedTestMetric "calc:service.teststepresponsetime" "Test Step Response Time" "RESPONSE_TIME" "MICRO_SECOND" "Test Step" "{RequestAttribute:TSN}" "SUM" 32 | # Full List of possible BASEMETRICS: CPU_TIME, DATABASE_CHILD_CALL_COUNT, DATABASE_CHILD_CALL_TIME, EXCEPTION_COUNT, FAILED_REQUEST_COUNT, FAILED_REQUEST_COUNT_CLIENT, FAILURE_RATE, FAILURE_RATE_CLIENT, HTTP_4XX_ERROR_COUNT, HTTP_4XX_ERROR_COUNT_CLIENT, HTTP_5XX_ERROR_COUNT, HTTP_5XX_ERROR_COUNT_CLIENT, IO_TIME, LOCK_TIME, NON_DATABASE_CHILD_CALL_COUNT, NON_DATABASE_CHILD_CALL_TIME, REQUEST_ATTRIBUTE, REQUEST_COUNT, RESPONSE_TIME, RESPONSE_TIME_CLIENT, SUCCESSFUL_REQUEST_COUNT, SUCCESSFUL_REQUEST_COUNT_CLIENT, TOTAL_PROCESSING_TIME, WAIT_TIME 33 | # Possible METRICUNIT values: MILLI_SECOND, MICRO_SECOND, COUNT, PERCENT 34 | # Possible DIMENSION_AGGREGATE: AVERAGE, COUNT, MAX, MIN, OF_INTEREST_RATIO, OTHER_RATIO, SINGLE_VALUE, SUM 35 | function createCalculatedTestMetric() { 36 | METRICKEY=$1 37 | METRICNAME=$2 38 | BASEMETRIC=$3 39 | METRICUNIT=$4 40 | DIMENSION_NAME=$5 41 | DIMENSION_PATTERN=$6 42 | DIMENSION_AGGREGATE=$7 43 | PAYLOAD='{ 44 | "tsmMetricKey": "'$METRICKEY'", 45 | "name": "'$METRICNAME'", 46 | "enabled": true, 47 | "metricDefinition": { 48 | "metric": "'$BASEMETRIC'", 49 | "requestAttribute": null 50 | }, 51 | "unit": "'$METRICUNIT'", 52 | "unitDisplayName": "", 53 | "conditions": [ 54 | { 55 | "attribute": "SERVICE_REQUEST_ATTRIBUTE", 56 | "comparisonInfo": { 57 | "type": "STRING_REQUEST_ATTRIBUTE", 58 | "comparison": "EXISTS", 59 | "value": null, 60 | "negate": false, 61 | "requestAttribute": "TSN", 62 | "caseSensitive": false 63 | } 64 | }, 65 | { 66 | "attribute": "SERVICE_TAG", 67 | "comparisonInfo": { 68 | "type": "TAG", 69 | "comparison": "TAG_KEY_EQUALS", 70 | "value": { 71 | "context": "'$CONDITION_CONTEXT'", 72 | "key": "'$CONDITION_KEY'", 73 | "value": "'$CONDITION_VALUE'" 74 | }, 75 | "negate": false 76 | } 77 | } 78 | ], 79 | "dimensionDefinition": { 80 | "name": "'$DIMENSION_NAME'", 81 | "dimension": "'$DIMENSION_PATTERN'", 82 | "placeholders": [], 83 | "topX": 10, 84 | "topXDirection": "DESCENDING", 85 | "topXAggregation": "'$DIMENSION_AGGREGATE'" 86 | } 87 | }' 88 | 89 | echo "" 90 | echo "Creating Metric $METRICNAME($METRICNAME)" 91 | echo "PUT https://$DT_TENANT/api/config/v1/calculatedMetrics/service/$METRICKEY" 92 | echo "$PAYLOAD" 93 | curl -X PUT \ 94 | "https://$DT_TENANT/api/config/v1/calculatedMetrics/service/$METRICKEY" \ 95 | -H 'accept: application/json; charset=utf-8' \ 96 | -H "Authorization: Api-Token $DT_API_TOKEN" \ 97 | -H 'Content-Type: application/json; charset=utf-8' \ 98 | -d "$PAYLOAD" \ 99 | -o curloutput.txt 100 | cat curloutput.txt 101 | echo "" 102 | } 103 | 104 | 105 | 106 | ########################################################################### 107 | # 1: we create Test Step Response Time 108 | ########################################################################### 109 | createCalculatedTestMetric "calc:service.teststepresponsetime" "Test Step Response Time" "RESPONSE_TIME" "MICRO_SECOND" "Test Step" "{RequestAttribute:TSN}" "SUM" 110 | 111 | ########################################################################### 112 | # 2: we create Test Step Service Calls 113 | ########################################################################### 114 | createCalculatedTestMetric "calc:service.teststepservicecalls" "Test Step Service Calls" "NON_DATABASE_CHILD_CALL_COUNT" "COUNT" "Test Step" "{RequestAttribute:TSN}" "SUM" 115 | 116 | ########################################################################### 117 | # 3: we create Test Step Database Calls 118 | ########################################################################### 119 | createCalculatedTestMetric "calc:service.teststepdbcalls" "Test Step DB Calls" "DATABASE_CHILD_CALL_COUNT" "COUNT" "Test Step" "{RequestAttribute:TSN}" "SUM" 120 | 121 | ########################################################################### 122 | # 4: we create Test Step Failurerate 123 | ########################################################################### 124 | createCalculatedTestMetric "calc:service.teststepfailurerate" "Test Step Failure Rate" "FAILURE_RATE" "PERCENT" "Test Step" "{RequestAttribute:TSN}" "OF_INTEREST_RATIO" 125 | 126 | ########################################################################### 127 | # 5: we create Test Step by HTTP Status 128 | ########################################################################### 129 | createCalculatedTestMetric "calc:service.testrequestsbyhttpstatus" "Test Requests by HTTP Status" "REQUEST_COUNT" "COUNT" "HttpStatusClass" "{HTTP-StatusClass}" "SINGLE_VALUE" 130 | 131 | ########################################################################### 132 | # 6: we create Test Step CPU Time 133 | ########################################################################### 134 | createCalculatedTestMetric "calc:service.teststepcpu" "Test Step CPU" "CPU_TIME" "MICRO_SECOND" "Test Step" "{RequestAttribute:TSN}" "SUM" 135 | -------------------------------------------------------------------------------- /simplenodeservice/helpers/gen_load.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | if [ -z $1 ] 4 | then 5 | echo "gen_load http://servicendpoint Production|Staging" 6 | exit 1 7 | fi 8 | 9 | if [ -z $2 ] 10 | then 11 | echo "gen_load http://servicendpoint Production|Staging" 12 | exit 1 13 | fi 14 | 15 | echo "Load Test Launched against $1 - $2 " >> ./loadtest.log 16 | while [ ! -f ./endloadtest.txt ]; 17 | do 18 | # In Production we sleep less which means we will have more load 19 | # In Testing we also add the x-dynatrace HTTP Header so that we can demo our "load testing integration" options using Request Attributes! 20 | if [[ $2 == *"Production"* ]]; then 21 | curl -s "$1/" -o nul &> loadtest.log 22 | curl -s "$1/version" -o nul &> loadtest.log 23 | curl -s "$1/api/echo?text=This is from a production user" -o nul &> loadtest.log 24 | curl -s "$1/api/invoke?url=http://www.dynatrace.com" -o nul &> loadtest.log 25 | curl -s "$1/api/invoke?url=http://blog.dynatrace.com" -o nul &> loadtest.log 26 | 27 | sleep 2; 28 | else 29 | curl -s "$1/" -H "x-dynatrace-test: TSN=Test.Homepage;" -o nul &> loadtest.log 30 | curl -s "$1/version" -H "x-dynatrace-test: TSN=Test.Version;" -o nul &> loadtest.log 31 | curl -s "$1/api/echo?text=This is from a testing script" -H "x-dynatrace-test: TSN=Test.Echo;" -o nul &> loadtest.log 32 | curl -s "$1/api/invoke?url=http://www.dynatrace.com" -H "x-dynatrace-test: TSN=Test.Invoke;" -o nul &> loadtest.log 33 | 34 | sleep 5; 35 | fi 36 | done; 37 | exit 0 -------------------------------------------------------------------------------- /simplenodeservice/images/4buildoverview.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/keptn/examples/9ff28ef074d0ab7580aec06706994bb99a08b27f/simplenodeservice/images/4buildoverview.png -------------------------------------------------------------------------------- /simplenodeservice/images/simplenodesersviceui.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/keptn/examples/9ff28ef074d0ab7580aec06706994bb99a08b27f/simplenodeservice/images/simplenodesersviceui.png -------------------------------------------------------------------------------- /simplenodeservice/keptn/add_resources.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | if [[ -z "$KEPTN_PROJECT" ]]; then 4 | KEPTN_PROJECT=$1 5 | fi 6 | if [[ -z "$KEPTN_STAGE" ]]; then 7 | KEPTN_STAGE=$2 8 | fi 9 | if [[ -z "$KEPTN_SERVICE" ]]; then 10 | KEPTN_SERVICE=$3 11 | fi 12 | 13 | if [[ -z "$SLI_VERSION" ]]; then 14 | SLI_VERSION="basic" 15 | fi 16 | 17 | if [[ -z "$KEPTN_PROJECT" || -z "$KEPTN_STAGE" || -z "$KEPTN_SERVICE" ]]; then 18 | echo "You have to either set KEPTN_PROJECT, KEPTN_STAGE & KEPTN_SERVICE or pass them as arguments" 19 | echo "usage: ./add_resources.sh simplenodeproject staging simplenode [basic|perftest]" 20 | exit 1 21 | fi 22 | 23 | echo "Lets upload all relevant resource files to Keptn, e.g: SLI, SLO, jmeter, ..." 24 | 25 | # adding resources 26 | keptn add-resource --project=$PROJECT --service=$SERVICE --stage=$STAGE --resource=jmeter/jmeter.conf.yaml --resourceUri=jmeter/jmeter.conf.yaml 27 | keptn add-resource --project=$PROJECT --service=$SERVICE --stage=$STAGE --resource=jmeter/basiccheck.jmx --resourceUri=jmeter/basiccheck.jmx 28 | keptn add-resource --project=$PROJECT --service=$SERVICE --stage=$STAGE --resource=jmeter/load.jmx --resourceUri=jmeter/load.jmx 29 | keptn add-resource --project=$PROJECT --service=$SERVICE --stage=$STAGE --resource=dynatrace/dynatrace.conf.yaml --resourceUri=dynatrace/dynatrace.conf.yaml 30 | keptn add-resource --project=$PROJECT --service=$SERVICE --stage=$STAGE --resource=dynatrace/sli_$SLI_VERSION.yaml --resourceUri=dynatrace/sli.yaml 31 | keptn add-resource --project=$PROJECT --service=$SERVICE --stage=$STAGE --resource=slo_$SLI_VERSION.yaml --resourceUri=slo.yaml 32 | -------------------------------------------------------------------------------- /simplenodeservice/keptn/charts.tgz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/keptn/examples/9ff28ef074d0ab7580aec06706994bb99a08b27f/simplenodeservice/keptn/charts.tgz -------------------------------------------------------------------------------- /simplenodeservice/keptn/charts/Chart.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | description: A Helm chart for Keptn 3 | name: keptn 4 | version: 0.1.0 -------------------------------------------------------------------------------- /simplenodeservice/keptn/charts/templates/deployment.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | apiVersion: apps/v1 3 | kind: Deployment 4 | metadata: 5 | name: simplenode 6 | spec: 7 | replicas: {{ .Values.replicaCount }} 8 | strategy: 9 | rollingUpdate: 10 | maxUnavailable: 0 11 | type: RollingUpdate 12 | selector: 13 | matchLabels: 14 | app: simplenode 15 | template: 16 | metadata: 17 | labels: 18 | app: simplenode 19 | app.kubernetes.io/name: {{ .Values.keptn.service }} 20 | app.kubernetes.io/instance: "{{ .Values.keptn.service }}-{{ .Values.keptn.deployment }}" 21 | app.kubernetes.io/component: api 22 | app.kubernetes.io/part-of: "{{ .Values.keptn.project }}" 23 | app.kubernetes.io/managed-by: Keptn 24 | app.kubernetes.io/version: {{ (split ":" .Values.image)._1 | default "latest" }} 25 | spec: 26 | containers: 27 | - name: simplenode 28 | image: "{{ .Values.image }}" 29 | imagePullPolicy: Always 30 | ports: 31 | - name: http 32 | protocol: TCP 33 | containerPort: 8080 34 | env: 35 | - name: DT_CUSTOM_PROP 36 | value: "keptn_project={{ .Values.keptn.project }} keptn_service={{ .Values.keptn.service }} keptn_stage={{ .Values.keptn.stage }} keptn_deployment={{ .Values.keptn.deployment }}" 37 | - name: POD_NAME 38 | valueFrom: 39 | fieldRef: 40 | fieldPath: "metadata.name" 41 | - name: DEPLOYMENT_NAME 42 | valueFrom: 43 | fieldRef: 44 | fieldPath: "metadata.labels['deployment']" 45 | - name: CONTAINER_IMAGE 46 | value: "{{ .Values.image }}" 47 | - name: KEPTN_PROJECT 48 | value: "{{ .Values.keptn.project }}" 49 | - name: KEPTN_STAGE 50 | value: "{{ .Values.keptn.stage }}" 51 | - name: KEPTN_SERVICE 52 | value: "{{ .Values.keptn.service }}" 53 | - name: KEPTN_DEPLOYMENT 54 | value: "{{ .Values.keptn.deployment }}" 55 | livenessProbe: 56 | httpGet: 57 | path: / 58 | port: 8080 59 | initialDelaySeconds: 60 60 | periodSeconds: 10 61 | timeoutSeconds: 15 62 | readinessProbe: 63 | httpGet: 64 | path: / 65 | port: 8080 66 | initialDelaySeconds: 60 67 | periodSeconds: 10 68 | timeoutSeconds: 15 69 | resources: 70 | limits: 71 | cpu: 200m 72 | memory: 500Mi 73 | requests: 74 | cpu: 200m 75 | memory: 500Mi -------------------------------------------------------------------------------- /simplenodeservice/keptn/charts/templates/services.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | apiVersion: v1 3 | kind: Service 4 | metadata: 5 | name: simplenode 6 | spec: 7 | type: ClusterIP 8 | ports: 9 | - name: http 10 | port: 80 11 | protocol: TCP 12 | targetPort: 8080 13 | selector: 14 | app: simplenode -------------------------------------------------------------------------------- /simplenodeservice/keptn/charts/values.yaml: -------------------------------------------------------------------------------- 1 | image: docker.io/grabnerandi/simplenodeservice:1.0.0 2 | replicaCount: 1 -------------------------------------------------------------------------------- /simplenodeservice/keptn/dynatrace/dynatrace.conf.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | spec_version: '0.1.0' 3 | dtCreds: dynatrace 4 | attachRules: 5 | tagRule: 6 | - meTypes: 7 | - SERVICE 8 | tags: 9 | - context: CONTEXTLESS 10 | key: $SERVICE -------------------------------------------------------------------------------- /simplenodeservice/keptn/dynatrace/sli_basic.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | spec_version: '1.0' 3 | indicators: 4 | throughput: "metricSelector=builtin:service.requestCount.total:merge(0):sum&entitySelector=tag(keptn_project:$PROJECT),tag(keptn_stage:$STAGE),tag(keptn_service:$SERVICE),tag(keptn_deployment:$DEPLOYMENT),type(SERVICE)" 5 | error_rate: "metricSelector=builtin:service.errors.total.rate:merge(0):avg&entitySelector=tag(keptn_project:$PROJECT),tag(keptn_stage:$STAGE),tag(keptn_service:$SERVICE),tag(keptn_deployment:$DEPLOYMENT),type(SERVICE)" 6 | response_time_p50: "metricSelector=builtin:service.response.time:merge(0):percentile(50)&entitySelector=tag(keptn_project:$PROJECT),tag(keptn_stage:$STAGE),tag(keptn_service:$SERVICE),tag(keptn_deployment:$DEPLOYMENT),type(SERVICE)" 7 | response_time_p90: "metricSelector=builtin:service.response.time:merge(0):percentile(90)&entitySelector=tag(keptn_project:$PROJECT),tag(keptn_stage:$STAGE),tag(keptn_service:$SERVICE),tag(keptn_deployment:$DEPLOYMENT),type(SERVICE)" 8 | response_time_p95: "metricSelector=builtin:service.response.time:merge(0):percentile(95)&entitySelector=tag(keptn_project:$PROJECT),tag(keptn_stage:$STAGE),tag(keptn_service:$SERVICE),tag(keptn_deployment:$DEPLOYMENT),type(SERVICE)" -------------------------------------------------------------------------------- /simplenodeservice/keptn/dynatrace/sli_perftest.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | spec_version: '1.0' 3 | indicators: 4 | throughput: "metricSelector=builtin:service.requestCount.total:merge(0):sum&entitySelector=tag(keptn_project:$PROJECT),tag(keptn_stage:$STAGE),tag(keptn_service:$SERVICE),tag(keptn_deployment:$DEPLOYMENT),type(SERVICE)" 5 | error_rate: "metricSelector=builtin:service.errors.total.rate:merge(0):avg&entitySelector=tag(keptn_project:$PROJECT),tag(keptn_stage:$STAGE),tag(keptn_service:$SERVICE),tag(keptn_deployment:$DEPLOYMENT),type(SERVICE)" 6 | response_time_p50: "metricSelector=builtin:service.response.time:merge(0):percentile(50)&entitySelector=tag(keptn_project:$PROJECT),tag(keptn_stage:$STAGE),tag(keptn_service:$SERVICE),tag(keptn_deployment:$DEPLOYMENT),type(SERVICE)" 7 | response_time_p90: "metricSelector=builtin:service.response.time:merge(0):percentile(90)&entitySelector=tag(keptn_project:$PROJECT),tag(keptn_stage:$STAGE),tag(keptn_service:$SERVICE),tag(keptn_deployment:$DEPLOYMENT),type(SERVICE)" 8 | response_time_p95: "metricSelector=builtin:service.response.time:merge(0):percentile(95)&entitySelector=tag(keptn_project:$PROJECT),tag(keptn_stage:$STAGE),tag(keptn_service:$SERVICE),tag(keptn_deployment:$DEPLOYMENT),type(SERVICE)" 9 | rt_test_invoke: "metricSelector=calc:service.teststepresponsetime:filter(eq(Test Step,invoke)):merge(0):avg&entitySelector=tag(keptn_project:$PROJECT),tag(keptn_stage:$STAGE),tag(keptn_service:$SERVICE),tag(keptn_deployment:$DEPLOYMENT),type(SERVICE)" 10 | rt_test_echo: "metricSelector=calc:service.teststepresponsetime:filter(eq(Test Step,echo)):merge(0):avg&entitySelector=tag(keptn_project:$PROJECT),tag(keptn_stage:$STAGE),tag(keptn_service:$SERVICE),tag(keptn_deployment:$DEPLOYMENT),type(SERVICE)" 11 | rt_test_version: "metricSelector=calc:service.teststepresponsetime:filter(eq(Test Step,version)):merge(0):avg&entitySelector=tag(keptn_project:$PROJECT),tag(keptn_stage:$STAGE),tag(keptn_service:$SERVICE),tag(keptn_deployment:$DEPLOYMENT),type(SERVICE)" 12 | rt_test_homepage: "metricSelector=calc:service.teststepresponsetime:filter(eq(Test Step,homepage)):merge(0):avg&entitySelector=tag(keptn_project:$PROJECT),tag(keptn_stage:$STAGE),tag(keptn_service:$SERVICE),tag(keptn_deployment:$DEPLOYMENT),type(SERVICE)" 13 | svcalls_test_invoke: "metricSelector=calc:service.teststepservicecalls:filter(eq(Test Step,invoke)):merge(0):sum&entitySelector=tag(keptn_project:$PROJECT),tag(keptn_stage:$STAGE),tag(keptn_service:$SERVICE),tag(keptn_deployment:$DEPLOYMENT),type(SERVICE)" 14 | svcalls_test_echo: "metricSelector=calc:service.teststepservicecalls:filter(eq(Test Step,echo)):merge(0):sum&entitySelector=tag(keptn_project:$PROJECT),tag(keptn_stage:$STAGE),tag(keptn_service:$SERVICE),tag(keptn_deployment:$DEPLOYMENT),type(SERVICE)" 15 | svcalls_test_version: "metricSelector=calc:service.teststepservicecalls:filter(eq(Test Step,version)):merge(0):sum&entitySelector=tag(keptn_project:$PROJECT),tag(keptn_stage:$STAGE),tag(keptn_service:$SERVICE),tag(keptn_deployment:$DEPLOYMENT),type(SERVICE)" 16 | svcalls_test_homepage: "metricSelector=calc:service.teststepservicecalls:filter(eq(Test Step,homepage)):merge(0):sum&entitySelector=tag(keptn_project:$PROJECT),tag(keptn_stage:$STAGE),tag(keptn_service:$SERVICE),tag(keptn_deployment:$DEPLOYMENT),type(SERVICE)" -------------------------------------------------------------------------------- /simplenodeservice/keptn/exposeBridge.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | echo "" 4 | echo "STARTING WITH KEPTN 0.7 THIS IS NO LONGER RELEVANT!!!" 5 | echo "With Keptn 0.7 simply use 'keptn configure bridge'" 6 | echo "" 7 | echo "" 8 | echo "Continue for Keptn 0.6.x!" 9 | echo "=============================================================" 10 | echo "About to create a VirtualService to the Keptn Bridge service" 11 | echo "=============================================================" 12 | read -rsp $'Press ctrl-c to abort. Press any key to continue...\n' -n1 key 13 | echo "" 14 | 15 | DOMAIN=$(kubectl get cm keptn-domain -n keptn -ojsonpath={.data.app_domain}) 16 | 17 | rm -f ./manifests/gen/bridge.yaml 18 | cat ./manifests/bridge.yaml | \ 19 | sed 's~DOMAIN_PLACEHOLDER~'"$DOMAIN"'~' > ./manifests/gen/bridge.yaml 20 | 21 | kubectl apply -f ./manifests/gen/bridge.yaml 22 | 23 | echo "Bridge URL: https://bridge.keptn.$DOMAIN" -------------------------------------------------------------------------------- /simplenodeservice/keptn/jmeter/basiccheck.jmx: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | false 7 | false 8 | 9 | 10 | 11 | SERVER_URL 12 | 13 | = 14 | 15 | 16 | CHECK_PATH 17 | / 18 | = 19 | 20 | 21 | DT_LTN 22 | Default 23 | = 24 | 25 | 26 | DefaultThinkTime 27 | 250 28 | = 29 | 30 | 31 | PROTOCOL 32 | http 33 | = 34 | 35 | 36 | VUCount 37 | 1 38 | = 39 | 40 | 41 | LoopCount 42 | 1 43 | = 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | continue 52 | 53 | false 54 | ${__P(LoopCount,${LoopCount})} 55 | 56 | ${__P(VUCount,${VUCount})} 57 | 1 58 | 1444323045000 59 | 1444323045000 60 | false 61 | 62 | 63 | 64 | 65 | 66 | 67 | false 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | true 76 | 77 | 78 | import org.apache.jmeter.util.JMeterUtils; 79 | import org.apache.jmeter.protocol.http.control.HeaderManager; 80 | import java.io; 81 | import java.util; 82 | 83 | // ------------------------------------------------------------------------------------- 84 | // Generate the x-dynatrace-test header based on this best practic 85 | // -> https://www.dynatrace.com/support/help/integrations/test-automation-frameworks/how-do-i-integrate-dynatrace-into-my-load-testing-process/ 86 | // ------------------------------------------------------------------------------------- 87 | String LTN=JMeterUtils.getProperty("DT_LTN"); 88 | if((LTN == null) || (LTN.length() == 0)) { 89 | if(vars != null) { 90 | LTN = vars.get("DT_LTN"); 91 | } 92 | } 93 | if(LTN == null) LTN = "NoTestName"; 94 | 95 | String LSN = (bsh.args.length > 0) ? bsh.args[0] : "Test Scenario"; 96 | String TSN = sampler.getName(); 97 | String VU = ctx.getThreadGroup().getName() + ctx.getThreadNum(); 98 | String headerValue = "LSN="+ LSN + ";TSN=" + TSN + ";LTN=" + LTN + ";VU=" + VU + ";"; 99 | 100 | // ------------------------------------------- 101 | // Set header 102 | // ------------------------------------------- 103 | HeaderManager hm = sampler.getHeaderManager(); 104 | hm.removeHeaderNamed("x-dynatrace-test"); 105 | hm.add(new org.apache.jmeter.protocol.http.control.Header("x-dynatrace-test", headerValue)); 106 | 107 | 108 | 109 | 110 | 111 | 112 | ${__P(SERVER_URL,${SERVER_URL})} 113 | ${__P(SERVER_PORT,${SERVER_PORT})} 114 | ${__P(PROTOCOL,${PROTOCOL})} 115 | 116 | ${__P(CHECK_PATH,${CHECK_PATH})} 117 | GET 118 | true 119 | false 120 | true 121 | false 122 | 123 | 124 | 125 | 126 | 127 | 128 | 129 | 130 | 131 | 132 | 133 | {__P(ThinkTime,${DefaultThinkTime})} 134 | 135 | 136 | 137 | 138 | false 139 | 140 | saveConfig 141 | 142 | 143 | true 144 | true 145 | true 146 | 147 | true 148 | true 149 | true 150 | true 151 | false 152 | true 153 | true 154 | false 155 | false 156 | false 157 | false 158 | false 159 | false 160 | false 161 | false 162 | 0 163 | true 164 | true 165 | 166 | 167 | 168 | 169 | 170 | 171 | 172 | 173 | -------------------------------------------------------------------------------- /simplenodeservice/keptn/jmeter/jmeter.conf.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | spec_version: '0.1.0' 3 | workloads: 4 | - teststrategy: performance_long 5 | vuser: 10 6 | loopcount: 2000 7 | thinktime: 250 8 | acceptederrorrate: 1.0 9 | script: jmeter/load.jmx 10 | - teststrategy: performance_100 11 | vuser: 100 12 | loopcount: 10 13 | thinktime: 250 14 | acceptederrorrate: 1.0 15 | script: jmeter/load.jmx 16 | - teststrategy: performance_50 17 | vuser: 50 18 | loopcount: 10 19 | thinktime: 250 20 | acceptederrorrate: 1.0 21 | script: jmeter/load.jmx 22 | - teststrategy: performance_10 23 | vuser: 50 24 | loopcount: 10 25 | thinktime: 250 26 | acceptederrorrate: 1.0 27 | script: jmeter/load.jmx 28 | - teststrategy: performance_quick 29 | vuser: 10 30 | loopcount: 100 31 | thinktime: 250 32 | acceptederrorrate: 1.0 33 | script: jmeter/load.jmx 34 | - teststrategy: performance_peak 35 | vuser: 10 36 | loopcount: 500 37 | thinktime: 250 38 | acceptederrorrate: 1.0 39 | script: jmeter/load.jmx 40 | - teststrategy: performance 41 | vuser: 10 42 | loopcount: 100 43 | thinktime: 250 44 | acceptederrorrate: 1.0 45 | script: jmeter/load.jmx -------------------------------------------------------------------------------- /simplenodeservice/keptn/manifests/bridge.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | apiVersion: networking.istio.io/v1alpha3 3 | kind: VirtualService 4 | metadata: 5 | name: bridge 6 | namespace: keptn 7 | spec: 8 | hosts: 9 | - "bridge.keptn.DOMAIN_PLACEHOLDER" 10 | gateways: 11 | - public-gateway.istio-system 12 | http: 13 | - route: 14 | - destination: 15 | host: bridge.keptn.svc.cluster.local 16 | websocketUpgrade: true -------------------------------------------------------------------------------- /simplenodeservice/keptn/manifests/gen/readme.md: -------------------------------------------------------------------------------- 1 | # will be used to generate yaml files -> wohooo! -------------------------------------------------------------------------------- /simplenodeservice/keptn/shipyard.yaml: -------------------------------------------------------------------------------- 1 | registry: simplenodeservice 2 | stages: 3 | - name: "staging" 4 | deployment_strategy: "direct" 5 | test_strategy: "performance" 6 | - name: "prod" 7 | deployment_strategy: "blue_green_service" 8 | test_strategy: "performance" 9 | remediation_strategy: "automated" 10 | -------------------------------------------------------------------------------- /simplenodeservice/keptn/shipyard_keptn07.yaml: -------------------------------------------------------------------------------- 1 | registry: simplenodeservice 2 | stages: 3 | - name: "dev" 4 | deployment_strategy: "direct" 5 | test_strategy: "functional" 6 | approval_strategy: 7 | pass: "automatic" 8 | warning: "automatic" 9 | - name: "staging" 10 | deployment_strategy: "direct" 11 | test_strategy: "performance_peak" 12 | approval_strategy: 13 | pass: "automatic" 14 | warning: "manual" 15 | - name: "prod" 16 | deployment_strategy: "blue_green_service" 17 | test_strategy: "performance" 18 | remediation_strategy: "automated" 19 | -------------------------------------------------------------------------------- /simplenodeservice/keptn/slo_basic.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | spec_version: '0.1.0' 3 | comparison: 4 | compare_with: "single_result" 5 | include_result_with_score: "pass" 6 | aggregate_function: avg 7 | objectives: 8 | - sli: response_time_p95 9 | pass: # pass if (relative change <= 10% AND absolute value is < 500) 10 | - criteria: 11 | - "<=+10%" # relative values require a prefixed sign (plus or minus) 12 | - "<600" # absolute values only require a logical operator 13 | warning: # if the response time is below 800ms, the result should be a warning 14 | - criteria: 15 | - "<=800" 16 | - sli: throughput 17 | pass: 18 | - criteria: 19 | - ">4000" 20 | - sli: error_rate 21 | weight: 2 22 | pass: 23 | - criteria: 24 | - "<=1%" 25 | warning: 26 | - criteria: 27 | - "<=2%" 28 | - sli: response_time_p50 29 | - sli: response_time_p90 30 | pass: 31 | - criteria: 32 | - "<=+10%" 33 | warning: 34 | - criteria: 35 | - "<=+10%" 36 | total_score: 37 | pass: "90%" 38 | warning: "75%" -------------------------------------------------------------------------------- /simplenodeservice/keptn/slo_empty.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | spec_version: '0.1.0' 3 | comparison: 4 | objectives: -------------------------------------------------------------------------------- /simplenodeservice/keptn/slo_perftest.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | spec_version: '0.1.0' 3 | comparison: 4 | compare_with: "single_result" 5 | include_result_with_score: "pass" 6 | aggregate_function: avg 7 | objectives: 8 | - sli: response_time_p95 9 | pass: # pass if (relative change <= 10% AND absolute value is < 500) 10 | - criteria: 11 | - "<=+10%" # relative values require a prefixed sign (plus or minus) 12 | - "<600" # absolute values only require a logical operator 13 | warning: # if the response time is below 800ms, the result should be a warning 14 | - criteria: 15 | - "<=800" 16 | - sli: throughput 17 | pass: 18 | - criteria: 19 | - ">20000" 20 | - sli: error_rate 21 | weight: 2 22 | pass: 23 | - criteria: 24 | - "<=1%" 25 | warning: 26 | - criteria: 27 | - "<=2%" 28 | - sli: response_time_p50 29 | - sli: response_time_p90 30 | pass: 31 | - criteria: 32 | - "<=+10%" 33 | warning: 34 | - criteria: 35 | - "<=+10%" 36 | - sli: rt_test_invoke 37 | pass: 38 | - criteria: 39 | - "<=+10%" 40 | - sli: rt_test_echo 41 | pass: 42 | - criteria: 43 | - "<=+10%" 44 | - sli: rt_test_version 45 | pass: 46 | - criteria: 47 | - "<=+10%" 48 | - sli: rt_test_homepage 49 | pass: 50 | - criteria: 51 | - "<=+10%" 52 | - sli: svcalls_test_invoke 53 | pass: 54 | - criteria: 55 | - "<=+10%" 56 | - sli: svcalls_test_echo 57 | pass: 58 | - criteria: 59 | - "<=+10%" 60 | - sli: svcalls_test_version 61 | pass: 62 | - criteria: 63 | - "<=+10%" 64 | - sli: svcalls_test_homepage 65 | pass: 66 | - criteria: 67 | - "<=+10%" 68 | total_score: 69 | pass: "90%" 70 | warning: "75%" -------------------------------------------------------------------------------- /simplenodeservice/keptnevents/configchangeevent.json: -------------------------------------------------------------------------------- 1 | { 2 | "type": "sh.keptn.event.configuration.change", 3 | "contenttype": "application/json", 4 | "specversion": "0.2", 5 | "source": "example-event", 6 | "data": { 7 | "project": "keptn07project", 8 | "stage": "dev", 9 | "service": "simplenode", 10 | "valuesCanary": { 11 | "image": "docker.io/grabnerandi/simplenodeservice:3.0.0" 12 | }, 13 | "labels": { 14 | "BuildId": "Build103", 15 | "runby": "Demo User" 16 | } 17 | } 18 | } -------------------------------------------------------------------------------- /simplenodeservice/quality-gate-only/dynatrace/dynatrace.conf.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | spec_version: '0.1.0' 3 | dtCreds: dynatrace 4 | attachRules: 5 | tagRule: 6 | - meTypes: 7 | - SERVICE 8 | tags: 9 | - context: CONTEXTLESS 10 | key: $SERVICE -------------------------------------------------------------------------------- /simplenodeservice/quality-gate-only/dynatrace/sli_basic.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | spec_version: '1.0' 3 | indicators: 4 | throughput_svc: "metricSelector=builtin:service.requestCount.total:merge(0):sum&entitySelector=tag($SERVICE),type(SERVICE)" 5 | error_rate_svc: "metricSelector=builtin:service.errors.total.rate:merge(0):avg&entitySelector=tag($SERVICE),type(SERVICE)" 6 | rt_svc_p50: "metricSelector=builtin:service.response.time:merge(0):percentile(50)&entitySelector=tag($SERVICE),type(SERVICE)" 7 | rt_svc_p90: "metricSelector=builtin:service.response.time:merge(0):percentile(90)&entitySelector=tag($SERVICE),type(SERVICE)" 8 | rt_svc_p95: "metricSelector=builtin:service.response.time:merge(0):percentile(95)&entitySelector=tag($SERVICE),type(SERVICE)" -------------------------------------------------------------------------------- /simplenodeservice/quality-gate-only/dynatrace/sli_perftest.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | spec_version: '1.0' 3 | indicators: 4 | throughput_svc: "metricSelector=builtin:service.requestCount.total:merge(0):sum&entitySelector=tag($SERVICE),type(SERVICE)" 5 | error_rate_svc: "metricSelector=builtin:service.errors.total.rate:merge(0):avg&entitySelector=tag($SERVICE),type(SERVICE)" 6 | rt_svc_p50: "metricSelector=builtin:service.response.time:merge(0):percentile(50)&entitySelector=tag($SERVICE),type(SERVICE)" 7 | rt_svc_p90: "metricSelector=builtin:service.response.time:merge(0):percentile(90)&entitySelector=tag($SERVICE),type(SERVICE)" 8 | rt_svc_p95: "metricSelector=builtin:service.response.time:merge(0):percentile(95)&entitySelector=tag($SERVICE),type(SERVICE)" 9 | rt_test_invoke: "metricSelector=calc:service.teststepresponsetime:filter(eq(Test Step,invoke)):merge(0):avg&entitySelector=tag($SERVICE),type(SERVICE)" 10 | rt_test_echo: "metricSelector=calc:service.teststepresponsetime:filter(eq(Test Step,echo)):merge(0):avg&entitySelector=tag($SERVICE),type(SERVICE)" 11 | rt_test_version: "metricSelector=calc:service.teststepresponsetime:filter(eq(Test Step,version)):merge(0):avg&entitySelector=tag($SERVICE),type(SERVICE)" 12 | rt_test_homepage: "metricSelector=calc:service.teststepresponsetime:filter(eq(Test Step,homepage)):merge(0):avg&entitySelector=tag($SERVICE),type(SERVICE)" 13 | svcalls_test_invoke: "metricSelector=calc:service.teststepservicecalls:filter(eq(Test Step,invoke)):merge(0):sum&entitySelector=tag($SERVICE),type(SERVICE)" 14 | svcalls_test_echo: "metricSelector=calc:service.teststepservicecalls:filter(eq(Test Step,echo)):merge(0):sum&entitySelector=tag($SERVICE),type(SERVICE)" 15 | svcalls_test_version: "metricSelector=calc:service.teststepservicecalls:filter(eq(Test Step,version)):merge(0):sum&entitySelector=tag($SERVICE),type(SERVICE)" 16 | svcalls_test_homepage: "metricSelector=calc:service.teststepservicecalls:filter(eq(Test Step,homepage)):merge(0):sum&entitySelector=tag($SERVICE),type(SERVICE)" -------------------------------------------------------------------------------- /simplenodeservice/quality-gate-only/shipyard_qualitystageonly.yaml: -------------------------------------------------------------------------------- 1 | registry: simplenodeservice 2 | stages: 3 | - name: "qualitystage" 4 | -------------------------------------------------------------------------------- /simplenodeservice/quality-gate-only/slo_basic.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | spec_version: '0.1.0' 3 | comparison: 4 | compare_with: "single_result" 5 | include_result_with_score: "pass" 6 | aggregate_function: avg 7 | objectives: 8 | - sli: rt_svc_p95 9 | pass: # pass if (relative change <= 10% AND absolute value is < 500) 10 | - criteria: 11 | - "<=+10%" # relative values require a prefixed sign (plus or minus) 12 | - "<600" # absolute values only require a logical operator 13 | warning: # if the response time is below 800ms, the result should be a warning 14 | - criteria: 15 | - "<=800" 16 | - sli: throughput_svc 17 | pass: 18 | - criteria: 19 | - ">20000" 20 | - sli: error_rate_svc 21 | weight: 2 22 | pass: 23 | - criteria: 24 | - "<=1%" 25 | warning: 26 | - criteria: 27 | - "<=2%" 28 | - sli: rt_svc_p50 29 | - sli: rt_svc_p90 30 | pass: 31 | - criteria: 32 | - "<=+10%" 33 | warning: 34 | - criteria: 35 | - "<=+10%" 36 | total_score: 37 | pass: "90%" 38 | warning: "75%" -------------------------------------------------------------------------------- /simplenodeservice/quality-gate-only/slo_perftest.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | spec_version: '0.1.0' 3 | comparison: 4 | compare_with: "single_result" 5 | include_result_with_score: "pass" 6 | aggregate_function: avg 7 | objectives: 8 | - sli: rt_svc_p95 9 | pass: # pass if (relative change <= 10% AND absolute value is < 500) 10 | - criteria: 11 | - "<=+10%" # relative values require a prefixed sign (plus or minus) 12 | - "<600" # absolute values only require a logical operator 13 | warning: # if the response time is below 800ms, the result should be a warning 14 | - criteria: 15 | - "<=800" 16 | - sli: throughput_svc 17 | pass: 18 | - criteria: 19 | - ">20000" 20 | - sli: error_rate_svc 21 | weight: 2 22 | pass: 23 | - criteria: 24 | - "<=1%" 25 | warning: 26 | - criteria: 27 | - "<=2%" 28 | - sli: rt_svc_p50 29 | - sli: rt_svc_p90 30 | pass: 31 | - criteria: 32 | - "<=+10%" 33 | warning: 34 | - criteria: 35 | - "<=+10%" 36 | - sli: rt_test_invoke 37 | pass: 38 | - criteria: 39 | - "<=+10%" 40 | - sli: rt_test_echo 41 | pass: 42 | - criteria: 43 | - "<=+10%" 44 | - sli: rt_test_version 45 | pass: 46 | - criteria: 47 | - "<=+10%" 48 | - sli: rt_test_homepage 49 | pass: 50 | - criteria: 51 | - "<=+10%" 52 | - sli: svcalls_test_invoke 53 | pass: 54 | - criteria: 55 | - "<=+10%" 56 | - sli: svcalls_test_echo 57 | pass: 58 | - criteria: 59 | - "<=+10%" 60 | - sli: svcalls_test_version 61 | pass: 62 | - criteria: 63 | - "<=+10%" 64 | - sli: svcalls_test_homepage 65 | pass: 66 | - criteria: 67 | - "<=+10%" 68 | total_score: 69 | pass: "90%" 70 | warning: "75%" -------------------------------------------------------------------------------- /simplenodeservice/twoframes.html: -------------------------------------------------------------------------------- 1 | 2 | 6 | 7 | 13 | 14 | 15 |
16 |
17 | 18 | -------------------------------------------------------------------------------- /unleash-server/README.md: -------------------------------------------------------------------------------- 1 | # Unleash Server 2 | 3 | This folder contains the files required for onboarding the [unleash feature toggle service](https://unleash.github.io/) 4 | including a postgres database. 5 | 6 | For the purpose of just hosting the unleash server, we use a single stage (dev) with direct deployment, see 7 | [shipyard.yaml](shipyard.yaml). 8 | 9 | ## Instructions 10 | 11 | 1. Create a new project 12 | ```console 13 | keptn create project unleash --shipyard=./shipyard.yaml 14 | ``` 15 | 2. Onboard unleash and unleash-db using the `keptn onboard service` command: 16 | ```console 17 | keptn onboard service unleash-db --project=unleash --chart=./unleash-db 18 | keptn onboard service unleash --project=unleash --chart=./unleash 19 | ``` 20 | 3. Send new artifacts for unleash and unleash-db using the `keptn send new-artifact` command: 21 | ```console 22 | keptn send event new-artifact --project=unleash --service=unleash-db --image=postgres:10.4 23 | keptn send event new-artifact --project=unleash --service=unleash --image=docker.io/keptnexamples/unleash:1.0.0 24 | ``` 25 | 4. Get the url (`unleash.unelash-dev.KEPTN_DOMAIN`): 26 | ```console 27 | echo http://unleash.unleash-dev.$(kubectl get cm -n keptn ingress-config -o=jsonpath='{.data.ingress_hostname_suffix}') 28 | ``` 29 | 5. Open the url in your browser and log in using the following credentials: 30 | * username: keptn 31 | * password: keptn 32 | -------------------------------------------------------------------------------- /unleash-server/shipyard.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: "spec.keptn.sh/0.2.0" 2 | kind: "Shipyard" 3 | metadata: 4 | name: "shipyard-unleash-server" 5 | spec: 6 | stages: 7 | - name: "dev" 8 | sequences: 9 | - name: "delivery" 10 | tasks: 11 | - name: "deployment" 12 | properties: 13 | deploymentstrategy: "direct" 14 | - name: "release" 15 | 16 | 17 | 18 | -------------------------------------------------------------------------------- /unleash-server/unleash-db.tgz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/keptn/examples/9ff28ef074d0ab7580aec06706994bb99a08b27f/unleash-server/unleash-db.tgz -------------------------------------------------------------------------------- /unleash-server/unleash-db/Chart.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | description: A Helm chart for the Unleash db 3 | name: unleash-db 4 | version: 0.1.0 5 | -------------------------------------------------------------------------------- /unleash-server/unleash-db/templates/db-deployment.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | kind: PersistentVolumeClaim 3 | apiVersion: v1 4 | metadata: 5 | name: unleash-db-claim 6 | labels: 7 | app: unleash 8 | spec: 9 | accessModes: 10 | - ReadWriteOnce 11 | resources: 12 | requests: 13 | storage: 1Gi 14 | --- 15 | apiVersion: v1 16 | kind: ConfigMap 17 | metadata: 18 | name: unleash-db-config 19 | labels: 20 | app: unleash-db 21 | data: 22 | POSTGRES_DB: db 23 | POSTGRES_USER: postgres 24 | POSTGRES_PASSWORD: somepassword 25 | --- 26 | apiVersion: apps/v1 27 | kind: Deployment 28 | metadata: 29 | name: unleash-db 30 | spec: 31 | replicas: {{ .Values.replicaCount }} 32 | strategy: 33 | type: Recreate 34 | selector: 35 | matchLabels: 36 | app: unleash-db 37 | template: 38 | metadata: 39 | labels: 40 | app: unleash-db 41 | spec: 42 | volumes: 43 | - name: unleash-db-storage 44 | persistentVolumeClaim: 45 | claimName: unleash-db-claim 46 | containers: 47 | - name: unleash 48 | image: {{ .Values.image }} 49 | imagePullPolicy: IfNotPresent 50 | ports: 51 | - containerPort: 5432 52 | env: 53 | - name: PGDATA 54 | value: /tmp 55 | envFrom: 56 | - configMapRef: 57 | name: unleash-db-config 58 | volumeMounts: 59 | - mountPath: /var/lib/postgresql/data 60 | name: unleash-db-storage 61 | -------------------------------------------------------------------------------- /unleash-server/unleash-db/templates/db-service.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | apiVersion: v1 3 | kind: Service 4 | metadata: 5 | name: unleash-db 6 | labels: 7 | app: unleash-db 8 | spec: 9 | type: NodePort 10 | ports: 11 | - port: 5432 12 | selector: 13 | app: unleash-db -------------------------------------------------------------------------------- /unleash-server/unleash-db/values.yaml: -------------------------------------------------------------------------------- 1 | image: postgres:10.4 2 | replicaCount: 1 -------------------------------------------------------------------------------- /unleash-server/unleash.tgz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/keptn/examples/9ff28ef074d0ab7580aec06706994bb99a08b27f/unleash-server/unleash.tgz -------------------------------------------------------------------------------- /unleash-server/unleash/Chart.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | description: A Helm chart for the Unleash server 3 | name: unleash 4 | version: 0.1.0 5 | -------------------------------------------------------------------------------- /unleash-server/unleash/templates/deployment.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | apiVersion: apps/v1 3 | kind: Deployment 4 | metadata: 5 | name: unleash 6 | labels: 7 | app: unleash 8 | spec: 9 | replicas: {{ .Values.replicaCount }} 10 | selector: 11 | matchLabels: 12 | app: unleash 13 | template: 14 | metadata: 15 | labels: 16 | app: unleash 17 | deployment: unleash 18 | app.kubernetes.io/name: {{ .Values.keptn.service }} 19 | app.kubernetes.io/instance: "{{ .Values.keptn.service }}-{{ .Values.keptn.deployment }}" 20 | app.kubernetes.io/component: unleash 21 | app.kubernetes.io/part-of: "{{ .Values.keptn.project }}" 22 | app.kubernetes.io/managed-by: Keptn 23 | app.kubernetes.io/version: {{ (split ":" .Values.image)._1 | default "latest" }} 24 | spec: 25 | containers: 26 | - name: unleash 27 | image: {{ .Values.image }} 28 | imagePullPolicy: Always 29 | ports: 30 | - containerPort: 4242 31 | env: 32 | - name: NODE_ENV 33 | value: 'production' 34 | - name: DATABASE_URL 35 | value: postgres://postgres:somepassword@unleash-db/db 36 | - name: POD_NAME 37 | valueFrom: 38 | fieldRef: 39 | fieldPath: metadata.name 40 | - name: POD_NAMESPACE 41 | valueFrom: 42 | fieldRef: 43 | fieldPath: metadata.namespace 44 | -------------------------------------------------------------------------------- /unleash-server/unleash/templates/service.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | apiVersion: v1 3 | kind: Service 4 | metadata: 5 | name: unleash 6 | annotations: 7 | service.alpha.kubernetes.io/app-protocols: '{"http-port":"HTTP"}' 8 | labels: 9 | app: unleash 10 | spec: 11 | type: NodePort 12 | selector: 13 | app: unleash 14 | ports: 15 | - name: http-port 16 | port: 80 17 | targetPort: 4242 -------------------------------------------------------------------------------- /unleash-server/unleash/values.yaml: -------------------------------------------------------------------------------- 1 | image: docker.io/keptnexamples/unleash:1.0.0 2 | replicaCount: 1 --------------------------------------------------------------------------------