diff --git a/.circleci/config.yml b/.circleci/config.yml index 783c558b..e7a1e854 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -1,256 +1,49 @@ -version: 2.1 +ersion: 2.1 jobs: - - build-binary: + website: docker: - - image: circleci/golang:1.12 - working_directory: ~/build + - image: circleci/node:12.8 steps: - checkout - restore_cache: + name: Restore Yarn Package Cache keys: - - go-mod-v3-{{ checksum "go.sum" }} + - yarn-packages-{{ checksum "yarn.lock" }} - run: - name: Run go fmt - command: make test-fmt - - run: - name: Build Flagger - command: | - CGO_ENABLED=0 GOOS=linux go build \ - -ldflags "-s -w -X github.com/weaveworks/flagger/pkg/version.REVISION=${CIRCLE_SHA1}" \ - -a -installsuffix cgo -o bin/flagger ./cmd/flagger/*.go - - run: - name: Build Flagger load tester - command: | - CGO_ENABLED=0 GOOS=linux go build \ - -a -installsuffix cgo -o bin/loadtester ./cmd/loadtester/*.go - - run: - name: Run unit tests - command: | - go test -race -coverprofile=coverage.txt -covermode=atomic $(go list ./pkg/...) - bash <(curl -s https://codecov.io/bash) - - run: - name: Verify code gen - command: make test-codegen + name: Install Dependencies + command: yarn install --frozen-lockfile - save_cache: - key: go-mod-v3-{{ checksum "go.sum" }} + name: Save Yarn Package Cache + key: yarn-packages-{{ checksum "yarn.lock" }} paths: - - "/go/pkg/mod/" - - persist_to_workspace: - root: bin - paths: - - flagger - - loadtester - - push-container: - docker: - - image: circleci/golang:1.12 - steps: - - checkout - - setup_remote_docker: - docker_layer_caching: true - - attach_workspace: - at: /tmp/bin - - run: test/container-build.sh - - run: test/container-push.sh - - push-binary: - docker: - - image: circleci/golang:1.12 - working_directory: ~/build - steps: - - checkout - - setup_remote_docker: - docker_layer_caching: true - - restore_cache: - keys: - - go-mod-v3-{{ checksum "go.sum" }} - - run: test/goreleaser.sh - - e2e-istio-testing: - machine: true - steps: - - checkout - - attach_workspace: - at: /tmp/bin - - run: test/container-build.sh - - run: test/e2e-kind.sh - - run: test/e2e-istio.sh - - run: test/e2e-tests.sh - - e2e-kubernetes-testing: - machine: true - steps: - - checkout - - attach_workspace: - at: /tmp/bin - - run: test/container-build.sh - - run: test/e2e-kind.sh - - run: test/e2e-kubernetes.sh - - run: test/e2e-kubernetes-tests.sh - - e2e-smi-istio-testing: - machine: true - steps: - - checkout - - attach_workspace: - at: /tmp/bin - - run: test/container-build.sh - - run: test/e2e-kind.sh - - run: test/e2e-smi-istio.sh - - run: test/e2e-tests.sh canary - - e2e-supergloo-testing: - machine: true - steps: - - checkout - - attach_workspace: - at: /tmp/bin - - run: test/container-build.sh - - run: test/e2e-kind.sh 0.2.1 - - run: test/e2e-supergloo.sh - - run: test/e2e-tests.sh canary - - e2e-gloo-testing: - machine: true - steps: - - checkout - - attach_workspace: - at: /tmp/bin - - run: test/container-build.sh - - run: test/e2e-kind.sh - - run: test/e2e-gloo.sh - - run: test/e2e-gloo-tests.sh - - e2e-nginx-testing: - machine: true - steps: - - checkout - - attach_workspace: - at: /tmp/bin - - run: test/container-build.sh - - run: test/e2e-kind.sh - - run: test/e2e-nginx.sh - - run: test/e2e-nginx-tests.sh - - e2e-linkerd-testing: - machine: true - steps: - - checkout - - attach_workspace: - at: /tmp/bin - - run: test/container-build.sh - - run: test/e2e-kind.sh - - run: test/e2e-linkerd.sh - - run: test/e2e-linkerd-tests.sh - - push-helm-charts: - docker: - - image: circleci/golang:1.12 - steps: - - checkout + - ~/.cache/yarn - run: - name: Install kubectl - command: sudo curl -L https://storage.googleapis.com/kubernetes-release/release/$(curl -s https://storage.googleapis.com/kubernetes-release/release/stable.txt)/bin/linux/amd64/kubectl -o /usr/local/bin/kubectl && sudo chmod +x /usr/local/bin/kubectl - - run: - name: Install helm - command: sudo curl -L https://storage.googleapis.com/kubernetes-helm/helm-v2.14.2-linux-amd64.tar.gz | tar xz && sudo mv linux-amd64/helm /bin/helm && sudo rm -rf linux-amd64 - - run: - name: Initialize helm - command: helm init --client-only --kubeconfig=$HOME/.kube/kubeconfig - - run: - name: Lint charts + name: Build website command: | - helm lint ./charts/* + yarn docs:build + mkdir $HOME/site + sudo apt-get update && sudo apt-get install rsync + rsync -avzh docs/.vuepress/dist/ $HOME/site - run: - name: Package charts + name: Publish website command: | - mkdir $HOME/charts - helm package ./charts/* --destination $HOME/charts - - run: - name: Publish charts - command: | - if echo "${CIRCLE_TAG}" | grep -Eq "[0-9]+(\.[0-9]+)*(-[a-z]+)?$"; then + if [[ $GITHUB_TOKEN ]]; then REPOSITORY="https://weaveworksbot:${GITHUB_TOKEN}@github.com/weaveworks/flagger.git" git config user.email weaveworksbot@users.noreply.github.com git config user.name weaveworksbot git remote set-url origin ${REPOSITORY} git checkout gh-pages - mv -f $HOME/charts/*.tgz . - helm repo index . --url https://flagger.app + rm -rf assets + rsync -avzh $HOME/site/ . git add . - git commit -m "Publish Helm charts v${CIRCLE_TAG}" + git commit -m "Publish website" git push origin gh-pages else - echo "Not a release! Skip charts publish" + echo "No GitHub token found! Skip publishing" fi workflows: version: 2 - build-test-push: + publish-website: jobs: - - build-binary: - filters: - branches: - ignore: - - gh-pages - - e2e-istio-testing: - requires: - - build-binary - - e2e-kubernetes-testing: - requires: - - build-binary -# - e2e-supergloo-testing: -# requires: -# - build-binary - - e2e-gloo-testing: - requires: - - build-binary - - e2e-nginx-testing: - requires: - - build-binary - - e2e-linkerd-testing: - requires: - - build-binary - - push-container: - requires: - - build-binary - - e2e-istio-testing - - e2e-kubernetes-testing - #- e2e-supergloo-testing - - e2e-gloo-testing - - e2e-nginx-testing - - e2e-linkerd-testing - - release: - jobs: - - build-binary: - filters: - branches: - ignore: /.*/ - tags: - ignore: /^chart.*/ - - push-container: - requires: - - build-binary - filters: - branches: - ignore: /.*/ - tags: - ignore: /^chart.*/ - - push-binary: - requires: - - push-container - filters: - branches: - ignore: /.*/ - tags: - ignore: /^chart.*/ - - push-helm-charts: - requires: - - push-container - filters: - branches: - ignore: /.*/ - tags: - ignore: /^chart.*/ \ No newline at end of file + - website diff --git a/.codecov.yml b/.codecov.yml deleted file mode 100644 index 58e6fa06..00000000 --- a/.codecov.yml +++ /dev/null @@ -1,11 +0,0 @@ -coverage: - status: - project: - default: - target: auto - threshold: 50 - base: auto - patch: off - -comment: - require_changes: yes \ No newline at end of file diff --git a/.gitbook.yaml b/.gitbook.yaml deleted file mode 100644 index 184ffc3d..00000000 --- a/.gitbook.yaml +++ /dev/null @@ -1 +0,0 @@ -root: ./docs/gitbook \ No newline at end of file diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS deleted file mode 100644 index 9de49750..00000000 --- a/.github/CODEOWNERS +++ /dev/null @@ -1 +0,0 @@ -* @stefanprodan diff --git a/.github/_main.workflow b/.github/_main.workflow deleted file mode 100644 index 114e4ea8..00000000 --- a/.github/_main.workflow +++ /dev/null @@ -1,17 +0,0 @@ -workflow "Publish Helm charts" { - on = "push" - resolves = ["helm-push"] -} - -action "helm-lint" { - uses = "stefanprodan/gh-actions/helm@master" - args = ["lint charts/*"] -} - -action "helm-push" { - needs = ["helm-lint"] - uses = "stefanprodan/gh-actions/helm-gh-pages@master" - args = ["charts/*","https://flagger.app"] - secrets = ["GITHUB_TOKEN"] -} - diff --git a/.gitignore b/.gitignore index 20da41e8..b5d58aea 100644 --- a/.gitignore +++ b/.gitignore @@ -1,19 +1,64 @@ -# Binaries for programs and plugins -*.exe -*.exe~ -*.dll -*.so -*.dylib +# Logs +logs +*.log +npm-debug.log* +yarn-debug.log* +yarn-error.log* -# Test binary, build with `go test -c` -*.test +# Runtime data +pids +*.pid +*.seed +*.pid.lock -# Output of the go coverage tool, specifically when used with LiteIDE -*.out -.DS_Store +# Directory for instrumented libs generated by jscoverage/JSCover +lib-cov + +# Coverage directory used by tools like istanbul +coverage + +# nyc test coverage +.nyc_output + +# Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files) +.grunt + +# Bower dependency directory (https://bower.io/) +bower_components + +# node-waf configuration +.lock-wscript + +# Compiled binary addons (https://nodejs.org/api/addons.html) +build/Release + +# Dependency directories +node_modules/ +jspm_packages/ + +# TypeScript v1 declaration files +typings/ + +# Optional npm cache directory +.npm + +# Optional eslint cache +.eslintcache + +# Optional REPL history +.node_repl_history + +# Output of 'npm pack' +*.tgz + +# Yarn Integrity file +.yarn-integrity + +# dotenv environment variables file +.env + +# next.js build output +.next bin/ -_tmp/ - -artifacts/gcloud/ -.idea \ No newline at end of file +docs/.vuepress/dist/ \ No newline at end of file diff --git a/.goreleaser.yml b/.goreleaser.yml deleted file mode 100644 index 67233801..00000000 --- a/.goreleaser.yml +++ /dev/null @@ -1,18 +0,0 @@ -builds: - - main: ./cmd/flagger - binary: flagger - ldflags: -s -w -X github.com/weaveworks/flagger/pkg/version.REVISION={{.Commit}} - goos: - - linux - goarch: - - amd64 - env: - - CGO_ENABLED=0 -archives: - - name_template: "{{ .Binary }}_{{ .Version }}_{{ .Os }}_{{ .Arch }}" - files: - - none* -changelog: - filters: - exclude: - - '^CircleCI' diff --git a/CHANGELOG.md b/CHANGELOG.md deleted file mode 100644 index babeaec6..00000000 --- a/CHANGELOG.md +++ /dev/null @@ -1,386 +0,0 @@ -# Changelog - -All notable changes to this project are documented in this file. - -## 0.18.2 (2019-08-05) - -Fixes multi-port support for Istio - -#### Fixes - -- Fix port discovery for multiple port services [#267](https://github.com/weaveworks/flagger/pull/267) - -#### Improvements - -- Update e2e testing to Istio v1.2.3, Gloo v0.18.8 and NGINX ingress chart v1.12.1 [#268](https://github.com/weaveworks/flagger/pull/268) - -## 0.18.1 (2019-07-30) - -Fixes Blue/Green style deployments for Kubernetes and Linkerd providers - -#### Fixes - -- Fix Blue/Green metrics provider and add e2e tests [#261](https://github.com/weaveworks/flagger/pull/261) - -## 0.18.0 (2019-07-29) - -Adds support for [manual gating](https://docs.flagger.app/how-it-works#manual-gating) and pausing/resuming an ongoing analysis - -#### Features - -- Implement confirm rollout gate, hook and API [#251](https://github.com/weaveworks/flagger/pull/251) - -#### Improvements - -- Refactor canary change detection and status [#240](https://github.com/weaveworks/flagger/pull/240) -- Implement finalising state [#257](https://github.com/weaveworks/flagger/pull/257) -- Add gRPC load testing tool [#248](https://github.com/weaveworks/flagger/pull/248) - -#### Breaking changes - -- Due to the status sub-resource changes in [#240](https://github.com/weaveworks/flagger/pull/240), when upgrading Flagger the canaries status phase will be reset to `Initialized` -- Upgrading Flagger with Helm will fail due to Helm poor support of CRDs, see [workaround](https://github.com/weaveworks/flagger/issues/223) - -## 0.17.0 (2019-07-08) - -Adds support for Linkerd (SMI Traffic Split API), MS Teams notifications and HA mode with leader election - -#### Features - -- Add Linkerd support [#230](https://github.com/weaveworks/flagger/pull/230) -- Implement MS Teams notifications [#235](https://github.com/weaveworks/flagger/pull/235) -- Implement leader election [#236](https://github.com/weaveworks/flagger/pull/236) - -#### Improvements - -- Add [Kustomize](https://docs.flagger.app/install/flagger-install-on-kubernetes#install-flagger-with-kustomize) installer [#232](https://github.com/weaveworks/flagger/pull/232) -- Add Pod Security Policy to Helm chart [#234](https://github.com/weaveworks/flagger/pull/234) - -## 0.16.0 (2019-06-23) - -Adds support for running [Blue/Green deployments](https://docs.flagger.app/usage/blue-green) without a service mesh or ingress controller - -#### Features - -- Allow blue/green deployments without a service mesh provider [#211](https://github.com/weaveworks/flagger/pull/211) -- Add the service mesh provider to the canary spec [#217](https://github.com/weaveworks/flagger/pull/217) -- Allow multi-port services and implement port discovery [#207](https://github.com/weaveworks/flagger/pull/207) - -#### Improvements - -- Add [FAQ page](https://docs.flagger.app/faq) to docs website -- Switch to go modules in CI [#218](https://github.com/weaveworks/flagger/pull/218) -- Update e2e testing to Kubernetes Kind 0.3.0 and Istio 1.2.0 - -#### Fixes - -- Update the primary HPA on canary promotion [#216](https://github.com/weaveworks/flagger/pull/216) - -## 0.15.0 (2019-06-12) - -Adds support for customising the Istio [traffic policy](https://docs.flagger.app/how-it-works#istio-routing) in the canary service spec - -#### Features - -- Generate Istio destination rules and allow traffic policy customisation [#200](https://github.com/weaveworks/flagger/pull/200) - -#### Improvements - -- Update Kubernetes packages to 1.14 and use go modules instead of dep [#202](https://github.com/weaveworks/flagger/pull/202) - -## 0.14.1 (2019-06-05) - -Adds support for running [acceptance/integration tests](https://docs.flagger.app/how-it-works#integration-testing) with Helm test or Bash Bats using pre-rollout hooks - -#### Features - -- Implement Helm and Bash pre-rollout hooks [#196](https://github.com/weaveworks/flagger/pull/196) - -#### Fixes - -- Fix promoting canary when max weight is not a multiple of step [#190](https://github.com/weaveworks/flagger/pull/190) -- Add ability to set Prometheus url with custom path without trailing '/' [#197](https://github.com/weaveworks/flagger/pull/197) - -## 0.14.0 (2019-05-21) - -Adds support for Service Mesh Interface and [Gloo](https://docs.flagger.app/usage/gloo-progressive-delivery) ingress controller - -#### Features - -- Add support for SMI (Istio weighted traffic) [#180](https://github.com/weaveworks/flagger/pull/180) -- Add support for Gloo ingress controller (weighted traffic) [#179](https://github.com/weaveworks/flagger/pull/179) - -## 0.13.2 (2019-04-11) - -Fixes for Jenkins X deployments (prevent the jx GC from removing the primary instance) - -#### Fixes - -- Do not copy labels from canary to primary deployment [#178](https://github.com/weaveworks/flagger/pull/178) - -#### Improvements - -- Add NGINX ingress controller e2e and unit tests [#176](https://github.com/weaveworks/flagger/pull/176) - -## 0.13.1 (2019-04-09) - -Fixes for custom metrics checks and NGINX Prometheus queries - -#### Fixes - -- Fix promql queries for custom checks and NGINX [#174](https://github.com/weaveworks/flagger/pull/174) - -## 0.13.0 (2019-04-08) - -Adds support for [NGINX](https://docs.flagger.app/usage/nginx-progressive-delivery) ingress controller - -#### Features - -- Add support for nginx ingress controller (weighted traffic and A/B testing) [#170](https://github.com/weaveworks/flagger/pull/170) -- Add Prometheus add-on to Flagger Helm chart for App Mesh and NGINX [79b3370](https://github.com/weaveworks/flagger/pull/170/commits/79b337089294a92961bc8446fd185b38c50a32df) - -#### Fixes - -- Fix duplicate hosts Istio error when using wildcards [#162](https://github.com/weaveworks/flagger/pull/162) - -## 0.12.0 (2019-04-29) - -Adds support for [SuperGloo](https://docs.flagger.app/install/flagger-install-with-supergloo) - -#### Features - -- Supergloo support for canary deployment (weighted traffic) [#151](https://github.com/weaveworks/flagger/pull/151) - -## 0.11.1 (2019-04-18) - -Move Flagger and the load tester container images to Docker Hub - -#### Features - -- Add Bash Automated Testing System support to Flagger tester for running acceptance tests as pre-rollout hooks - -## 0.11.0 (2019-04-17) - -Adds pre/post rollout [webhooks](https://docs.flagger.app/how-it-works#webhooks) - -#### Features - -- Add `pre-rollout` and `post-rollout` webhook types [#147](https://github.com/weaveworks/flagger/pull/147) - -#### Improvements - -- Unify App Mesh and Istio builtin metric checks [#146](https://github.com/weaveworks/flagger/pull/146) -- Make the pod selector label configurable [#148](https://github.com/weaveworks/flagger/pull/148) - -#### Breaking changes - -- Set default `mesh` Istio gateway only if no gateway is specified [#141](https://github.com/weaveworks/flagger/pull/141) - -## 0.10.0 (2019-03-27) - -Adds support for App Mesh - -#### Features - -- AWS App Mesh integration - [#107](https://github.com/weaveworks/flagger/pull/107) - [#123](https://github.com/weaveworks/flagger/pull/123) - -#### Improvements - -- Reconcile Kubernetes ClusterIP services [#122](https://github.com/weaveworks/flagger/pull/122) - -#### Fixes - -- Preserve pod labels on canary promotion [#105](https://github.com/weaveworks/flagger/pull/105) -- Fix canary status Prometheus metric [#121](https://github.com/weaveworks/flagger/pull/121) - -## 0.9.0 (2019-03-11) - -Allows A/B testing scenarios where instead of weighted routing, the traffic is split between the -primary and canary based on HTTP headers or cookies. - -#### Features - -- A/B testing - canary with session affinity [#88](https://github.com/weaveworks/flagger/pull/88) - -#### Fixes - -- Update the analysis interval when the custom resource changes [#91](https://github.com/weaveworks/flagger/pull/91) - -## 0.8.0 (2019-03-06) - -Adds support for CORS policy and HTTP request headers manipulation - -#### Features - -- CORS policy support [#83](https://github.com/weaveworks/flagger/pull/83) -- Allow headers to be appended to HTTP requests [#82](https://github.com/weaveworks/flagger/pull/82) - -#### Improvements - -- Refactor the routing management - [#72](https://github.com/weaveworks/flagger/pull/72) - [#80](https://github.com/weaveworks/flagger/pull/80) -- Fine-grained RBAC [#73](https://github.com/weaveworks/flagger/pull/73) -- Add option to limit Flagger to a single namespace [#78](https://github.com/weaveworks/flagger/pull/78) - -## 0.7.0 (2019-02-28) - -Adds support for custom metric checks, HTTP timeouts and HTTP retries - -#### Features - -- Allow custom promql queries in the canary analysis spec [#60](https://github.com/weaveworks/flagger/pull/60) -- Add HTTP timeout and retries to canary service spec [#62](https://github.com/weaveworks/flagger/pull/62) - -## 0.6.0 (2019-02-25) - -Allows for [HTTPMatchRequests](https://istio.io/docs/reference/config/istio.networking.v1alpha3/#HTTPMatchRequest) -and [HTTPRewrite](https://istio.io/docs/reference/config/istio.networking.v1alpha3/#HTTPRewrite) -to be customized in the service spec of the canary custom resource. - -#### Features - -- Add HTTP match conditions and URI rewrite to the canary service spec [#55](https://github.com/weaveworks/flagger/pull/55) -- Update virtual service when the canary service spec changes - [#54](https://github.com/weaveworks/flagger/pull/54) - [#51](https://github.com/weaveworks/flagger/pull/51) - -#### Improvements - -- Run e2e testing on [Kubernetes Kind](https://github.com/kubernetes-sigs/kind) for canary promotion - [#53](https://github.com/weaveworks/flagger/pull/53) - -## 0.5.1 (2019-02-14) - -Allows skipping the analysis phase to ship changes directly to production - -#### Features - -- Add option to skip the canary analysis [#46](https://github.com/weaveworks/flagger/pull/46) - -#### Fixes - -- Reject deployment if the pod label selector doesn't match `app: ` [#43](https://github.com/weaveworks/flagger/pull/43) - -## 0.5.0 (2019-01-30) - -Track changes in ConfigMaps and Secrets [#37](https://github.com/weaveworks/flagger/pull/37) - -#### Features - -- Promote configmaps and secrets changes from canary to primary -- Detect changes in configmaps and/or secrets and (re)start canary analysis -- Add configs checksum to Canary CRD status -- Create primary configmaps and secrets at bootstrap -- Scan canary volumes and containers for configmaps and secrets - -#### Fixes - -- Copy deployment labels from canary to primary at bootstrap and promotion - -## 0.4.1 (2019-01-24) - -Load testing webhook [#35](https://github.com/weaveworks/flagger/pull/35) - -#### Features - -- Add the load tester chart to Flagger Helm repository -- Implement a load test runner based on [rakyll/hey](https://github.com/rakyll/hey) -- Log warning when no values are found for Istio metric due to lack of traffic - -#### Fixes - -- Run wekbooks before the metrics checks to avoid failures when using a load tester - -## 0.4.0 (2019-01-18) - -Restart canary analysis if revision changes [#31](https://github.com/weaveworks/flagger/pull/31) - -#### Breaking changes - -- Drop support for Kubernetes 1.10 - -#### Features - -- Detect changes during canary analysis and reset advancement -- Add status and additional printer columns to CRD -- Add canary name and namespace to controller structured logs - -#### Fixes - -- Allow canary name to be different to the target name -- Check if multiple canaries have the same target and log error -- Use deep copy when updating Kubernetes objects -- Skip readiness checks if canary analysis has finished - -## 0.3.0 (2019-01-11) - -Configurable canary analysis duration [#20](https://github.com/weaveworks/flagger/pull/20) - -#### Breaking changes - -- Helm chart: flag `controlLoopInterval` has been removed - -#### Features - -- CRD: canaries.flagger.app v1alpha3 -- Schedule canary analysis independently based on `canaryAnalysis.interval` -- Add analysis interval to Canary CRD (defaults to one minute) -- Make autoscaler (HPA) reference optional - -## 0.2.0 (2019-01-04) - -Webhooks [#18](https://github.com/weaveworks/flagger/pull/18) - -#### Features - -- CRD: canaries.flagger.app v1alpha2 -- Implement canary external checks based on webhooks HTTP POST calls -- Add webhooks to Canary CRD -- Move docs to gitbook [docs.flagger.app](https://docs.flagger.app) - -## 0.1.2 (2018-12-06) - -Improve Slack notifications [#14](https://github.com/weaveworks/flagger/pull/14) - -#### Features - -- Add canary analysis metadata to init and start Slack messages -- Add rollback reason to failed canary Slack messages - -## 0.1.1 (2018-11-28) - -Canary progress deadline [#10](https://github.com/weaveworks/flagger/pull/10) - -#### Features - -- Rollback canary based on the deployment progress deadline check -- Add progress deadline to Canary CRD (defaults to 10 minutes) - -## 0.1.0 (2018-11-25) - -First stable release - -#### Features - -- CRD: canaries.flagger.app v1alpha1 -- Notifications: post canary events to Slack -- Instrumentation: expose Prometheus metrics for canary status and traffic weight percentage -- Autoscaling: add HPA reference to CRD and create primary HPA at bootstrap -- Bootstrap: create primary deployment, ClusterIP services and Istio virtual service based on CRD spec - - -## 0.0.1 (2018-10-07) - -Initial semver release - -#### Features - -- Implement canary rollback based on failed checks threshold -- Scale up the deployment when canary revision changes -- Add OpenAPI v3 schema validation to Canary CRD -- Use CRD status for canary state persistence -- Add Helm charts for Flagger and Grafana -- Add canary analysis Grafana dashboard \ No newline at end of file diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md deleted file mode 100644 index 9536a644..00000000 --- a/CONTRIBUTING.md +++ /dev/null @@ -1,72 +0,0 @@ -# How to Contribute - -Flagger is [Apache 2.0 licensed](LICENSE) and accepts contributions via GitHub -pull requests. This document outlines some of the conventions on development -workflow, commit message formatting, contact points and other resources to make -it easier to get your contribution accepted. - -We gratefully welcome improvements to documentation as well as to code. - -## Certificate of Origin - -By contributing to this project you agree to the Developer Certificate of -Origin (DCO). This document was created by the Linux Kernel community and is a -simple statement that you, as a contributor, have the legal right to make the -contribution. - -## Chat - -The project uses Slack: To join the conversation, simply join the -[Weave community](https://slack.weave.works/) Slack workspace. - -## Getting Started - -- Fork the repository on GitHub -- If you want to contribute as a developer, continue reading this document for further instructions -- If you have questions, concerns, get stuck or need a hand, let us know - on the Slack channel. We are happy to help and look forward to having - you part of the team. No matter in which capacity. -- Play with the project, submit bugs, submit pull requests! - -## Contribution workflow - -This is a rough outline of how to prepare a contribution: - -- Create a topic branch from where you want to base your work (usually branched from master). -- Make commits of logical units. -- Make sure your commit messages are in the proper format (see below). -- Push your changes to a topic branch in your fork of the repository. -- If you changed code: - - add automated tests to cover your changes -- Submit a pull request to the original repository. - -## Acceptance policy - -These things will make a PR more likely to be accepted: - -- a well-described requirement -- new code and tests follow the conventions in old code and tests -- a good commit message (see below) -- All code must abide [Go Code Review Comments](https://github.com/golang/go/wiki/CodeReviewComments) -- Names should abide [What's in a name](https://talks.golang.org/2014/names.slide#1) -- Code must build on both Linux and Darwin, via plain `go build` -- Code should have appropriate test coverage and tests should be written - to work with `go test` - -In general, we will merge a PR once one maintainer has endorsed it. -For substantial changes, more people may become involved, and you might -get asked to resubmit the PR or divide the changes into more than one PR. - -### Format of the Commit Message - -For Flux we prefer the following rules for good commit messages: - -- Limit the subject to 50 characters and write as the continuation - of the sentence "If applied, this commit will ..." -- Explain what and why in the body, if more than a trivial change; - wrap it at 72 characters. - -The [following article](https://chris.beams.io/posts/git-commit/#seven-rules) -has some more helpful advice on documenting your work. - -This doc is adapted from the [Weaveworks Flux](https://github.com/weaveworks/flux/blob/master/CONTRIBUTING.md) diff --git a/Dockerfile b/Dockerfile deleted file mode 100644 index f0cf8bd3..00000000 --- a/Dockerfile +++ /dev/null @@ -1,16 +0,0 @@ -FROM alpine:3.9 - -RUN addgroup -S flagger \ - && adduser -S -g flagger flagger \ - && apk --no-cache add ca-certificates - -WORKDIR /home/flagger - -COPY /bin/flagger . - -RUN chown -R flagger:flagger ./ - -USER flagger - -ENTRYPOINT ["./flagger"] - diff --git a/Dockerfile.loadtester b/Dockerfile.loadtester deleted file mode 100644 index 220c2738..00000000 --- a/Dockerfile.loadtester +++ /dev/null @@ -1,27 +0,0 @@ -FROM bats/bats:v1.1.0 - -RUN addgroup -S app \ - && adduser -S -g app app \ - && apk --no-cache add ca-certificates curl jq - -WORKDIR /home/app - -RUN curl -sSLo hey "https://storage.googleapis.com/jblabs/dist/hey_linux_v0.1.2" && \ -chmod +x hey && mv hey /usr/local/bin/hey - -RUN curl -sSL "https://get.helm.sh/helm-v2.12.3-linux-amd64.tar.gz" | tar xvz && \ -chmod +x linux-amd64/helm && mv linux-amd64/helm /usr/local/bin/helm && \ -rm -rf linux-amd64 - -RUN curl -sSL "https://github.com/bojand/ghz/releases/download/v0.39.0/ghz_0.39.0_Linux_x86_64.tar.gz" | tar xz -C /tmp && \ -mv /tmp/ghz /usr/local/bin && chmod +x /usr/local/bin/ghz && rm -rf /tmp/ghz-web - -RUN ls /tmp - -COPY ./bin/loadtester . - -RUN chown -R app:app ./ - -USER app - -ENTRYPOINT ["./loadtester"] diff --git a/LICENSE b/LICENSE index 6e292ed2..c32f60a7 100644 --- a/LICENSE +++ b/LICENSE @@ -186,7 +186,7 @@ same "printed page" as the copyright notice for easier identification within third-party archives. - Copyright 2018 Weaveworks. All rights reserved. + Copyright 2019 Weaveworks Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. diff --git a/MAINTAINERS b/MAINTAINERS deleted file mode 100644 index 98c398d9..00000000 --- a/MAINTAINERS +++ /dev/null @@ -1,5 +0,0 @@ -The maintainers are generally available in Slack at -https://weave-community.slack.com/messages/flagger/ (obtain an invitation -at https://slack.weave.works/). - -Stefan Prodan, Weaveworks (Slack: @stefan Twitter: @stefanprodan) diff --git a/Makefile b/Makefile deleted file mode 100644 index 5685b975..00000000 --- a/Makefile +++ /dev/null @@ -1,127 +0,0 @@ -TAG?=latest -VERSION?=$(shell grep 'VERSION' pkg/version/version.go | awk '{ print $$4 }' | tr -d '"') -VERSION_MINOR:=$(shell grep 'VERSION' pkg/version/version.go | awk '{ print $$4 }' | tr -d '"' | rev | cut -d'.' -f2- | rev) -PATCH:=$(shell grep 'VERSION' pkg/version/version.go | awk '{ print $$4 }' | tr -d '"' | awk -F. '{print $$NF}') -SOURCE_DIRS = cmd pkg/apis pkg/controller pkg/server pkg/canary pkg/metrics pkg/router pkg/notifier -LT_VERSION?=$(shell grep 'VERSION' cmd/loadtester/main.go | awk '{ print $$4 }' | tr -d '"' | head -n1) -TS=$(shell date +%Y-%m-%d_%H-%M-%S) - -run: - GO111MODULE=on go run cmd/flagger/* -kubeconfig=$$HOME/.kube/config -log-level=info -mesh-provider=istio -namespace=test \ - -metrics-server=https://prometheus.istio.weavedx.com \ - -enable-leader-election=true - -run2: - GO111MODULE=on go run cmd/flagger/* -kubeconfig=$$HOME/.kube/config -log-level=info -mesh-provider=istio -namespace=test \ - -metrics-server=https://prometheus.istio.weavedx.com \ - -enable-leader-election=true \ - -port=9092 - -run-appmesh: - GO111MODULE=on go run cmd/flagger/* -kubeconfig=$$HOME/.kube/config -log-level=info -mesh-provider=appmesh \ - -metrics-server=http://acfc235624ca911e9a94c02c4171f346-1585187926.us-west-2.elb.amazonaws.com:9090 - -run-nginx: - GO111MODULE=on go run cmd/flagger/* -kubeconfig=$$HOME/.kube/config -log-level=info -mesh-provider=nginx -namespace=nginx \ - -metrics-server=http://prometheus-weave.istio.weavedx.com - -run-smi: - GO111MODULE=on go run cmd/flagger/* -kubeconfig=$$HOME/.kube/config -log-level=info -mesh-provider=smi:istio -namespace=smi \ - -metrics-server=https://prometheus.istio.weavedx.com - -run-gloo: - GO111MODULE=on go run cmd/flagger/* -kubeconfig=$$HOME/.kube/config -log-level=info -mesh-provider=gloo -namespace=gloo \ - -metrics-server=https://prometheus.istio.weavedx.com - -run-nop: - GO111MODULE=on go run cmd/flagger/* -kubeconfig=$$HOME/.kube/config -log-level=info -mesh-provider=none -namespace=bg \ - -metrics-server=https://prometheus.istio.weavedx.com - -run-linkerd: - GO111MODULE=on go run cmd/flagger/* -kubeconfig=$$HOME/.kube/config -log-level=info -mesh-provider=smi:linkerd -namespace=demo \ - -metrics-server=https://linkerd-prometheus.istio.weavedx.com - -build: - GIT_COMMIT=$$(git rev-list -1 HEAD) && GO111MODULE=on CGO_ENABLED=0 GOOS=linux go build -ldflags "-s -w -X github.com/weaveworks/flagger/pkg/version.REVISION=$${GIT_COMMIT}" -a -installsuffix cgo -o ./bin/flagger ./cmd/flagger/* - docker build -t weaveworks/flagger:$(TAG) . -f Dockerfile - -push: - docker tag weaveworks/flagger:$(TAG) weaveworks/flagger:$(VERSION) - docker push weaveworks/flagger:$(VERSION) - -fmt: - gofmt -l -s -w $(SOURCE_DIRS) - -test-fmt: - gofmt -l -s $(SOURCE_DIRS) | grep ".*\.go"; if [ "$$?" = "0" ]; then exit 1; fi - -test-codegen: - ./hack/verify-codegen.sh - -test: test-fmt test-codegen - go test ./... - -helm-package: - cd charts/ && helm package ./* - mv charts/*.tgz bin/ - curl -s https://raw.githubusercontent.com/weaveworks/flagger/gh-pages/index.yaml > ./bin/index.yaml - helm repo index bin --url https://flagger.app --merge ./bin/index.yaml - -helm-up: - helm upgrade --install flagger ./charts/flagger --namespace=istio-system --set crd.create=false - helm upgrade --install flagger-grafana ./charts/grafana --namespace=istio-system - -version-set: - @next="$(TAG)" && \ - current="$(VERSION)" && \ - sed -i '' "s/$$current/$$next/g" pkg/version/version.go && \ - sed -i '' "s/flagger:$$current/flagger:$$next/g" artifacts/flagger/deployment.yaml && \ - sed -i '' "s/tag: $$current/tag: $$next/g" charts/flagger/values.yaml && \ - sed -i '' "s/appVersion: $$current/appVersion: $$next/g" charts/flagger/Chart.yaml && \ - sed -i '' "s/version: $$current/version: $$next/g" charts/flagger/Chart.yaml && \ - sed -i '' "s/newTag: $$current/newTag: $$next/g" kustomize/base/flagger/kustomization.yaml && \ - echo "Version $$next set in code, deployment, chart and kustomize" - -version-up: - @next="$(VERSION_MINOR).$$(($(PATCH) + 1))" && \ - current="$(VERSION)" && \ - sed -i '' "s/$$current/$$next/g" pkg/version/version.go && \ - sed -i '' "s/flagger:$$current/flagger:$$next/g" artifacts/flagger/deployment.yaml && \ - sed -i '' "s/tag: $$current/tag: $$next/g" charts/flagger/values.yaml && \ - sed -i '' "s/appVersion: $$current/appVersion: $$next/g" charts/flagger/Chart.yaml && \ - echo "Version $$next set in code, deployment and chart" - -dev-up: version-up - @echo "Starting build/push/deploy pipeline for $(VERSION)" - docker build -t quay.io/stefanprodan/flagger:$(VERSION) . -f Dockerfile - docker push quay.io/stefanprodan/flagger:$(VERSION) - kubectl apply -f ./artifacts/flagger/crd.yaml - helm upgrade -i flagger ./charts/flagger --namespace=istio-system --set crd.create=false - -release: - git tag $(VERSION) - git push origin $(VERSION) - -release-set: fmt version-set helm-package - git add . - git commit -m "Release $(VERSION)" - git push origin master - git tag $(VERSION) - git push origin $(VERSION) - -reset-test: - kubectl delete -f ./artifacts/namespaces - kubectl apply -f ./artifacts/namespaces - kubectl apply -f ./artifacts/canaries - -loadtester-run: loadtester-build - docker build -t weaveworks/flagger-loadtester:$(LT_VERSION) . -f Dockerfile.loadtester - docker rm -f tester || true - docker run -dp 8888:9090 --name tester weaveworks/flagger-loadtester:$(LT_VERSION) - -loadtester-build: - GO111MODULE=on CGO_ENABLED=0 GOOS=linux go build -a -installsuffix cgo -o ./bin/loadtester ./cmd/loadtester/* - -loadtester-push: - docker build -t weaveworks/flagger-loadtester:$(LT_VERSION) . -f Dockerfile.loadtester - docker push weaveworks/flagger-loadtester:$(LT_VERSION) diff --git a/README.md b/README.md index a598f509..97fd1eae 100644 --- a/README.md +++ b/README.md @@ -1,198 +1,4 @@ -# flagger +# flagger website -[![build](https://img.shields.io/circleci/build/github/weaveworks/flagger/master.svg)](https://circleci.com/gh/weaveworks/flagger) -[![report](https://goreportcard.com/badge/github.com/weaveworks/flagger)](https://goreportcard.com/report/github.com/weaveworks/flagger) -[![codecov](https://codecov.io/gh/weaveworks/flagger/branch/master/graph/badge.svg)](https://codecov.io/gh/weaveworks/flagger) -[![license](https://img.shields.io/github/license/weaveworks/flagger.svg)](https://github.com/weaveworks/flagger/blob/master/LICENSE) -[![release](https://img.shields.io/github/release/weaveworks/flagger/all.svg)](https://github.com/weaveworks/flagger/releases) +[flagger.app](https://flagger.app) -Flagger is a Kubernetes operator that automates the promotion of canary deployments -using Istio, Linkerd, App Mesh, NGINX or Gloo routing for traffic shifting and Prometheus metrics for canary analysis. -The canary analysis can be extended with webhooks for running acceptance tests, -load tests or any other custom validation. - -Flagger implements a control loop that gradually shifts traffic to the canary while measuring key performance -indicators like HTTP requests success rate, requests average duration and pods health. -Based on analysis of the KPIs a canary is promoted or aborted, and the analysis result is published to Slack or MS Teams. - -![flagger-overview](https://raw.githubusercontent.com/weaveworks/flagger/master/docs/diagrams/flagger-canary-overview.png) - -## Documentation - -Flagger documentation can be found at [docs.flagger.app](https://docs.flagger.app) - -* Install - * [Flagger install on Kubernetes](https://docs.flagger.app/install/flagger-install-on-kubernetes) - * [Flagger install on GKE Istio](https://docs.flagger.app/install/flagger-install-on-google-cloud) - * [Flagger install on EKS App Mesh](https://docs.flagger.app/install/flagger-install-on-eks-appmesh) - * [Flagger install with SuperGloo](https://docs.flagger.app/install/flagger-install-with-supergloo) -* How it works - * [Canary custom resource](https://docs.flagger.app/how-it-works#canary-custom-resource) - * [Routing](https://docs.flagger.app/how-it-works#istio-routing) - * [Canary deployment stages](https://docs.flagger.app/how-it-works#canary-deployment) - * [Canary analysis](https://docs.flagger.app/how-it-works#canary-analysis) - * [HTTP metrics](https://docs.flagger.app/how-it-works#http-metrics) - * [Custom metrics](https://docs.flagger.app/how-it-works#custom-metrics) - * [Webhooks](https://docs.flagger.app/how-it-works#webhooks) - * [Load testing](https://docs.flagger.app/how-it-works#load-testing) - * [Manual gating](https://docs.flagger.app/how-it-works#manual-gating) - * [FAQ](https://docs.flagger.app/faq) -* Usage - * [Istio canary deployments](https://docs.flagger.app/usage/progressive-delivery) - * [Istio A/B testing](https://docs.flagger.app/usage/ab-testing) - * [Linkerd canary deployments](https://docs.flagger.app/usage/linkerd-progressive-delivery) - * [App Mesh canary deployments](https://docs.flagger.app/usage/appmesh-progressive-delivery) - * [NGINX ingress controller canary deployments](https://docs.flagger.app/usage/nginx-progressive-delivery) - * [Gloo ingress controller canary deployments](https://docs.flagger.app/usage/gloo-progressive-delivery) - * [Blue/Green deployments](https://docs.flagger.app/usage/blue-green) - * [Monitoring](https://docs.flagger.app/usage/monitoring) - * [Alerting](https://docs.flagger.app/usage/alerting) -* Tutorials - * [Canary deployments with Helm charts and Weave Flux](https://docs.flagger.app/tutorials/canary-helm-gitops) - -## Canary CRD - -Flagger takes a Kubernetes deployment and optionally a horizontal pod autoscaler (HPA), -then creates a series of objects (Kubernetes deployments, ClusterIP services and Istio or App Mesh virtual services). -These objects expose the application on the mesh and drive the canary analysis and promotion. - -Flagger keeps track of ConfigMaps and Secrets referenced by a Kubernetes Deployment and triggers a canary analysis if any of those objects change. -When promoting a workload in production, both code (container images) and configuration (config maps and secrets) are being synchronised. - -For a deployment named _podinfo_, a canary promotion can be defined using Flagger's custom resource: - -```yaml -apiVersion: flagger.app/v1alpha3 -kind: Canary -metadata: - name: podinfo - namespace: test -spec: - # service mesh provider (optional) - # can be: kubernetes, istio, linkerd, appmesh, nginx, gloo, supergloo - # use the kubernetes provider for Blue/Green style deployments - provider: istio - # deployment reference - targetRef: - apiVersion: apps/v1 - kind: Deployment - name: podinfo - # the maximum time in seconds for the canary deployment - # to make progress before it is rollback (default 600s) - progressDeadlineSeconds: 60 - # HPA reference (optional) - autoscalerRef: - apiVersion: autoscaling/v2beta1 - kind: HorizontalPodAutoscaler - name: podinfo - service: - # container port - port: 9898 - # Istio gateways (optional) - gateways: - - public-gateway.istio-system.svc.cluster.local - # Istio virtual service host names (optional) - hosts: - - podinfo.example.com - # HTTP match conditions (optional) - match: - - uri: - prefix: / - # HTTP rewrite (optional) - rewrite: - uri: / - # cross-origin resource sharing policy (optional) - corsPolicy: - allowOrigin: - - example.com - # request timeout (optional) - timeout: 5s - # promote the canary without analysing it (default false) - skipAnalysis: false - # define the canary analysis timing and KPIs - canaryAnalysis: - # schedule interval (default 60s) - interval: 1m - # max number of failed metric checks before rollback - threshold: 10 - # max traffic percentage routed to canary - # percentage (0-100) - maxWeight: 50 - # canary increment step - # percentage (0-100) - stepWeight: 5 - # Istio Prometheus checks - metrics: - # builtin checks - - name: request-success-rate - # minimum req success rate (non 5xx responses) - # percentage (0-100) - threshold: 99 - interval: 1m - - name: request-duration - # maximum req duration P99 - # milliseconds - threshold: 500 - interval: 30s - # custom check - - name: "kafka lag" - threshold: 100 - query: | - avg_over_time( - kafka_consumergroup_lag{ - consumergroup=~"podinfo-consumer-.*", - topic="podinfo" - }[1m] - ) - # external checks (optional) - webhooks: - - name: load-test - url: http://flagger-loadtester.test/ - timeout: 5s - metadata: - cmd: "hey -z 1m -q 10 -c 2 http://podinfo.test:9898/" -``` - -For more details on how the canary analysis and promotion works please [read the docs](https://docs.flagger.app/how-it-works). - -## Features - -| Feature | Istio | Linkerd | App Mesh | NGINX | Gloo | -| -------------------------------------------- | ------------------ | ------------------ |------------------ |------------------ |------------------ | -| Canary deployments (weighted traffic) | :heavy_check_mark: | :heavy_check_mark: | :heavy_check_mark: | :heavy_check_mark: | :heavy_check_mark: | -| A/B testing (headers and cookies filters) | :heavy_check_mark: | :heavy_minus_sign: | :heavy_minus_sign: | :heavy_check_mark: | :heavy_minus_sign: | -| Webhooks (acceptance/load testing) | :heavy_check_mark: | :heavy_check_mark: | :heavy_check_mark: | :heavy_check_mark: | :heavy_check_mark: | -| Request success rate check (L7 metric) | :heavy_check_mark: | :heavy_check_mark: | :heavy_check_mark: | :heavy_check_mark: | :heavy_check_mark: | -| Request duration check (L7 metric) | :heavy_check_mark: | :heavy_check_mark: | :heavy_minus_sign: | :heavy_check_mark: | :heavy_check_mark: | -| Custom promql checks | :heavy_check_mark: | :heavy_check_mark: | :heavy_check_mark: | :heavy_check_mark: | :heavy_check_mark: | -| Traffic policy, CORS, retries and timeouts | :heavy_check_mark: | :heavy_minus_sign: | :heavy_minus_sign: | :heavy_minus_sign: | :heavy_minus_sign: | - -## Roadmap - -* Integrate with other ingress controllers like Contour, HAProxy, ALB -* Add support for comparing the canary metrics to the primary ones and do the validation based on the derivation between the two - -## Contributing - -Flagger is Apache 2.0 licensed and accepts contributions via GitHub pull requests. - -When submitting bug reports please include as much details as possible: - -* which Flagger version -* which Flagger CRD version -* which Kubernetes/Istio version -* what configuration (canary, virtual service and workloads definitions) -* what happened (Flagger, Istio Pilot and Proxy logs) - -## Getting Help - -If you have any questions about Flagger and progressive delivery: - -* Read the Flagger [docs](https://docs.flagger.app). -* Invite yourself to the [Weave community slack](https://slack.weave.works/) - and join the [#flagger](https://weave-community.slack.com/messages/flagger/) channel. -* Join the [Weave User Group](https://www.meetup.com/pro/Weave/) and get invited to online talks, - hands-on training and meetups in your area. -* File an [issue](https://github.com/weaveworks/flagger/issues/new). - -Your feedback is always welcome! diff --git a/artifacts/ab-testing/canary.yaml b/artifacts/ab-testing/canary.yaml deleted file mode 100644 index eb96fd22..00000000 --- a/artifacts/ab-testing/canary.yaml +++ /dev/null @@ -1,62 +0,0 @@ -apiVersion: flagger.app/v1alpha3 -kind: Canary -metadata: - name: abtest - namespace: test -spec: - # deployment reference - targetRef: - apiVersion: apps/v1 - kind: Deployment - name: abtest - # the maximum time in seconds for the canary deployment - # to make progress before it is rollback (default 600s) - progressDeadlineSeconds: 60 - # HPA reference (optional) - autoscalerRef: - apiVersion: autoscaling/v2beta1 - kind: HorizontalPodAutoscaler - name: abtest - service: - # container port - port: 9898 - # Istio gateways (optional) - gateways: - - public-gateway.istio-system.svc.cluster.local - - mesh - # Istio virtual service host names (optional) - hosts: - - abtest.istio.weavedx.com - canaryAnalysis: - # schedule interval (default 60s) - interval: 10s - # max number of failed metric checks before rollback - threshold: 10 - # total number of iterations - iterations: 10 - # canary match condition - match: - - headers: - user-agent: - regex: "^(?!.*Chrome)(?=.*\bSafari\b).*$" - - headers: - cookie: - regex: "^(.*?;)?(type=insider)(;.*)?$" - metrics: - - name: request-success-rate - # minimum req success rate (non 5xx responses) - # percentage (0-100) - threshold: 99 - interval: 1m - - name: request-duration - # maximum req duration P99 - # milliseconds - threshold: 500 - interval: 30s - # external checks (optional) - webhooks: - - name: load-test - url: http://flagger-loadtester.test/ - timeout: 5s - metadata: - cmd: "hey -z 1m -q 10 -c 2 -H 'Cookie: type=insider' http://podinfo.test:9898/" diff --git a/artifacts/ab-testing/deployment.yaml b/artifacts/ab-testing/deployment.yaml deleted file mode 100644 index 66c0174e..00000000 --- a/artifacts/ab-testing/deployment.yaml +++ /dev/null @@ -1,67 +0,0 @@ -apiVersion: apps/v1 -kind: Deployment -metadata: - name: abtest - namespace: test - labels: - app: abtest -spec: - minReadySeconds: 5 - revisionHistoryLimit: 5 - progressDeadlineSeconds: 60 - strategy: - rollingUpdate: - maxUnavailable: 0 - type: RollingUpdate - selector: - matchLabels: - app: abtest - template: - metadata: - annotations: - prometheus.io/scrape: "true" - labels: - app: abtest - spec: - containers: - - name: podinfod - image: quay.io/stefanprodan/podinfo:1.7.0 - imagePullPolicy: IfNotPresent - ports: - - containerPort: 9898 - name: http - protocol: TCP - command: - - ./podinfo - - --port=9898 - - --level=info - - --random-delay=false - - --random-error=false - env: - - name: PODINFO_UI_COLOR - value: blue - livenessProbe: - exec: - command: - - podcli - - check - - http - - localhost:9898/healthz - initialDelaySeconds: 5 - timeoutSeconds: 5 - readinessProbe: - exec: - command: - - podcli - - check - - http - - localhost:9898/readyz - initialDelaySeconds: 5 - timeoutSeconds: 5 - resources: - limits: - cpu: 2000m - memory: 512Mi - requests: - cpu: 100m - memory: 64Mi diff --git a/artifacts/ab-testing/hpa.yaml b/artifacts/ab-testing/hpa.yaml deleted file mode 100644 index a5048150..00000000 --- a/artifacts/ab-testing/hpa.yaml +++ /dev/null @@ -1,19 +0,0 @@ -apiVersion: autoscaling/v2beta1 -kind: HorizontalPodAutoscaler -metadata: - name: abtest - namespace: test -spec: - scaleTargetRef: - apiVersion: apps/v1 - kind: Deployment - name: abtest - minReplicas: 2 - maxReplicas: 4 - metrics: - - type: Resource - resource: - name: cpu - # scale up if usage is above - # 99% of the requested CPU (100m) - targetAverageUtilization: 99 diff --git a/artifacts/appmesh/canary.yaml b/artifacts/appmesh/canary.yaml deleted file mode 100644 index d16ea07d..00000000 --- a/artifacts/appmesh/canary.yaml +++ /dev/null @@ -1,50 +0,0 @@ -apiVersion: flagger.app/v1alpha3 -kind: Canary -metadata: - name: podinfo - namespace: test -spec: - # deployment reference - targetRef: - apiVersion: apps/v1 - kind: Deployment - name: podinfo - # the maximum time in seconds for the canary deployment - # to make progress before it is rollback (default 600s) - progressDeadlineSeconds: 60 - # HPA reference (optional) - autoscalerRef: - apiVersion: autoscaling/v2beta1 - kind: HorizontalPodAutoscaler - name: podinfo - service: - # container port - port: 9898 - # App Mesh reference - meshName: global - # define the canary analysis timing and KPIs - canaryAnalysis: - # schedule interval (default 60s) - interval: 10s - # max number of failed metric checks before rollback - threshold: 10 - # max traffic percentage routed to canary - # percentage (0-100) - maxWeight: 50 - # canary increment step - # percentage (0-100) - stepWeight: 5 - # App Mesh Prometheus checks - metrics: - - name: request-success-rate - # minimum req success rate (non 5xx responses) - # percentage (0-100) - threshold: 99 - interval: 1m - # external checks (optional) - webhooks: - - name: load-test - url: http://flagger-loadtester.test/ - timeout: 5s - metadata: - cmd: "hey -z 1m -q 10 -c 2 http://podinfo.test:9898/" diff --git a/artifacts/appmesh/deployment.yaml b/artifacts/appmesh/deployment.yaml deleted file mode 100644 index 6166cfaf..00000000 --- a/artifacts/appmesh/deployment.yaml +++ /dev/null @@ -1,65 +0,0 @@ -apiVersion: apps/v1 -kind: Deployment -metadata: - name: podinfo - namespace: test - labels: - app: podinfo -spec: - minReadySeconds: 5 - revisionHistoryLimit: 5 - progressDeadlineSeconds: 60 - strategy: - rollingUpdate: - maxUnavailable: 0 - type: RollingUpdate - selector: - matchLabels: - app: podinfo - template: - metadata: - annotations: - prometheus.io/scrape: "true" - labels: - app: podinfo - spec: - containers: - - name: podinfod - image: quay.io/stefanprodan/podinfo:1.7.0 - imagePullPolicy: IfNotPresent - ports: - - containerPort: 9898 - name: http - protocol: TCP - command: - - ./podinfo - - --port=9898 - - --level=info - env: - - name: PODINFO_UI_COLOR - value: blue - livenessProbe: - exec: - command: - - podcli - - check - - http - - localhost:9898/healthz - initialDelaySeconds: 5 - timeoutSeconds: 5 - readinessProbe: - exec: - command: - - podcli - - check - - http - - localhost:9898/readyz - initialDelaySeconds: 5 - timeoutSeconds: 5 - resources: - limits: - cpu: 2000m - memory: 512Mi - requests: - cpu: 100m - memory: 64Mi diff --git a/artifacts/appmesh/global-mesh.yaml b/artifacts/appmesh/global-mesh.yaml deleted file mode 100644 index 01d6c8ff..00000000 --- a/artifacts/appmesh/global-mesh.yaml +++ /dev/null @@ -1,6 +0,0 @@ -apiVersion: appmesh.k8s.aws/v1beta1 -kind: Mesh -metadata: - name: global -spec: - serviceDiscoveryType: dns diff --git a/artifacts/appmesh/hpa.yaml b/artifacts/appmesh/hpa.yaml deleted file mode 100644 index fa2b5a6f..00000000 --- a/artifacts/appmesh/hpa.yaml +++ /dev/null @@ -1,19 +0,0 @@ -apiVersion: autoscaling/v2beta1 -kind: HorizontalPodAutoscaler -metadata: - name: podinfo - namespace: test -spec: - scaleTargetRef: - apiVersion: apps/v1 - kind: Deployment - name: podinfo - minReplicas: 2 - maxReplicas: 4 - metrics: - - type: Resource - resource: - name: cpu - # scale up if usage is above - # 99% of the requested CPU (100m) - targetAverageUtilization: 99 diff --git a/artifacts/appmesh/ingress.yaml b/artifacts/appmesh/ingress.yaml deleted file mode 100644 index 188717d2..00000000 --- a/artifacts/appmesh/ingress.yaml +++ /dev/null @@ -1,177 +0,0 @@ ---- -kind: ConfigMap -apiVersion: v1 -metadata: - name: ingress-config - namespace: test - labels: - app: ingress -data: - envoy.yaml: | - static_resources: - listeners: - - address: - socket_address: - address: 0.0.0.0 - port_value: 80 - filter_chains: - - filters: - - name: envoy.http_connection_manager - config: - access_log: - - name: envoy.file_access_log - config: - path: /dev/stdout - codec_type: auto - stat_prefix: ingress_http - http_filters: - - name: envoy.router - config: {} - route_config: - name: local_route - virtual_hosts: - - name: local_service - domains: ["*"] - routes: - - match: - prefix: "/" - route: - cluster: podinfo - host_rewrite: podinfo.test - timeout: 15s - retry_policy: - retry_on: "gateway-error,connect-failure,refused-stream" - num_retries: 10 - per_try_timeout: 5s - clusters: - - name: podinfo - connect_timeout: 0.30s - type: strict_dns - lb_policy: round_robin - http2_protocol_options: {} - hosts: - - socket_address: - address: podinfo.test - port_value: 9898 - admin: - access_log_path: /dev/null - address: - socket_address: - address: 0.0.0.0 - port_value: 9999 ---- -apiVersion: apps/v1 -kind: Deployment -metadata: - name: ingress - namespace: test - labels: - app: ingress -spec: - replicas: 1 - selector: - matchLabels: - app: ingress - strategy: - type: RollingUpdate - rollingUpdate: - maxUnavailable: 0 - template: - metadata: - labels: - app: ingress - annotations: - prometheus.io/path: "/stats/prometheus" - prometheus.io/port: "9999" - prometheus.io/scrape: "true" - # dummy port to exclude ingress from mesh traffic - # only egress should go over the mesh - appmesh.k8s.aws/ports: "444" - spec: - terminationGracePeriodSeconds: 30 - containers: - - name: ingress - image: "envoyproxy/envoy-alpine:d920944aed67425f91fc203774aebce9609e5d9a" - securityContext: - capabilities: - drop: - - ALL - add: - - NET_BIND_SERVICE - command: - - /usr/bin/dumb-init - - -- - args: - - /usr/local/bin/envoy - - --base-id 30 - - --v2-config-only - - -l - - $loglevel - - -c - - /config/envoy.yaml - ports: - - name: admin - containerPort: 9999 - protocol: TCP - - name: http - containerPort: 80 - protocol: TCP - - name: https - containerPort: 443 - protocol: TCP - livenessProbe: - initialDelaySeconds: 5 - tcpSocket: - port: admin - readinessProbe: - initialDelaySeconds: 5 - tcpSocket: - port: admin - resources: - requests: - cpu: 100m - memory: 64Mi - volumeMounts: - - name: config - mountPath: /config - volumes: - - name: config - configMap: - name: ingress-config ---- -kind: Service -apiVersion: v1 -metadata: - name: ingress - namespace: test -spec: - selector: - app: ingress - ports: - - protocol: TCP - name: http - port: 80 - targetPort: 80 - - protocol: TCP - name: https - port: 443 - targetPort: 443 - type: LoadBalancer ---- -apiVersion: appmesh.k8s.aws/v1beta1 -kind: VirtualNode -metadata: - name: ingress - namespace: test -spec: - meshName: global - listeners: - - portMapping: - port: 80 - protocol: http - serviceDiscovery: - dns: - hostName: ingress.test - backends: - - virtualService: - virtualServiceName: podinfo.test \ No newline at end of file diff --git a/artifacts/canaries/canary.yaml b/artifacts/canaries/canary.yaml deleted file mode 100644 index e113a862..00000000 --- a/artifacts/canaries/canary.yaml +++ /dev/null @@ -1,88 +0,0 @@ -apiVersion: flagger.app/v1alpha3 -kind: Canary -metadata: - name: podinfo - namespace: test -spec: - # service mesh provider (default istio) - # can be: kubernetes, istio, appmesh, smi, nginx, gloo, supergloo - # use the kubernetes provider for Blue/Green style deployments - provider: istio - # deployment reference - targetRef: - apiVersion: apps/v1 - kind: Deployment - name: podinfo - # the maximum time in seconds for the canary deployment - # to make progress before it is rollback (default 600s) - progressDeadlineSeconds: 60 - # HPA reference (optional) - autoscalerRef: - apiVersion: autoscaling/v2beta1 - kind: HorizontalPodAutoscaler - name: podinfo - service: - # container port - port: 9898 - # port name can be http or grpc (default http) - portName: http - # add all the other container ports - # when generating ClusterIP services (default false) - portDiscovery: false - # Istio gateways (optional) - gateways: - - public-gateway.istio-system.svc.cluster.local - # remove the mesh gateway if the public host is - # shared across multiple virtual services - - mesh - # Istio virtual service host names (optional) - hosts: - - app.istio.weavedx.com - # Istio traffic policy (optional) - trafficPolicy: - tls: - # use ISTIO_MUTUAL when mTLS is enabled - mode: DISABLE - # HTTP match conditions (optional) - match: - - uri: - prefix: / - # HTTP rewrite (optional) - rewrite: - uri: / - # HTTP timeout (optional) - timeout: 30s - # promote the canary without analysing it (default false) - skipAnalysis: false - canaryAnalysis: - # schedule interval (default 60s) - interval: 10s - # max number of failed metric checks before rollback - threshold: 10 - # max traffic percentage routed to canary - # percentage (0-100) - maxWeight: 50 - # canary increment step - # percentage (0-100) - stepWeight: 5 - # Prometheus checks - metrics: - - name: request-success-rate - # minimum req success rate (non 5xx responses) - # percentage (0-100) - threshold: 99 - interval: 1m - - name: request-duration - # maximum req duration P99 - # milliseconds - threshold: 500 - interval: 30s - # external checks (optional) - webhooks: - - name: load-test - url: http://flagger-loadtester.test/ - timeout: 5s - metadata: - type: cmd - cmd: "hey -z 1m -q 10 -c 2 http://podinfo-canary.test:9898/" - logCmdOutput: "true" diff --git a/artifacts/canaries/deployment.yaml b/artifacts/canaries/deployment.yaml deleted file mode 100644 index fe2043ea..00000000 --- a/artifacts/canaries/deployment.yaml +++ /dev/null @@ -1,67 +0,0 @@ -apiVersion: apps/v1 -kind: Deployment -metadata: - name: podinfo - namespace: test - labels: - app: podinfo -spec: - minReadySeconds: 5 - revisionHistoryLimit: 5 - progressDeadlineSeconds: 60 - strategy: - rollingUpdate: - maxUnavailable: 0 - type: RollingUpdate - selector: - matchLabels: - app: podinfo - template: - metadata: - annotations: - prometheus.io/scrape: "true" - labels: - app: podinfo - spec: - containers: - - name: podinfod - image: stefanprodan/podinfo:2.0.0 - imagePullPolicy: IfNotPresent - ports: - - containerPort: 9898 - name: http - protocol: TCP - command: - - ./podinfo - - --port=9898 - - --level=info - - --random-delay=false - - --random-error=false - env: - - name: PODINFO_UI_COLOR - value: blue - livenessProbe: - exec: - command: - - podcli - - check - - http - - localhost:9898/healthz - initialDelaySeconds: 5 - timeoutSeconds: 5 - readinessProbe: - exec: - command: - - podcli - - check - - http - - localhost:9898/readyz - initialDelaySeconds: 5 - timeoutSeconds: 5 - resources: - limits: - cpu: 2000m - memory: 512Mi - requests: - cpu: 100m - memory: 64Mi diff --git a/artifacts/canaries/hpa.yaml b/artifacts/canaries/hpa.yaml deleted file mode 100644 index fa2b5a6f..00000000 --- a/artifacts/canaries/hpa.yaml +++ /dev/null @@ -1,19 +0,0 @@ -apiVersion: autoscaling/v2beta1 -kind: HorizontalPodAutoscaler -metadata: - name: podinfo - namespace: test -spec: - scaleTargetRef: - apiVersion: apps/v1 - kind: Deployment - name: podinfo - minReplicas: 2 - maxReplicas: 4 - metrics: - - type: Resource - resource: - name: cpu - # scale up if usage is above - # 99% of the requested CPU (100m) - targetAverageUtilization: 99 diff --git a/artifacts/cluster/namespaces/test.yaml b/artifacts/cluster/namespaces/test.yaml deleted file mode 100644 index 6126d753..00000000 --- a/artifacts/cluster/namespaces/test.yaml +++ /dev/null @@ -1,6 +0,0 @@ -apiVersion: v1 -kind: Namespace -metadata: - name: test - labels: - istio-injection: enabled diff --git a/artifacts/cluster/releases/test/backend.yaml b/artifacts/cluster/releases/test/backend.yaml deleted file mode 100644 index 79ac9bbb..00000000 --- a/artifacts/cluster/releases/test/backend.yaml +++ /dev/null @@ -1,26 +0,0 @@ -apiVersion: flux.weave.works/v1beta1 -kind: HelmRelease -metadata: - name: backend - namespace: test - annotations: - flux.weave.works/automated: "true" - flux.weave.works/tag.chart-image: regexp:^1.7.* -spec: - releaseName: backend - chart: - repository: https://flagger.app/ - name: podinfo - version: 2.2.0 - values: - image: - repository: quay.io/stefanprodan/podinfo - tag: 1.7.0 - httpServer: - timeout: 30s - canary: - enabled: true - istioIngress: - enabled: false - loadtest: - enabled: true diff --git a/artifacts/cluster/releases/test/frontend.yaml b/artifacts/cluster/releases/test/frontend.yaml deleted file mode 100644 index 0a62c895..00000000 --- a/artifacts/cluster/releases/test/frontend.yaml +++ /dev/null @@ -1,27 +0,0 @@ -apiVersion: flux.weave.works/v1beta1 -kind: HelmRelease -metadata: - name: frontend - namespace: test - annotations: - flux.weave.works/automated: "true" - flux.weave.works/tag.chart-image: semver:~1.7 -spec: - releaseName: frontend - chart: - repository: https://flagger.app/ - name: podinfo - version: 2.2.0 - values: - image: - repository: quay.io/stefanprodan/podinfo - tag: 1.7.0 - backend: http://backend-podinfo:9898/echo - canary: - enabled: true - istioIngress: - enabled: true - gateway: public-gateway.istio-system.svc.cluster.local - host: frontend.istio.example.com - loadtest: - enabled: true diff --git a/artifacts/cluster/releases/test/loadtester.yaml b/artifacts/cluster/releases/test/loadtester.yaml deleted file mode 100644 index bd742d60..00000000 --- a/artifacts/cluster/releases/test/loadtester.yaml +++ /dev/null @@ -1,18 +0,0 @@ -apiVersion: flux.weave.works/v1beta1 -kind: HelmRelease -metadata: - name: loadtester - namespace: test - annotations: - flux.weave.works/automated: "true" - flux.weave.works/tag.chart-image: glob:0.* -spec: - releaseName: flagger-loadtester - chart: - repository: https://flagger.app/ - name: loadtester - version: 0.6.0 - values: - image: - repository: weaveworks/flagger-loadtester - tag: 0.6.1 diff --git a/artifacts/eks/appmesh-prometheus.yaml b/artifacts/eks/appmesh-prometheus.yaml deleted file mode 100644 index c9386d6f..00000000 --- a/artifacts/eks/appmesh-prometheus.yaml +++ /dev/null @@ -1,264 +0,0 @@ ---- -apiVersion: rbac.authorization.k8s.io/v1beta1 -kind: ClusterRole -metadata: - name: prometheus - labels: - app: prometheus -rules: - - apiGroups: [""] - resources: - - nodes - - services - - endpoints - - pods - - nodes/proxy - verbs: ["get", "list", "watch"] - - apiGroups: [""] - resources: - - configmaps - verbs: ["get"] - - nonResourceURLs: ["/metrics"] - verbs: ["get"] ---- -apiVersion: rbac.authorization.k8s.io/v1beta1 -kind: ClusterRoleBinding -metadata: - name: prometheus - labels: - app: prometheus -roleRef: - apiGroup: rbac.authorization.k8s.io - kind: ClusterRole - name: prometheus -subjects: - - kind: ServiceAccount - name: prometheus - namespace: appmesh-system ---- -apiVersion: v1 -kind: ServiceAccount -metadata: - name: prometheus - namespace: appmesh-system - labels: - app: prometheus ---- -apiVersion: v1 -kind: ConfigMap -metadata: - name: prometheus - namespace: appmesh-system - labels: - app: prometheus -data: - prometheus.yml: |- - global: - scrape_interval: 5s - scrape_configs: - - # Scrape config for AppMesh Envoy sidecar - - job_name: 'appmesh-envoy' - metrics_path: /stats/prometheus - kubernetes_sd_configs: - - role: pod - - relabel_configs: - - source_labels: [__meta_kubernetes_pod_container_name] - action: keep - regex: '^envoy$' - - source_labels: [__address__, __meta_kubernetes_pod_annotation_prometheus_io_port] - action: replace - regex: ([^:]+)(?::\d+)?;(\d+) - replacement: ${1}:9901 - target_label: __address__ - - action: labelmap - regex: __meta_kubernetes_pod_label_(.+) - - source_labels: [__meta_kubernetes_namespace] - action: replace - target_label: kubernetes_namespace - - source_labels: [__meta_kubernetes_pod_name] - action: replace - target_label: kubernetes_pod_name - - # Exclude high cardinality metrics - metric_relabel_configs: - - source_labels: [ cluster_name ] - regex: '(outbound|inbound|prometheus_stats).*' - action: drop - - source_labels: [ tcp_prefix ] - regex: '(outbound|inbound|prometheus_stats).*' - action: drop - - source_labels: [ listener_address ] - regex: '(.+)' - action: drop - - source_labels: [ http_conn_manager_listener_prefix ] - regex: '(.+)' - action: drop - - source_labels: [ http_conn_manager_prefix ] - regex: '(.+)' - action: drop - - source_labels: [ __name__ ] - regex: 'envoy_tls.*' - action: drop - - source_labels: [ __name__ ] - regex: 'envoy_tcp_downstream.*' - action: drop - - source_labels: [ __name__ ] - regex: 'envoy_http_(stats|admin).*' - action: drop - - source_labels: [ __name__ ] - regex: 'envoy_cluster_(lb|retry|bind|internal|max|original).*' - action: drop - - # Scrape config for API servers - - job_name: 'kubernetes-apiservers' - kubernetes_sd_configs: - - role: endpoints - namespaces: - names: - - default - scheme: https - tls_config: - ca_file: /var/run/secrets/kubernetes.io/serviceaccount/ca.crt - bearer_token_file: /var/run/secrets/kubernetes.io/serviceaccount/token - relabel_configs: - - source_labels: [__meta_kubernetes_service_name, __meta_kubernetes_endpoint_port_name] - action: keep - regex: kubernetes;https - - # Scrape config for nodes - - job_name: 'kubernetes-nodes' - scheme: https - tls_config: - ca_file: /var/run/secrets/kubernetes.io/serviceaccount/ca.crt - bearer_token_file: /var/run/secrets/kubernetes.io/serviceaccount/token - kubernetes_sd_configs: - - role: node - relabel_configs: - - action: labelmap - regex: __meta_kubernetes_node_label_(.+) - - target_label: __address__ - replacement: kubernetes.default.svc:443 - - source_labels: [__meta_kubernetes_node_name] - regex: (.+) - target_label: __metrics_path__ - replacement: /api/v1/nodes/${1}/proxy/metrics - - # scrape config for cAdvisor - - job_name: 'kubernetes-cadvisor' - scheme: https - tls_config: - ca_file: /var/run/secrets/kubernetes.io/serviceaccount/ca.crt - bearer_token_file: /var/run/secrets/kubernetes.io/serviceaccount/token - kubernetes_sd_configs: - - role: node - relabel_configs: - - action: labelmap - regex: __meta_kubernetes_node_label_(.+) - - target_label: __address__ - replacement: kubernetes.default.svc:443 - - source_labels: [__meta_kubernetes_node_name] - regex: (.+) - target_label: __metrics_path__ - replacement: /api/v1/nodes/${1}/proxy/metrics/cadvisor - - # scrape config for pods - - job_name: kubernetes-pods - kubernetes_sd_configs: - - role: pod - relabel_configs: - - action: keep - regex: true - source_labels: - - __meta_kubernetes_pod_annotation_prometheus_io_scrape - - source_labels: [ __address__ ] - regex: '.*9901.*' - action: drop - - action: replace - regex: (.+) - source_labels: - - __meta_kubernetes_pod_annotation_prometheus_io_path - target_label: __metrics_path__ - - action: replace - regex: ([^:]+)(?::\d+)?;(\d+) - replacement: $1:$2 - source_labels: - - __address__ - - __meta_kubernetes_pod_annotation_prometheus_io_port - target_label: __address__ - - action: labelmap - regex: __meta_kubernetes_pod_label_(.+) - - action: replace - source_labels: - - __meta_kubernetes_namespace - target_label: kubernetes_namespace - - action: replace - source_labels: - - __meta_kubernetes_pod_name - target_label: kubernetes_pod_name ---- -apiVersion: apps/v1 -kind: Deployment -metadata: - name: prometheus - namespace: appmesh-system - labels: - app: prometheus -spec: - replicas: 1 - selector: - matchLabels: - app: prometheus - template: - metadata: - labels: - app: prometheus - annotations: - version: "appmesh-v1alpha1" - spec: - serviceAccountName: prometheus - containers: - - name: prometheus - image: "docker.io/prom/prometheus:v2.7.1" - imagePullPolicy: IfNotPresent - args: - - '--storage.tsdb.retention=6h' - - '--config.file=/etc/prometheus/prometheus.yml' - ports: - - containerPort: 9090 - name: http - livenessProbe: - httpGet: - path: /-/healthy - port: 9090 - readinessProbe: - httpGet: - path: /-/ready - port: 9090 - resources: - requests: - cpu: 10m - memory: 128Mi - volumeMounts: - - name: config-volume - mountPath: /etc/prometheus - volumes: - - name: config-volume - configMap: - name: prometheus ---- -apiVersion: v1 -kind: Service -metadata: - name: prometheus - namespace: appmesh-system - labels: - name: prometheus -spec: - selector: - app: prometheus - ports: - - name: http - protocol: TCP - port: 9090 diff --git a/artifacts/flagger/account.yaml b/artifacts/flagger/account.yaml deleted file mode 100644 index 0507289e..00000000 --- a/artifacts/flagger/account.yaml +++ /dev/null @@ -1,102 +0,0 @@ -apiVersion: v1 -kind: ServiceAccount -metadata: - name: flagger - namespace: istio-system - labels: - app: flagger ---- -apiVersion: rbac.authorization.k8s.io/v1beta1 -kind: ClusterRole -metadata: - name: flagger - labels: - app: flagger -rules: - - apiGroups: - - "" - resources: - - events - - configmaps - - secrets - - services - verbs: ["*"] - - apiGroups: - - apps - resources: - - deployments - verbs: ["*"] - - apiGroups: - - autoscaling - resources: - - horizontalpodautoscalers - verbs: ["*"] - - apiGroups: - - "extensions" - resources: - - ingresses - - ingresses/status - verbs: ["*"] - - apiGroups: - - flagger.app - resources: - - canaries - - canaries/status - verbs: ["*"] - - apiGroups: - - networking.istio.io - resources: - - virtualservices - - virtualservices/status - - destinationrules - - destinationrules/status - verbs: ["*"] - - apiGroups: - - appmesh.k8s.aws - resources: - - meshes - - meshes/status - - virtualnodes - - virtualnodes/status - - virtualservices - - virtualservices/status - verbs: ["*"] - - apiGroups: - - split.smi-spec.io - resources: - - trafficsplits - verbs: ["*"] - - apiGroups: - - gloo.solo.io - resources: - - settings - - upstreams - - upstreamgroups - - proxies - - virtualservices - verbs: ["*"] - - apiGroups: - - gateway.solo.io - resources: - - virtualservices - - gateways - verbs: ["*"] - - nonResourceURLs: - - /version - verbs: - - get ---- -apiVersion: rbac.authorization.k8s.io/v1beta1 -kind: ClusterRoleBinding -metadata: - name: flagger - labels: - app: flagger -roleRef: - apiGroup: rbac.authorization.k8s.io - kind: ClusterRole - name: flagger -subjects: -- kind: ServiceAccount - name: flagger - namespace: istio-system diff --git a/artifacts/flagger/crd.yaml b/artifacts/flagger/crd.yaml deleted file mode 100644 index 842076d1..00000000 --- a/artifacts/flagger/crd.yaml +++ /dev/null @@ -1,286 +0,0 @@ -apiVersion: apiextensions.k8s.io/v1beta1 -kind: CustomResourceDefinition -metadata: - name: canaries.flagger.app - annotations: - helm.sh/resource-policy: keep -spec: - group: flagger.app - version: v1alpha3 - versions: - - name: v1alpha3 - served: true - storage: true - - name: v1alpha2 - served: true - storage: false - - name: v1alpha1 - served: true - storage: false - names: - plural: canaries - singular: canary - kind: Canary - categories: - - all - scope: Namespaced - subresources: - status: {} - additionalPrinterColumns: - - name: Status - type: string - JSONPath: .status.phase - - name: Weight - type: string - JSONPath: .status.canaryWeight - - name: LastTransitionTime - type: string - JSONPath: .status.lastTransitionTime - validation: - openAPIV3Schema: - properties: - spec: - required: - - targetRef - - service - - canaryAnalysis - properties: - provider: - description: Traffic managent provider - type: string - progressDeadlineSeconds: - description: Deployment progress deadline - type: number - targetRef: - description: Deployment selector - type: object - required: ['apiVersion', 'kind', 'name'] - properties: - apiVersion: - type: string - kind: - type: string - name: - type: string - autoscalerRef: - description: HPA selector - anyOf: - - type: string - - type: object - required: ['apiVersion', 'kind', 'name'] - properties: - apiVersion: - type: string - kind: - type: string - name: - type: string - ingressRef: - description: NGINX ingress selector - anyOf: - - type: string - - type: object - required: ['apiVersion', 'kind', 'name'] - properties: - apiVersion: - type: string - kind: - type: string - name: - type: string - service: - type: object - required: ['port'] - properties: - port: - description: Container port number - type: number - portName: - description: Container port name - type: string - portDiscovery: - description: Enable port dicovery - type: boolean - meshName: - description: AppMesh mesh name - type: string - backends: - description: AppMesh backend array - anyOf: - - type: string - - type: object - timeout: - description: Istio HTTP or gRPC request timeout - type: string - trafficPolicy: - description: Istio traffic policy - anyOf: - - type: string - - type: object - match: - description: Istio URL match conditions - anyOf: - - type: string - - type: array - rewrite: - description: Istio URL rewrite - anyOf: - - type: string - - type: object - headers: - description: Istio headers operations - anyOf: - - type: string - - type: object - corsPolicy: - description: Istio CORS policy - anyOf: - - type: string - - type: object - gateways: - description: Istio gateways list - anyOf: - - type: string - - type: array - hosts: - description: Istio hosts list - anyOf: - - type: string - - type: array - skipAnalysis: - type: boolean - canaryAnalysis: - properties: - interval: - description: Canary schedule interval - type: string - pattern: "^[0-9]+(m|s)" - iterations: - description: Number of checks to run for A/B Testing and Blue/Green - type: number - threshold: - description: Max number of failed checks before rollback - type: number - maxWeight: - description: Max traffic percentage routed to canary - type: number - stepWeight: - description: Canary incremental traffic percentage step - type: number - match: - description: A/B testing match conditions - anyOf: - - type: string - - type: array - metrics: - description: Prometheus query list for this canary - type: array - properties: - items: - type: object - required: ['name', 'threshold'] - properties: - name: - description: Name of the Prometheus metric - type: string - interval: - description: Interval of the promql query - type: string - pattern: "^[0-9]+(m|s)" - threshold: - description: Max scalar value accepted for this metric - type: number - query: - description: Prometheus query - type: string - webhooks: - description: Webhook list for this canary - type: array - properties: - items: - type: object - required: ['name', 'url', 'timeout'] - properties: - name: - description: Name of the webhook - type: string - type: - description: Type of the webhook pre, post or during rollout - type: string - enum: - - "" - - confirm-rollout - - pre-rollout - - rollout - - post-rollout - url: - description: URL address of this webhook - type: string - format: url - timeout: - description: Request timeout for this webhook - type: string - pattern: "^[0-9]+(m|s)" - metadata: - description: Metadata (key-value pairs) for this webhook - anyOf: - - type: string - - type: object - status: - properties: - phase: - description: Analysis phase of this canary - type: string - enum: - - "" - - Initializing - - Initialized - - Waiting - - Progressing - - Finalising - - Succeeded - - Failed - canaryWeight: - description: Traffic weight percentage routed to canary - type: number - failedChecks: - description: Failed check count of the current canary analysis - type: number - iterations: - description: Iteration count of the current canary analysis - type: number - lastAppliedSpec: - description: LastAppliedSpec of this canary - type: string - lastTransitionTime: - description: LastTransitionTime of this canary - format: date-time - type: string - conditions: - description: Status conditions of this canary - type: array - properties: - items: - type: object - required: ['type', 'status', 'reason'] - properties: - lastTransitionTime: - description: LastTransitionTime of this condition - format: date-time - type: string - lastUpdateTime: - description: LastUpdateTime of this condition - format: date-time - type: string - message: - description: Message associated with this condition - type: string - reason: - description: Reason for the current status of this condition - type: string - status: - description: Status of this condition - type: string - type: - description: Type of this condition - type: string diff --git a/artifacts/flagger/deployment.yaml b/artifacts/flagger/deployment.yaml deleted file mode 100644 index c8a85f4b..00000000 --- a/artifacts/flagger/deployment.yaml +++ /dev/null @@ -1,65 +0,0 @@ -apiVersion: apps/v1 -kind: Deployment -metadata: - name: flagger - namespace: istio-system - labels: - app: flagger -spec: - replicas: 1 - strategy: - type: Recreate - selector: - matchLabels: - app: flagger - template: - metadata: - labels: - app: flagger - annotations: - prometheus.io/scrape: "true" - spec: - serviceAccountName: flagger - containers: - - name: flagger - image: weaveworks/flagger:0.18.2 - imagePullPolicy: IfNotPresent - ports: - - name: http - containerPort: 8080 - command: - - ./flagger - - -log-level=info - - -control-loop-interval=10s - - -mesh-provider=$(MESH_PROVIDER) - - -metrics-server=http://prometheus.istio-system.svc.cluster.local:9090 - livenessProbe: - exec: - command: - - wget - - --quiet - - --tries=1 - - --timeout=2 - - --spider - - http://localhost:8080/healthz - timeoutSeconds: 5 - readinessProbe: - exec: - command: - - wget - - --quiet - - --tries=1 - - --timeout=2 - - --spider - - http://localhost:8080/healthz - timeoutSeconds: 5 - resources: - limits: - memory: "512Mi" - cpu: "1000m" - requests: - memory: "32Mi" - cpu: "10m" - securityContext: - readOnlyRootFilesystem: true - runAsUser: 10001 diff --git a/artifacts/gke/istio-gateway.yaml b/artifacts/gke/istio-gateway.yaml deleted file mode 100644 index 79c01615..00000000 --- a/artifacts/gke/istio-gateway.yaml +++ /dev/null @@ -1,27 +0,0 @@ -apiVersion: networking.istio.io/v1alpha3 -kind: Gateway -metadata: - name: public-gateway - namespace: istio-system -spec: - selector: - istio: ingressgateway - servers: - - port: - number: 80 - name: http - protocol: HTTP - hosts: - - "*" - tls: - httpsRedirect: true - - port: - number: 443 - name: https - protocol: HTTPS - hosts: - - "*" - tls: - mode: SIMPLE - privateKey: /etc/istio/ingressgateway-certs/tls.key - serverCertificate: /etc/istio/ingressgateway-certs/tls.crt diff --git a/artifacts/gke/istio-prometheus.yaml b/artifacts/gke/istio-prometheus.yaml deleted file mode 100644 index 07944d6e..00000000 --- a/artifacts/gke/istio-prometheus.yaml +++ /dev/null @@ -1,834 +0,0 @@ -# Source: istio/charts/prometheus/templates/configmap.yaml -apiVersion: v1 -kind: ConfigMap -metadata: - name: prometheus - namespace: istio-system - labels: - app: prometheus - chart: prometheus-1.0.6 - heritage: Tiller - release: istio -data: - prometheus.yml: |- - global: - scrape_interval: 15s - scrape_configs: - - - job_name: 'istio-mesh' - # Override the global default and scrape targets from this job every 5 seconds. - scrape_interval: 5s - - kubernetes_sd_configs: - - role: endpoints - namespaces: - names: - - istio-system - - relabel_configs: - - source_labels: [__meta_kubernetes_service_name, __meta_kubernetes_endpoint_port_name] - action: keep - regex: istio-telemetry;prometheus - - - # Scrape config for envoy stats - - job_name: 'envoy-stats' - metrics_path: /stats/prometheus - kubernetes_sd_configs: - - role: pod - - relabel_configs: - - source_labels: [__meta_kubernetes_pod_container_port_name] - action: keep - regex: '.*-envoy-prom' - - source_labels: [__address__, __meta_kubernetes_pod_annotation_prometheus_io_port] - action: replace - regex: ([^:]+)(?::\d+)?;(\d+) - replacement: $1:15090 - target_label: __address__ - - action: labelmap - regex: __meta_kubernetes_pod_label_(.+) - - source_labels: [__meta_kubernetes_namespace] - action: replace - target_label: namespace - - source_labels: [__meta_kubernetes_pod_name] - action: replace - target_label: pod_name - - metric_relabel_configs: - # Exclude some of the envoy metrics that have massive cardinality - # This list may need to be pruned further moving forward, as informed - # by performance and scalability testing. - - source_labels: [ cluster_name ] - regex: '(outbound|inbound|prometheus_stats).*' - action: drop - - source_labels: [ tcp_prefix ] - regex: '(outbound|inbound|prometheus_stats).*' - action: drop - - source_labels: [ listener_address ] - regex: '(.+)' - action: drop - - source_labels: [ http_conn_manager_listener_prefix ] - regex: '(.+)' - action: drop - - source_labels: [ http_conn_manager_prefix ] - regex: '(.+)' - action: drop - - source_labels: [ __name__ ] - regex: 'envoy_tls.*' - action: drop - - source_labels: [ __name__ ] - regex: 'envoy_tcp_downstream.*' - action: drop - - source_labels: [ __name__ ] - regex: 'envoy_http_(stats|admin).*' - action: drop - - source_labels: [ __name__ ] - regex: 'envoy_cluster_(lb|retry|bind|internal|max|original).*' - action: drop - - - - job_name: 'istio-policy' - # Override the global default and scrape targets from this job every 5 seconds. - scrape_interval: 5s - # metrics_path defaults to '/metrics' - # scheme defaults to 'http'. - - kubernetes_sd_configs: - - role: endpoints - namespaces: - names: - - istio-system - - - relabel_configs: - - source_labels: [__meta_kubernetes_service_name, __meta_kubernetes_endpoint_port_name] - action: keep - regex: istio-policy;http-monitoring - - - job_name: 'istio-telemetry' - # Override the global default and scrape targets from this job every 5 seconds. - scrape_interval: 5s - # metrics_path defaults to '/metrics' - # scheme defaults to 'http'. - - kubernetes_sd_configs: - - role: endpoints - namespaces: - names: - - istio-system - - relabel_configs: - - source_labels: [__meta_kubernetes_service_name, __meta_kubernetes_endpoint_port_name] - action: keep - regex: istio-telemetry;http-monitoring - - - job_name: 'pilot' - # Override the global default and scrape targets from this job every 5 seconds. - scrape_interval: 5s - # metrics_path defaults to '/metrics' - # scheme defaults to 'http'. - - kubernetes_sd_configs: - - role: endpoints - namespaces: - names: - - istio-system - - relabel_configs: - - source_labels: [__meta_kubernetes_service_name, __meta_kubernetes_endpoint_port_name] - action: keep - regex: istio-pilot;http-monitoring - - - job_name: 'galley' - # Override the global default and scrape targets from this job every 5 seconds. - scrape_interval: 5s - # metrics_path defaults to '/metrics' - # scheme defaults to 'http'. - - kubernetes_sd_configs: - - role: endpoints - namespaces: - names: - - istio-system - - relabel_configs: - - source_labels: [__meta_kubernetes_service_name, __meta_kubernetes_endpoint_port_name] - action: keep - regex: istio-galley;http-monitoring - - # scrape config for API servers - - job_name: 'kubernetes-apiservers' - kubernetes_sd_configs: - - role: endpoints - namespaces: - names: - - default - scheme: https - tls_config: - ca_file: /var/run/secrets/kubernetes.io/serviceaccount/ca.crt - bearer_token_file: /var/run/secrets/kubernetes.io/serviceaccount/token - relabel_configs: - - source_labels: [__meta_kubernetes_service_name, __meta_kubernetes_endpoint_port_name] - action: keep - regex: kubernetes;https - - # scrape config for nodes (kubelet) - - job_name: 'kubernetes-nodes' - scheme: https - tls_config: - ca_file: /var/run/secrets/kubernetes.io/serviceaccount/ca.crt - bearer_token_file: /var/run/secrets/kubernetes.io/serviceaccount/token - kubernetes_sd_configs: - - role: node - relabel_configs: - - action: labelmap - regex: __meta_kubernetes_node_label_(.+) - - target_label: __address__ - replacement: kubernetes.default.svc:443 - - source_labels: [__meta_kubernetes_node_name] - regex: (.+) - target_label: __metrics_path__ - replacement: /api/v1/nodes/${1}/proxy/metrics - - # Scrape config for Kubelet cAdvisor. - # - # This is required for Kubernetes 1.7.3 and later, where cAdvisor metrics - # (those whose names begin with 'container_') have been removed from the - # Kubelet metrics endpoint. This job scrapes the cAdvisor endpoint to - # retrieve those metrics. - # - # In Kubernetes 1.7.0-1.7.2, these metrics are only exposed on the cAdvisor - # HTTP endpoint; use "replacement: /api/v1/nodes/${1}:4194/proxy/metrics" - # in that case (and ensure cAdvisor's HTTP server hasn't been disabled with - # the --cadvisor-port=0 Kubelet flag). - # - # This job is not necessary and should be removed in Kubernetes 1.6 and - # earlier versions, or it will cause the metrics to be scraped twice. - - job_name: 'kubernetes-cadvisor' - scheme: https - tls_config: - ca_file: /var/run/secrets/kubernetes.io/serviceaccount/ca.crt - bearer_token_file: /var/run/secrets/kubernetes.io/serviceaccount/token - kubernetes_sd_configs: - - role: node - relabel_configs: - - action: labelmap - regex: __meta_kubernetes_node_label_(.+) - - target_label: __address__ - replacement: kubernetes.default.svc:443 - - source_labels: [__meta_kubernetes_node_name] - regex: (.+) - target_label: __metrics_path__ - replacement: /api/v1/nodes/${1}/proxy/metrics/cadvisor - - # scrape config for service endpoints. - - job_name: 'kubernetes-service-endpoints' - kubernetes_sd_configs: - - role: endpoints - relabel_configs: - - source_labels: [__meta_kubernetes_service_annotation_prometheus_io_scrape] - action: keep - regex: true - - source_labels: [__meta_kubernetes_service_annotation_prometheus_io_scheme] - action: replace - target_label: __scheme__ - regex: (https?) - - source_labels: [__meta_kubernetes_service_annotation_prometheus_io_path] - action: replace - target_label: __metrics_path__ - regex: (.+) - - source_labels: [__address__, __meta_kubernetes_service_annotation_prometheus_io_port] - action: replace - target_label: __address__ - regex: ([^:]+)(?::\d+)?;(\d+) - replacement: $1:$2 - - action: labelmap - regex: __meta_kubernetes_service_label_(.+) - - source_labels: [__meta_kubernetes_namespace] - action: replace - target_label: kubernetes_namespace - - source_labels: [__meta_kubernetes_service_name] - action: replace - target_label: kubernetes_name - - - job_name: 'kubernetes-pods' - kubernetes_sd_configs: - - role: pod - relabel_configs: # If first two labels are present, pod should be scraped by the istio-secure job. - - source_labels: [__meta_kubernetes_pod_annotation_prometheus_io_scrape] - action: keep - regex: true - - source_labels: [__meta_kubernetes_pod_annotation_sidecar_istio_io_status] - action: drop - regex: (.+) - - source_labels: [__meta_kubernetes_pod_annotation_istio_mtls] - action: drop - regex: (true) - - source_labels: [__meta_kubernetes_pod_annotation_prometheus_io_path] - action: replace - target_label: __metrics_path__ - regex: (.+) - - source_labels: [__address__, __meta_kubernetes_pod_annotation_prometheus_io_port] - action: replace - regex: ([^:]+)(?::\d+)?;(\d+) - replacement: $1:$2 - target_label: __address__ - - action: labelmap - regex: __meta_kubernetes_pod_label_(.+) - - source_labels: [__meta_kubernetes_namespace] - action: replace - target_label: namespace - - source_labels: [__meta_kubernetes_pod_name] - action: replace - target_label: pod_name - - - job_name: 'kubernetes-pods-istio-secure' - scheme: https - tls_config: - ca_file: /etc/istio-certs/root-cert.pem - cert_file: /etc/istio-certs/cert-chain.pem - key_file: /etc/istio-certs/key.pem - insecure_skip_verify: true # prometheus does not support secure naming. - kubernetes_sd_configs: - - role: pod - relabel_configs: - - source_labels: [__meta_kubernetes_pod_annotation_prometheus_io_scrape] - action: keep - regex: true - # sidecar status annotation is added by sidecar injector and - # istio_workload_mtls_ability can be specifically placed on a pod to indicate its ability to receive mtls traffic. - - source_labels: [__meta_kubernetes_pod_annotation_sidecar_istio_io_status, __meta_kubernetes_pod_annotation_istio_mtls] - action: keep - regex: (([^;]+);([^;]*))|(([^;]*);(true)) - - source_labels: [__meta_kubernetes_pod_annotation_prometheus_io_path] - action: replace - target_label: __metrics_path__ - regex: (.+) - - source_labels: [__address__] # Only keep address that is host:port - action: keep # otherwise an extra target with ':443' is added for https scheme - regex: ([^:]+):(\d+) - - source_labels: [__address__, __meta_kubernetes_pod_annotation_prometheus_io_port] - action: replace - regex: ([^:]+)(?::\d+)?;(\d+) - replacement: $1:$2 - target_label: __address__ - - action: labelmap - regex: __meta_kubernetes_pod_label_(.+) - - source_labels: [__meta_kubernetes_namespace] - action: replace - target_label: namespace - - source_labels: [__meta_kubernetes_pod_name] - action: replace - target_label: pod_name - ---- - -# Source: istio/charts/prometheus/templates/clusterrole.yaml -apiVersion: rbac.authorization.k8s.io/v1beta1 -kind: ClusterRole -metadata: - name: prometheus-istio-system - labels: - app: prometheus - chart: prometheus-1.0.6 - heritage: Tiller - release: istio -rules: - - apiGroups: [""] - resources: - - nodes - - services - - endpoints - - pods - - nodes/proxy - verbs: ["get", "list", "watch"] - - apiGroups: [""] - resources: - - configmaps - verbs: ["get"] - - nonResourceURLs: ["/metrics"] - verbs: ["get"] - ---- - -# Source: istio/charts/prometheus/templates/serviceaccount.yaml -apiVersion: v1 -kind: ServiceAccount -metadata: - name: prometheus - namespace: istio-system - labels: - app: prometheus - chart: prometheus-1.0.6 - heritage: Tiller - release: istio - ---- - -# Source: istio/charts/prometheus/templates/clusterrolebindings.yaml -apiVersion: rbac.authorization.k8s.io/v1beta1 -kind: ClusterRoleBinding -metadata: - name: prometheus-istio-system - labels: - app: prometheus - chart: prometheus-1.0.6 - heritage: Tiller - release: istio -roleRef: - apiGroup: rbac.authorization.k8s.io - kind: ClusterRole - name: prometheus-istio-system -subjects: - - kind: ServiceAccount - name: prometheus - namespace: istio-system - ---- - -# Source: istio/charts/prometheus/templates/service.yaml -apiVersion: v1 -kind: Service -metadata: - name: prometheus - namespace: istio-system - annotations: - prometheus.io/scrape: 'true' - labels: - name: prometheus -spec: - selector: - app: prometheus - ports: - - name: http-prometheus - protocol: TCP - port: 9090 - ---- - -# Source: istio/charts/prometheus/templates/deployment.yaml -apiVersion: apps/v1beta1 -kind: Deployment -metadata: - name: prometheus - namespace: istio-system - labels: - app: prometheus - chart: prometheus-1.0.6 - heritage: Tiller - release: istio -spec: - replicas: 1 - selector: - matchLabels: - app: prometheus - template: - metadata: - labels: - app: prometheus - annotations: - sidecar.istio.io/inject: "false" - scheduler.alpha.kubernetes.io/critical-pod: "" - spec: - serviceAccountName: prometheus - containers: - - name: prometheus - image: "docker.io/prom/prometheus:v2.3.1" - imagePullPolicy: IfNotPresent - args: - - '--storage.tsdb.retention=6h' - - '--config.file=/etc/prometheus/prometheus.yml' - ports: - - containerPort: 9090 - name: http - livenessProbe: - httpGet: - path: /-/healthy - port: 9090 - readinessProbe: - httpGet: - path: /-/ready - port: 9090 - resources: - requests: - cpu: 10m - - volumeMounts: - - name: config-volume - mountPath: /etc/prometheus - - mountPath: /etc/istio-certs - name: istio-certs - volumes: - - name: config-volume - configMap: - name: prometheus - - name: istio-certs - secret: - defaultMode: 420 - optional: true - secretName: istio.default - affinity: - nodeAffinity: - requiredDuringSchedulingIgnoredDuringExecution: - nodeSelectorTerms: - - matchExpressions: - - key: beta.kubernetes.io/arch - operator: In - values: - - amd64 - - ppc64le - - s390x - preferredDuringSchedulingIgnoredDuringExecution: - - weight: 2 - preference: - matchExpressions: - - key: beta.kubernetes.io/arch - operator: In - values: - - amd64 - - weight: 2 - preference: - matchExpressions: - - key: beta.kubernetes.io/arch - operator: In - values: - - ppc64le - - weight: 2 - preference: - matchExpressions: - - key: beta.kubernetes.io/arch - operator: In - values: - - s390x - ---- -apiVersion: "config.istio.io/v1alpha2" -kind: metric -metadata: - name: requestcount - namespace: istio-system -spec: - value: "1" - dimensions: - reporter: conditional((context.reporter.kind | "inbound") == "outbound", "source", "destination") - source_workload: source.workload.name | "unknown" - source_workload_namespace: source.workload.namespace | "unknown" - source_principal: source.principal | "unknown" - source_app: source.labels["app"] | "unknown" - source_version: source.labels["version"] | "unknown" - destination_workload: destination.workload.name | "unknown" - destination_workload_namespace: destination.workload.namespace | "unknown" - destination_principal: destination.principal | "unknown" - destination_app: destination.labels["app"] | "unknown" - destination_version: destination.labels["version"] | "unknown" - destination_service: destination.service.host | "unknown" - destination_service_name: destination.service.name | "unknown" - destination_service_namespace: destination.service.namespace | "unknown" - request_protocol: api.protocol | context.protocol | "unknown" - response_code: response.code | 200 - connection_security_policy: conditional((context.reporter.kind | "inbound") == "outbound", "unknown", conditional(connection.mtls | false, "mutual_tls", "none")) - monitored_resource_type: '"UNSPECIFIED"' ---- -apiVersion: "config.istio.io/v1alpha2" -kind: metric -metadata: - name: requestduration - namespace: istio-system -spec: - value: response.duration | "0ms" - dimensions: - reporter: conditional((context.reporter.kind | "inbound") == "outbound", "source", "destination") - source_workload: source.workload.name | "unknown" - source_workload_namespace: source.workload.namespace | "unknown" - source_principal: source.principal | "unknown" - source_app: source.labels["app"] | "unknown" - source_version: source.labels["version"] | "unknown" - destination_workload: destination.workload.name | "unknown" - destination_workload_namespace: destination.workload.namespace | "unknown" - destination_principal: destination.principal | "unknown" - destination_app: destination.labels["app"] | "unknown" - destination_version: destination.labels["version"] | "unknown" - destination_service: destination.service.host | "unknown" - destination_service_name: destination.service.name | "unknown" - destination_service_namespace: destination.service.namespace | "unknown" - request_protocol: api.protocol | context.protocol | "unknown" - response_code: response.code | 200 - connection_security_policy: conditional((context.reporter.kind | "inbound") == "outbound", "unknown", conditional(connection.mtls | false, "mutual_tls", "none")) - monitored_resource_type: '"UNSPECIFIED"' ---- -apiVersion: "config.istio.io/v1alpha2" -kind: metric -metadata: - name: requestsize - namespace: istio-system -spec: - value: request.size | 0 - dimensions: - reporter: conditional((context.reporter.kind | "inbound") == "outbound", "source", "destination") - source_workload: source.workload.name | "unknown" - source_workload_namespace: source.workload.namespace | "unknown" - source_principal: source.principal | "unknown" - source_app: source.labels["app"] | "unknown" - source_version: source.labels["version"] | "unknown" - destination_workload: destination.workload.name | "unknown" - destination_workload_namespace: destination.workload.namespace | "unknown" - destination_principal: destination.principal | "unknown" - destination_app: destination.labels["app"] | "unknown" - destination_version: destination.labels["version"] | "unknown" - destination_service: destination.service.host | "unknown" - destination_service_name: destination.service.name | "unknown" - destination_service_namespace: destination.service.namespace | "unknown" - request_protocol: api.protocol | context.protocol | "unknown" - response_code: response.code | 200 - connection_security_policy: conditional((context.reporter.kind | "inbound") == "outbound", "unknown", conditional(connection.mtls | false, "mutual_tls", "none")) - monitored_resource_type: '"UNSPECIFIED"' ---- -apiVersion: "config.istio.io/v1alpha2" -kind: metric -metadata: - name: responsesize - namespace: istio-system -spec: - value: response.size | 0 - dimensions: - reporter: conditional((context.reporter.kind | "inbound") == "outbound", "source", "destination") - source_workload: source.workload.name | "unknown" - source_workload_namespace: source.workload.namespace | "unknown" - source_principal: source.principal | "unknown" - source_app: source.labels["app"] | "unknown" - source_version: source.labels["version"] | "unknown" - destination_workload: destination.workload.name | "unknown" - destination_workload_namespace: destination.workload.namespace | "unknown" - destination_principal: destination.principal | "unknown" - destination_app: destination.labels["app"] | "unknown" - destination_version: destination.labels["version"] | "unknown" - destination_service: destination.service.host | "unknown" - destination_service_name: destination.service.name | "unknown" - destination_service_namespace: destination.service.namespace | "unknown" - request_protocol: api.protocol | context.protocol | "unknown" - response_code: response.code | 200 - connection_security_policy: conditional((context.reporter.kind | "inbound") == "outbound", "unknown", conditional(connection.mtls | false, "mutual_tls", "none")) - monitored_resource_type: '"UNSPECIFIED"' ---- -apiVersion: "config.istio.io/v1alpha2" -kind: metric -metadata: - name: tcpbytesent - namespace: istio-system -spec: - value: connection.sent.bytes | 0 - dimensions: - reporter: conditional((context.reporter.kind | "inbound") == "outbound", "source", "destination") - source_workload: source.workload.name | "unknown" - source_workload_namespace: source.workload.namespace | "unknown" - source_principal: source.principal | "unknown" - source_app: source.labels["app"] | "unknown" - source_version: source.labels["version"] | "unknown" - destination_workload: destination.workload.name | "unknown" - destination_workload_namespace: destination.workload.namespace | "unknown" - destination_principal: destination.principal | "unknown" - destination_app: destination.labels["app"] | "unknown" - destination_version: destination.labels["version"] | "unknown" - destination_service: destination.service.name | "unknown" - destination_service_name: destination.service.name | "unknown" - destination_service_namespace: destination.service.namespace | "unknown" - connection_security_policy: conditional((context.reporter.kind | "inbound") == "outbound", "unknown", conditional(connection.mtls | false, "mutual_tls", "none")) - monitored_resource_type: '"UNSPECIFIED"' ---- -apiVersion: "config.istio.io/v1alpha2" -kind: metric -metadata: - name: tcpbytereceived - namespace: istio-system -spec: - value: connection.received.bytes | 0 - dimensions: - reporter: conditional((context.reporter.kind | "inbound") == "outbound", "source", "destination") - source_workload: source.workload.name | "unknown" - source_workload_namespace: source.workload.namespace | "unknown" - source_principal: source.principal | "unknown" - source_app: source.labels["app"] | "unknown" - source_version: source.labels["version"] | "unknown" - destination_workload: destination.workload.name | "unknown" - destination_workload_namespace: destination.workload.namespace | "unknown" - destination_principal: destination.principal | "unknown" - destination_app: destination.labels["app"] | "unknown" - destination_version: destination.labels["version"] | "unknown" - destination_service: destination.service.name | "unknown" - destination_service_name: destination.service.name | "unknown" - destination_service_namespace: destination.service.namespace | "unknown" - connection_security_policy: conditional((context.reporter.kind | "inbound") == "outbound", "unknown", conditional(connection.mtls | false, "mutual_tls", "none")) - monitored_resource_type: '"UNSPECIFIED"' ---- -apiVersion: "config.istio.io/v1alpha2" -kind: prometheus -metadata: - name: handler - namespace: istio-system -spec: - metrics: - - name: requests_total - instance_name: requestcount.metric.istio-system - kind: COUNTER - label_names: - - reporter - - source_app - - source_principal - - source_workload - - source_workload_namespace - - source_version - - destination_app - - destination_principal - - destination_workload - - destination_workload_namespace - - destination_version - - destination_service - - destination_service_name - - destination_service_namespace - - request_protocol - - response_code - - connection_security_policy - - name: request_duration_seconds - instance_name: requestduration.metric.istio-system - kind: DISTRIBUTION - label_names: - - reporter - - source_app - - source_principal - - source_workload - - source_workload_namespace - - source_version - - destination_app - - destination_principal - - destination_workload - - destination_workload_namespace - - destination_version - - destination_service - - destination_service_name - - destination_service_namespace - - request_protocol - - response_code - - connection_security_policy - buckets: - explicit_buckets: - bounds: [0.005, 0.01, 0.025, 0.05, 0.1, 0.25, 0.5, 1, 2.5, 5, 10] - - name: request_bytes - instance_name: requestsize.metric.istio-system - kind: DISTRIBUTION - label_names: - - reporter - - source_app - - source_principal - - source_workload - - source_workload_namespace - - source_version - - destination_app - - destination_principal - - destination_workload - - destination_workload_namespace - - destination_version - - destination_service - - destination_service_name - - destination_service_namespace - - request_protocol - - response_code - - connection_security_policy - buckets: - exponentialBuckets: - numFiniteBuckets: 8 - scale: 1 - growthFactor: 10 - - name: response_bytes - instance_name: responsesize.metric.istio-system - kind: DISTRIBUTION - label_names: - - reporter - - source_app - - source_principal - - source_workload - - source_workload_namespace - - source_version - - destination_app - - destination_principal - - destination_workload - - destination_workload_namespace - - destination_version - - destination_service - - destination_service_name - - destination_service_namespace - - request_protocol - - response_code - - connection_security_policy - buckets: - exponentialBuckets: - numFiniteBuckets: 8 - scale: 1 - growthFactor: 10 - - name: tcp_sent_bytes_total - instance_name: tcpbytesent.metric.istio-system - kind: COUNTER - label_names: - - reporter - - source_app - - source_principal - - source_workload - - source_workload_namespace - - source_version - - destination_app - - destination_principal - - destination_workload - - destination_workload_namespace - - destination_version - - destination_service - - destination_service_name - - destination_service_namespace - - connection_security_policy - - name: tcp_received_bytes_total - instance_name: tcpbytereceived.metric.istio-system - kind: COUNTER - label_names: - - reporter - - source_app - - source_principal - - source_workload - - source_workload_namespace - - source_version - - destination_app - - destination_principal - - destination_workload - - destination_workload_namespace - - destination_version - - destination_service - - destination_service_name - - destination_service_namespace - - connection_security_policy ---- -apiVersion: "config.istio.io/v1alpha2" -kind: rule -metadata: - name: promhttp - namespace: istio-system -spec: - match: context.protocol == "http" || context.protocol == "grpc" - actions: - - handler: handler.prometheus - instances: - - requestcount.metric - - requestduration.metric - - requestsize.metric - - responsesize.metric ---- -apiVersion: "config.istio.io/v1alpha2" -kind: rule -metadata: - name: promtcp - namespace: istio-system -spec: - match: context.protocol == "tcp" - actions: - - handler: handler.prometheus - instances: - - tcpbytesent.metric - - tcpbytereceived.metric ---- diff --git a/artifacts/gloo/canary.yaml b/artifacts/gloo/canary.yaml deleted file mode 100644 index 3be05a78..00000000 --- a/artifacts/gloo/canary.yaml +++ /dev/null @@ -1,36 +0,0 @@ -apiVersion: flagger.app/v1alpha3 -kind: Canary -metadata: - name: podinfo - namespace: test -spec: - targetRef: - apiVersion: apps/v1 - kind: Deployment - name: podinfo - progressDeadlineSeconds: 60 - autoscalerRef: - apiVersion: autoscaling/v2beta1 - kind: HorizontalPodAutoscaler - name: podinfo - service: - port: 9898 - canaryAnalysis: - interval: 10s - threshold: 10 - maxWeight: 50 - stepWeight: 5 - metrics: - - name: request-success-rate - threshold: 99 - interval: 1m - - name: request-duration - threshold: 500 - interval: 30s - webhooks: - - name: load-test - url: http://flagger-loadtester.test/ - timeout: 5s - metadata: - type: cmd - cmd: "hey -z 1m -q 10 -c 2 http://gloo.example.com/" diff --git a/artifacts/gloo/deployment.yaml b/artifacts/gloo/deployment.yaml deleted file mode 100644 index dacb34be..00000000 --- a/artifacts/gloo/deployment.yaml +++ /dev/null @@ -1,67 +0,0 @@ -apiVersion: apps/v1 -kind: Deployment -metadata: - name: podinfo - namespace: test - labels: - app: podinfo -spec: - minReadySeconds: 5 - revisionHistoryLimit: 5 - progressDeadlineSeconds: 60 - strategy: - rollingUpdate: - maxUnavailable: 0 - type: RollingUpdate - selector: - matchLabels: - app: podinfo - template: - metadata: - annotations: - prometheus.io/scrape: "true" - labels: - app: podinfo - spec: - containers: - - name: podinfod - image: quay.io/stefanprodan/podinfo:1.7.0 - imagePullPolicy: IfNotPresent - ports: - - containerPort: 9898 - name: http - protocol: TCP - command: - - ./podinfo - - --port=9898 - - --level=info - - --random-delay=false - - --random-error=false - env: - - name: PODINFO_UI_COLOR - value: blue - livenessProbe: - exec: - command: - - podcli - - check - - http - - localhost:9898/healthz - initialDelaySeconds: 5 - timeoutSeconds: 5 - readinessProbe: - exec: - command: - - podcli - - check - - http - - localhost:9898/readyz - initialDelaySeconds: 5 - timeoutSeconds: 5 - resources: - limits: - cpu: 2000m - memory: 512Mi - requests: - cpu: 100m - memory: 64Mi diff --git a/artifacts/gloo/hpa.yaml b/artifacts/gloo/hpa.yaml deleted file mode 100644 index 48ec76e8..00000000 --- a/artifacts/gloo/hpa.yaml +++ /dev/null @@ -1,19 +0,0 @@ -apiVersion: autoscaling/v2beta1 -kind: HorizontalPodAutoscaler -metadata: - name: podinfo - namespace: test -spec: - scaleTargetRef: - apiVersion: apps/v1 - kind: Deployment - name: podinfo - minReplicas: 1 - maxReplicas: 4 - metrics: - - type: Resource - resource: - name: cpu - # scale up if usage is above - # 99% of the requested CPU (100m) - targetAverageUtilization: 99 diff --git a/artifacts/gloo/virtual-service.yaml b/artifacts/gloo/virtual-service.yaml deleted file mode 100644 index 53d010dd..00000000 --- a/artifacts/gloo/virtual-service.yaml +++ /dev/null @@ -1,17 +0,0 @@ -apiVersion: gateway.solo.io/v1 -kind: VirtualService -metadata: - name: podinfo - namespace: test -spec: - virtualHost: - domains: - - '*' - name: podinfo.default - routes: - - matcher: - prefix: / - routeAction: - upstreamGroup: - name: podinfo - namespace: gloo diff --git a/artifacts/helmtester/deployment.yaml b/artifacts/helmtester/deployment.yaml deleted file mode 100644 index a8906e17..00000000 --- a/artifacts/helmtester/deployment.yaml +++ /dev/null @@ -1,58 +0,0 @@ -apiVersion: apps/v1 -kind: Deployment -metadata: - name: flagger-helmtester - namespace: kube-system - labels: - app: flagger-helmtester -spec: - selector: - matchLabels: - app: flagger-helmtester - template: - metadata: - labels: - app: flagger-helmtester - annotations: - prometheus.io/scrape: "true" - spec: - serviceAccountName: tiller - containers: - - name: helmtester - image: weaveworks/flagger-loadtester:0.4.0 - imagePullPolicy: IfNotPresent - ports: - - name: http - containerPort: 8080 - command: - - ./loadtester - - -port=8080 - - -log-level=info - - -timeout=1h - livenessProbe: - exec: - command: - - wget - - --quiet - - --tries=1 - - --timeout=4 - - --spider - - http://localhost:8080/healthz - timeoutSeconds: 5 - readinessProbe: - exec: - command: - - wget - - --quiet - - --tries=1 - - --timeout=4 - - --spider - - http://localhost:8080/healthz - timeoutSeconds: 5 - resources: - limits: - memory: "512Mi" - cpu: "1000m" - requests: - memory: "32Mi" - cpu: "10m" diff --git a/artifacts/helmtester/service.yaml b/artifacts/helmtester/service.yaml deleted file mode 100644 index 61d8c228..00000000 --- a/artifacts/helmtester/service.yaml +++ /dev/null @@ -1,16 +0,0 @@ -apiVersion: v1 -kind: Service -metadata: - name: flagger-helmtester - namespace: kube-system - labels: - app: flagger-helmtester -spec: - type: ClusterIP - selector: - app: flagger-helmtester - ports: - - name: http - port: 80 - protocol: TCP - targetPort: http \ No newline at end of file diff --git a/artifacts/loadtester/config.yaml b/artifacts/loadtester/config.yaml deleted file mode 100644 index b9d0f568..00000000 --- a/artifacts/loadtester/config.yaml +++ /dev/null @@ -1,19 +0,0 @@ ---- -apiVersion: v1 -kind: ConfigMap -metadata: - name: flagger-loadtester-bats -data: - tests: | - #!/usr/bin/env bats - - @test "check message" { - curl -sS http://${URL} | jq -r .message | { - run cut -d $' ' -f1 - [ $output = "greetings" ] - } - } - - @test "check headers" { - curl -sS http://${URL}/headers | grep X-Request-Id - } diff --git a/artifacts/loadtester/deployment.yaml b/artifacts/loadtester/deployment.yaml deleted file mode 100644 index 8e7a117f..00000000 --- a/artifacts/loadtester/deployment.yaml +++ /dev/null @@ -1,67 +0,0 @@ -apiVersion: apps/v1 -kind: Deployment -metadata: - name: flagger-loadtester - labels: - app: flagger-loadtester -spec: - selector: - matchLabels: - app: flagger-loadtester - template: - metadata: - labels: - app: flagger-loadtester - annotations: - prometheus.io/scrape: "true" - spec: - containers: - - name: loadtester - image: weaveworks/flagger-loadtester:0.6.1 - imagePullPolicy: IfNotPresent - ports: - - name: http - containerPort: 8080 - command: - - ./loadtester - - -port=8080 - - -log-level=info - - -timeout=1h - livenessProbe: - exec: - command: - - wget - - --quiet - - --tries=1 - - --timeout=4 - - --spider - - http://localhost:8080/healthz - timeoutSeconds: 5 - readinessProbe: - exec: - command: - - wget - - --quiet - - --tries=1 - - --timeout=4 - - --spider - - http://localhost:8080/healthz - timeoutSeconds: 5 - resources: - limits: - memory: "512Mi" - cpu: "1000m" - requests: - memory: "32Mi" - cpu: "10m" - securityContext: - readOnlyRootFilesystem: true - runAsUser: 10001 -# volumeMounts: -# - name: tests -# mountPath: /bats -# readOnly: true -# volumes: -# - name: tests -# configMap: -# name: flagger-loadtester-bats \ No newline at end of file diff --git a/artifacts/loadtester/service.yaml b/artifacts/loadtester/service.yaml deleted file mode 100644 index 772b20af..00000000 --- a/artifacts/loadtester/service.yaml +++ /dev/null @@ -1,15 +0,0 @@ -apiVersion: v1 -kind: Service -metadata: - name: flagger-loadtester - labels: - app: flagger-loadtester -spec: - type: ClusterIP - selector: - app: flagger-loadtester - ports: - - name: http - port: 80 - protocol: TCP - targetPort: http \ No newline at end of file diff --git a/artifacts/namespaces/test.yaml b/artifacts/namespaces/test.yaml deleted file mode 100644 index cff2ab62..00000000 --- a/artifacts/namespaces/test.yaml +++ /dev/null @@ -1,7 +0,0 @@ -apiVersion: v1 -kind: Namespace -metadata: - name: test - labels: - istio-injection: enabled - appmesh.k8s.aws/sidecarInjectorWebhook: enabled diff --git a/artifacts/nginx/canary.yaml b/artifacts/nginx/canary.yaml deleted file mode 100644 index 6186889b..00000000 --- a/artifacts/nginx/canary.yaml +++ /dev/null @@ -1,68 +0,0 @@ -apiVersion: flagger.app/v1alpha3 -kind: Canary -metadata: - name: podinfo - namespace: test -spec: - # deployment reference - targetRef: - apiVersion: apps/v1 - kind: Deployment - name: podinfo - # ingress reference - ingressRef: - apiVersion: extensions/v1beta1 - kind: Ingress - name: podinfo - # HPA reference (optional) - autoscalerRef: - apiVersion: autoscaling/v2beta1 - kind: HorizontalPodAutoscaler - name: podinfo - # the maximum time in seconds for the canary deployment - # to make progress before it is rollback (default 600s) - progressDeadlineSeconds: 60 - service: - # container port - port: 9898 - canaryAnalysis: - # schedule interval (default 60s) - interval: 10s - # max number of failed metric checks before rollback - threshold: 10 - # max traffic percentage routed to canary - # percentage (0-100) - maxWeight: 50 - # canary increment step - # percentage (0-100) - stepWeight: 5 - # NGINX Prometheus checks - metrics: - - name: request-success-rate - # minimum req success rate (non 5xx responses) - # percentage (0-100) - threshold: 99 - interval: 1m - - name: "latency" - threshold: 0.5 - interval: 1m - query: | - histogram_quantile(0.99, - sum( - rate( - http_request_duration_seconds_bucket{ - kubernetes_namespace="test", - kubernetes_pod_name=~"podinfo-[0-9a-zA-Z]+(-[0-9a-zA-Z]+)" - }[1m] - ) - ) by (le) - ) - # external checks (optional) - webhooks: - - name: load-test - url: http://flagger-loadtester.test/ - timeout: 5s - metadata: - type: cmd - cmd: "hey -z 1m -q 10 -c 2 http://app.example.com/" - logCmdOutput: "true" diff --git a/artifacts/nginx/deployment.yaml b/artifacts/nginx/deployment.yaml deleted file mode 100644 index 4baf9424..00000000 --- a/artifacts/nginx/deployment.yaml +++ /dev/null @@ -1,69 +0,0 @@ -apiVersion: apps/v1 -kind: Deployment -metadata: - name: podinfo - namespace: test - labels: - app: podinfo -spec: - replicas: 1 - strategy: - rollingUpdate: - maxUnavailable: 0 - type: RollingUpdate - selector: - matchLabels: - app: podinfo - template: - metadata: - annotations: - prometheus.io/scrape: "true" - labels: - app: podinfo - spec: - containers: - - name: podinfod - image: quay.io/stefanprodan/podinfo:1.7.0 - imagePullPolicy: IfNotPresent - ports: - - containerPort: 9898 - name: http - protocol: TCP - command: - - ./podinfo - - --port=9898 - - --level=info - - --random-delay=false - - --random-error=false - env: - - name: PODINFO_UI_COLOR - value: green - livenessProbe: - exec: - command: - - podcli - - check - - http - - localhost:9898/healthz - failureThreshold: 3 - periodSeconds: 10 - successThreshold: 1 - timeoutSeconds: 2 - readinessProbe: - exec: - command: - - podcli - - check - - http - - localhost:9898/readyz - failureThreshold: 3 - periodSeconds: 3 - successThreshold: 1 - timeoutSeconds: 2 - resources: - limits: - cpu: 1000m - memory: 256Mi - requests: - cpu: 100m - memory: 16Mi diff --git a/artifacts/nginx/hpa.yaml b/artifacts/nginx/hpa.yaml deleted file mode 100644 index fa2b5a6f..00000000 --- a/artifacts/nginx/hpa.yaml +++ /dev/null @@ -1,19 +0,0 @@ -apiVersion: autoscaling/v2beta1 -kind: HorizontalPodAutoscaler -metadata: - name: podinfo - namespace: test -spec: - scaleTargetRef: - apiVersion: apps/v1 - kind: Deployment - name: podinfo - minReplicas: 2 - maxReplicas: 4 - metrics: - - type: Resource - resource: - name: cpu - # scale up if usage is above - # 99% of the requested CPU (100m) - targetAverageUtilization: 99 diff --git a/artifacts/nginx/ingress.yaml b/artifacts/nginx/ingress.yaml deleted file mode 100644 index c5a6fa62..00000000 --- a/artifacts/nginx/ingress.yaml +++ /dev/null @@ -1,17 +0,0 @@ -apiVersion: extensions/v1beta1 -kind: Ingress -metadata: - name: podinfo - namespace: test - labels: - app: podinfo - annotations: - kubernetes.io/ingress.class: "nginx" -spec: - rules: - - host: app.example.com - http: - paths: - - backend: - serviceName: podinfo - servicePort: 9898 diff --git a/artifacts/smi/istio-adapter.yaml b/artifacts/smi/istio-adapter.yaml deleted file mode 100644 index eaebdcb8..00000000 --- a/artifacts/smi/istio-adapter.yaml +++ /dev/null @@ -1,131 +0,0 @@ -apiVersion: apiextensions.k8s.io/v1beta1 -kind: CustomResourceDefinition -metadata: - name: trafficsplits.split.smi-spec.io -spec: - additionalPrinterColumns: - - JSONPath: .spec.service - description: The service - name: Service - type: string - group: split.smi-spec.io - names: - kind: TrafficSplit - listKind: TrafficSplitList - plural: trafficsplits - singular: trafficsplit - scope: Namespaced - subresources: - status: {} - version: v1alpha1 - versions: - - name: v1alpha1 - served: true - storage: true ---- -apiVersion: v1 -kind: ServiceAccount -metadata: - name: smi-adapter-istio - namespace: istio-system ---- -apiVersion: rbac.authorization.k8s.io/v1 -kind: ClusterRole -metadata: - name: smi-adapter-istio -rules: - - apiGroups: - - "" - resources: - - pods - - services - - endpoints - - persistentvolumeclaims - - events - - configmaps - - secrets - verbs: - - '*' - - apiGroups: - - apps - resources: - - deployments - - daemonsets - - replicasets - - statefulsets - verbs: - - '*' - - apiGroups: - - monitoring.coreos.com - resources: - - servicemonitors - verbs: - - get - - create - - apiGroups: - - apps - resourceNames: - - smi-adapter-istio - resources: - - deployments/finalizers - verbs: - - update - - apiGroups: - - split.smi-spec.io - resources: - - '*' - verbs: - - '*' - - apiGroups: - - networking.istio.io - resources: - - '*' - verbs: - - '*' ---- -kind: ClusterRoleBinding -apiVersion: rbac.authorization.k8s.io/v1 -metadata: - name: smi-adapter-istio -subjects: - - kind: ServiceAccount - name: smi-adapter-istio - namespace: istio-system -roleRef: - kind: ClusterRole - name: smi-adapter-istio - apiGroup: rbac.authorization.k8s.io ---- -apiVersion: apps/v1 -kind: Deployment -metadata: - name: smi-adapter-istio - namespace: istio-system -spec: - replicas: 1 - selector: - matchLabels: - name: smi-adapter-istio - template: - metadata: - labels: - name: smi-adapter-istio - annotations: - sidecar.istio.io/inject: "false" - spec: - serviceAccountName: smi-adapter-istio - containers: - - name: smi-adapter-istio - image: docker.io/stefanprodan/smi-adapter-istio:0.0.2-beta.1 - command: - - smi-adapter-istio - imagePullPolicy: Always - env: - - name: WATCH_NAMESPACE - value: "" - - name: POD_NAME - valueFrom: - fieldRef: - fieldPath: metadata.name - - name: OPERATOR_NAME - value: "smi-adapter-istio" diff --git a/charts/flagger/.helmignore b/charts/flagger/.helmignore deleted file mode 100644 index f0c13194..00000000 --- a/charts/flagger/.helmignore +++ /dev/null @@ -1,21 +0,0 @@ -# Patterns to ignore when building packages. -# This supports shell glob matching, relative path matching, and -# negation (prefixed with !). Only one pattern per line. -.DS_Store -# Common VCS dirs -.git/ -.gitignore -.bzr/ -.bzrignore -.hg/ -.hgignore -.svn/ -# Common backup files -*.swp -*.bak -*.tmp -*~ -# Various IDEs -.project -.idea/ -*.tmproj diff --git a/charts/flagger/Chart.yaml b/charts/flagger/Chart.yaml deleted file mode 100644 index 41125eec..00000000 --- a/charts/flagger/Chart.yaml +++ /dev/null @@ -1,21 +0,0 @@ -apiVersion: v1 -name: flagger -version: 0.18.2 -appVersion: 0.18.2 -kubeVersion: ">=1.11.0-0" -engine: gotpl -description: Flagger is a Kubernetes operator that automates the promotion of canary deployments using Istio, Linkerd, App Mesh, Gloo or NGINX routing for traffic shifting and Prometheus metrics for canary analysis. -home: https://docs.flagger.app -icon: https://raw.githubusercontent.com/weaveworks/flagger/master/docs/logo/flagger-icon.png -sources: -- https://github.com/weaveworks/flagger -maintainers: -- name: stefanprodan - url: https://github.com/stefanprodan - email: stefanprodan@users.noreply.github.com -keywords: -- canary -- istio -- appmesh -- linkerd -- gitops diff --git a/charts/flagger/README.md b/charts/flagger/README.md deleted file mode 100644 index e5e9ba7a..00000000 --- a/charts/flagger/README.md +++ /dev/null @@ -1,108 +0,0 @@ -# Flagger - -[Flagger](https://github.com/weaveworks/flagger) is a Kubernetes operator that automates the promotion of -canary deployments using Istio, Linkerd, App Mesh, NGINX or Gloo routing for traffic shifting and Prometheus metrics for canary analysis. -Flagger implements a control loop that gradually shifts traffic to the canary while measuring key performance indicators -like HTTP requests success rate, requests average duration and pods health. -Based on the KPIs analysis a canary is promoted or aborted and the analysis result is published to Slack or MS Teams. - -## Prerequisites - -* Kubernetes >= 1.11 -* Prometheus >= 2.6 - -## Installing the Chart - -Add Flagger Helm repository: - -```console -$ helm repo add flagger https://flagger.app -``` - -Install Flagger's custom resource definitions: - -```console -$ kubectl apply -f https://raw.githubusercontent.com/weaveworks/flagger/master/artifacts/flagger/crd.yaml -``` - -To install the chart with the release name `flagger` for Istio: - -```console -$ helm upgrade -i flagger flagger/flagger \ - --namespace=istio-system \ - --set crd.create=false \ - --set meshProvider=istio \ - --set metricsServer=http://prometheus:9090 -``` - -To install the chart with the release name `flagger` for Linkerd: - -```console -$ helm upgrade -i flagger flagger/flagger \ - --namespace=linkerd \ - --set crd.create=false \ - --set meshProvider=linkerd \ - --set metricsServer=http://linkerd-prometheus:9090 -``` - -The [configuration](#configuration) section lists the parameters that can be configured during installation. - -## Uninstalling the Chart - -To uninstall/delete the `flagger` deployment: - -```console -$ helm delete --purge flagger -``` - -The command removes all the Kubernetes components associated with the chart and deletes the release. - -## Configuration - -The following tables lists the configurable parameters of the Flagger chart and their default values. - -Parameter | Description | Default ---- | --- | --- -`image.repository` | image repository | `weaveworks/flagger` -`image.tag` | image tag | `` -`image.pullPolicy` | image pull policy | `IfNotPresent` -`prometheus.install` | if `true`, installs Prometheus configured to scrape all pods in the custer including the App Mesh sidecar | `false` -`metricsServer` | Prometheus URL, used when `prometheus.install` is `false` | `http://prometheus.istio-system:9090` -`slack.url` | Slack incoming webhook | None -`slack.channel` | Slack channel | None -`slack.user` | Slack username | `flagger` -`msteams.url` | Microsoft Teams incoming webhook | None -`leaderElection.enabled` | leader election must be enabled when running more than one replica | `false` -`leaderElection.replicaCount` | number of replicas | `1` -`rbac.create` | if `true`, create and use RBAC resources | `true` -`rbac.pspEnabled` | If `true`, create and use a restricted pod security policy | `false` -`crd.create` | if `true`, create Flagger's CRDs | `true` -`resources.requests/cpu` | pod CPU request | `10m` -`resources.requests/memory` | pod memory request | `32Mi` -`resources.limits/cpu` | pod CPU limit | `1000m` -`resources.limits/memory` | pod memory limit | `512Mi` -`affinity` | node/pod affinities | None -`nodeSelector` | node labels for pod assignment | `{}` -`tolerations` | list of node taints to tolerate | `[]` - -Specify each parameter using the `--set key=value[,key=value]` argument to `helm upgrade`. For example, - -```console -$ helm upgrade -i flagger flagger/flagger \ - --namespace istio-system \ - --set crd.create=false \ - --set slack.url=https://hooks.slack.com/services/YOUR/SLACK/WEBHOOK \ - --set slack.channel=general -``` - -Alternatively, a YAML file that specifies the values for the above parameters can be provided while installing the chart. For example, - -```console -$ helm upgrade -i flagger flagger/flagger \ - --namespace istio-system \ - -f values.yaml -``` - -> **Tip**: You can use the default [values.yaml](values.yaml) - - diff --git a/charts/flagger/templates/NOTES.txt b/charts/flagger/templates/NOTES.txt deleted file mode 100644 index 4c39dfe7..00000000 --- a/charts/flagger/templates/NOTES.txt +++ /dev/null @@ -1 +0,0 @@ -Flagger installed diff --git a/charts/flagger/templates/_helpers.tpl b/charts/flagger/templates/_helpers.tpl deleted file mode 100644 index a37d87fc..00000000 --- a/charts/flagger/templates/_helpers.tpl +++ /dev/null @@ -1,42 +0,0 @@ -{{/* -Create chart name and version as used by the chart label. -*/}} -{{- define "flagger.chart" -}} -{{- printf "%s-%s" .Chart.Name .Chart.Version | replace "+" "_" | trunc 63 | trimSuffix "-" -}} -{{- end -}} - -{{/* -Expand the name of the chart. -*/}} -{{- define "flagger.name" -}} -{{- default .Chart.Name .Values.nameOverride | trunc 63 | trimSuffix "-" -}} -{{- end -}} - -{{/* -Create a default fully qualified app name. -We truncate at 63 chars because some Kubernetes name fields are limited to this (by the DNS naming spec). -If release name contains chart name it will be used as a full name. -*/}} -{{- define "flagger.fullname" -}} -{{- if .Values.fullnameOverride -}} -{{- .Values.fullnameOverride | trunc 63 | trimSuffix "-" -}} -{{- else -}} -{{- $name := default .Chart.Name .Values.nameOverride -}} -{{- if contains $name .Release.Name -}} -{{- .Release.Name | trunc 63 | trimSuffix "-" -}} -{{- else -}} -{{- printf "%s-%s" .Release.Name $name | trunc 63 | trimSuffix "-" -}} -{{- end -}} -{{- end -}} -{{- end -}} - -{{/* -Create the name of the service account to use -*/}} -{{- define "flagger.serviceAccountName" -}} -{{- if .Values.serviceAccount.create -}} - {{ default (include "flagger.fullname" .) .Values.serviceAccount.name }} -{{- else -}} - {{ default "default" .Values.serviceAccount.name }} -{{- end -}} -{{- end -}} \ No newline at end of file diff --git a/charts/flagger/templates/account.yaml b/charts/flagger/templates/account.yaml deleted file mode 100644 index b314ec21..00000000 --- a/charts/flagger/templates/account.yaml +++ /dev/null @@ -1,11 +0,0 @@ -{{- if .Values.serviceAccount.create }} -apiVersion: v1 -kind: ServiceAccount -metadata: - name: {{ template "flagger.serviceAccountName" . }} - labels: - helm.sh/chart: {{ template "flagger.chart" . }} - app.kubernetes.io/name: {{ template "flagger.name" . }} - app.kubernetes.io/managed-by: {{ .Release.Service }} - app.kubernetes.io/instance: {{ .Release.Name }} -{{- end }} diff --git a/charts/flagger/templates/crd.yaml b/charts/flagger/templates/crd.yaml deleted file mode 100644 index 76d56ae8..00000000 --- a/charts/flagger/templates/crd.yaml +++ /dev/null @@ -1,288 +0,0 @@ -{{- if .Values.crd.create }} -apiVersion: apiextensions.k8s.io/v1beta1 -kind: CustomResourceDefinition -metadata: - name: canaries.flagger.app - annotations: - helm.sh/resource-policy: keep -spec: - group: flagger.app - version: v1alpha3 - versions: - - name: v1alpha3 - served: true - storage: true - - name: v1alpha2 - served: true - storage: false - - name: v1alpha1 - served: true - storage: false - names: - plural: canaries - singular: canary - kind: Canary - categories: - - all - scope: Namespaced - subresources: - status: {} - additionalPrinterColumns: - - name: Status - type: string - JSONPath: .status.phase - - name: Weight - type: string - JSONPath: .status.canaryWeight - - name: LastTransitionTime - type: string - JSONPath: .status.lastTransitionTime - validation: - openAPIV3Schema: - properties: - spec: - required: - - targetRef - - service - - canaryAnalysis - properties: - provider: - description: Traffic managent provider - type: string - progressDeadlineSeconds: - description: Deployment progress deadline - type: number - targetRef: - description: Deployment selector - type: object - required: ['apiVersion', 'kind', 'name'] - properties: - apiVersion: - type: string - kind: - type: string - name: - type: string - autoscalerRef: - description: HPA selector - anyOf: - - type: string - - type: object - required: ['apiVersion', 'kind', 'name'] - properties: - apiVersion: - type: string - kind: - type: string - name: - type: string - ingressRef: - description: NGINX ingress selector - anyOf: - - type: string - - type: object - required: ['apiVersion', 'kind', 'name'] - properties: - apiVersion: - type: string - kind: - type: string - name: - type: string - service: - type: object - required: ['port'] - properties: - port: - description: Container port number - type: number - portName: - description: Container port name - type: string - portDiscovery: - description: Enable port dicovery - type: boolean - meshName: - description: AppMesh mesh name - type: string - backends: - description: AppMesh backend array - anyOf: - - type: string - - type: object - timeout: - description: Istio HTTP or gRPC request timeout - type: string - trafficPolicy: - description: Istio traffic policy - anyOf: - - type: string - - type: object - match: - description: Istio URL match conditions - anyOf: - - type: string - - type: array - rewrite: - description: Istio URL rewrite - anyOf: - - type: string - - type: object - headers: - description: Istio headers operations - anyOf: - - type: string - - type: object - corsPolicy: - description: Istio CORS policy - anyOf: - - type: string - - type: object - gateways: - description: Istio gateways list - anyOf: - - type: string - - type: array - hosts: - description: Istio hosts list - anyOf: - - type: string - - type: array - skipAnalysis: - type: boolean - canaryAnalysis: - properties: - interval: - description: Canary schedule interval - type: string - pattern: "^[0-9]+(m|s)" - iterations: - description: Number of checks to run for A/B Testing and Blue/Green - type: number - threshold: - description: Max number of failed checks before rollback - type: number - maxWeight: - description: Max traffic percentage routed to canary - type: number - stepWeight: - description: Canary incremental traffic percentage step - type: number - match: - description: A/B testing match conditions - anyOf: - - type: string - - type: array - metrics: - description: Prometheus query list for this canary - type: array - properties: - items: - type: object - required: ['name', 'threshold'] - properties: - name: - description: Name of the Prometheus metric - type: string - interval: - description: Interval of the promql query - type: string - pattern: "^[0-9]+(m|s)" - threshold: - description: Max scalar value accepted for this metric - type: number - query: - description: Prometheus query - type: string - webhooks: - description: Webhook list for this canary - type: array - properties: - items: - type: object - required: ['name', 'url', 'timeout'] - properties: - name: - description: Name of the webhook - type: string - type: - description: Type of the webhook pre, post or during rollout - type: string - enum: - - "" - - confirm-rollout - - pre-rollout - - rollout - - post-rollout - url: - description: URL address of this webhook - type: string - format: url - timeout: - description: Request timeout for this webhook - type: string - pattern: "^[0-9]+(m|s)" - metadata: - description: Metadata (key-value pairs) for this webhook - anyOf: - - type: string - - type: object - status: - properties: - phase: - description: Analysis phase of this canary - type: string - enum: - - "" - - Initializing - - Initialized - - Waiting - - Progressing - - Finalising - - Succeeded - - Failed - canaryWeight: - description: Traffic weight percentage routed to canary - type: number - failedChecks: - description: Failed check count of the current canary analysis - type: number - iterations: - description: Iteration count of the current canary analysis - type: number - lastAppliedSpec: - description: LastAppliedSpec of this canary - type: string - lastTransitionTime: - description: LastTransitionTime of this canary - format: date-time - type: string - conditions: - description: Status conditions of this canary - type: array - properties: - items: - type: object - required: ['type', 'status', 'reason'] - properties: - lastTransitionTime: - description: LastTransitionTime of this condition - format: date-time - type: string - lastUpdateTime: - description: LastUpdateTime of this condition - format: date-time - type: string - message: - description: Message associated with this condition - type: string - reason: - description: Reason for the current status of this condition - type: string - status: - description: Status of this condition - type: string - type: - description: Type of this condition - type: string -{{- end }} diff --git a/charts/flagger/templates/deployment.yaml b/charts/flagger/templates/deployment.yaml deleted file mode 100644 index 13b87e50..00000000 --- a/charts/flagger/templates/deployment.yaml +++ /dev/null @@ -1,104 +0,0 @@ -apiVersion: apps/v1 -kind: Deployment -metadata: - name: {{ include "flagger.fullname" . }} - labels: - helm.sh/chart: {{ template "flagger.chart" . }} - app.kubernetes.io/name: {{ template "flagger.name" . }} - app.kubernetes.io/managed-by: {{ .Release.Service }} - app.kubernetes.io/instance: {{ .Release.Name }} -spec: - replicas: {{ .Values.leaderElection.replicaCount }} - strategy: - type: Recreate - selector: - matchLabels: - app.kubernetes.io/name: {{ template "flagger.name" . }} - app.kubernetes.io/instance: {{ .Release.Name }} - template: - metadata: - labels: - app.kubernetes.io/name: {{ template "flagger.name" . }} - app.kubernetes.io/instance: {{ .Release.Name }} - spec: - serviceAccountName: {{ template "flagger.serviceAccountName" . }} - affinity: - podAntiAffinity: - preferredDuringSchedulingIgnoredDuringExecution: - - weight: 100 - podAffinityTerm: - labelSelector: - matchLabels: - app.kubernetes.io/name: {{ template "flagger.name" . }} - app.kubernetes.io/instance: {{ .Release.Name }} - topologyKey: kubernetes.io/hostname - {{- if .Values.image.pullSecret }} - imagePullSecrets: - - name: {{ .Values.image.pullSecret }} - {{- end }} - containers: - - name: flagger - securityContext: - readOnlyRootFilesystem: true - runAsUser: 10001 - image: "{{ .Values.image.repository }}:{{ .Values.image.tag }}" - imagePullPolicy: {{ .Values.image.pullPolicy }} - ports: - - name: http - containerPort: 8080 - command: - - ./flagger - - -log-level=info - {{- if .Values.meshProvider }} - - -mesh-provider={{ .Values.meshProvider }} - {{- end }} - {{- if .Values.prometheus.install }} - - -metrics-server=http://{{ template "flagger.fullname" . }}-prometheus:9090 - {{- else }} - - -metrics-server={{ .Values.metricsServer }} - {{- end }} - {{- if .Values.namespace }} - - -namespace={{ .Values.namespace }} - {{- end }} - {{- if .Values.slack.url }} - - -slack-url={{ .Values.slack.url }} - - -slack-user={{ .Values.slack.user }} - - -slack-channel={{ .Values.slack.channel }} - {{- end }} - {{- if .Values.msteams.url }} - - -msteams-url={{ .Values.msteams.url }} - {{- end }} - {{- if .Values.leaderElection.enabled }} - - -enable-leader-election=true - - -leader-election-namespace={{ .Release.Namespace }} - {{- end }} - livenessProbe: - exec: - command: - - wget - - --quiet - - --tries=1 - - --timeout=4 - - --spider - - http://localhost:8080/healthz - timeoutSeconds: 5 - readinessProbe: - exec: - command: - - wget - - --quiet - - --tries=1 - - --timeout=4 - - --spider - - http://localhost:8080/healthz - timeoutSeconds: 5 - resources: -{{ toYaml .Values.resources | indent 12 }} - {{- with .Values.nodeSelector }} - nodeSelector: -{{ toYaml . | indent 8 }} - {{- end }} - {{- with .Values.tolerations }} - tolerations: -{{ toYaml . | indent 8 }} - {{- end }} diff --git a/charts/flagger/templates/prometheus.yaml b/charts/flagger/templates/prometheus.yaml deleted file mode 100644 index bca9ab14..00000000 --- a/charts/flagger/templates/prometheus.yaml +++ /dev/null @@ -1,292 +0,0 @@ -{{- if .Values.prometheus.install }} -apiVersion: rbac.authorization.k8s.io/v1beta1 -kind: ClusterRole -metadata: - name: {{ template "flagger.fullname" . }}-prometheus - labels: - helm.sh/chart: {{ template "flagger.chart" . }} - app.kubernetes.io/name: {{ template "flagger.name" . }} - app.kubernetes.io/managed-by: {{ .Release.Service }} - app.kubernetes.io/instance: {{ .Release.Name }} -rules: - - apiGroups: [""] - resources: - - nodes - - services - - endpoints - - pods - - nodes/proxy - verbs: ["get", "list", "watch"] - - apiGroups: [""] - resources: - - configmaps - verbs: ["get"] - - nonResourceURLs: ["/metrics"] - verbs: ["get"] ---- -apiVersion: rbac.authorization.k8s.io/v1beta1 -kind: ClusterRoleBinding -metadata: - name: {{ template "flagger.fullname" . }}-prometheus - labels: - helm.sh/chart: {{ template "flagger.chart" . }} - app.kubernetes.io/name: {{ template "flagger.name" . }} - app.kubernetes.io/managed-by: {{ .Release.Service }} - app.kubernetes.io/instance: {{ .Release.Name }} -roleRef: - apiGroup: rbac.authorization.k8s.io - kind: ClusterRole - name: {{ template "flagger.fullname" . }}-prometheus -subjects: - - kind: ServiceAccount - name: {{ template "flagger.serviceAccountName" . }}-prometheus - namespace: {{ .Release.Namespace }} ---- -apiVersion: v1 -kind: ServiceAccount -metadata: - name: {{ template "flagger.serviceAccountName" . }}-prometheus - namespace: {{ .Release.Namespace }} - labels: - helm.sh/chart: {{ template "flagger.chart" . }} - app.kubernetes.io/name: {{ template "flagger.name" . }} - app.kubernetes.io/managed-by: {{ .Release.Service }} - app.kubernetes.io/instance: {{ .Release.Name }} ---- -apiVersion: v1 -kind: ConfigMap -metadata: - name: {{ template "flagger.fullname" . }}-prometheus - namespace: {{ .Release.Namespace }} - labels: - helm.sh/chart: {{ template "flagger.chart" . }} - app.kubernetes.io/name: {{ template "flagger.name" . }} - app.kubernetes.io/managed-by: {{ .Release.Service }} - app.kubernetes.io/instance: {{ .Release.Name }} -data: - prometheus.yml: |- - global: - scrape_interval: 5s - scrape_configs: - - # Scrape config for AppMesh Envoy sidecar - - job_name: 'appmesh-envoy' - metrics_path: /stats/prometheus - kubernetes_sd_configs: - - role: pod - - relabel_configs: - - source_labels: [__meta_kubernetes_pod_container_name] - action: keep - regex: '^envoy$' - - source_labels: [__address__, __meta_kubernetes_pod_annotation_prometheus_io_port] - action: replace - regex: ([^:]+)(?::\d+)?;(\d+) - replacement: ${1}:9901 - target_label: __address__ - - action: labelmap - regex: __meta_kubernetes_pod_label_(.+) - - source_labels: [__meta_kubernetes_namespace] - action: replace - target_label: kubernetes_namespace - - source_labels: [__meta_kubernetes_pod_name] - action: replace - target_label: kubernetes_pod_name - - # Exclude high cardinality metrics - metric_relabel_configs: - - source_labels: [ cluster_name ] - regex: '(outbound|inbound|prometheus_stats).*' - action: drop - - source_labels: [ tcp_prefix ] - regex: '(outbound|inbound|prometheus_stats).*' - action: drop - - source_labels: [ listener_address ] - regex: '(.+)' - action: drop - - source_labels: [ http_conn_manager_listener_prefix ] - regex: '(.+)' - action: drop - - source_labels: [ http_conn_manager_prefix ] - regex: '(.+)' - action: drop - - source_labels: [ __name__ ] - regex: 'envoy_tls.*' - action: drop - - source_labels: [ __name__ ] - regex: 'envoy_tcp_downstream.*' - action: drop - - source_labels: [ __name__ ] - regex: 'envoy_http_(stats|admin).*' - action: drop - - source_labels: [ __name__ ] - regex: 'envoy_cluster_(lb|retry|bind|internal|max|original).*' - action: drop - - # Scrape config for API servers - - job_name: 'kubernetes-apiservers' - kubernetes_sd_configs: - - role: endpoints - namespaces: - names: - - default - scheme: https - tls_config: - ca_file: /var/run/secrets/kubernetes.io/serviceaccount/ca.crt - bearer_token_file: /var/run/secrets/kubernetes.io/serviceaccount/token - relabel_configs: - - source_labels: [__meta_kubernetes_service_name, __meta_kubernetes_endpoint_port_name] - action: keep - regex: kubernetes;https - - # Scrape config for nodes - - job_name: 'kubernetes-nodes' - scheme: https - tls_config: - ca_file: /var/run/secrets/kubernetes.io/serviceaccount/ca.crt - bearer_token_file: /var/run/secrets/kubernetes.io/serviceaccount/token - kubernetes_sd_configs: - - role: node - relabel_configs: - - action: labelmap - regex: __meta_kubernetes_node_label_(.+) - - target_label: __address__ - replacement: kubernetes.default.svc:443 - - source_labels: [__meta_kubernetes_node_name] - regex: (.+) - target_label: __metrics_path__ - replacement: /api/v1/nodes/${1}/proxy/metrics - - # scrape config for cAdvisor - - job_name: 'kubernetes-cadvisor' - scheme: https - tls_config: - ca_file: /var/run/secrets/kubernetes.io/serviceaccount/ca.crt - bearer_token_file: /var/run/secrets/kubernetes.io/serviceaccount/token - kubernetes_sd_configs: - - role: node - relabel_configs: - - action: labelmap - regex: __meta_kubernetes_node_label_(.+) - - target_label: __address__ - replacement: kubernetes.default.svc:443 - - source_labels: [__meta_kubernetes_node_name] - regex: (.+) - target_label: __metrics_path__ - replacement: /api/v1/nodes/${1}/proxy/metrics/cadvisor - - # scrape config for pods - - job_name: kubernetes-pods - kubernetes_sd_configs: - - role: pod - relabel_configs: - - action: keep - regex: true - source_labels: - - __meta_kubernetes_pod_annotation_prometheus_io_scrape - - source_labels: [ __address__ ] - regex: '.*9901.*' - action: drop - - action: replace - regex: (.+) - source_labels: - - __meta_kubernetes_pod_annotation_prometheus_io_path - target_label: __metrics_path__ - - action: replace - regex: ([^:]+)(?::\d+)?;(\d+) - replacement: $1:$2 - source_labels: - - __address__ - - __meta_kubernetes_pod_annotation_prometheus_io_port - target_label: __address__ - - action: labelmap - regex: __meta_kubernetes_pod_label_(.+) - - action: replace - source_labels: - - __meta_kubernetes_namespace - target_label: kubernetes_namespace - - action: replace - source_labels: - - __meta_kubernetes_pod_name - target_label: kubernetes_pod_name ---- -apiVersion: apps/v1 -kind: Deployment -metadata: - name: {{ template "flagger.fullname" . }}-prometheus - namespace: {{ .Release.Namespace }} - labels: - helm.sh/chart: {{ template "flagger.chart" . }} - app.kubernetes.io/name: {{ template "flagger.name" . }} - app.kubernetes.io/managed-by: {{ .Release.Service }} - app.kubernetes.io/instance: {{ .Release.Name }} -spec: - replicas: 1 - selector: - matchLabels: - app.kubernetes.io/name: {{ template "flagger.name" . }}-prometheus - app.kubernetes.io/instance: {{ .Release.Name }} - template: - metadata: - labels: - app.kubernetes.io/name: {{ template "flagger.name" . }}-prometheus - app.kubernetes.io/instance: {{ .Release.Name }} - annotations: - appmesh.k8s.aws/sidecarInjectorWebhook: disabled - sidecar.istio.io/inject: "false" - spec: - serviceAccountName: {{ template "flagger.serviceAccountName" . }}-prometheus - containers: - - name: prometheus - image: "docker.io/prom/prometheus:v2.10.0" - imagePullPolicy: IfNotPresent - args: - - '--storage.tsdb.retention=2h' - - '--config.file=/etc/prometheus/prometheus.yml' - ports: - - containerPort: 9090 - name: http - livenessProbe: - httpGet: - path: /-/healthy - port: 9090 - readinessProbe: - httpGet: - path: /-/ready - port: 9090 - resources: - requests: - cpu: 10m - memory: 128Mi - volumeMounts: - - name: config-volume - mountPath: /etc/prometheus - - name: data-volume - mountPath: /prometheus/data - - volumes: - - name: config-volume - configMap: - name: {{ template "flagger.fullname" . }}-prometheus - - name: data-volume - emptyDir: {} ---- -apiVersion: v1 -kind: Service -metadata: - name: {{ template "flagger.fullname" . }}-prometheus - namespace: {{ .Release.Namespace }} - labels: - helm.sh/chart: {{ template "flagger.chart" . }} - app.kubernetes.io/name: {{ template "flagger.name" . }} - app.kubernetes.io/managed-by: {{ .Release.Service }} - app.kubernetes.io/instance: {{ .Release.Name }} -spec: - selector: - app.kubernetes.io/name: {{ template "flagger.name" . }}-prometheus - app.kubernetes.io/instance: {{ .Release.Name }} - ports: - - name: http - protocol: TCP - port: 9090 -{{- end }} diff --git a/charts/flagger/templates/psp.yaml b/charts/flagger/templates/psp.yaml deleted file mode 100644 index 8e314591..00000000 --- a/charts/flagger/templates/psp.yaml +++ /dev/null @@ -1,66 +0,0 @@ -{{- if .Values.rbac.pspEnabled }} -apiVersion: policy/v1beta1 -kind: PodSecurityPolicy -metadata: - name: {{ template "flagger.fullname" . }} - labels: - helm.sh/chart: {{ template "flagger.chart" . }} - app.kubernetes.io/name: {{ template "flagger.name" . }} - app.kubernetes.io/managed-by: {{ .Release.Service }} - app.kubernetes.io/instance: {{ .Release.Name }} - annotations: - seccomp.security.alpha.kubernetes.io/allowedProfileNames: '*' -spec: - privileged: false - hostIPC: false - hostNetwork: false - hostPID: false - readOnlyRootFilesystem: false - allowPrivilegeEscalation: false - allowedCapabilities: - - '*' - fsGroup: - rule: RunAsAny - runAsUser: - rule: RunAsAny - seLinux: - rule: RunAsAny - supplementalGroups: - rule: RunAsAny - volumes: - - '*' ---- -kind: ClusterRole -apiVersion: rbac.authorization.k8s.io/v1 -metadata: - name: {{ template "flagger.fullname" . }}-psp - labels: - helm.sh/chart: {{ template "flagger.chart" . }} - app.kubernetes.io/name: {{ template "flagger.name" . }} - app.kubernetes.io/managed-by: {{ .Release.Service }} - app.kubernetes.io/instance: {{ .Release.Name }} -rules: - - apiGroups: ['policy'] - resources: ['podsecuritypolicies'] - verbs: ['use'] - resourceNames: - - {{ template "flagger.fullname" . }} ---- -apiVersion: rbac.authorization.k8s.io/v1 -kind: RoleBinding -metadata: - name: {{ template "flagger.fullname" . }}-psp - labels: - helm.sh/chart: {{ template "flagger.chart" . }} - app.kubernetes.io/name: {{ template "flagger.name" . }} - app.kubernetes.io/managed-by: {{ .Release.Service }} - app.kubernetes.io/instance: {{ .Release.Name }} -roleRef: - apiGroup: rbac.authorization.k8s.io - kind: ClusterRole - name: {{ template "flagger.fullname" . }}-psp -subjects: - - kind: ServiceAccount - name: {{ template "flagger.serviceAccountName" . }} - namespace: {{ .Release.Namespace }} -{{- end }} diff --git a/charts/flagger/templates/rbac.yaml b/charts/flagger/templates/rbac.yaml deleted file mode 100644 index 111b1622..00000000 --- a/charts/flagger/templates/rbac.yaml +++ /dev/null @@ -1,102 +0,0 @@ -{{- if .Values.rbac.create }} -apiVersion: rbac.authorization.k8s.io/v1beta1 -kind: ClusterRole -metadata: - name: {{ template "flagger.fullname" . }} - labels: - helm.sh/chart: {{ template "flagger.chart" . }} - app.kubernetes.io/name: {{ template "flagger.name" . }} - app.kubernetes.io/managed-by: {{ .Release.Service }} - app.kubernetes.io/instance: {{ .Release.Name }} -rules: - - apiGroups: - - "" - resources: - - events - - configmaps - - secrets - - services - verbs: ["*"] - - apiGroups: - - apps - resources: - - deployments - verbs: ["*"] - - apiGroups: - - autoscaling - resources: - - horizontalpodautoscalers - verbs: ["*"] - - apiGroups: - - "extensions" - resources: - - ingresses - - ingresses/status - verbs: ["*"] - - apiGroups: - - flagger.app - resources: - - canaries - - canaries/status - verbs: ["*"] - - apiGroups: - - networking.istio.io - resources: - - virtualservices - - virtualservices/status - - destinationrules - - destinationrules/status - verbs: ["*"] - - apiGroups: - - appmesh.k8s.aws - resources: - - meshes - - meshes/status - - virtualnodes - - virtualnodes/status - - virtualservices - - virtualservices/status - verbs: ["*"] - - apiGroups: - - split.smi-spec.io - resources: - - trafficsplits - verbs: ["*"] - - apiGroups: - - gloo.solo.io - resources: - - settings - - upstreams - - upstreamgroups - - proxies - - virtualservices - verbs: ["*"] - - apiGroups: - - gateway.solo.io - resources: - - virtualservices - - gateways - verbs: ["*"] - - nonResourceURLs: - - /version - verbs: - - get ---- -apiVersion: rbac.authorization.k8s.io/v1beta1 -kind: ClusterRoleBinding -metadata: - name: {{ template "flagger.fullname" . }} - labels: - helm.sh/chart: {{ template "flagger.chart" . }} - app.kubernetes.io/name: {{ template "flagger.name" . }} - app.kubernetes.io/managed-by: {{ .Release.Service }} - app.kubernetes.io/instance: {{ .Release.Name }} -roleRef: - apiGroup: rbac.authorization.k8s.io - kind: ClusterRole - name: {{ template "flagger.fullname" . }} -subjects: -- name: {{ template "flagger.serviceAccountName" . }} - namespace: {{ .Release.Namespace }} - kind: ServiceAccount -{{- end }} diff --git a/charts/flagger/values.yaml b/charts/flagger/values.yaml deleted file mode 100644 index fc67ff5f..00000000 --- a/charts/flagger/values.yaml +++ /dev/null @@ -1,64 +0,0 @@ -# Default values for flagger. - -image: - repository: weaveworks/flagger - tag: 0.18.2 - pullPolicy: IfNotPresent - pullSecret: - -metricsServer: "http://prometheus:9090" - -# accepted values are istio, appmesh, nginx or supergloo:mesh.namespace (defaults to istio) -meshProvider: "" - -# single namespace restriction -namespace: "" - -slack: - user: flagger - channel: - # incoming webhook https://api.slack.com/incoming-webhooks - url: - -msteams: - # MS Teams incoming webhook URL - url: - -leaderElection: - enabled: false - replicaCount: 1 - -serviceAccount: - # serviceAccount.create: Whether to create a service account or not - create: true - # serviceAccount.name: The name of the service account to create or use - name: "" - -rbac: - # rbac.create: `true` if rbac resources should be created - create: true - # rbac.pspEnabled: `true` if PodSecurityPolicy resources should be created - pspEnabled: false - -crd: - # crd.create: `true` if custom resource definitions should be created - create: true - -nameOverride: "" -fullnameOverride: "" - -resources: - limits: - memory: "512Mi" - cpu: "1000m" - requests: - memory: "32Mi" - cpu: "10m" - -nodeSelector: {} - -tolerations: [] - -prometheus: - # to be used with AppMesh or nginx ingress - install: false diff --git a/charts/grafana/.helmignore b/charts/grafana/.helmignore deleted file mode 100644 index f0c13194..00000000 --- a/charts/grafana/.helmignore +++ /dev/null @@ -1,21 +0,0 @@ -# Patterns to ignore when building packages. -# This supports shell glob matching, relative path matching, and -# negation (prefixed with !). Only one pattern per line. -.DS_Store -# Common VCS dirs -.git/ -.gitignore -.bzr/ -.bzrignore -.hg/ -.hgignore -.svn/ -# Common backup files -*.swp -*.bak -*.tmp -*~ -# Various IDEs -.project -.idea/ -*.tmproj diff --git a/charts/grafana/Chart.yaml b/charts/grafana/Chart.yaml deleted file mode 100644 index 554e09df..00000000 --- a/charts/grafana/Chart.yaml +++ /dev/null @@ -1,13 +0,0 @@ -apiVersion: v1 -name: grafana -version: 1.3.0 -appVersion: 6.2.5 -description: Grafana dashboards for monitoring Flagger canary deployments -icon: https://raw.githubusercontent.com/weaveworks/flagger/master/docs/logo/flagger-icon.png -home: https://flagger.app -sources: -- https://github.com/weaveworks/flagger -maintainers: -- name: stefanprodan - url: https://github.com/stefanprodan - email: stefanprodan@users.noreply.github.com diff --git a/charts/grafana/README.md b/charts/grafana/README.md deleted file mode 100644 index 15c54f98..00000000 --- a/charts/grafana/README.md +++ /dev/null @@ -1,79 +0,0 @@ -# Flagger Grafana - -Grafana dashboards for monitoring progressive deployments powered by Istio, Prometheus and Flagger. - -![flagger-grafana](https://raw.githubusercontent.com/weaveworks/flagger/master/docs/screens/grafana-canary-analysis.png) - -## Prerequisites - -* Kubernetes >= 1.11 -* Istio >= 1.0 -* Prometheus >= 2.6 - -## Installing the Chart - -Add Flagger Helm repository: - -```console -helm repo add flagger https://flagger.app -``` - -To install the chart with the release name `flagger-grafana`: - -```console -helm upgrade -i flagger-grafana flagger/grafana \ ---namespace=istio-system \ ---set url=http://prometheus:9090 \ ---set user=admin \ ---set password=admin -``` - -The command deploys Grafana on the Kubernetes cluster in the default namespace. -The [configuration](#configuration) section lists the parameters that can be configured during installation. - -## Uninstalling the Chart - -To uninstall/delete the `flagger-grafana` deployment: - -```console -helm delete --purge flagger-grafana -``` - -The command removes all the Kubernetes components associated with the chart and deletes the release. - -## Configuration - -The following tables lists the configurable parameters of the Grafana chart and their default values. - -Parameter | Description | Default ---- | --- | --- -`image.repository` | Image repository | `grafana/grafana` -`image.pullPolicy` | Image pull policy | `IfNotPresent` -`image.tag` | Image tag | `` -`replicaCount` | desired number of pods | `1` -`resources` | pod resources | `none` -`tolerations` | List of node taints to tolerate | `[]` -`affinity` | node/pod affinities | `node` -`nodeSelector` | node labels for pod assignment | `{}` -`service.type` | type of service | `ClusterIP` -`url` | Prometheus URL, used when Weave Cloud token is empty | `http://prometheus:9090` -`token` | Weave Cloud token | `none` -`user` | Grafana admin username | `admin` -`password` | Grafana admin password | `admin` - -Specify each parameter using the `--set key=value[,key=value]` argument to `helm install`. For example, - -```console -helm install flagger/grafana --name flagger-grafana \ ---set token=WEAVE-CLOUD-TOKEN -``` - -Alternatively, a YAML file that specifies the values for the above parameters can be provided while installing the chart. For example, - -```console -helm install flagger/grafana --name flagger-grafana -f values.yaml -``` - -> **Tip**: You can use the default [values.yaml](values.yaml) - - diff --git a/charts/grafana/dashboards/appmesh.json b/charts/grafana/dashboards/appmesh.json deleted file mode 100644 index 1544ee5b..00000000 --- a/charts/grafana/dashboards/appmesh.json +++ /dev/null @@ -1,1248 +0,0 @@ -{ - "annotations": { - "list": [ - { - "builtIn": 1, - "datasource": "-- Grafana --", - "enable": true, - "hide": true, - "iconColor": "rgba(0, 211, 255, 1)", - "name": "Annotations & Alerts", - "type": "dashboard" - } - ] - }, - "editable": true, - "gnetId": null, - "graphTooltip": 0, - "id": 2, - "iteration": 1553160305729, - "links": [], - "panels": [ - { - "content": "
\nRED: $canary.$namespace\n
", - "gridPos": { - "h": 3, - "w": 24, - "x": 0, - "y": 0 - }, - "id": 89, - "links": [], - "mode": "html", - "title": "", - "transparent": true, - "type": "text" - }, - { - "cacheTimeout": null, - "colorBackground": false, - "colorValue": false, - "colors": [ - "rgba(245, 54, 54, 0.9)", - "rgba(237, 129, 40, 0.89)", - "rgba(50, 172, 45, 0.97)" - ], - "datasource": "prometheus", - "format": "ops", - "gauge": { - "maxValue": 100, - "minValue": 0, - "show": false, - "thresholdLabels": false, - "thresholdMarkers": true - }, - "gridPos": { - "h": 4, - "w": 6, - "x": 0, - "y": 3 - }, - "id": 90, - "interval": null, - "links": [], - "mappingType": 1, - "mappingTypes": [ - { - "name": "value to text", - "value": 1 - }, - { - "name": "range to text", - "value": 2 - } - ], - "maxDataPoints": 100, - "nullPointMode": "connected", - "nullText": null, - "postfix": "", - "postfixFontSize": "50%", - "prefix": "", - "prefixFontSize": "50%", - "rangeMaps": [ - { - "from": "null", - "text": "N/A", - "to": "null" - } - ], - "sparkline": { - "fillColor": "rgba(31, 118, 189, 0.18)", - "full": true, - "lineColor": "rgb(31, 120, 193)", - "show": true - }, - "tableColumn": "", - "targets": [ - { - "expr": "round(sum(rate(envoy_cluster_upstream_rq{kubernetes_namespace=~\"$namespace\",app=~\"$primary\"}[30s])), 0.001)", - "format": "time_series", - "intervalFactor": 1, - "legendFormat": "", - "refId": "A", - "step": 4 - } - ], - "thresholds": "", - "title": "Primary: Incoming Request Volume", - "transparent": false, - "type": "singlestat", - "valueFontSize": "80%", - "valueMaps": [ - { - "op": "=", - "text": "N/A", - "value": "null" - } - ], - "valueName": "current" - }, - { - "cacheTimeout": null, - "colorBackground": false, - "colorValue": false, - "colors": [ - "rgba(50, 172, 45, 0.97)", - "rgba(237, 129, 40, 0.89)", - "rgba(245, 54, 54, 0.9)" - ], - "datasource": "prometheus", - "decimals": null, - "format": "percentunit", - "gauge": { - "maxValue": 100, - "minValue": 80, - "show": false, - "thresholdLabels": false, - "thresholdMarkers": false - }, - "gridPos": { - "h": 4, - "w": 6, - "x": 6, - "y": 3 - }, - "id": 98, - "interval": null, - "links": [], - "mappingType": 1, - "mappingTypes": [ - { - "name": "value to text", - "value": 1 - }, - { - "name": "range to text", - "value": 2 - } - ], - "maxDataPoints": 100, - "nullPointMode": "connected", - "nullText": null, - "postfix": "", - "postfixFontSize": "50%", - "prefix": "", - "prefixFontSize": "50%", - "rangeMaps": [ - { - "from": "null", - "text": "N/A", - "to": "null" - } - ], - "sparkline": { - "fillColor": "rgba(31, 118, 189, 0.18)", - "full": true, - "lineColor": "rgb(31, 120, 193)", - "show": true - }, - "tableColumn": "", - "targets": [ - { - "expr": "sum(irate(envoy_cluster_upstream_rq{kubernetes_namespace=~\"$namespace\",app=~\"$primary\",envoy_response_code!~\"5.*\"}[30s])) / sum(irate(envoy_cluster_upstream_rq{kubernetes_namespace=~\"$namespace\",app=~\"$primary\"}[30s]))", - "format": "time_series", - "intervalFactor": 1, - "legendFormat": "", - "refId": "B" - } - ], - "thresholds": "95, 99, 99.5", - "title": "Incoming Success Rate", - "transparent": false, - "type": "singlestat", - "valueFontSize": "80%", - "valueMaps": [ - { - "op": "=", - "text": "N/A", - "value": "null" - } - ], - "valueName": "avg" - }, - { - "cacheTimeout": null, - "colorBackground": false, - "colorValue": false, - "colors": [ - "rgba(245, 54, 54, 0.9)", - "rgba(237, 129, 40, 0.89)", - "rgba(50, 172, 45, 0.97)" - ], - "datasource": "prometheus", - "format": "ops", - "gauge": { - "maxValue": 100, - "minValue": 0, - "show": false, - "thresholdLabels": false, - "thresholdMarkers": true - }, - "gridPos": { - "h": 4, - "w": 6, - "x": 12, - "y": 3 - }, - "id": 97, - "interval": null, - "links": [], - "mappingType": 1, - "mappingTypes": [ - { - "name": "value to text", - "value": 1 - }, - { - "name": "range to text", - "value": 2 - } - ], - "maxDataPoints": 100, - "nullPointMode": "connected", - "nullText": null, - "postfix": "", - "postfixFontSize": "50%", - "prefix": "", - "prefixFontSize": "50%", - "rangeMaps": [ - { - "from": "null", - "text": "N/A", - "to": "null" - } - ], - "sparkline": { - "fillColor": "rgba(40, 224, 65, 0.18)", - "full": true, - "lineColor": "#7eb26d", - "show": true - }, - "tableColumn": "", - "targets": [ - { - "expr": "round(sum(rate(envoy_cluster_upstream_rq{kubernetes_namespace=~\"$namespace\",app=~\"$canary\"}[30s])), 0.001)", - "format": "time_series", - "intervalFactor": 1, - "legendFormat": "", - "refId": "A", - "step": 4 - } - ], - "thresholds": "", - "title": "Canary: Incoming Request Volume", - "transparent": false, - "type": "singlestat", - "valueFontSize": "80%", - "valueMaps": [ - { - "op": "=", - "text": "N/A", - "value": "null" - } - ], - "valueName": "current" - }, - { - "cacheTimeout": null, - "colorBackground": false, - "colorValue": false, - "colors": [ - "rgba(50, 172, 45, 0.97)", - "rgba(237, 129, 40, 0.89)", - "rgba(245, 54, 54, 0.9)" - ], - "datasource": "prometheus", - "decimals": null, - "format": "percentunit", - "gauge": { - "maxValue": 100, - "minValue": 80, - "show": false, - "thresholdLabels": false, - "thresholdMarkers": false - }, - "gridPos": { - "h": 4, - "w": 6, - "x": 18, - "y": 3 - }, - "id": 99, - "interval": null, - "links": [], - "mappingType": 1, - "mappingTypes": [ - { - "name": "value to text", - "value": 1 - }, - { - "name": "range to text", - "value": 2 - } - ], - "maxDataPoints": 100, - "nullPointMode": "connected", - "nullText": null, - "postfix": "", - "postfixFontSize": "50%", - "prefix": "", - "prefixFontSize": "50%", - "rangeMaps": [ - { - "from": "null", - "text": "N/A", - "to": "null" - } - ], - "sparkline": { - "fillColor": "rgba(40, 224, 65, 0.18)", - "full": true, - "lineColor": "#7eb26d", - "show": true - }, - "tableColumn": "", - "targets": [ - { - "expr": "sum(irate(envoy_cluster_upstream_rq{kubernetes_namespace=~\"$namespace\",app=~\"$canary\",envoy_response_code!~\"5.*\"}[30s])) / sum(irate(envoy_cluster_upstream_rq{kubernetes_namespace=~\"$namespace\",app=~\"$canary\"}[30s]))", - "format": "time_series", - "intervalFactor": 1, - "legendFormat": "", - "refId": "B" - } - ], - "thresholds": "95, 99, 99.5", - "title": "Incoming Success Rate", - "transparent": false, - "type": "singlestat", - "valueFontSize": "80%", - "valueMaps": [ - { - "op": "=", - "text": "N/A", - "value": "null" - } - ], - "valueName": "current" - }, - { - "aliasColors": {}, - "bars": false, - "dashLength": 10, - "dashes": false, - "datasource": "prometheus", - "fill": 1, - "gridPos": { - "h": 4, - "w": 12, - "x": 0, - "y": 7 - }, - "id": 96, - "legend": { - "alignAsTable": false, - "avg": false, - "current": false, - "hideEmpty": false, - "hideZero": false, - "max": false, - "min": false, - "rightSide": true, - "show": false, - "total": false, - "values": false - }, - "lines": true, - "linewidth": 1, - "links": [], - "nullPointMode": "null", - "percentage": false, - "pointradius": 5, - "points": false, - "renderer": "flot", - "seriesOverrides": [], - "spaceLength": 10, - "stack": false, - "steppedLine": false, - "targets": [ - { - "expr": "(sum(rate(envoy_cluster_upstream_cx_rx_bytes_total{kubernetes_namespace=~\"$namespace\",app=~\"$primary\"}[30s])))", - "format": "time_series", - "interval": "", - "intervalFactor": 1, - "legendFormat": "traffic", - "refId": "A" - } - ], - "thresholds": [], - "timeFrom": null, - "timeRegions": [], - "timeShift": null, - "title": "Primary: Incoming Traffic", - "tooltip": { - "shared": true, - "sort": 0, - "value_type": "individual" - }, - "type": "graph", - "xaxis": { - "buckets": null, - "mode": "time", - "name": null, - "show": true, - "values": [] - }, - "yaxes": [ - { - "format": "Bps", - "label": null, - "logBase": 1, - "max": null, - "min": null, - "show": true - }, - { - "format": "short", - "label": null, - "logBase": 1, - "max": null, - "min": null, - "show": false - } - ], - "yaxis": { - "align": false, - "alignLevel": null - } - }, - { - "aliasColors": {}, - "bars": false, - "dashLength": 10, - "dashes": false, - "datasource": "prometheus", - "fill": 1, - "gridPos": { - "h": 4, - "w": 12, - "x": 12, - "y": 7 - }, - "id": 91, - "legend": { - "alignAsTable": false, - "avg": false, - "current": false, - "hideEmpty": false, - "hideZero": false, - "max": false, - "min": false, - "rightSide": true, - "show": false, - "total": false, - "values": false - }, - "lines": true, - "linewidth": 1, - "links": [], - "nullPointMode": "null", - "percentage": false, - "pointradius": 5, - "points": false, - "renderer": "flot", - "seriesOverrides": [], - "spaceLength": 10, - "stack": false, - "steppedLine": false, - "targets": [ - { - "expr": "(sum(rate(envoy_cluster_upstream_cx_rx_bytes_total{kubernetes_namespace=~\"$namespace\",app=~\"$canary\"}[30s])))", - "format": "time_series", - "interval": "", - "intervalFactor": 1, - "legendFormat": "traffic", - "refId": "A" - } - ], - "thresholds": [], - "timeFrom": null, - "timeRegions": [], - "timeShift": null, - "title": "Canary: Incoming Traffic", - "tooltip": { - "shared": true, - "sort": 0, - "value_type": "individual" - }, - "type": "graph", - "xaxis": { - "buckets": null, - "mode": "time", - "name": null, - "show": true, - "values": [] - }, - "yaxes": [ - { - "format": "Bps", - "label": null, - "logBase": 1, - "max": null, - "min": null, - "show": true - }, - { - "format": "short", - "label": null, - "logBase": 1, - "max": null, - "min": null, - "show": false - } - ], - "yaxis": { - "align": false, - "alignLevel": null - } - }, - { - "content": "
\nUSE: $canary.$namespace\n
", - "gridPos": { - "h": 3, - "w": 24, - "x": 0, - "y": 11 - }, - "id": 101, - "links": [], - "mode": "html", - "title": "", - "transparent": true, - "type": "text" - }, - { - "aliasColors": {}, - "bars": false, - "dashLength": 10, - "dashes": false, - "datasource": "prometheus", - "fill": 1, - "gridPos": { - "h": 6, - "w": 12, - "x": 0, - "y": 14 - }, - "id": 100, - "legend": { - "alignAsTable": false, - "avg": false, - "current": false, - "hideEmpty": false, - "hideZero": false, - "max": false, - "min": false, - "rightSide": false, - "show": true, - "total": false, - "values": false - }, - "lines": true, - "linewidth": 1, - "links": [], - "nullPointMode": "null", - "percentage": false, - "pointradius": 5, - "points": false, - "renderer": "flot", - "seriesOverrides": [], - "spaceLength": 10, - "stack": true, - "steppedLine": false, - "targets": [ - { - "expr": "sum(rate(container_cpu_usage_seconds_total{cpu=\"total\",namespace=\"$namespace\",pod_name=~\"$primary.*\", container_name!~\"POD|istio-proxy\"}[1m])) by (pod_name)", - "format": "time_series", - "hide": false, - "intervalFactor": 1, - "legendFormat": "{{ pod_name }}", - "refId": "B" - } - ], - "thresholds": [], - "timeFrom": null, - "timeRegions": [], - "timeShift": null, - "title": "Primary: CPU Usage by Pod", - "tooltip": { - "shared": true, - "sort": 0, - "value_type": "individual" - }, - "type": "graph", - "xaxis": { - "buckets": null, - "mode": "time", - "name": null, - "show": true, - "values": [] - }, - "yaxes": [ - { - "format": "s", - "label": "CPU seconds / second", - "logBase": 1, - "max": null, - "min": null, - "show": true - }, - { - "format": "short", - "label": null, - "logBase": 1, - "max": null, - "min": null, - "show": false - } - ], - "yaxis": { - "align": false, - "alignLevel": null - } - }, - { - "aliasColors": {}, - "bars": false, - "dashLength": 10, - "dashes": false, - "datasource": "prometheus", - "fill": 1, - "gridPos": { - "h": 6, - "w": 12, - "x": 12, - "y": 14 - }, - "id": 102, - "legend": { - "alignAsTable": false, - "avg": false, - "current": false, - "hideEmpty": false, - "hideZero": false, - "max": false, - "min": false, - "rightSide": false, - "show": true, - "total": false, - "values": false - }, - "lines": true, - "linewidth": 1, - "links": [], - "nullPointMode": "null", - "percentage": false, - "pointradius": 5, - "points": false, - "renderer": "flot", - "seriesOverrides": [], - "spaceLength": 10, - "stack": true, - "steppedLine": false, - "targets": [ - { - "expr": "sum(rate(container_cpu_usage_seconds_total{cpu=\"total\",namespace=\"$namespace\",pod_name=~\"$canary.*\", pod_name!~\"$primary.*\", container_name!~\"POD|istio-proxy\"}[1m])) by (pod_name)", - "format": "time_series", - "hide": false, - "intervalFactor": 1, - "legendFormat": "{{ pod_name }}", - "refId": "B" - } - ], - "thresholds": [], - "timeFrom": null, - "timeRegions": [], - "timeShift": null, - "title": "Canary: CPU Usage by Pod", - "tooltip": { - "shared": true, - "sort": 0, - "value_type": "individual" - }, - "type": "graph", - "xaxis": { - "buckets": null, - "mode": "time", - "name": null, - "show": true, - "values": [] - }, - "yaxes": [ - { - "format": "s", - "label": "CPU seconds / second", - "logBase": 1, - "max": null, - "min": null, - "show": true - }, - { - "format": "short", - "label": null, - "logBase": 1, - "max": null, - "min": null, - "show": false - } - ], - "yaxis": { - "align": false, - "alignLevel": null - } - }, - { - "aliasColors": {}, - "bars": false, - "dashLength": 10, - "dashes": false, - "datasource": "prometheus", - "fill": 1, - "gridPos": { - "h": 6, - "w": 12, - "x": 0, - "y": 20 - }, - "id": 103, - "legend": { - "alignAsTable": false, - "avg": false, - "current": false, - "hideEmpty": false, - "hideZero": false, - "max": false, - "min": false, - "rightSide": false, - "show": true, - "total": false, - "values": false - }, - "lines": true, - "linewidth": 1, - "links": [], - "nullPointMode": "null", - "percentage": false, - "pointradius": 5, - "points": false, - "renderer": "flot", - "seriesOverrides": [], - "spaceLength": 10, - "stack": true, - "steppedLine": false, - "targets": [ - { - "expr": "sum(container_memory_working_set_bytes{namespace=\"$namespace\",pod_name=~\"$primary.*\", container_name!~\"POD|istio-proxy\"}) by (pod_name)", - "format": "time_series", - "hide": false, - "interval": "", - "intervalFactor": 1, - "legendFormat": "{{ pod_name }}", - "refId": "B" - } - ], - "thresholds": [], - "timeFrom": null, - "timeRegions": [], - "timeShift": null, - "title": "Primary: Memory Usage by Pod", - "tooltip": { - "shared": true, - "sort": 0, - "value_type": "individual" - }, - "type": "graph", - "xaxis": { - "buckets": null, - "mode": "time", - "name": null, - "show": true, - "values": [] - }, - "yaxes": [ - { - "decimals": null, - "format": "bytes", - "label": "", - "logBase": 1, - "max": null, - "min": null, - "show": true - }, - { - "format": "short", - "label": null, - "logBase": 1, - "max": null, - "min": null, - "show": false - } - ], - "yaxis": { - "align": false, - "alignLevel": null - } - }, - { - "aliasColors": {}, - "bars": false, - "dashLength": 10, - "dashes": false, - "datasource": "prometheus", - "fill": 1, - "gridPos": { - "h": 6, - "w": 12, - "x": 12, - "y": 20 - }, - "id": 104, - "legend": { - "alignAsTable": false, - "avg": false, - "current": false, - "hideEmpty": false, - "hideZero": false, - "max": false, - "min": false, - "rightSide": false, - "show": true, - "total": false, - "values": false - }, - "lines": true, - "linewidth": 1, - "links": [], - "nullPointMode": "null", - "percentage": false, - "pointradius": 5, - "points": false, - "renderer": "flot", - "seriesOverrides": [], - "spaceLength": 10, - "stack": true, - "steppedLine": false, - "targets": [ - { - "expr": "sum(container_memory_working_set_bytes{namespace=\"$namespace\",pod_name=~\"$canary.*\", pod_name!~\"$primary.*\", container_name!~\"POD|istio-proxy\"}) by (pod_name)", - "format": "time_series", - "hide": false, - "interval": "", - "intervalFactor": 1, - "legendFormat": "{{ pod_name }}", - "refId": "B" - } - ], - "thresholds": [], - "timeFrom": null, - "timeRegions": [], - "timeShift": null, - "title": "Canary: Memory Usage by Pod", - "tooltip": { - "shared": true, - "sort": 0, - "value_type": "individual" - }, - "type": "graph", - "xaxis": { - "buckets": null, - "mode": "time", - "name": null, - "show": true, - "values": [] - }, - "yaxes": [ - { - "decimals": null, - "format": "bytes", - "label": "", - "logBase": 1, - "max": null, - "min": null, - "show": true - }, - { - "format": "short", - "label": null, - "logBase": 1, - "max": null, - "min": null, - "show": false - } - ], - "yaxis": { - "align": false, - "alignLevel": null - } - }, - { - "aliasColors": {}, - "bars": false, - "dashLength": 10, - "dashes": false, - "datasource": "prometheus", - "fill": 1, - "gridPos": { - "h": 6, - "w": 12, - "x": 0, - "y": 26 - }, - "id": 105, - "legend": { - "alignAsTable": false, - "avg": false, - "current": false, - "hideEmpty": false, - "hideZero": false, - "max": false, - "min": false, - "rightSide": false, - "show": true, - "total": false, - "values": false - }, - "lines": true, - "linewidth": 1, - "links": [], - "nullPointMode": "null", - "percentage": false, - "pointradius": 5, - "points": false, - "renderer": "flot", - "seriesOverrides": [ - { - "alias": "received", - "color": "#f9d9f9" - }, - { - "alias": "transmited", - "color": "#f29191" - } - ], - "spaceLength": 10, - "stack": false, - "steppedLine": false, - "targets": [ - { - "expr": "sum(rate (container_network_receive_bytes_total{namespace=\"$namespace\",pod_name=~\"$primary.*\"}[1m])) ", - "format": "time_series", - "intervalFactor": 1, - "legendFormat": "received", - "refId": "A" - }, - { - "expr": "-sum (rate (container_network_transmit_bytes_total{namespace=\"$namespace\",pod_name=~\"$primary.*\"}[1m]))", - "format": "time_series", - "intervalFactor": 1, - "legendFormat": "transmited", - "refId": "B" - } - ], - "thresholds": [], - "timeFrom": null, - "timeRegions": [], - "timeShift": null, - "title": "Primary: Network I/O", - "tooltip": { - "shared": true, - "sort": 0, - "value_type": "individual" - }, - "type": "graph", - "xaxis": { - "buckets": null, - "mode": "time", - "name": null, - "show": true, - "values": [] - }, - "yaxes": [ - { - "decimals": null, - "format": "Bps", - "label": "", - "logBase": 1, - "max": null, - "min": null, - "show": true - }, - { - "format": "short", - "label": null, - "logBase": 1, - "max": null, - "min": null, - "show": false - } - ], - "yaxis": { - "align": false, - "alignLevel": null - } - }, - { - "aliasColors": {}, - "bars": false, - "dashLength": 10, - "dashes": false, - "datasource": "prometheus", - "fill": 1, - "gridPos": { - "h": 6, - "w": 12, - "x": 12, - "y": 26 - }, - "id": 106, - "legend": { - "alignAsTable": false, - "avg": false, - "current": false, - "hideEmpty": false, - "hideZero": false, - "max": false, - "min": false, - "rightSide": false, - "show": true, - "total": false, - "values": false - }, - "lines": true, - "linewidth": 1, - "links": [], - "nullPointMode": "null", - "percentage": false, - "pointradius": 5, - "points": false, - "renderer": "flot", - "seriesOverrides": [ - { - "alias": "received", - "color": "#f9d9f9" - }, - { - "alias": "transmited", - "color": "#f29191" - } - ], - "spaceLength": 10, - "stack": false, - "steppedLine": false, - "targets": [ - { - "expr": "sum(rate (container_network_receive_bytes_total{namespace=\"$namespace\",pod_name=~\"$canary.*\",pod_name!~\"$primary.*\"}[1m])) ", - "format": "time_series", - "intervalFactor": 1, - "legendFormat": "received", - "refId": "A" - }, - { - "expr": "-sum (rate (container_network_transmit_bytes_total{namespace=\"$namespace\",pod_name=~\"$canary.*\",pod_name!~\"$primary.*\"}[1m]))", - "format": "time_series", - "intervalFactor": 1, - "legendFormat": "transmited", - "refId": "B" - } - ], - "thresholds": [], - "timeFrom": null, - "timeRegions": [], - "timeShift": null, - "title": "Canary: Network I/O", - "tooltip": { - "shared": true, - "sort": 0, - "value_type": "individual" - }, - "type": "graph", - "xaxis": { - "buckets": null, - "mode": "time", - "name": null, - "show": true, - "values": [] - }, - "yaxes": [ - { - "decimals": null, - "format": "Bps", - "label": "", - "logBase": 1, - "max": null, - "min": null, - "show": true - }, - { - "format": "short", - "label": null, - "logBase": 1, - "max": null, - "min": null, - "show": false - } - ], - "yaxis": { - "align": false, - "alignLevel": null - } - } - ], - "refresh": "10s", - "schemaVersion": 16, - "style": "dark", - "tags": [], - "templating": { - "list": [ - { - "allValue": null, - "current": null, - "datasource": "prometheus", - "definition": "query_result(sum(envoy_cluster_upstream_rq) by (kubernetes_namespace))", - "hide": 0, - "includeAll": false, - "label": "Namespace", - "multi": false, - "name": "namespace", - "options": [], - "query": "query_result(sum(envoy_cluster_upstream_rq) by (kubernetes_namespace))", - "refresh": 1, - "regex": "/.*_namespace=\"([^\"]*).*/", - "skipUrlSync": false, - "sort": 0, - "tagValuesQuery": "", - "tags": [], - "tagsQuery": "", - "type": "query", - "useTags": false - }, - { - "allValue": null, - "current": null, - "datasource": "prometheus", - "definition": "query_result(sum(envoy_cluster_upstream_rq{kubernetes_namespace=\"$namespace\",app=~\".*-primary\"}) by (app))", - "hide": 0, - "includeAll": false, - "label": "Primary", - "multi": false, - "name": "primary", - "options": [], - "query": "query_result(sum(envoy_cluster_upstream_rq{kubernetes_namespace=\"$namespace\",app=~\".*-primary\"}) by (app))", - "refresh": 1, - "regex": "/.*app=\"([^\"]*).*/", - "skipUrlSync": false, - "sort": 1, - "tagValuesQuery": "", - "tags": [], - "tagsQuery": "", - "type": "query", - "useTags": false - }, - { - "allValue": null, - "current": null, - "datasource": "prometheus", - "definition": "query_result(sum(envoy_cluster_upstream_rq{kubernetes_namespace=\"$namespace\",app!~\".*-primary\"}) by (app))", - "hide": 0, - "includeAll": false, - "label": "Canary", - "multi": false, - "name": "canary", - "options": [], - "query": "query_result(sum(envoy_cluster_upstream_rq{kubernetes_namespace=\"$namespace\",app!~\".*-primary\"}) by (app))", - "refresh": 1, - "regex": "/.*app=\"([^\"]*).*/", - "skipUrlSync": false, - "sort": 1, - "tagValuesQuery": "", - "tags": [], - "tagsQuery": "", - "type": "query", - "useTags": false - } - ] - }, - "time": { - "from": "now-5m", - "to": "now" - }, - "timepicker": { - "refresh_intervals": [ - "5s", - "10s", - "30s", - "1m", - "5m", - "15m", - "30m", - "1h", - "2h", - "1d" - ], - "time_options": [ - "5m", - "15m", - "1h", - "6h", - "12h", - "24h", - "2d", - "7d", - "30d" - ] - }, - "timezone": "", - "title": "AppMesh Canary", - "uid": "flagger-appmesh", - "version": 4 -} \ No newline at end of file diff --git a/charts/grafana/dashboards/istio.json b/charts/grafana/dashboards/istio.json deleted file mode 100644 index 84bc0a64..00000000 --- a/charts/grafana/dashboards/istio.json +++ /dev/null @@ -1,1685 +0,0 @@ -{ - "annotations": { - "list": [ - { - "builtIn": 1, - "datasource": "-- Grafana --", - "enable": true, - "hide": true, - "iconColor": "rgba(0, 211, 255, 1)", - "name": "Annotations & Alerts", - "type": "dashboard" - } - ] - }, - "editable": true, - "gnetId": null, - "graphTooltip": 0, - "id": 1, - "iteration": 1549736611069, - "links": [], - "panels": [ - { - "content": "
\nRED: $canary.$namespace\n
", - "gridPos": { - "h": 3, - "w": 24, - "x": 0, - "y": 0 - }, - "id": 89, - "links": [], - "mode": "html", - "title": "", - "transparent": true, - "type": "text" - }, - { - "cacheTimeout": null, - "colorBackground": false, - "colorValue": false, - "colors": [ - "rgba(245, 54, 54, 0.9)", - "rgba(237, 129, 40, 0.89)", - "rgba(50, 172, 45, 0.97)" - ], - "datasource": "prometheus", - "format": "ops", - "gauge": { - "maxValue": 100, - "minValue": 0, - "show": false, - "thresholdLabels": false, - "thresholdMarkers": true - }, - "gridPos": { - "h": 4, - "w": 6, - "x": 0, - "y": 3 - }, - "id": 90, - "interval": null, - "links": [], - "mappingType": 1, - "mappingTypes": [ - { - "name": "value to text", - "value": 1 - }, - { - "name": "range to text", - "value": 2 - } - ], - "maxDataPoints": 100, - "nullPointMode": "connected", - "nullText": null, - "postfix": "", - "postfixFontSize": "50%", - "prefix": "", - "prefixFontSize": "50%", - "rangeMaps": [ - { - "from": "null", - "text": "N/A", - "to": "null" - } - ], - "sparkline": { - "fillColor": "rgba(31, 118, 189, 0.18)", - "full": true, - "lineColor": "rgb(31, 120, 193)", - "show": true - }, - "tableColumn": "", - "targets": [ - { - "expr": "round(sum(rate(istio_requests_total{reporter=\"destination\",destination_workload_namespace=~\"$namespace\",destination_workload=~\"$primary\"}[30s])), 0.001)", - "format": "time_series", - "intervalFactor": 1, - "refId": "A", - "step": 4 - } - ], - "thresholds": "", - "title": "Primary: Incoming Request Volume", - "transparent": false, - "type": "singlestat", - "valueFontSize": "80%", - "valueMaps": [ - { - "op": "=", - "text": "N/A", - "value": "null" - } - ], - "valueName": "current" - }, - { - "cacheTimeout": null, - "colorBackground": false, - "colorValue": false, - "colors": [ - "rgba(50, 172, 45, 0.97)", - "rgba(237, 129, 40, 0.89)", - "rgba(245, 54, 54, 0.9)" - ], - "datasource": "prometheus", - "decimals": null, - "format": "percentunit", - "gauge": { - "maxValue": 100, - "minValue": 80, - "show": false, - "thresholdLabels": false, - "thresholdMarkers": false - }, - "gridPos": { - "h": 4, - "w": 6, - "x": 6, - "y": 3 - }, - "id": 98, - "interval": null, - "links": [], - "mappingType": 1, - "mappingTypes": [ - { - "name": "value to text", - "value": 1 - }, - { - "name": "range to text", - "value": 2 - } - ], - "maxDataPoints": 100, - "nullPointMode": "connected", - "nullText": null, - "postfix": "", - "postfixFontSize": "50%", - "prefix": "", - "prefixFontSize": "50%", - "rangeMaps": [ - { - "from": "null", - "text": "N/A", - "to": "null" - } - ], - "sparkline": { - "fillColor": "rgba(31, 118, 189, 0.18)", - "full": true, - "lineColor": "rgb(31, 120, 193)", - "show": true - }, - "tableColumn": "", - "targets": [ - { - "expr": "sum(irate(istio_requests_total{reporter=\"destination\",destination_workload_namespace=~\"$namespace\",destination_workload=~\"$primary\",response_code!~\"5.*\"}[30s])) / sum(irate(istio_requests_total{reporter=\"destination\",destination_workload_namespace=~\"$namespace\",destination_workload=~\"$primary\"}[30s]))", - "format": "time_series", - "intervalFactor": 1, - "refId": "B" - } - ], - "thresholds": "95, 99, 99.5", - "title": "Incoming Success Rate", - "transparent": false, - "type": "singlestat", - "valueFontSize": "80%", - "valueMaps": [ - { - "op": "=", - "text": "N/A", - "value": "null" - } - ], - "valueName": "avg" - }, - { - "cacheTimeout": null, - "colorBackground": false, - "colorValue": false, - "colors": [ - "rgba(245, 54, 54, 0.9)", - "rgba(237, 129, 40, 0.89)", - "rgba(50, 172, 45, 0.97)" - ], - "datasource": "prometheus", - "format": "ops", - "gauge": { - "maxValue": 100, - "minValue": 0, - "show": false, - "thresholdLabels": false, - "thresholdMarkers": true - }, - "gridPos": { - "h": 4, - "w": 6, - "x": 12, - "y": 3 - }, - "id": 97, - "interval": null, - "links": [], - "mappingType": 1, - "mappingTypes": [ - { - "name": "value to text", - "value": 1 - }, - { - "name": "range to text", - "value": 2 - } - ], - "maxDataPoints": 100, - "nullPointMode": "connected", - "nullText": null, - "postfix": "", - "postfixFontSize": "50%", - "prefix": "", - "prefixFontSize": "50%", - "rangeMaps": [ - { - "from": "null", - "text": "N/A", - "to": "null" - } - ], - "sparkline": { - "fillColor": "rgba(40, 224, 65, 0.18)", - "full": true, - "lineColor": "#7eb26d", - "show": true - }, - "tableColumn": "", - "targets": [ - { - "expr": "round(sum(rate(istio_requests_total{reporter=\"destination\",destination_workload_namespace=~\"$namespace\",destination_workload=~\"$canary\"}[30s])), 0.001)", - "format": "time_series", - "intervalFactor": 1, - "refId": "A", - "step": 4 - } - ], - "thresholds": "", - "title": "Canary: Incoming Request Volume", - "transparent": false, - "type": "singlestat", - "valueFontSize": "80%", - "valueMaps": [ - { - "op": "=", - "text": "N/A", - "value": "null" - } - ], - "valueName": "current" - }, - { - "cacheTimeout": null, - "colorBackground": false, - "colorValue": false, - "colors": [ - "rgba(50, 172, 45, 0.97)", - "rgba(237, 129, 40, 0.89)", - "rgba(245, 54, 54, 0.9)" - ], - "datasource": "prometheus", - "decimals": null, - "format": "percentunit", - "gauge": { - "maxValue": 100, - "minValue": 80, - "show": false, - "thresholdLabels": false, - "thresholdMarkers": false - }, - "gridPos": { - "h": 4, - "w": 6, - "x": 18, - "y": 3 - }, - "id": 99, - "interval": null, - "links": [], - "mappingType": 1, - "mappingTypes": [ - { - "name": "value to text", - "value": 1 - }, - { - "name": "range to text", - "value": 2 - } - ], - "maxDataPoints": 100, - "nullPointMode": "connected", - "nullText": null, - "postfix": "", - "postfixFontSize": "50%", - "prefix": "", - "prefixFontSize": "50%", - "rangeMaps": [ - { - "from": "null", - "text": "N/A", - "to": "null" - } - ], - "sparkline": { - "fillColor": "rgba(40, 224, 65, 0.18)", - "full": true, - "lineColor": "#7eb26d", - "show": true - }, - "tableColumn": "", - "targets": [ - { - "expr": "sum(irate(istio_requests_total{reporter=\"destination\",destination_workload_namespace=~\"$namespace\",destination_workload=~\"$canary\",response_code!~\"5.*\"}[30s])) / sum(irate(istio_requests_total{reporter=\"destination\",destination_workload_namespace=~\"$namespace\",destination_workload=~\"$canary\"}[30s]))", - "format": "time_series", - "intervalFactor": 1, - "refId": "B" - } - ], - "thresholds": "95, 99, 99.5", - "title": "Incoming Success Rate", - "transparent": false, - "type": "singlestat", - "valueFontSize": "80%", - "valueMaps": [ - { - "op": "=", - "text": "N/A", - "value": "null" - } - ], - "valueName": "current" - }, - { - "aliasColors": {}, - "bars": false, - "dashLength": 10, - "dashes": false, - "datasource": "prometheus", - "fill": 1, - "gridPos": { - "h": 4, - "w": 12, - "x": 0, - "y": 7 - }, - "id": 96, - "legend": { - "alignAsTable": false, - "avg": false, - "current": false, - "hideEmpty": false, - "hideZero": false, - "max": false, - "min": false, - "rightSide": true, - "show": false, - "total": false, - "values": false - }, - "lines": true, - "linewidth": 1, - "links": [], - "nullPointMode": "null", - "percentage": false, - "pointradius": 5, - "points": false, - "renderer": "flot", - "seriesOverrides": [], - "spaceLength": 10, - "stack": false, - "steppedLine": false, - "targets": [ - { - "expr": "histogram_quantile(0.50, sum(irate(istio_request_duration_seconds_bucket{reporter=\"destination\",destination_workload=~\"$primary\", destination_workload_namespace=~\"$namespace\"}[1m])) by (le))", - "format": "time_series", - "interval": "", - "intervalFactor": 1, - "legendFormat": "P50", - "refId": "A" - }, - { - "expr": "histogram_quantile(0.90, sum(irate(istio_request_duration_seconds_bucket{reporter=\"destination\",destination_workload=~\"$primary\", destination_workload_namespace=~\"$namespace\"}[1m])) by (le))", - "format": "time_series", - "hide": false, - "intervalFactor": 1, - "legendFormat": "P90", - "refId": "B" - }, - { - "expr": "histogram_quantile(0.99, sum(irate(istio_request_duration_seconds_bucket{reporter=\"destination\",destination_workload=~\"$primary\", destination_workload_namespace=~\"$namespace\"}[1m])) by (le))", - "format": "time_series", - "hide": false, - "intervalFactor": 1, - "legendFormat": "P99", - "refId": "C" - } - ], - "thresholds": [], - "timeFrom": null, - "timeRegions": [], - "timeShift": null, - "title": "Primary: Request Duration", - "tooltip": { - "shared": true, - "sort": 0, - "value_type": "individual" - }, - "type": "graph", - "xaxis": { - "buckets": null, - "mode": "time", - "name": null, - "show": true, - "values": [] - }, - "yaxes": [ - { - "format": "s", - "label": null, - "logBase": 1, - "max": null, - "min": null, - "show": true - }, - { - "format": "short", - "label": null, - "logBase": 1, - "max": null, - "min": null, - "show": false - } - ], - "yaxis": { - "align": false, - "alignLevel": null - } - }, - { - "aliasColors": {}, - "bars": false, - "dashLength": 10, - "dashes": false, - "datasource": "prometheus", - "fill": 1, - "gridPos": { - "h": 4, - "w": 12, - "x": 12, - "y": 7 - }, - "id": 91, - "legend": { - "alignAsTable": false, - "avg": false, - "current": false, - "hideEmpty": false, - "hideZero": false, - "max": false, - "min": false, - "rightSide": true, - "show": false, - "total": false, - "values": false - }, - "lines": true, - "linewidth": 1, - "links": [], - "nullPointMode": "null", - "percentage": false, - "pointradius": 5, - "points": false, - "renderer": "flot", - "seriesOverrides": [], - "spaceLength": 10, - "stack": false, - "steppedLine": false, - "targets": [ - { - "expr": "histogram_quantile(0.50, sum(irate(istio_request_duration_seconds_bucket{reporter=\"destination\",destination_workload=~\"$canary\", destination_workload_namespace=~\"$namespace\"}[1m])) by (le))", - "format": "time_series", - "interval": "", - "intervalFactor": 1, - "legendFormat": "P50", - "refId": "A" - }, - { - "expr": "histogram_quantile(0.90, sum(irate(istio_request_duration_seconds_bucket{reporter=\"destination\",destination_workload=~\"$canary\", destination_workload_namespace=~\"$namespace\"}[1m])) by (le))", - "format": "time_series", - "hide": false, - "intervalFactor": 1, - "legendFormat": "P90", - "refId": "B" - }, - { - "expr": "histogram_quantile(0.99, sum(irate(istio_request_duration_seconds_bucket{reporter=\"destination\",destination_workload=~\"$canary\", destination_workload_namespace=~\"$namespace\"}[1m])) by (le))", - "format": "time_series", - "hide": false, - "intervalFactor": 1, - "legendFormat": "P99", - "refId": "C" - } - ], - "thresholds": [], - "timeFrom": null, - "timeRegions": [], - "timeShift": null, - "title": "Canary: Request Duration", - "tooltip": { - "shared": true, - "sort": 0, - "value_type": "individual" - }, - "type": "graph", - "xaxis": { - "buckets": null, - "mode": "time", - "name": null, - "show": true, - "values": [] - }, - "yaxes": [ - { - "format": "s", - "label": null, - "logBase": 1, - "max": null, - "min": null, - "show": true - }, - { - "format": "short", - "label": null, - "logBase": 1, - "max": null, - "min": null, - "show": false - } - ], - "yaxis": { - "align": false, - "alignLevel": null - } - }, - { - "content": "
\nUSE: $canary.$namespace\n
", - "gridPos": { - "h": 3, - "w": 24, - "x": 0, - "y": 11 - }, - "id": 101, - "links": [], - "mode": "html", - "title": "", - "transparent": true, - "type": "text" - }, - { - "aliasColors": {}, - "bars": false, - "dashLength": 10, - "dashes": false, - "datasource": "prometheus", - "fill": 1, - "gridPos": { - "h": 6, - "w": 12, - "x": 0, - "y": 14 - }, - "id": 100, - "legend": { - "alignAsTable": false, - "avg": false, - "current": false, - "hideEmpty": false, - "hideZero": false, - "max": false, - "min": false, - "rightSide": false, - "show": true, - "total": false, - "values": false - }, - "lines": true, - "linewidth": 1, - "links": [], - "nullPointMode": "null", - "percentage": false, - "pointradius": 5, - "points": false, - "renderer": "flot", - "seriesOverrides": [], - "spaceLength": 10, - "stack": true, - "steppedLine": false, - "targets": [ - { - "expr": "sum(rate(container_cpu_usage_seconds_total{cpu=\"total\",namespace=\"$namespace\",pod_name=~\"$primary.*\", container_name!~\"POD|istio-proxy\"}[1m])) by (pod_name)", - "format": "time_series", - "hide": false, - "intervalFactor": 1, - "legendFormat": "{{ pod_name }}", - "refId": "B" - } - ], - "thresholds": [], - "timeFrom": null, - "timeRegions": [], - "timeShift": null, - "title": "Primary: CPU Usage by Pod", - "tooltip": { - "shared": true, - "sort": 0, - "value_type": "individual" - }, - "type": "graph", - "xaxis": { - "buckets": null, - "mode": "time", - "name": null, - "show": true, - "values": [] - }, - "yaxes": [ - { - "format": "s", - "label": "CPU seconds / second", - "logBase": 1, - "max": null, - "min": null, - "show": true - }, - { - "format": "short", - "label": null, - "logBase": 1, - "max": null, - "min": null, - "show": false - } - ], - "yaxis": { - "align": false, - "alignLevel": null - } - }, - { - "aliasColors": {}, - "bars": false, - "dashLength": 10, - "dashes": false, - "datasource": "prometheus", - "fill": 1, - "gridPos": { - "h": 6, - "w": 12, - "x": 12, - "y": 14 - }, - "id": 102, - "legend": { - "alignAsTable": false, - "avg": false, - "current": false, - "hideEmpty": false, - "hideZero": false, - "max": false, - "min": false, - "rightSide": false, - "show": true, - "total": false, - "values": false - }, - "lines": true, - "linewidth": 1, - "links": [], - "nullPointMode": "null", - "percentage": false, - "pointradius": 5, - "points": false, - "renderer": "flot", - "seriesOverrides": [], - "spaceLength": 10, - "stack": true, - "steppedLine": false, - "targets": [ - { - "expr": "sum(rate(container_cpu_usage_seconds_total{cpu=\"total\",namespace=\"$namespace\",pod_name=~\"$canary.*\", pod_name!~\"$primary.*\", container_name!~\"POD|istio-proxy\"}[1m])) by (pod_name)", - "format": "time_series", - "hide": false, - "intervalFactor": 1, - "legendFormat": "{{ pod_name }}", - "refId": "B" - } - ], - "thresholds": [], - "timeFrom": null, - "timeRegions": [], - "timeShift": null, - "title": "Canary: CPU Usage by Pod", - "tooltip": { - "shared": true, - "sort": 0, - "value_type": "individual" - }, - "type": "graph", - "xaxis": { - "buckets": null, - "mode": "time", - "name": null, - "show": true, - "values": [] - }, - "yaxes": [ - { - "format": "s", - "label": "CPU seconds / second", - "logBase": 1, - "max": null, - "min": null, - "show": true - }, - { - "format": "short", - "label": null, - "logBase": 1, - "max": null, - "min": null, - "show": false - } - ], - "yaxis": { - "align": false, - "alignLevel": null - } - }, - { - "aliasColors": {}, - "bars": false, - "dashLength": 10, - "dashes": false, - "datasource": "prometheus", - "fill": 1, - "gridPos": { - "h": 6, - "w": 12, - "x": 0, - "y": 20 - }, - "id": 103, - "legend": { - "alignAsTable": false, - "avg": false, - "current": false, - "hideEmpty": false, - "hideZero": false, - "max": false, - "min": false, - "rightSide": false, - "show": true, - "total": false, - "values": false - }, - "lines": true, - "linewidth": 1, - "links": [], - "nullPointMode": "null", - "percentage": false, - "pointradius": 5, - "points": false, - "renderer": "flot", - "seriesOverrides": [], - "spaceLength": 10, - "stack": true, - "steppedLine": false, - "targets": [ - { - "expr": "sum(container_memory_working_set_bytes{namespace=\"$namespace\",pod_name=~\"$primary.*\", container_name!~\"POD|istio-proxy\"}) by (pod_name)", - "format": "time_series", - "hide": false, - "interval": "", - "intervalFactor": 1, - "legendFormat": "{{ pod_name }}", - "refId": "B" - } - ], - "thresholds": [], - "timeFrom": null, - "timeRegions": [], - "timeShift": null, - "title": "Primary: Memory Usage by Pod", - "tooltip": { - "shared": true, - "sort": 0, - "value_type": "individual" - }, - "type": "graph", - "xaxis": { - "buckets": null, - "mode": "time", - "name": null, - "show": true, - "values": [] - }, - "yaxes": [ - { - "decimals": null, - "format": "bytes", - "label": "", - "logBase": 1, - "max": null, - "min": null, - "show": true - }, - { - "format": "short", - "label": null, - "logBase": 1, - "max": null, - "min": null, - "show": false - } - ], - "yaxis": { - "align": false, - "alignLevel": null - } - }, - { - "aliasColors": {}, - "bars": false, - "dashLength": 10, - "dashes": false, - "datasource": "prometheus", - "fill": 1, - "gridPos": { - "h": 6, - "w": 12, - "x": 12, - "y": 20 - }, - "id": 104, - "legend": { - "alignAsTable": false, - "avg": false, - "current": false, - "hideEmpty": false, - "hideZero": false, - "max": false, - "min": false, - "rightSide": false, - "show": true, - "total": false, - "values": false - }, - "lines": true, - "linewidth": 1, - "links": [], - "nullPointMode": "null", - "percentage": false, - "pointradius": 5, - "points": false, - "renderer": "flot", - "seriesOverrides": [], - "spaceLength": 10, - "stack": true, - "steppedLine": false, - "targets": [ - { - "expr": "sum(container_memory_working_set_bytes{namespace=\"$namespace\",pod_name=~\"$canary.*\", pod_name!~\"$primary.*\", container_name!~\"POD|istio-proxy\"}) by (pod_name)", - "format": "time_series", - "hide": false, - "interval": "", - "intervalFactor": 1, - "legendFormat": "{{ pod_name }}", - "refId": "B" - } - ], - "thresholds": [], - "timeFrom": null, - "timeRegions": [], - "timeShift": null, - "title": "Canary: Memory Usage by Pod", - "tooltip": { - "shared": true, - "sort": 0, - "value_type": "individual" - }, - "type": "graph", - "xaxis": { - "buckets": null, - "mode": "time", - "name": null, - "show": true, - "values": [] - }, - "yaxes": [ - { - "decimals": null, - "format": "bytes", - "label": "", - "logBase": 1, - "max": null, - "min": null, - "show": true - }, - { - "format": "short", - "label": null, - "logBase": 1, - "max": null, - "min": null, - "show": false - } - ], - "yaxis": { - "align": false, - "alignLevel": null - } - }, - { - "aliasColors": {}, - "bars": false, - "dashLength": 10, - "dashes": false, - "datasource": "prometheus", - "fill": 1, - "gridPos": { - "h": 6, - "w": 12, - "x": 0, - "y": 26 - }, - "id": 105, - "legend": { - "alignAsTable": false, - "avg": false, - "current": false, - "hideEmpty": false, - "hideZero": false, - "max": false, - "min": false, - "rightSide": false, - "show": true, - "total": false, - "values": false - }, - "lines": true, - "linewidth": 1, - "links": [], - "nullPointMode": "null", - "percentage": false, - "pointradius": 5, - "points": false, - "renderer": "flot", - "seriesOverrides": [ - { - "alias": "received", - "color": "#f9d9f9" - }, - { - "alias": "transmited", - "color": "#f29191" - } - ], - "spaceLength": 10, - "stack": false, - "steppedLine": false, - "targets": [ - { - "expr": "sum(rate (container_network_receive_bytes_total{namespace=\"$namespace\",pod_name=~\"$primary.*\"}[1m])) ", - "format": "time_series", - "intervalFactor": 1, - "legendFormat": "received", - "refId": "A" - }, - { - "expr": "-sum (rate (container_network_transmit_bytes_total{namespace=\"$namespace\",pod_name=~\"$primary.*\"}[1m]))", - "format": "time_series", - "intervalFactor": 1, - "legendFormat": "transmited", - "refId": "B" - } - ], - "thresholds": [], - "timeFrom": null, - "timeRegions": [], - "timeShift": null, - "title": "Primary: Network I/O", - "tooltip": { - "shared": true, - "sort": 0, - "value_type": "individual" - }, - "type": "graph", - "xaxis": { - "buckets": null, - "mode": "time", - "name": null, - "show": true, - "values": [] - }, - "yaxes": [ - { - "decimals": null, - "format": "Bps", - "label": "", - "logBase": 1, - "max": null, - "min": null, - "show": true - }, - { - "format": "short", - "label": null, - "logBase": 1, - "max": null, - "min": null, - "show": false - } - ], - "yaxis": { - "align": false, - "alignLevel": null - } - }, - { - "aliasColors": {}, - "bars": false, - "dashLength": 10, - "dashes": false, - "datasource": "prometheus", - "fill": 1, - "gridPos": { - "h": 6, - "w": 12, - "x": 12, - "y": 26 - }, - "id": 106, - "legend": { - "alignAsTable": false, - "avg": false, - "current": false, - "hideEmpty": false, - "hideZero": false, - "max": false, - "min": false, - "rightSide": false, - "show": true, - "total": false, - "values": false - }, - "lines": true, - "linewidth": 1, - "links": [], - "nullPointMode": "null", - "percentage": false, - "pointradius": 5, - "points": false, - "renderer": "flot", - "seriesOverrides": [ - { - "alias": "received", - "color": "#f9d9f9" - }, - { - "alias": "transmited", - "color": "#f29191" - } - ], - "spaceLength": 10, - "stack": false, - "steppedLine": false, - "targets": [ - { - "expr": "sum(rate (container_network_receive_bytes_total{namespace=\"$namespace\",pod_name=~\"$canary.*\",pod_name!~\"$primary.*\"}[1m])) ", - "format": "time_series", - "intervalFactor": 1, - "legendFormat": "received", - "refId": "A" - }, - { - "expr": "-sum (rate (container_network_transmit_bytes_total{namespace=\"$namespace\",pod_name=~\"$canary.*\",pod_name!~\"$primary.*\"}[1m]))", - "format": "time_series", - "intervalFactor": 1, - "legendFormat": "transmited", - "refId": "B" - } - ], - "thresholds": [], - "timeFrom": null, - "timeRegions": [], - "timeShift": null, - "title": "Canary: Network I/O", - "tooltip": { - "shared": true, - "sort": 0, - "value_type": "individual" - }, - "type": "graph", - "xaxis": { - "buckets": null, - "mode": "time", - "name": null, - "show": true, - "values": [] - }, - "yaxes": [ - { - "decimals": null, - "format": "Bps", - "label": "", - "logBase": 1, - "max": null, - "min": null, - "show": true - }, - { - "format": "short", - "label": null, - "logBase": 1, - "max": null, - "min": null, - "show": false - } - ], - "yaxis": { - "align": false, - "alignLevel": null - } - }, - { - "content": "
\nIN/OUTBOUND: $canary.$namespace\n
", - "gridPos": { - "h": 3, - "w": 24, - "x": 0, - "y": 32 - }, - "id": 45, - "links": [], - "mode": "html", - "title": "", - "transparent": true, - "type": "text" - }, - { - "aliasColors": {}, - "bars": false, - "dashLength": 10, - "dashes": false, - "datasource": "prometheus", - "fill": 0, - "gridPos": { - "h": 6, - "w": 12, - "x": 0, - "y": 35 - }, - "id": 25, - "legend": { - "avg": false, - "current": false, - "hideEmpty": true, - "max": false, - "min": false, - "show": true, - "total": false, - "values": false - }, - "lines": true, - "linewidth": 1, - "links": [], - "nullPointMode": "null as zero", - "percentage": false, - "pointradius": 5, - "points": false, - "renderer": "flot", - "seriesOverrides": [], - "spaceLength": 10, - "stack": false, - "steppedLine": false, - "targets": [ - { - "expr": "round(sum(irate(istio_requests_total{connection_security_policy=\"mutual_tls\", destination_workload_namespace=~\"$namespace\", destination_workload=~\"$primary\", reporter=\"destination\"}[30s])) by (source_workload, source_workload_namespace, response_code), 0.001)", - "format": "time_series", - "hide": false, - "intervalFactor": 1, - "legendFormat": "{{ source_workload }}.{{ source_workload_namespace }} : {{ response_code }} (🔐mTLS)", - "refId": "B", - "step": 2 - }, - { - "expr": "round(sum(irate(istio_requests_total{connection_security_policy!=\"mutual_tls\", destination_workload_namespace=~\"$namespace\", destination_workload=~\"$primary\", reporter=\"destination\"}[30s])) by (source_workload, source_workload_namespace, response_code), 0.001)", - "format": "time_series", - "hide": false, - "intervalFactor": 1, - "legendFormat": "{{ source_workload }}.{{ source_workload_namespace }} : {{ response_code }}", - "refId": "A", - "step": 2 - } - ], - "thresholds": [], - "timeFrom": null, - "timeRegions": [], - "timeShift": null, - "title": "Primary: Incoming Requests by Source And Response Code", - "tooltip": { - "shared": false, - "sort": 0, - "value_type": "individual" - }, - "type": "graph", - "xaxis": { - "buckets": null, - "mode": "time", - "name": null, - "show": true, - "values": [ - "total" - ] - }, - "yaxes": [ - { - "format": "ops", - "label": null, - "logBase": 1, - "max": null, - "min": "0", - "show": true - }, - { - "format": "short", - "label": null, - "logBase": 1, - "max": null, - "min": null, - "show": false - } - ], - "yaxis": { - "align": false, - "alignLevel": null - } - }, - { - "aliasColors": {}, - "bars": false, - "dashLength": 10, - "dashes": false, - "datasource": "prometheus", - "fill": 0, - "gridPos": { - "h": 6, - "w": 12, - "x": 12, - "y": 35 - }, - "id": 92, - "legend": { - "avg": false, - "current": false, - "hideEmpty": true, - "max": false, - "min": false, - "show": true, - "total": false, - "values": false - }, - "lines": true, - "linewidth": 1, - "links": [], - "nullPointMode": "null as zero", - "percentage": false, - "pointradius": 5, - "points": false, - "renderer": "flot", - "seriesOverrides": [], - "spaceLength": 10, - "stack": false, - "steppedLine": false, - "targets": [ - { - "expr": "round(sum(irate(istio_requests_total{connection_security_policy=\"mutual_tls\", destination_workload_namespace=~\"$namespace\", destination_workload=~\"$canary\", reporter=\"destination\"}[30s])) by (source_workload, source_workload_namespace, response_code), 0.001)", - "format": "time_series", - "hide": false, - "intervalFactor": 1, - "legendFormat": "{{ source_workload }}.{{ source_workload_namespace }} : {{ response_code }} (🔐mTLS)", - "refId": "B", - "step": 2 - }, - { - "expr": "round(sum(irate(istio_requests_total{connection_security_policy!=\"mutual_tls\", destination_workload_namespace=~\"$namespace\", destination_workload=~\"$canary\", reporter=\"destination\"}[30s])) by (source_workload, source_workload_namespace, response_code), 0.001)", - "format": "time_series", - "hide": false, - "intervalFactor": 1, - "legendFormat": "{{ source_workload }}.{{ source_workload_namespace }} : {{ response_code }}", - "refId": "A", - "step": 2 - } - ], - "thresholds": [], - "timeFrom": null, - "timeRegions": [], - "timeShift": null, - "title": "Canary: Incoming Requests by Source And Response Code", - "tooltip": { - "shared": false, - "sort": 0, - "value_type": "individual" - }, - "type": "graph", - "xaxis": { - "buckets": null, - "mode": "time", - "name": null, - "show": true, - "values": [ - "total" - ] - }, - "yaxes": [ - { - "format": "ops", - "label": null, - "logBase": 1, - "max": null, - "min": "0", - "show": true - }, - { - "format": "short", - "label": null, - "logBase": 1, - "max": null, - "min": null, - "show": false - } - ], - "yaxis": { - "align": false, - "alignLevel": null - } - }, - { - "aliasColors": {}, - "bars": false, - "dashLength": 10, - "dashes": false, - "datasource": "prometheus", - "fill": 0, - "gridPos": { - "h": 6, - "w": 12, - "x": 0, - "y": 41 - }, - "id": 70, - "legend": { - "avg": false, - "current": false, - "hideEmpty": true, - "max": false, - "min": false, - "show": true, - "total": false, - "values": false - }, - "lines": true, - "linewidth": 1, - "links": [], - "nullPointMode": "null as zero", - "percentage": false, - "pointradius": 5, - "points": false, - "renderer": "flot", - "seriesOverrides": [], - "spaceLength": 10, - "stack": false, - "steppedLine": false, - "targets": [ - { - "expr": "round(sum(irate(istio_requests_total{connection_security_policy=\"mutual_tls\", source_workload_namespace=~\"$namespace\", source_workload=~\"$primary\", reporter=\"source\"}[30s])) by (destination_service, response_code), 0.001)", - "format": "time_series", - "intervalFactor": 1, - "legendFormat": "{{ destination_service }} : {{ response_code }} (🔐mTLS)", - "refId": "B", - "step": 2 - }, - { - "expr": "round(sum(irate(istio_requests_total{connection_security_policy!=\"mutual_tls\", source_workload_namespace=~\"$namespace\", source_workload=~\"$primary\", reporter=\"source\"}[30s])) by (destination_service, response_code), 0.001)", - "format": "time_series", - "hide": false, - "intervalFactor": 1, - "legendFormat": "{{ destination_service }} : {{ response_code }}", - "refId": "A", - "step": 2 - } - ], - "thresholds": [], - "timeFrom": null, - "timeRegions": [], - "timeShift": null, - "title": "Primary: Outgoing Requests by Destination And Response Code", - "tooltip": { - "shared": false, - "sort": 0, - "value_type": "individual" - }, - "type": "graph", - "xaxis": { - "buckets": null, - "mode": "time", - "name": null, - "show": true, - "values": [ - "total" - ] - }, - "yaxes": [ - { - "format": "ops", - "label": null, - "logBase": 1, - "max": null, - "min": "0", - "show": true - }, - { - "format": "short", - "label": null, - "logBase": 1, - "max": null, - "min": null, - "show": false - } - ], - "yaxis": { - "align": false, - "alignLevel": null - } - }, - { - "aliasColors": {}, - "bars": false, - "dashLength": 10, - "dashes": false, - "datasource": "prometheus", - "fill": 0, - "gridPos": { - "h": 6, - "w": 12, - "x": 12, - "y": 41 - }, - "id": 94, - "legend": { - "avg": false, - "current": false, - "hideEmpty": true, - "max": false, - "min": false, - "show": true, - "total": false, - "values": false - }, - "lines": true, - "linewidth": 1, - "links": [], - "nullPointMode": "null as zero", - "percentage": false, - "pointradius": 5, - "points": false, - "renderer": "flot", - "seriesOverrides": [], - "spaceLength": 10, - "stack": false, - "steppedLine": false, - "targets": [ - { - "expr": "round(sum(irate(istio_requests_total{connection_security_policy=\"mutual_tls\", source_workload_namespace=~\"$namespace\", source_workload=~\"$canary\", reporter=\"source\"}[30s])) by (destination_service, response_code), 0.001)", - "format": "time_series", - "intervalFactor": 1, - "legendFormat": "{{ destination_service }} : {{ response_code }} (🔐mTLS)", - "refId": "B", - "step": 2 - }, - { - "expr": "round(sum(irate(istio_requests_total{connection_security_policy!=\"mutual_tls\", source_workload_namespace=~\"$namespace\", source_workload=~\"$canary\", reporter=\"source\"}[30s])) by (destination_service, response_code), 0.001)", - "format": "time_series", - "hide": false, - "intervalFactor": 1, - "legendFormat": "{{ destination_service }} : {{ response_code }}", - "refId": "A", - "step": 2 - } - ], - "thresholds": [], - "timeFrom": null, - "timeRegions": [], - "timeShift": null, - "title": "Canary: Outgoing Requests by Destination And Response Code", - "tooltip": { - "shared": false, - "sort": 0, - "value_type": "individual" - }, - "type": "graph", - "xaxis": { - "buckets": null, - "mode": "time", - "name": null, - "show": true, - "values": [ - "total" - ] - }, - "yaxes": [ - { - "format": "ops", - "label": null, - "logBase": 1, - "max": null, - "min": "0", - "show": true - }, - { - "format": "short", - "label": null, - "logBase": 1, - "max": null, - "min": null, - "show": false - } - ], - "yaxis": { - "align": false, - "alignLevel": null - } - } - ], - "refresh": "10s", - "schemaVersion": 16, - "style": "dark", - "tags": [], - "templating": { - "list": [ - { - "allValue": null, - "current": null, - "datasource": "prometheus", - "definition": "", - "hide": 0, - "includeAll": false, - "label": "Namespace", - "multi": false, - "name": "namespace", - "options": [], - "query": "query_result(sum(istio_requests_total) by (destination_workload_namespace) or sum(istio_tcp_sent_bytes_total) by (destination_workload_namespace))", - "refresh": 1, - "regex": "/.*_namespace=\"([^\"]*).*/", - "skipUrlSync": false, - "sort": 0, - "tagValuesQuery": "", - "tags": [], - "tagsQuery": "", - "type": "query", - "useTags": false - }, - { - "allValue": null, - "current": null, - "datasource": "prometheus", - "definition": "", - "hide": 0, - "includeAll": false, - "label": "Primary", - "multi": false, - "name": "primary", - "options": [], - "query": "query_result(sum(istio_requests_total{destination_workload_namespace=~\"$namespace\"}) by (destination_workload))", - "refresh": 1, - "regex": "/.*destination_workload=\"([^\"]*).*/", - "skipUrlSync": false, - "sort": 1, - "tagValuesQuery": "", - "tags": [], - "tagsQuery": "", - "type": "query", - "useTags": false - }, - { - "allValue": null, - "current": null, - "datasource": "prometheus", - "definition": "", - "hide": 0, - "includeAll": false, - "label": "Canary", - "multi": false, - "name": "canary", - "options": [], - "query": "query_result(sum(istio_requests_total{destination_workload_namespace=~\"$namespace\"}) by (destination_workload))", - "refresh": 1, - "regex": "/.*destination_workload=\"([^\"]*).*/", - "skipUrlSync": false, - "sort": 1, - "tagValuesQuery": "", - "tags": [], - "tagsQuery": "", - "type": "query", - "useTags": false - } - ] - }, - "time": { - "from": "now-5m", - "to": "now" - }, - "timepicker": { - "refresh_intervals": [ - "5s", - "10s", - "30s", - "1m", - "5m", - "15m", - "30m", - "1h", - "2h", - "1d" - ], - "time_options": [ - "5m", - "15m", - "1h", - "6h", - "12h", - "24h", - "2d", - "7d", - "30d" - ] - }, - "timezone": "", - "title": "Istio Canary", - "uid": "flagger-istio", - "version": 3 -} \ No newline at end of file diff --git a/charts/grafana/templates/NOTES.txt b/charts/grafana/templates/NOTES.txt deleted file mode 100644 index f3f868aa..00000000 --- a/charts/grafana/templates/NOTES.txt +++ /dev/null @@ -1,7 +0,0 @@ -1. Run the port forward command: - -kubectl -n {{ .Release.Namespace }} port-forward svc/{{ .Release.Name }} 3000:80 - -2. Navigate to: - -http://localhost:3000 \ No newline at end of file diff --git a/charts/grafana/templates/_helpers.tpl b/charts/grafana/templates/_helpers.tpl deleted file mode 100644 index 7ca9802e..00000000 --- a/charts/grafana/templates/_helpers.tpl +++ /dev/null @@ -1,32 +0,0 @@ -{{/* vim: set filetype=mustache: */}} -{{/* -Expand the name of the chart. -*/}} -{{- define "grafana.name" -}} -{{- default .Chart.Name .Values.nameOverride | trunc 63 | trimSuffix "-" -}} -{{- end -}} - -{{/* -Create a default fully qualified app name. -We truncate at 63 chars because some Kubernetes name fields are limited to this (by the DNS naming spec). -If release name contains chart name it will be used as a full name. -*/}} -{{- define "grafana.fullname" -}} -{{- if .Values.fullnameOverride -}} -{{- .Values.fullnameOverride | trunc 63 | trimSuffix "-" -}} -{{- else -}} -{{- $name := default .Chart.Name .Values.nameOverride -}} -{{- if contains $name .Release.Name -}} -{{- .Release.Name | trunc 63 | trimSuffix "-" -}} -{{- else -}} -{{- printf "%s-%s" .Release.Name $name | trunc 63 | trimSuffix "-" -}} -{{- end -}} -{{- end -}} -{{- end -}} - -{{/* -Create chart name and version as used by the chart label. -*/}} -{{- define "grafana.chart" -}} -{{- printf "%s-%s" .Chart.Name .Chart.Version | replace "+" "_" | trunc 63 | trimSuffix "-" -}} -{{- end -}} diff --git a/charts/grafana/templates/dashboards-cfg.yaml b/charts/grafana/templates/dashboards-cfg.yaml deleted file mode 100644 index 41aa3078..00000000 --- a/charts/grafana/templates/dashboards-cfg.yaml +++ /dev/null @@ -1,6 +0,0 @@ -apiVersion: v1 -kind: ConfigMap -metadata: - name: {{ template "grafana.fullname" . }}-dashboards -data: -{{ (.Files.Glob "dashboards/*").AsConfig | indent 2 }} diff --git a/charts/grafana/templates/datasources-cfg.yaml b/charts/grafana/templates/datasources-cfg.yaml deleted file mode 100644 index 903bf5d4..00000000 --- a/charts/grafana/templates/datasources-cfg.yaml +++ /dev/null @@ -1,32 +0,0 @@ -apiVersion: v1 -kind: ConfigMap -metadata: - name: {{ template "grafana.fullname" . }}-datasources -data: - datasources.yaml: |- - apiVersion: 1 - - deleteDatasources: - - name: prometheus -{{- if .Values.token }} - datasources: - - name: prometheus - type: prometheus - access: proxy - url: https://cloud.weave.works/api/prom - isDefault: true - editable: true - version: 1 - basicAuth: true - basicAuthUser: weave - basicAuthPassword: {{ .Values.token }} -{{- else }} - datasources: - - name: prometheus - type: prometheus - access: proxy - url: {{ .Values.url }} - isDefault: true - editable: true - version: 1 -{{- end }} diff --git a/charts/grafana/templates/deployment.yaml b/charts/grafana/templates/deployment.yaml deleted file mode 100644 index f3235011..00000000 --- a/charts/grafana/templates/deployment.yaml +++ /dev/null @@ -1,90 +0,0 @@ -apiVersion: apps/v1beta2 -kind: Deployment -metadata: - name: {{ template "grafana.fullname" . }} - labels: - app: {{ template "grafana.fullname" . }} - chart: {{ template "grafana.chart" . }} - release: {{ .Release.Name }} - heritage: {{ .Release.Service }} -spec: - replicas: {{ .Values.replicaCount }} - selector: - matchLabels: - app: {{ template "grafana.fullname" . }} - release: {{ .Release.Name }} - template: - metadata: - labels: - app: {{ template "grafana.fullname" . }} - release: {{ .Release.Name }} - annotations: - prometheus.io/scrape: 'false' - spec: - containers: - - name: {{ .Chart.Name }} - image: "{{ .Values.image.repository }}:{{ .Values.image.tag }}" - imagePullPolicy: {{ .Values.image.pullPolicy }} - ports: - - name: http - containerPort: 3000 - protocol: TCP -# livenessProbe: -# httpGet: -# path: / -# port: http -# readinessProbe: -# httpGet: -# path: / -# port: http - env: - - name: GF_PATHS_PROVISIONING - value: /etc/grafana/provisioning/ - {{- if .Values.password }} - - name: GF_SECURITY_ADMIN_USER - value: {{ .Values.user }} - - name: GF_SECURITY_ADMIN_PASSWORD - value: {{ .Values.password }} - {{- else }} - - name: GF_AUTH_BASIC_ENABLED - value: "false" - - name: GF_AUTH_ANONYMOUS_ENABLED - value: "true" - - name: GF_AUTH_ANONYMOUS_ORG_ROLE - value: Admin - {{- end }} - volumeMounts: - - name: grafana - mountPath: /var/lib/grafana - - name: dashboards - mountPath: /etc/grafana/dashboards - - name: datasources - mountPath: /etc/grafana/provisioning/datasources - - name: providers - mountPath: /etc/grafana/provisioning/dashboards - resources: -{{ toYaml .Values.resources | indent 12 }} - {{- with .Values.nodeSelector }} - nodeSelector: -{{ toYaml . | indent 8 }} - {{- end }} - {{- with .Values.affinity }} - affinity: -{{ toYaml . | indent 8 }} - {{- end }} - {{- with .Values.tolerations }} - tolerations: -{{ toYaml . | indent 8 }} - {{- end }} - volumes: - - name: grafana - emptyDir: {} - - name: dashboards - configMap: - name: {{ template "grafana.fullname" . }}-dashboards - - name: providers - configMap: - name: {{ template "grafana.fullname" . }}-providers - - name: datasources - configMap: - name: {{ template "grafana.fullname" . }}-datasources diff --git a/charts/grafana/templates/providers-cfg.yaml b/charts/grafana/templates/providers-cfg.yaml deleted file mode 100644 index e9a8bd87..00000000 --- a/charts/grafana/templates/providers-cfg.yaml +++ /dev/null @@ -1,17 +0,0 @@ -apiVersion: v1 -kind: ConfigMap -metadata: - name: {{ template "grafana.fullname" . }}-providers -data: - providers.yaml: |+ - apiVersion: 1 - - providers: - - name: 'default' - orgId: 1 - folder: '' - type: file - disableDeletion: false - editable: true - options: - path: /etc/grafana/dashboards diff --git a/charts/grafana/templates/service.yaml b/charts/grafana/templates/service.yaml deleted file mode 100644 index 3508886d..00000000 --- a/charts/grafana/templates/service.yaml +++ /dev/null @@ -1,19 +0,0 @@ -apiVersion: v1 -kind: Service -metadata: - name: {{ template "grafana.fullname" . }} - labels: - app: {{ template "grafana.name" . }} - chart: {{ template "grafana.chart" . }} - release: {{ .Release.Name }} - heritage: {{ .Release.Service }} -spec: - type: {{ .Values.service.type }} - ports: - - port: {{ .Values.service.port }} - targetPort: http - protocol: TCP - name: http - selector: - app: {{ template "grafana.fullname" . }} - release: {{ .Release.Name }} diff --git a/charts/grafana/values.yaml b/charts/grafana/values.yaml deleted file mode 100644 index 5b4ad8c4..00000000 --- a/charts/grafana/values.yaml +++ /dev/null @@ -1,37 +0,0 @@ -# Default values for grafana. -# This is a YAML-formatted file. -# Declare variables to be passed into your templates. - -replicaCount: 1 - -image: - repository: grafana/grafana - tag: 6.2.5 - pullPolicy: IfNotPresent - -service: - type: ClusterIP - port: 80 - -resources: {} - # limits: - # cpu: 100m - # memory: 128Mi - # requests: - # cpu: 100m - # memory: 128Mi - -nodeSelector: {} - -tolerations: [] - -affinity: {} - -user: admin -password: - -# Istio Prometheus instance -url: http://prometheus:9090 - -# Weave Cloud instance token -token: diff --git a/charts/loadtester/.helmignore b/charts/loadtester/.helmignore deleted file mode 100644 index 50af0317..00000000 --- a/charts/loadtester/.helmignore +++ /dev/null @@ -1,22 +0,0 @@ -# Patterns to ignore when building packages. -# This supports shell glob matching, relative path matching, and -# negation (prefixed with !). Only one pattern per line. -.DS_Store -# Common VCS dirs -.git/ -.gitignore -.bzr/ -.bzrignore -.hg/ -.hgignore -.svn/ -# Common backup files -*.swp -*.bak -*.tmp -*~ -# Various IDEs -.project -.idea/ -*.tmproj -.vscode/ diff --git a/charts/loadtester/Chart.yaml b/charts/loadtester/Chart.yaml deleted file mode 100644 index 745d4b13..00000000 --- a/charts/loadtester/Chart.yaml +++ /dev/null @@ -1,21 +0,0 @@ -apiVersion: v1 -name: loadtester -version: 0.6.0 -appVersion: 0.6.1 -kubeVersion: ">=1.11.0-0" -engine: gotpl -description: Flagger's load testing services based on rakyll/hey and bojand/ghz that generates traffic during canary analysis when configured as a webhook. -home: https://docs.flagger.app -icon: https://raw.githubusercontent.com/weaveworks/flagger/master/docs/logo/flagger-icon.png -sources: - - https://github.com/weaveworks/flagger -maintainers: - - name: stefanprodan - url: https://github.com/stefanprodan - email: stefanprodan@users.noreply.github.com -keywords: - - canary - - istio - - appmesh - - gitops - - load testing diff --git a/charts/loadtester/README.md b/charts/loadtester/README.md deleted file mode 100644 index b1172617..00000000 --- a/charts/loadtester/README.md +++ /dev/null @@ -1,78 +0,0 @@ -# Flagger load testing service - -[Flagger's](https://github.com/weaveworks/flagger) load testing service is based on -[rakyll/hey](https://github.com/rakyll/hey) -and can be used to generates traffic during canary analysis when configured as a webhook. - -## Prerequisites - -* Kubernetes >= 1.11 - -## Installing the Chart - -Add Flagger Helm repository: - -```console -helm repo add flagger https://flagger.app -``` - -To install the chart with the release name `flagger-loadtester`: - -```console -helm upgrade -i flagger-loadtester flagger/loadtester -``` - -The command deploys Grafana on the Kubernetes cluster in the default namespace. - -> **Tip**: Note that the namespace where you deploy the load tester should have the Istio or App Mesh sidecar injection enabled - -The [configuration](#configuration) section lists the parameters that can be configured during installation. - -## Uninstalling the Chart - -To uninstall/delete the `flagger-loadtester` deployment: - -```console -helm delete --purge flagger-loadtester -``` - -The command removes all the Kubernetes components associated with the chart and deletes the release. - -## Configuration - -The following tables lists the configurable parameters of the load tester chart and their default values. - -Parameter | Description | Default ---- | --- | --- -`image.repository` | Image repository | `quay.io/stefanprodan/flagger-loadtester` -`image.pullPolicy` | Image pull policy | `IfNotPresent` -`image.tag` | Image tag | `` -`replicaCount` | Desired number of pods | `1` -`serviceAccountName` | Kubernetes service account name | `none` -`resources.requests.cpu` | CPU requests | `10m` -`resources.requests.memory` | Memory requests | `64Mi` -`tolerations` | List of node taints to tolerate | `[]` -`affinity` | node/pod affinities | `node` -`nodeSelector` | Node labels for pod assignment | `{}` -`service.type` | Type of service | `ClusterIP` -`service.port` | ClusterIP port | `80` -`cmd.timeout` | Command execution timeout | `1h` -`logLevel` | Log level can be debug, info, warning, error or panic | `info` -`meshName` | AWS App Mesh name | `none` -`backends` | AWS App Mesh virtual services | `none` - -Specify each parameter using the `--set key=value[,key=value]` argument to `helm install`. For example, - -```console -helm install flagger/loadtester --name flagger-loadtester -``` - -Alternatively, a YAML file that specifies the values for the above parameters can be provided while installing the chart. For example, - -```console -helm install flagger/loadtester --name flagger-loadtester -f values.yaml -``` - -> **Tip**: You can use the default [values.yaml](values.yaml) - - diff --git a/charts/loadtester/templates/NOTES.txt b/charts/loadtester/templates/NOTES.txt deleted file mode 100644 index b8747e8d..00000000 --- a/charts/loadtester/templates/NOTES.txt +++ /dev/null @@ -1 +0,0 @@ -Flagger's load testing service is available at http://{{ include "loadtester.fullname" . }}.{{ .Release.Namespace }}/ \ No newline at end of file diff --git a/charts/loadtester/templates/_helpers.tpl b/charts/loadtester/templates/_helpers.tpl deleted file mode 100644 index b2c39a81..00000000 --- a/charts/loadtester/templates/_helpers.tpl +++ /dev/null @@ -1,32 +0,0 @@ -{{/* vim: set filetype=mustache: */}} -{{/* -Expand the name of the chart. -*/}} -{{- define "loadtester.name" -}} -{{- default .Chart.Name .Values.nameOverride | trunc 63 | trimSuffix "-" -}} -{{- end -}} - -{{/* -Create a default fully qualified app name. -We truncate at 63 chars because some Kubernetes name fields are limited to this (by the DNS naming spec). -If release name contains chart name it will be used as a full name. -*/}} -{{- define "loadtester.fullname" -}} -{{- if .Values.fullnameOverride -}} -{{- .Values.fullnameOverride | trunc 63 | trimSuffix "-" -}} -{{- else -}} -{{- $name := default .Chart.Name .Values.nameOverride -}} -{{- if contains $name .Release.Name -}} -{{- .Release.Name | trunc 63 | trimSuffix "-" -}} -{{- else -}} -{{- printf "%s-%s" .Release.Name $name | trunc 63 | trimSuffix "-" -}} -{{- end -}} -{{- end -}} -{{- end -}} - -{{/* -Create chart name and version as used by the chart label. -*/}} -{{- define "loadtester.chart" -}} -{{- printf "%s-%s" .Chart.Name .Chart.Version | replace "+" "_" | trunc 63 | trimSuffix "-" -}} -{{- end -}} diff --git a/charts/loadtester/templates/deployment.yaml b/charts/loadtester/templates/deployment.yaml deleted file mode 100644 index ec7fef88..00000000 --- a/charts/loadtester/templates/deployment.yaml +++ /dev/null @@ -1,70 +0,0 @@ -apiVersion: apps/v1 -kind: Deployment -metadata: - name: {{ include "loadtester.fullname" . }} - labels: - app.kubernetes.io/name: {{ include "loadtester.name" . }} - helm.sh/chart: {{ include "loadtester.chart" . }} - app.kubernetes.io/instance: {{ .Release.Name }} - app.kubernetes.io/managed-by: {{ .Release.Service }} -spec: - replicas: {{ .Values.replicaCount }} - selector: - matchLabels: - app: {{ include "loadtester.name" . }} - template: - metadata: - labels: - app: {{ include "loadtester.name" . }} - annotations: - appmesh.k8s.aws/ports: "444" - spec: - {{- if .Values.serviceAccountName }} - serviceAccountName: {{ .Values.serviceAccountName }} - {{- end }} - containers: - - name: {{ .Chart.Name }} - image: "{{ .Values.image.repository }}:{{ .Values.image.tag }}" - imagePullPolicy: {{ .Values.image.pullPolicy }} - ports: - - name: http - containerPort: 8080 - command: - - ./loadtester - - -port=8080 - - -log-level={{ .Values.logLevel }} - - -timeout={{ .Values.cmd.timeout }} - livenessProbe: - exec: - command: - - wget - - --quiet - - --tries=1 - - --timeout=4 - - --spider - - http://localhost:8080/healthz - timeoutSeconds: 5 - readinessProbe: - exec: - command: - - wget - - --quiet - - --tries=1 - - --timeout=4 - - --spider - - http://localhost:8080/healthz - timeoutSeconds: 5 - resources: - {{- toYaml .Values.resources | nindent 12 }} - {{- with .Values.nodeSelector }} - nodeSelector: - {{- toYaml . | nindent 8 }} - {{- end }} - {{- with .Values.affinity }} - affinity: - {{- toYaml . | nindent 8 }} - {{- end }} - {{- with .Values.tolerations }} - tolerations: - {{- toYaml . | nindent 8 }} - {{- end }} diff --git a/charts/loadtester/templates/service.yaml b/charts/loadtester/templates/service.yaml deleted file mode 100644 index 70d1acca..00000000 --- a/charts/loadtester/templates/service.yaml +++ /dev/null @@ -1,18 +0,0 @@ -apiVersion: v1 -kind: Service -metadata: - name: {{ include "loadtester.fullname" . }} - labels: - app.kubernetes.io/name: {{ include "loadtester.name" . }} - helm.sh/chart: {{ include "loadtester.chart" . }} - app.kubernetes.io/instance: {{ .Release.Name }} - app.kubernetes.io/managed-by: {{ .Release.Service }} -spec: - type: {{ .Values.service.type }} - ports: - - port: {{ .Values.service.port }} - targetPort: http - protocol: TCP - name: http - selector: - app: {{ include "loadtester.name" . }} diff --git a/charts/loadtester/templates/virtual-node.yaml b/charts/loadtester/templates/virtual-node.yaml deleted file mode 100644 index d6520ba1..00000000 --- a/charts/loadtester/templates/virtual-node.yaml +++ /dev/null @@ -1,27 +0,0 @@ -{{- if .Values.meshName }} -apiVersion: appmesh.k8s.aws/v1beta1 -kind: VirtualNode -metadata: - name: {{ include "loadtester.fullname" . }} - labels: - app.kubernetes.io/name: {{ include "loadtester.name" . }} - helm.sh/chart: {{ include "loadtester.chart" . }} - app.kubernetes.io/instance: {{ .Release.Name }} - app.kubernetes.io/managed-by: {{ .Release.Service }} -spec: - meshName: {{ .Values.meshName }} - listeners: - - portMapping: - port: 444 - protocol: http - serviceDiscovery: - dns: - hostName: {{ include "loadtester.fullname" . }}.{{ .Release.Namespace }} - {{- if .Values.backends }} - backends: - {{- range .Values.backends }} - - virtualService: - virtualServiceName: {{ . }} - {{- end }} - {{- end }} -{{- end }} diff --git a/charts/loadtester/values.yaml b/charts/loadtester/values.yaml deleted file mode 100644 index 36ff9bf1..00000000 --- a/charts/loadtester/values.yaml +++ /dev/null @@ -1,36 +0,0 @@ -replicaCount: 1 - -image: - repository: weaveworks/flagger-loadtester - tag: 0.6.1 - pullPolicy: IfNotPresent - -logLevel: info -cmd: - timeout: 1h - -nameOverride: "" -fullnameOverride: "" - -service: - type: ClusterIP - port: 80 - -resources: - requests: - cpu: 10m - memory: 64Mi - -nodeSelector: {} - -tolerations: [] - -affinity: {} - -serviceAccountName: "" - -# App Mesh virtual node settings -meshName: "" -#backends: -# - app1.namespace -# - app2.namespace diff --git a/charts/podinfo/.helmignore b/charts/podinfo/.helmignore deleted file mode 100644 index f0c13194..00000000 --- a/charts/podinfo/.helmignore +++ /dev/null @@ -1,21 +0,0 @@ -# Patterns to ignore when building packages. -# This supports shell glob matching, relative path matching, and -# negation (prefixed with !). Only one pattern per line. -.DS_Store -# Common VCS dirs -.git/ -.gitignore -.bzr/ -.bzrignore -.hg/ -.hgignore -.svn/ -# Common backup files -*.swp -*.bak -*.tmp -*~ -# Various IDEs -.project -.idea/ -*.tmproj diff --git a/charts/podinfo/Chart.yaml b/charts/podinfo/Chart.yaml deleted file mode 100644 index 48442097..00000000 --- a/charts/podinfo/Chart.yaml +++ /dev/null @@ -1,12 +0,0 @@ -apiVersion: v1 -version: 3.0.0 -appVersion: 2.0.0 -name: podinfo -engine: gotpl -description: Flagger canary deployment demo chart -home: https://github.com/weaveworks/flagger -maintainers: -- email: stefanprodan@users.noreply.github.com - name: stefanprodan -sources: -- https://github.com/weaveworks/flagger diff --git a/charts/podinfo/README.md b/charts/podinfo/README.md deleted file mode 100644 index 6a626022..00000000 --- a/charts/podinfo/README.md +++ /dev/null @@ -1,79 +0,0 @@ -# Podinfo - -Podinfo is a tiny web application made with Go -that showcases best practices of running canary deployments with Flagger and Istio. - -## Installing the Chart - -Add Flagger Helm repository: - -```console -helm repo add flagger https://flagger.app -``` - -To install the chart with the release name `frontend`: - -```console -helm upgrade -i frontend flagger/podinfo \ ---namespace test \ ---set nameOverride=frontend \ ---set backend=http://backend.test:9898/echo \ ---set canary.enabled=true \ ---set canary.istioIngress.enabled=true \ ---set canary.istioIngress.gateway=public-gateway.istio-system.svc.cluster.local \ ---set canary.istioIngress.host=frontend.istio.example.com -``` - -To install the chart as `backend`: - -```console -helm upgrade -i backend flagger/podinfo \ ---namespace test \ ---set nameOverride=backend \ ---set canary.enabled=true -``` - -## Uninstalling the Chart - -To uninstall/delete the `frontend` deployment: - -```console -$ helm delete --purge frontend -``` - -The command removes all the Kubernetes components associated with the chart and deletes the release. - -## Configuration - -The following tables lists the configurable parameters of the podinfo chart and their default values. - -Parameter | Description | Default ---- | --- | --- -`image.repository` | image repository | `quay.io/stefanprodan/podinfo` -`image.tag` | image tag | `` -`image.pullPolicy` | image pull policy | `IfNotPresent` -`hpa.enabled` | enables HPA | `true` -`hpa.cpu` | target CPU usage per pod | `80` -`hpa.memory` | target memory usage per pod | `512Mi` -`hpa.minReplicas` | maximum pod replicas | `2` -`hpa.maxReplicas` | maximum pod replicas | `4` -`resources.requests/cpu` | pod CPU request | `1m` -`resources.requests/memory` | pod memory request | `16Mi` -`backend` | backend URL | None -`faults.delay` | random HTTP response delays between 0 and 5 seconds | `false` -`faults.error` | 1/3 chances of a random HTTP response error | `false` - -Specify each parameter using the `--set key=value[,key=value]` argument to `helm install`. For example, - -```console -$ helm install flagger/podinfo --name frontend \ - --set=image.tag=1.4.1,hpa.enabled=false -``` - -Alternatively, a YAML file that specifies the values for the above parameters can be provided while installing the chart. For example, - -```console -$ helm install flagger/podinfo --name frontend -f values.yaml -``` - - diff --git a/charts/podinfo/templates/NOTES.txt b/charts/podinfo/templates/NOTES.txt deleted file mode 100644 index 92c7709d..00000000 --- a/charts/podinfo/templates/NOTES.txt +++ /dev/null @@ -1 +0,0 @@ -podinfo {{ .Release.Name }} deployed! \ No newline at end of file diff --git a/charts/podinfo/templates/_helpers.tpl b/charts/podinfo/templates/_helpers.tpl deleted file mode 100644 index 3fe7a70f..00000000 --- a/charts/podinfo/templates/_helpers.tpl +++ /dev/null @@ -1,43 +0,0 @@ -{{/* vim: set filetype=mustache: */}} -{{/* -Expand the name of the chart. -*/}} -{{- define "podinfo.name" -}} -{{- default .Chart.Name .Values.nameOverride | trunc 63 | trimSuffix "-" -}} -{{- end -}} - -{{/* -Create a default fully qualified app name. -We truncate at 63 chars because some Kubernetes name fields are limited to this (by the DNS naming spec). -If release name contains chart name it will be used as a full name. -*/}} -{{- define "podinfo.fullname" -}} -{{- if .Values.fullnameOverride -}} -{{- .Values.fullnameOverride | trunc 63 | trimSuffix "-" -}} -{{- else -}} -{{- $name := default .Chart.Name .Values.nameOverride -}} -{{- if contains $name .Release.Name -}} -{{- .Release.Name | trunc 63 | trimSuffix "-" -}} -{{- else -}} -{{- printf "%s-%s" .Release.Name $name | trunc 63 | trimSuffix "-" -}} -{{- end -}} -{{- end -}} -{{- end -}} - -{{/* -Create chart name and version as used by the chart label. -*/}} -{{- define "podinfo.chart" -}} -{{- printf "%s-%s" .Chart.Name .Chart.Version | replace "+" "_" | trunc 63 | trimSuffix "-" -}} -{{- end -}} - -{{/* -Create chart name suffix. -*/}} -{{- define "podinfo.suffix" -}} -{{- if .Values.canary.enabled -}} -{{- "-primary" -}} -{{- else -}} -{{- "" -}} -{{- end -}} -{{- end -}} \ No newline at end of file diff --git a/charts/podinfo/templates/canary.yaml b/charts/podinfo/templates/canary.yaml deleted file mode 100644 index c1176a17..00000000 --- a/charts/podinfo/templates/canary.yaml +++ /dev/null @@ -1,66 +0,0 @@ -{{- if .Values.canary.enabled }} -apiVersion: flagger.app/v1alpha3 -kind: Canary -metadata: - name: {{ template "podinfo.fullname" . }} - labels: - app: {{ template "podinfo.name" . }} - chart: {{ template "podinfo.chart" . }} - release: {{ .Release.Name }} - heritage: {{ .Release.Service }} -spec: - targetRef: - apiVersion: apps/v1 - kind: Deployment - name: {{ template "podinfo.fullname" . }} - progressDeadlineSeconds: 60 - autoscalerRef: - apiVersion: autoscaling/v2beta1 - kind: HorizontalPodAutoscaler - name: {{ template "podinfo.fullname" . }} - service: - port: {{ .Values.service.port }} - {{- if .Values.canary.istioIngress.enabled }} - gateways: - - {{ .Values.canary.istioIngress.gateway }} - hosts: - - {{ .Values.canary.istioIngress.host }} - {{- end }} - trafficPolicy: - tls: - mode: {{ .Values.canary.istioTLS }} - canaryAnalysis: - interval: {{ .Values.canary.analysis.interval }} - threshold: {{ .Values.canary.analysis.threshold }} - maxWeight: {{ .Values.canary.analysis.maxWeight }} - stepWeight: {{ .Values.canary.analysis.stepWeight }} - metrics: - - name: request-success-rate - threshold: {{ .Values.canary.thresholds.successRate }} - interval: 1m - - name: request-duration - threshold: {{ .Values.canary.thresholds.latency }} - interval: 1m - webhooks: - {{- if .Values.canary.helmtest.enabled }} - - name: "helm test" - type: pre-rollout - url: {{ .Values.canary.helmtest.url }} - timeout: 3m - metadata: - type: "helm" - cmd: "test {{ .Release.Name }} --cleanup" - {{- end }} - {{- if .Values.canary.loadtest.enabled }} - - name: load-test-get - url: {{ .Values.canary.loadtest.url }} - timeout: 5s - metadata: - cmd: "hey -z 1m -q 5 -c 2 http://{{ template "podinfo.fullname" . }}.{{ .Release.Namespace }}:{{ .Values.service.port }}" - - name: load-test-post - url: {{ .Values.canary.loadtest.url }} - timeout: 5s - metadata: - cmd: "hey -z 1m -q 5 -c 2 -m POST -d '{\"test\": true}' http://{{ template "podinfo.fullname" . }}.{{ .Release.Namespace }}:{{ .Values.service.port }}/echo" - {{- end }} -{{- end }} \ No newline at end of file diff --git a/charts/podinfo/templates/configmap.yaml b/charts/podinfo/templates/configmap.yaml deleted file mode 100644 index 14b6a765..00000000 --- a/charts/podinfo/templates/configmap.yaml +++ /dev/null @@ -1,15 +0,0 @@ -apiVersion: v1 -kind: ConfigMap -metadata: - name: {{ template "podinfo.fullname" . }} - labels: - app: {{ template "podinfo.name" . }} - chart: {{ template "podinfo.chart" . }} - release: {{ .Release.Name }} - heritage: {{ .Release.Service }} -data: - config.yaml: |- - # http settings - http-client-timeout: 1m - http-server-timeout: {{ .Values.httpServer.timeout }} - http-server-shutdown-timeout: 5s diff --git a/charts/podinfo/templates/deployment.yaml b/charts/podinfo/templates/deployment.yaml deleted file mode 100644 index 60b2cd01..00000000 --- a/charts/podinfo/templates/deployment.yaml +++ /dev/null @@ -1,93 +0,0 @@ -apiVersion: apps/v1 -kind: Deployment -metadata: - name: {{ template "podinfo.fullname" . }} - labels: - app: {{ template "podinfo.name" . }} - chart: {{ template "podinfo.chart" . }} - release: {{ .Release.Name }} - heritage: {{ .Release.Service }} -spec: - strategy: - type: RollingUpdate - rollingUpdate: - maxUnavailable: 1 - selector: - matchLabels: - app: {{ template "podinfo.fullname" . }} - template: - metadata: - labels: - app: {{ template "podinfo.fullname" . }} - annotations: - prometheus.io/scrape: 'true' - spec: - terminationGracePeriodSeconds: 30 - containers: - - name: podinfo - image: "{{ .Values.image.repository }}:{{ .Values.image.tag }}" - imagePullPolicy: {{ .Values.image.pullPolicy }} - command: - - ./podinfo - - --port={{ .Values.service.port }} - - --level={{ .Values.logLevel }} - - --random-delay={{ .Values.faults.delay }} - - --random-error={{ .Values.faults.error }} - - --config-path=/podinfo/config - env: - {{- if .Values.message }} - - name: PODINFO_UI_MESSAGE - value: {{ .Values.message }} - {{- end }} - {{- if .Values.backend }} - - name: PODINFO_BACKEND_URL - value: {{ .Values.backend }} - {{- end }} - ports: - - name: http - containerPort: {{ .Values.service.port }} - protocol: TCP - livenessProbe: - exec: - command: - - podcli - - check - - http - - localhost:{{ .Values.service.port }}/healthz - initialDelaySeconds: 5 - timeoutSeconds: 5 - readinessProbe: - exec: - command: - - podcli - - check - - http - - localhost:{{ .Values.service.port }}/readyz - initialDelaySeconds: 5 - timeoutSeconds: 5 - volumeMounts: - - name: data - mountPath: /data - - name: config - mountPath: /podinfo/config - readOnly: true - resources: -{{ toYaml .Values.resources | indent 12 }} - {{- with .Values.nodeSelector }} - nodeSelector: -{{ toYaml . | indent 8 }} - {{- end }} - {{- with .Values.affinity }} - affinity: -{{ toYaml . | indent 8 }} - {{- end }} - {{- with .Values.tolerations }} - tolerations: -{{ toYaml . | indent 8 }} - {{- end }} - volumes: - - name: data - emptyDir: {} - - name: config - configMap: - name: {{ template "podinfo.fullname" . }} diff --git a/charts/podinfo/templates/hpa.yaml b/charts/podinfo/templates/hpa.yaml deleted file mode 100644 index 9905cda6..00000000 --- a/charts/podinfo/templates/hpa.yaml +++ /dev/null @@ -1,37 +0,0 @@ -{{- if .Values.hpa.enabled -}} -apiVersion: autoscaling/v2beta1 -kind: HorizontalPodAutoscaler -metadata: - name: {{ template "podinfo.fullname" . }} - labels: - app: {{ template "podinfo.name" . }} - chart: {{ template "podinfo.chart" . }} - release: {{ .Release.Name }} - heritage: {{ .Release.Service }} -spec: - scaleTargetRef: - apiVersion: apps/v1beta2 - kind: Deployment - name: {{ template "podinfo.fullname" . }} - minReplicas: {{ .Values.hpa.minReplicas }} - maxReplicas: {{ .Values.hpa.maxReplicas }} - metrics: - {{- if .Values.hpa.cpu }} - - type: Resource - resource: - name: cpu - targetAverageUtilization: {{ .Values.hpa.cpu }} - {{- end }} - {{- if .Values.hpa.memory }} - - type: Resource - resource: - name: memory - targetAverageValue: {{ .Values.hpa.memory }} - {{- end }} - {{- if .Values.hpa.requests }} - - type: Pod - pods: - metricName: http_requests - targetAverageValue: {{ .Values.hpa.requests }} - {{- end }} -{{- end }} diff --git a/charts/podinfo/templates/service.yaml b/charts/podinfo/templates/service.yaml deleted file mode 100644 index 82b9451c..00000000 --- a/charts/podinfo/templates/service.yaml +++ /dev/null @@ -1,20 +0,0 @@ -{{- if not .Values.canary.enabled }} -apiVersion: v1 -kind: Service -metadata: - name: {{ template "podinfo.fullname" . }} - labels: - app: {{ template "podinfo.name" . }} - chart: {{ template "podinfo.chart" . }} - release: {{ .Release.Name }} - heritage: {{ .Release.Service }} -spec: - type: {{ .Values.service.type }} - ports: - - port: {{ .Values.service.port }} - targetPort: http - protocol: TCP - name: http - selector: - app: {{ template "podinfo.fullname" . }} -{{- end }} \ No newline at end of file diff --git a/charts/podinfo/templates/tests/test-config.yaml b/charts/podinfo/templates/tests/test-config.yaml deleted file mode 100755 index 49a95554..00000000 --- a/charts/podinfo/templates/tests/test-config.yaml +++ /dev/null @@ -1,22 +0,0 @@ -{{- $url := printf "%s%s.%s:%v" (include "podinfo.fullname" .) (include "podinfo.suffix" .) .Release.Namespace .Values.service.port -}} -apiVersion: v1 -kind: ConfigMap -metadata: - name: {{ template "podinfo.fullname" . }}-tests - labels: - heritage: {{ .Release.Service }} - release: {{ .Release.Name }} - chart: {{ .Chart.Name }}-{{ .Chart.Version }} - app: {{ template "podinfo.name" . }} -data: - run.sh: |- - @test "HTTP POST /echo" { - run curl --retry 3 --connect-timeout 2 -sSX POST -d 'test' {{ $url }}/echo - [ $output = "test" ] - } - @test "HTTP POST /store" { - curl --retry 3 --connect-timeout 2 -sSX POST -d 'test' {{ $url }}/store - } - @test "HTTP GET /" { - curl --retry 3 --connect-timeout 2 -sS {{ $url }} | grep hostname - } diff --git a/charts/podinfo/templates/tests/test-pod.yaml b/charts/podinfo/templates/tests/test-pod.yaml deleted file mode 100755 index 5105ef4b..00000000 --- a/charts/podinfo/templates/tests/test-pod.yaml +++ /dev/null @@ -1,43 +0,0 @@ -apiVersion: v1 -kind: Pod -metadata: - name: {{ template "podinfo.fullname" . }}-tests-{{ randAlphaNum 5 | lower }} - annotations: - "helm.sh/hook": test-success - sidecar.istio.io/inject: "false" - labels: - heritage: {{ .Release.Service }} - release: {{ .Release.Name }} - chart: {{ .Chart.Name }}-{{ .Chart.Version }} - app: {{ template "podinfo.name" . }} -spec: - initContainers: - - name: "test-framework" - image: "dduportal/bats:0.4.0" - command: - - "bash" - - "-c" - - | - set -ex - # copy bats to tools dir - cp -R /usr/local/libexec/ /tools/bats/ - volumeMounts: - - mountPath: /tools - name: tools - containers: - - name: {{ .Release.Name }}-ui-test - image: dduportal/bats:0.4.0 - command: ["/tools/bats/bats", "-t", "/tests/run.sh"] - volumeMounts: - - mountPath: /tests - name: tests - readOnly: true - - mountPath: /tools - name: tools - volumes: - - name: tests - configMap: - name: {{ template "podinfo.fullname" . }}-tests - - name: tools - emptyDir: {} - restartPolicy: Never diff --git a/charts/podinfo/values.yaml b/charts/podinfo/values.yaml deleted file mode 100644 index b1b47681..00000000 --- a/charts/podinfo/values.yaml +++ /dev/null @@ -1,79 +0,0 @@ -# Default values for podinfo. -image: - repository: stefanprodan/podinfo - tag: 2.0.0 - pullPolicy: IfNotPresent - -service: - type: ClusterIP - port: 9898 - -hpa: - enabled: true - minReplicas: 2 - maxReplicas: 2 - cpu: 80 - memory: 512Mi - -canary: - enabled: true - # Istio traffic policy tls can be DISABLE or ISTIO_MUTUAL - istioTLS: DISABLE - istioIngress: - enabled: false - # Istio ingress gateway name - gateway: public-gateway.istio-system.svc.cluster.local - # external host name eg. podinfo.example.com - host: - analysis: - # schedule interval (default 60s) - interval: 15s - # max number of failed metric checks before rollback - threshold: 10 - # max traffic percentage routed to canary - # percentage (0-100) - maxWeight: 50 - # canary increment step - # percentage (0-100) - stepWeight: 5 - thresholds: - # minimum req success rate (non 5xx responses) - # percentage (0-100) - successRate: 99 - # maximum req duration P99 - # milliseconds - latency: 500 - loadtest: - enabled: false - # load tester address - url: http://flagger-loadtester.test/ - helmtest: - enabled: false - # helm tester address - url: http://flagger-helmtester.kube-system/ - -resources: - limits: - requests: - cpu: 100m - memory: 32Mi - -nodeSelector: {} - -tolerations: [] - -affinity: {} - -nameOverride: "" -fullnameOverride: "" - -logLevel: info -backend: #http://backend-podinfo:9898/echo -message: #UI greetings - -faults: - delay: false - error: false - -httpServer: - timeout: 30s diff --git a/cmd/flagger/main.go b/cmd/flagger/main.go deleted file mode 100644 index 9a17aaba..00000000 --- a/cmd/flagger/main.go +++ /dev/null @@ -1,304 +0,0 @@ -package main - -import ( - "context" - "flag" - "fmt" - "log" - "os" - "strings" - "time" - - "github.com/Masterminds/semver" - clientset "github.com/weaveworks/flagger/pkg/client/clientset/versioned" - informers "github.com/weaveworks/flagger/pkg/client/informers/externalversions" - "github.com/weaveworks/flagger/pkg/controller" - "github.com/weaveworks/flagger/pkg/logger" - "github.com/weaveworks/flagger/pkg/metrics" - "github.com/weaveworks/flagger/pkg/notifier" - "github.com/weaveworks/flagger/pkg/router" - "github.com/weaveworks/flagger/pkg/server" - "github.com/weaveworks/flagger/pkg/signals" - "github.com/weaveworks/flagger/pkg/version" - "go.uber.org/zap" - "k8s.io/apimachinery/pkg/util/uuid" - "k8s.io/client-go/kubernetes" - _ "k8s.io/client-go/plugin/pkg/client/auth/gcp" - "k8s.io/client-go/tools/cache" - "k8s.io/client-go/tools/clientcmd" - "k8s.io/client-go/tools/leaderelection" - "k8s.io/client-go/tools/leaderelection/resourcelock" - "k8s.io/client-go/transport" - _ "k8s.io/code-generator/cmd/client-gen/generators" -) - -var ( - masterURL string - kubeconfig string - metricsServer string - controlLoopInterval time.Duration - logLevel string - port string - msteamsURL string - slackURL string - slackUser string - slackChannel string - threadiness int - zapReplaceGlobals bool - zapEncoding string - namespace string - meshProvider string - selectorLabels string - enableLeaderElection bool - leaderElectionNamespace string - ver bool -) - -func init() { - flag.StringVar(&kubeconfig, "kubeconfig", "", "Path to a kubeconfig. Only required if out-of-cluster.") - flag.StringVar(&masterURL, "master", "", "The address of the Kubernetes API server. Overrides any value in kubeconfig. Only required if out-of-cluster.") - flag.StringVar(&metricsServer, "metrics-server", "http://prometheus:9090", "Prometheus URL.") - flag.DurationVar(&controlLoopInterval, "control-loop-interval", 10*time.Second, "Kubernetes API sync interval.") - flag.StringVar(&logLevel, "log-level", "debug", "Log level can be: debug, info, warning, error.") - flag.StringVar(&port, "port", "8080", "Port to listen on.") - flag.StringVar(&slackURL, "slack-url", "", "Slack hook URL.") - flag.StringVar(&slackUser, "slack-user", "flagger", "Slack user name.") - flag.StringVar(&slackChannel, "slack-channel", "", "Slack channel.") - flag.StringVar(&msteamsURL, "msteams-url", "", "MS Teams incoming webhook URL.") - flag.IntVar(&threadiness, "threadiness", 2, "Worker concurrency.") - flag.BoolVar(&zapReplaceGlobals, "zap-replace-globals", false, "Whether to change the logging level of the global zap logger.") - flag.StringVar(&zapEncoding, "zap-encoding", "json", "Zap logger encoding.") - flag.StringVar(&namespace, "namespace", "", "Namespace that flagger would watch canary object.") - flag.StringVar(&meshProvider, "mesh-provider", "istio", "Service mesh provider, can be istio, linkerd, appmesh, supergloo, nginx or smi.") - flag.StringVar(&selectorLabels, "selector-labels", "app,name,app.kubernetes.io/name", "List of pod labels that Flagger uses to create pod selectors.") - flag.BoolVar(&enableLeaderElection, "enable-leader-election", false, "Enable leader election.") - flag.StringVar(&leaderElectionNamespace, "leader-election-namespace", "kube-system", "Namespace used to create the leader election config map.") - flag.BoolVar(&ver, "version", false, "Print version") -} - -func main() { - flag.Parse() - - if ver { - fmt.Println("Flagger version", version.VERSION, "revision ", version.REVISION) - os.Exit(0) - } - - logger, err := logger.NewLoggerWithEncoding(logLevel, zapEncoding) - if err != nil { - log.Fatalf("Error creating logger: %v", err) - } - if zapReplaceGlobals { - zap.ReplaceGlobals(logger.Desugar()) - } - - defer logger.Sync() - - stopCh := signals.SetupSignalHandler() - - cfg, err := clientcmd.BuildConfigFromFlags(masterURL, kubeconfig) - if err != nil { - logger.Fatalf("Error building kubeconfig: %v", err) - } - - kubeClient, err := kubernetes.NewForConfig(cfg) - if err != nil { - logger.Fatalf("Error building kubernetes clientset: %v", err) - } - - meshClient, err := clientset.NewForConfig(cfg) - if err != nil { - logger.Fatalf("Error building mesh clientset: %v", err) - } - - flaggerClient, err := clientset.NewForConfig(cfg) - if err != nil { - logger.Fatalf("Error building flagger clientset: %s", err.Error()) - } - - flaggerInformerFactory := informers.NewSharedInformerFactoryWithOptions(flaggerClient, time.Second*30, informers.WithNamespace(namespace)) - - canaryInformer := flaggerInformerFactory.Flagger().V1alpha3().Canaries() - - logger.Infof("Starting flagger version %s revision %s mesh provider %s", version.VERSION, version.REVISION, meshProvider) - - ver, err := kubeClient.Discovery().ServerVersion() - if err != nil { - logger.Fatalf("Error calling Kubernetes API: %v", err) - } - - k8sVersionConstraint := "^1.11.0" - - // We append -alpha.1 to the end of our version constraint so that prebuilds of later versions - // are considered valid for our purposes, as well as some managed solutions like EKS where they provide - // a version like `v1.12.6-eks-d69f1b`. It doesn't matter what the prelease value is here, just that it - // exists in our constraint. - semverConstraint, err := semver.NewConstraint(k8sVersionConstraint + "-alpha.1") - if err != nil { - logger.Fatalf("Error parsing kubernetes version constraint: %v", err) - } - - k8sSemver, err := semver.NewVersion(ver.GitVersion) - if err != nil { - logger.Fatalf("Error parsing kubernetes version as a semantic version: %v", err) - } - - if !semverConstraint.Check(k8sSemver) { - logger.Fatalf("Unsupported version of kubernetes detected. Expected %s, got %v", k8sVersionConstraint, ver) - } - - labels := strings.Split(selectorLabels, ",") - if len(labels) < 1 { - logger.Fatalf("At least one selector label is required") - } - - logger.Infof("Connected to Kubernetes API %s", ver) - if namespace != "" { - logger.Infof("Watching namespace %s", namespace) - } - - observerFactory, err := metrics.NewFactory(metricsServer, meshProvider, 5*time.Second) - if err != nil { - logger.Fatalf("Error building prometheus client: %s", err.Error()) - } - - ok, err := observerFactory.Client.IsOnline() - if ok { - logger.Infof("Connected to metrics server %s", metricsServer) - } else { - logger.Errorf("Metrics server %s unreachable %v", metricsServer, err) - } - - // setup Slack or MS Teams notifications - notifierClient := initNotifier(logger) - - // start HTTP server - go server.ListenAndServe(port, 3*time.Second, logger, stopCh) - - routerFactory := router.NewFactory(cfg, kubeClient, flaggerClient, logger, meshClient) - - c := controller.NewController( - kubeClient, - meshClient, - flaggerClient, - canaryInformer, - controlLoopInterval, - logger, - notifierClient, - routerFactory, - observerFactory, - meshProvider, - version.VERSION, - labels, - ) - - flaggerInformerFactory.Start(stopCh) - - logger.Info("Waiting for informer caches to sync") - for _, synced := range []cache.InformerSynced{ - canaryInformer.Informer().HasSynced, - } { - if ok := cache.WaitForCacheSync(stopCh, synced); !ok { - logger.Fatalf("Failed to wait for cache sync") - } - } - - // leader election context - ctx, cancel := context.WithCancel(context.Background()) - defer cancel() - - // prevents new requests when leadership is lost - cfg.Wrap(transport.ContextCanceller(ctx, fmt.Errorf("the leader is shutting down"))) - - // cancel leader election context on shutdown signals - go func() { - <-stopCh - cancel() - }() - - // wrap controller run - runController := func() { - if err := c.Run(threadiness, stopCh); err != nil { - logger.Fatalf("Error running controller: %v", err) - } - } - - // run controller when this instance wins the leader election - if enableLeaderElection { - ns := leaderElectionNamespace - if namespace != "" { - ns = namespace - } - startLeaderElection(ctx, runController, ns, kubeClient, logger) - } else { - runController() - } -} - -func startLeaderElection(ctx context.Context, run func(), ns string, kubeClient kubernetes.Interface, logger *zap.SugaredLogger) { - configMapName := "flagger-leader-election" - id, err := os.Hostname() - if err != nil { - logger.Fatalf("Error running controller: %v", err) - } - id = id + "_" + string(uuid.NewUUID()) - - lock, err := resourcelock.New( - resourcelock.ConfigMapsResourceLock, - ns, - configMapName, - kubeClient.CoreV1(), - kubeClient.CoordinationV1(), - resourcelock.ResourceLockConfig{ - Identity: id, - }, - ) - if err != nil { - logger.Fatalf("Error running controller: %v", err) - } - - logger.Infof("Starting leader election id: %s configmap: %s namespace: %s", id, configMapName, ns) - leaderelection.RunOrDie(ctx, leaderelection.LeaderElectionConfig{ - Lock: lock, - ReleaseOnCancel: true, - LeaseDuration: 60 * time.Second, - RenewDeadline: 15 * time.Second, - RetryPeriod: 5 * time.Second, - Callbacks: leaderelection.LeaderCallbacks{ - OnStartedLeading: func(ctx context.Context) { - logger.Info("Acting as elected leader") - run() - }, - OnStoppedLeading: func() { - logger.Infof("Leadership lost") - os.Exit(1) - }, - OnNewLeader: func(identity string) { - if identity != id { - logger.Infof("Another instance has been elected as leader: %v", identity) - } - }, - }, - }) -} - -func initNotifier(logger *zap.SugaredLogger) (client notifier.Interface) { - provider := "slack" - notifierURL := slackURL - if msteamsURL != "" { - provider = "msteams" - notifierURL = msteamsURL - } - notifierFactory := notifier.NewFactory(notifierURL, slackUser, slackChannel) - - if notifierURL != "" { - var err error - client, err = notifierFactory.Notifier(provider) - if err != nil { - logger.Errorf("Notifier %v", err) - } else { - logger.Infof("Notifications enabled for %s", notifierURL[0:30]) - } - } - return -} diff --git a/cmd/loadtester/main.go b/cmd/loadtester/main.go deleted file mode 100644 index 7f790767..00000000 --- a/cmd/loadtester/main.go +++ /dev/null @@ -1,53 +0,0 @@ -package main - -import ( - "flag" - "github.com/weaveworks/flagger/pkg/loadtester" - "github.com/weaveworks/flagger/pkg/logger" - "github.com/weaveworks/flagger/pkg/signals" - "go.uber.org/zap" - "log" - "time" -) - -var VERSION = "0.6.1" -var ( - logLevel string - port string - timeout time.Duration - zapReplaceGlobals bool - zapEncoding string -) - -func init() { - flag.StringVar(&logLevel, "log-level", "debug", "Log level can be: debug, info, warning, error.") - flag.StringVar(&port, "port", "9090", "Port to listen on.") - flag.DurationVar(&timeout, "timeout", time.Hour, "Load test exec timeout.") - flag.BoolVar(&zapReplaceGlobals, "zap-replace-globals", false, "Whether to change the logging level of the global zap logger.") - flag.StringVar(&zapEncoding, "zap-encoding", "json", "Zap logger encoding.") -} - -func main() { - flag.Parse() - - logger, err := logger.NewLoggerWithEncoding(logLevel, zapEncoding) - if err != nil { - log.Fatalf("Error creating logger: %v", err) - } - if zapReplaceGlobals { - zap.ReplaceGlobals(logger.Desugar()) - } - - defer logger.Sync() - - stopCh := signals.SetupSignalHandler() - - taskRunner := loadtester.NewTaskRunner(logger, timeout) - - go taskRunner.Start(100*time.Millisecond, stopCh) - - logger.Infof("Starting load tester v%s API on port %s", VERSION, port) - - gateStorage := loadtester.NewGateStorage("in-memory") - loadtester.ListenAndServe(port, time.Minute, logger, taskRunner, gateStorage, stopCh) -} diff --git a/code-of-conduct.md b/code-of-conduct.md deleted file mode 100644 index 6aa7f9a2..00000000 --- a/code-of-conduct.md +++ /dev/null @@ -1,73 +0,0 @@ -# Contributor Covenant Code of Conduct - -## Our Pledge - -In the interest of fostering an open and welcoming environment, we as -contributors and maintainers pledge to making participation in our project and -our community a harassment-free experience for everyone, regardless of age, body -size, disability, ethnicity, gender identity and expression, level of experience, -education, socio-economic status, nationality, personal appearance, race, -religion, or sexual identity and orientation. - -## Our Standards - -Examples of behavior that contributes to creating a positive environment -include: - -* Using welcoming and inclusive language -* Being respectful of differing viewpoints and experiences -* Gracefully accepting constructive criticism -* Focusing on what is best for the community -* Showing empathy towards other community members - -Examples of unacceptable behavior by participants include: - -* The use of sexualized language or imagery and unwelcome sexual attention or - advances -* Trolling, insulting/derogatory comments, and personal or political attacks -* Public or private harassment -* Publishing others' private information, such as a physical or electronic - address, without explicit permission -* Other conduct which could reasonably be considered inappropriate in a - professional setting - -## Our Responsibilities - -Project maintainers are responsible for clarifying the standards of acceptable -behavior and are expected to take appropriate and fair corrective action in -response to any instances of unacceptable behavior. - -Project maintainers have the right and responsibility to remove, edit, or -reject comments, commits, code, wiki edits, issues, and other contributions -that are not aligned to this Code of Conduct, or to ban temporarily or -permanently any contributor for other behaviors that they deem inappropriate, -threatening, offensive, or harmful. - -## Scope - -This Code of Conduct applies both within project spaces and in public spaces -when an individual is representing the project or its community. Examples of -representing a project or community include using an official project e-mail -address, posting via an official social media account, or acting as an appointed -representative at an online or offline event. Representation of a project may be -further defined and clarified by project maintainers. - -## Enforcement - -Instances of abusive, harassing, or otherwise unacceptable behavior -may be reported by contacting stefan.prodan(at)gmail.com. -All complaints will be reviewed and investigated and will result in a response that is deemed -necessary and appropriate to the circumstances. The project team is -obligated to maintain confidentiality with regard to the reporter of -an incident. Further details of specific enforcement policies may be -posted separately. - -Project maintainers who do not follow or enforce the Code of Conduct in good -faith may face temporary or permanent repercussions as determined by other -members of the project's leadership. - -## Attribution - -This Code of Conduct is adapted from the [Contributor Covenant](https://www.contributor-covenant.org), version 1.4, -available at https://www.contributor-covenant.org/version/1/4/code-of-conduct.html - diff --git a/docs/.vuepress/config.js b/docs/.vuepress/config.js new file mode 100644 index 00000000..09fb0534 --- /dev/null +++ b/docs/.vuepress/config.js @@ -0,0 +1,17 @@ +module.exports = { + title: 'Flagger', + description: 'Progressive Delivery operator for Kubernetes', + themeConfig: { + search: false, + activeHeaderLinks: false, + repo: 'weaveworks/flagger', + nav: [ + { text: 'Docs', link: 'https://docs.flagger.app' }, + { text: 'Changelog', link: 'https://github.com/weaveworks/flagger/blob/master/CHANGELOG.md' } + ] + }, + head: [ + ['link', { rel: 'icon', href: '/favicon.png' }], + ['link', { rel: 'stylesheet', href: '/website.css' }] + ] +}; diff --git a/docs/.vuepress/public/favicon.png b/docs/.vuepress/public/favicon.png new file mode 100644 index 00000000..f9678eb2 Binary files /dev/null and b/docs/.vuepress/public/favicon.png differ diff --git a/docs/.vuepress/public/flagger-gitops.png b/docs/.vuepress/public/flagger-gitops.png new file mode 100644 index 00000000..80d0f840 Binary files /dev/null and b/docs/.vuepress/public/flagger-gitops.png differ diff --git a/docs/.vuepress/public/flagger-overview.png b/docs/.vuepress/public/flagger-overview.png new file mode 100644 index 00000000..3cfc7ab8 Binary files /dev/null and b/docs/.vuepress/public/flagger-overview.png differ diff --git a/docs/.vuepress/public/website.css b/docs/.vuepress/public/website.css new file mode 100644 index 00000000..5252aa8a --- /dev/null +++ b/docs/.vuepress/public/website.css @@ -0,0 +1,9 @@ +.icon.outbound { + display: none !important; +} +.site-name { + padding-left: 30px; + position: relative; + background: url(favicon.png) left 50% no-repeat; + background-size: 20px 20px; +} diff --git a/docs/README.md b/docs/README.md new file mode 100644 index 00000000..145402c8 --- /dev/null +++ b/docs/README.md @@ -0,0 +1,95 @@ +--- +title: Flagger +meta: + - name: "keywords" + content: "gitops kubernetes flagger istio linkerd appmesh" + - name: "twitter:card" + content: "summary_large_image" + - name: "twitter:title" + content: "Flagger" + - name: "twitter:description" + content: "Progressive delivery Kubernetes operator (Canary, A/B Testing and Blue/Green deployments)" + - name: "twitter:image:src" + content: "https://flagger.app/flagger-overview.png" +home: true +#heroImage: /flagger-overview.png +heroText: Flagger +tagline: Progressive Delivery Operator for Kubernetes +actionText: Get Started → +actionLink: https://docs.flagger.app +features: +- title: Safer Releases + details: Reduce the risk of introducing a new software version in production by gradually shifting traffic to the new version while measuring metrics like HTTP/gRPC request success rate and latency. +- title: Flexible Traffic Routing + details: Shift and route traffic between app versions using a service mesh like Istio, Linkerd or AWS App Mesh. If you're not using a service mesh then an ingress controller like NGINX or Gloo can also be used. +- title: Extensible Validation + details: Besides the builtin metrics checks, the application analysis can be extended with custom Prometheus metrics and webhooks for running acceptance tests, load tests, or any other custom validation. +footer: Apache License 2.0 | Copyright © 2019 Weaveworks +--- + +## Progressive Delivery + +Flagger was designed to give developers confidence in automating production releases with progressive delivery techniques. + +::: tip Canary release + +A benefit of using canary releases is the ability to do capacity testing of the new version in a production environment +with a safe rollback strategy if issues are found. By slowly ramping up the load, you can monitor and capture metrics +about how the new version impacts the production environment. + +[Martin Fowler](https://martinfowler.com/bliki/CanaryRelease.html) +::: + +Flagger can run automated application analysis, testing, promotion and rollback for the following deployment strategies: +* **Canary** (progressive traffic shifting) + * [Istio](https://docs.flagger.app/usage/progressive-delivery), + [Linkerd](https://docs.flagger.app/usage/linkerd-progressive-delivery), + [App Mesh](https://docs.flagger.app/usage/appmesh-progressive-delivery), + [NGINX](https://docs.flagger.app/usage/nginx-progressive-delivery), + [Gloo](https://docs.flagger.app/usage/gloo-progressive-delivery) +* **A/B Testing** (HTTP headers and cookies traffic routing) + * [Istio](https://docs.flagger.app/usage/ab-testing), + [NGINX](https://docs.flagger.app/usage/nginx-progressive-delivery#a-b-testing) +* **Blue/Green** (traffic switching) + * [Kubernetes CNI](https://docs.flagger.app/usage/blue-green) + +Flagger can be configures to send notifications to a +[Slack](https://docs.flagger.app/usage/alerting#slack) or +[Microsoft Teams](https://docs.flagger.app/usage/alerting#microsoft-teams) channel. +It will post messages when a deployment has been initialised, +when a new revision has been detected and if the canary analysis failed or succeeded. + +## GitOps + +![GtiOps with Flagger and FluxCD](/flagger-gitops.png) + +You can build fully automated GitOps pipelines for canary deployments with Flagger and +[FluxCD](https://github.com/fluxcd) (CNCF sandbox project). + +::: tip GitOps + +GitOps is a way to do Kubernetes cluster management and application delivery. +It works by using Git as a single source of truth for declarative infrastructure and applications. +With Git at the center of your delivery pipelines, developers can make pull requests +to accelerate and simplify application deployments and operations tasks to Kubernetes. + +[Weaveworks](https://www.weave.works/technologies/gitops/) +::: + +GitOps tutorials: +* [Progressive Delivery for Istio with Flagger and Flux](https://github.com/stefanprodan/gitops-istio) +* [Progressive Delivery for Linkerd with Flagger and Flux](https://helm.workshop.flagger.dev) + +## Getting Help + + +If you have any questions about Flagger and progressive delivery: + +* Read the Flagger [docs](https://docs.flagger.app). +* Invite yourself to the [Weave community slack](https://slack.weave.works/) + and join the [#flagger](https://weave-community.slack.com/messages/flagger/) channel. +* Join the [Weave User Group](https://www.meetup.com/pro/Weave/) and get invited to online talks, + hands-on training and meetups in your area. +* File an [issue](https://github.com/weaveworks/flagger/issues/new). + +Your feedback is always welcome! diff --git a/docs/diagrams/flagger-abtest-steps.png b/docs/diagrams/flagger-abtest-steps.png deleted file mode 100644 index db3ed35d..00000000 Binary files a/docs/diagrams/flagger-abtest-steps.png and /dev/null differ diff --git a/docs/diagrams/flagger-bluegreen-steps.png b/docs/diagrams/flagger-bluegreen-steps.png deleted file mode 100644 index d1d2c938..00000000 Binary files a/docs/diagrams/flagger-bluegreen-steps.png and /dev/null differ diff --git a/docs/diagrams/flagger-canary-hpa.png b/docs/diagrams/flagger-canary-hpa.png deleted file mode 100644 index 2e448763..00000000 Binary files a/docs/diagrams/flagger-canary-hpa.png and /dev/null differ diff --git a/docs/diagrams/flagger-canary-overview.png b/docs/diagrams/flagger-canary-overview.png deleted file mode 100644 index c703c1c8..00000000 Binary files a/docs/diagrams/flagger-canary-overview.png and /dev/null differ diff --git a/docs/diagrams/flagger-canary-steps.png b/docs/diagrams/flagger-canary-steps.png deleted file mode 100644 index b27f2c10..00000000 Binary files a/docs/diagrams/flagger-canary-steps.png and /dev/null differ diff --git a/docs/diagrams/flagger-flux-gitops.png b/docs/diagrams/flagger-flux-gitops.png deleted file mode 100644 index 1c9a8d0d..00000000 Binary files a/docs/diagrams/flagger-flux-gitops.png and /dev/null differ diff --git a/docs/diagrams/flagger-gitops-aws.png b/docs/diagrams/flagger-gitops-aws.png deleted file mode 100644 index f9b275c2..00000000 Binary files a/docs/diagrams/flagger-gitops-aws.png and /dev/null differ diff --git a/docs/diagrams/flagger-gitops-istio.png b/docs/diagrams/flagger-gitops-istio.png deleted file mode 100644 index 1974198e..00000000 Binary files a/docs/diagrams/flagger-gitops-istio.png and /dev/null differ diff --git a/docs/diagrams/flagger-gke-istio.png b/docs/diagrams/flagger-gke-istio.png deleted file mode 100644 index c15d772e..00000000 Binary files a/docs/diagrams/flagger-gke-istio.png and /dev/null differ diff --git a/docs/diagrams/flagger-gloo-overview.png b/docs/diagrams/flagger-gloo-overview.png deleted file mode 100644 index 393428e9..00000000 Binary files a/docs/diagrams/flagger-gloo-overview.png and /dev/null differ diff --git a/docs/diagrams/flagger-linkerd-traffic-split.png b/docs/diagrams/flagger-linkerd-traffic-split.png deleted file mode 100644 index 8ab024f5..00000000 Binary files a/docs/diagrams/flagger-linkerd-traffic-split.png and /dev/null differ diff --git a/docs/diagrams/flagger-load-testing.png b/docs/diagrams/flagger-load-testing.png deleted file mode 100644 index 6a809688..00000000 Binary files a/docs/diagrams/flagger-load-testing.png and /dev/null differ diff --git a/docs/diagrams/flagger-nginx-linkerd.png b/docs/diagrams/flagger-nginx-linkerd.png deleted file mode 100644 index 55e7bfa8..00000000 Binary files a/docs/diagrams/flagger-nginx-linkerd.png and /dev/null differ diff --git a/docs/diagrams/flagger-nginx-overview.png b/docs/diagrams/flagger-nginx-overview.png deleted file mode 100644 index f8dcaadc..00000000 Binary files a/docs/diagrams/flagger-nginx-overview.png and /dev/null differ diff --git a/docs/diagrams/flagger-overview.png b/docs/diagrams/flagger-overview.png deleted file mode 100644 index c703c1c8..00000000 Binary files a/docs/diagrams/flagger-overview.png and /dev/null differ diff --git a/docs/diagrams/istio-cert-manager-gke.png b/docs/diagrams/istio-cert-manager-gke.png deleted file mode 100644 index 46470f75..00000000 Binary files a/docs/diagrams/istio-cert-manager-gke.png and /dev/null differ diff --git a/docs/gitbook/README.md b/docs/gitbook/README.md deleted file mode 100644 index 946105a7..00000000 --- a/docs/gitbook/README.md +++ /dev/null @@ -1,22 +0,0 @@ ---- -description: Flagger is a progressive delivery Kubernetes operator ---- - -# Introduction - -[Flagger](https://github.com/weaveworks/flagger) is a **Kubernetes** operator that automates the promotion of canary -deployments using **Istio**, **Linkerd**, **App Mesh**, **NGINX** or **Gloo** routing for traffic shifting and **Prometheus** metrics for canary analysis. -The canary analysis can be extended with webhooks for running system integration/acceptance tests, load tests, or any other custom validation. - -Flagger implements a control loop that gradually shifts traffic to the canary while measuring key performance -indicators like HTTP requests success rate, requests average duration and pods health. -Based on analysis of the **KPIs** a canary is promoted or aborted, and the analysis result is published to **Slack** or **MS Teams**. - -![Flagger overview diagram](https://raw.githubusercontent.com/weaveworks/flagger/master/docs/diagrams/flagger-canary-overview.png) - -Flagger can be configured with Kubernetes custom resources and is compatible with -any CI/CD solutions made for Kubernetes. Since Flagger is declarative and reacts to Kubernetes events, -it can be used in **GitOps** pipelines together with Weave Flux or JenkinsX. - -This project is sponsored by [Weaveworks](https://www.weave.works/) - diff --git a/docs/gitbook/SUMMARY.md b/docs/gitbook/SUMMARY.md deleted file mode 100644 index b842f7a3..00000000 --- a/docs/gitbook/SUMMARY.md +++ /dev/null @@ -1,30 +0,0 @@ -# Table of contents - -* [Introduction](README.md) -* [How it works](how-it-works.md) -* [FAQ](faq.md) - -## Install - -* [Flagger Install on Kubernetes](install/flagger-install-on-kubernetes.md) -* [Flagger Install on GKE Istio](install/flagger-install-on-google-cloud.md) -* [Flagger Install on EKS App Mesh](install/flagger-install-on-eks-appmesh.md) -* [Flagger Install with SuperGloo](install/flagger-install-with-supergloo.md) - -## Usage - -* [Istio Canary Deployments](usage/progressive-delivery.md) -* [Istio A/B Testing](usage/ab-testing.md) -* [Linkerd Canary Deployments](usage/linkerd-progressive-delivery.md) -* [App Mesh Canary Deployments](usage/appmesh-progressive-delivery.md) -* [NGINX Canary Deployments](usage/nginx-progressive-delivery.md) -* [Gloo Canary Deployments](usage/gloo-progressive-delivery.md) -* [Blue/Green Deployments](usage/blue-green.md) -* [Monitoring](usage/monitoring.md) -* [Alerting](usage/alerting.md) - -## Tutorials - -* [SMI Istio Canary Deployments](tutorials/flagger-smi-istio.md) -* [Canaries with Helm charts and GitOps](tutorials/canary-helm-gitops.md) -* [Zero downtime deployments](tutorials/zero-downtime-deployments.md) diff --git a/docs/gitbook/faq.md b/docs/gitbook/faq.md deleted file mode 100644 index 1ae56485..00000000 --- a/docs/gitbook/faq.md +++ /dev/null @@ -1,397 +0,0 @@ -# Frequently asked questions - -### Deployment Strategies - -**Which deployment strategies are supported by Flagger?** - -Flagger can run automated application analysis, promotion and rollback for the following deployment strategies: -* Canary (progressive traffic shifting) - * Istio, Linkerd, App Mesh, NGINX, Gloo -* A/B Testing (HTTP headers and cookies traffic routing) - * Istio, NGINX -* Blue/Green (traffic switch) - * Kubernetes CNI - -For Canary deployments and A/B testing you'll need a Layer 7 traffic management solution like a service mesh or an ingress controller. -For Blue/Green deployments no service mesh or ingress controller is required. - -**When should I use A/B testing instead of progressive traffic shifting?** - -For frontend applications that require session affinity you should use HTTP headers or cookies match conditions -to ensure a set of users will stay on the same version for the whole duration of the canary analysis. -A/B testing is supported by Istio and NGINX only. - -Istio example: - -```yaml - canaryAnalysis: - # schedule interval (default 60s) - interval: 1m - # total number of iterations - iterations: 10 - # max number of failed iterations before rollback - threshold: 2 - # canary match condition - match: - - headers: - x-canary: - regex: ".*insider.*" - - headers: - cookie: - regex: "^(.*?;)?(canary=always)(;.*)?$" -``` - -NGINX example: - -```yaml - canaryAnalysis: - interval: 1m - threshold: 10 - iterations: 2 - match: - - headers: - x-canary: - exact: "insider" - - headers: - cookie: - exact: "canary" -``` - -Note that the NGINX ingress controller supports only exact matching for a single header and the cookie value is set to `always`. - -The above configurations will route users with the x-canary header or canary cookie to the canary instance during analysis: - -```bash -curl -H 'X-Canary: insider' http://app.example.com -curl -b 'canary=always' http://app.example.com -``` - -**Can I use Flagger to manage applications that live outside of a service mesh?** - -For applications that are not deployed on a service mesh, Flagger can orchestrate Blue/Green style deployments -with Kubernetes L4 networking. - -Blue/Green example: - -```yaml -apiVersion: flagger.app/v1alpha3 -kind: Canary -spec: - provider: kubernetes - canaryAnalysis: - interval: 30s - threshold: 2 - iterations: 10 - metrics: - - name: request-success-rate - threshold: 99 - interval: 1m - - name: request-duration - threshold: 500 - interval: 30s - webhooks: - - name: load-test - url: http://flagger-loadtester.test/ - timeout: 5s - metadata: - type: cmd - cmd: "hey -z 1m -q 10 -c 2 http://podinfo-canary.test:9898/" -``` - -The above configuration will run an analysis for five minutes. -Flagger starts the load test for the canary service (green version) and checks the Prometheus metrics every 30 seconds. -If the analysis result is positive, Flagger will promote the canary (green version) to primary (blue version). - -### Kubernetes services - -**How is an application exposed inside the cluster?** - -Assuming the app name is podinfo you can define a canary like: - -```yaml -apiVersion: flagger.app/v1alpha3 -kind: Canary -metadata: - name: podinfo - namespace: test -spec: - targetRef: - apiVersion: apps/v1 - kind: Deployment - name: podinfo - service: - # container port (required) - port: 9898 - # port name can be http or grpc (default http) - portName: http -``` - -Based on the canary spec service, Flagger generates the following Kubernetes ClusterIP service: - -* `..svc.cluster.local` - selector `app=-primary` -* `-primary..svc.cluster.local` - selector `app=-primary` -* `-canary..svc.cluster.local` - selector `app=` - -This ensures that traffic coming from a namespace outside the mesh to `podinfo.test:9898` -will be routed to the latest stable release of your app. - - -```yaml -apiVersion: v1 -kind: Service -metadata: - name: podinfo -spec: - type: ClusterIP - selector: - app: podinfo-primary - ports: - - name: http - port: 9898 - protocol: TCP - targetPort: http ---- -apiVersion: v1 -kind: Service -metadata: - name: podinfo-primary -spec: - type: ClusterIP - selector: - app: podinfo-primary - ports: - - name: http - port: 9898 - protocol: TCP - targetPort: http ---- -apiVersion: v1 -kind: Service -metadata: - name: podinfo-canary -spec: - type: ClusterIP - selector: - app: podinfo - ports: - - name: http - port: 9898 - protocol: TCP - targetPort: http -``` - -The `podinfo-canary.test:9898` address is available only during the -canary analysis and can be used for conformance testing or load testing. - -### Multiple ports - -**My application listens on multiple ports, how can I expose them inside the cluster?** - -If port discovery is enabled, Flagger scans the deployment spec and extracts the containers -ports excluding the port specified in the canary service and Envoy sidecar ports. -`These ports will be used when generating the ClusterIP services. - -For a deployment that exposes two ports: - -```yaml -apiVersion: apps/v1 -kind: Deployment -spec: - template: - metadata: - annotations: - prometheus.io/scrape: "true" - prometheus.io/port: "9899" - spec: - containers: - - name: app - ports: - - containerPort: 8080 - - containerPort: 9090 -``` - -You can enable port discovery so that Prometheus will be able to reach port `9090` over mTLS: - -```yaml -apiVersion: flagger.app/v1alpha3 -kind: Canary -spec: - service: - # container port used for canary analysis - port: 8080 - # port name can be http or grpc (default http) - portName: http - # add all the other container ports - # to the ClusterIP services (default false) - portDiscovery: true - trafficPolicy: - tls: - mode: ISTIO_MUTUAL -``` - -Both port `8080` and `9090` will be added to the ClusterIP services. - -### Label selectors - -**What labels selectors are supported by Flagger?** - -The target deployment must have a single label selector in the format `app: `: - -```yaml -apiVersion: apps/v1 -kind: Deployment -metadata: - name: podinfo -spec: - selector: - matchLabels: - app: podinfo - template: - metadata: - labels: - app: podinfo -``` - -Besides `app` Flagger supports `name` and `app.kubernetes.io/name` selectors. If you use a different -convention you can specify your label with the `-selector-labels` flag. - -**Is pod affinity and anti affinity supported?** - -For pod affinity to work you need to use a different label than the `app`, `name` or `app.kubernetes.io/name`. - -Anti affinity example: - -```yaml -apiVersion: apps/v1 -kind: Deployment -metadata: - name: podinfo -spec: - selector: - matchLabels: - app: podinfo - affinity: podinfo - template: - metadata: - labels: - app: podinfo - affinity: podinfo - spec: - affinity: - podAntiAffinity: - preferredDuringSchedulingIgnoredDuringExecution: - - weight: 100 - podAffinityTerm: - labelSelector: - matchLabels: - affinity: podinfo - topologyKey: kubernetes.io/hostname -``` - -### Istio Ingress Gateway - -**How can I expose multiple canaries on the same external domain?** - -Assuming you have two apps, one that servers the main website and one that serves the REST API. -For each app you can define a canary object as: - -```yaml -apiVersion: flagger.app/v1alpha3 -kind: Canary -metadata: - name: website -spec: - service: - port: 8080 - gateways: - - public-gateway.istio-system.svc.cluster.local - hosts: - - my-site.com - match: - - uri: - prefix: / - rewrite: - uri: / ---- -apiVersion: flagger.app/v1alpha3 -kind: Canary -metadata: - name: webapi -spec: - service: - port: 8080 - gateways: - - public-gateway.istio-system.svc.cluster.local - hosts: - - my-site.com - match: - - uri: - prefix: /api - rewrite: - uri: / -``` - -Based on the above configuration, Flagger will create two virtual services bounded to the same ingress gateway and external host. -Istio Pilot will [merge](https://istio.io/help/ops/traffic-management/deploy-guidelines/#multiple-virtual-services-and-destination-rules-for-the-same-host) -the two services and the website rule will be moved to the end of the list in the merged configuration. - -Note that host merging only works if the canaries are bounded to a ingress gateway other than the `mesh` gateway. - -### Istio Mutual TLS - -**How can I enable mTLS for a canary?** - -When deploying Istio with global mTLS enabled, you have to set the TLS mode to `ISTIO_MUTUAL`: - -```yaml -apiVersion: flagger.app/v1alpha3 -kind: Canary -spec: - service: - trafficPolicy: - tls: - mode: ISTIO_MUTUAL -``` - -If you run Istio in permissive mode you can disable TLS: - -```yaml -apiVersion: flagger.app/v1alpha3 -kind: Canary -spec: - service: - trafficPolicy: - tls: - mode: DISABLE -``` - -**If Flagger is outside of the mesh, how can it start the load test?** - -In order for Flagger to be able to call the load tester service from outside the mesh, you need to disable mTLS on port 80: - -```yaml -apiVersion: networking.istio.io/v1alpha3 -kind: DestinationRule -metadata: - name: flagger-loadtester - namespace: test -spec: - host: "flagger-loadtester.test.svc.cluster.local" - trafficPolicy: - tls: - mode: DISABLE ---- -apiVersion: authentication.istio.io/v1alpha1 -kind: Policy -metadata: - name: flagger-loadtester - namespace: test -spec: - targets: - - name: flagger-loadtester - ports: - - number: 80 -``` diff --git a/docs/gitbook/how-it-works.md b/docs/gitbook/how-it-works.md deleted file mode 100644 index 34a0806d..00000000 --- a/docs/gitbook/how-it-works.md +++ /dev/null @@ -1,950 +0,0 @@ -# How it works - -[Flagger](https://github.com/weaveworks/flagger) takes a Kubernetes deployment and optionally -a horizontal pod autoscaler \(HPA\) and creates a series of objects -\(Kubernetes deployments, ClusterIP services, virtual service, traffic split or ingress\) to drive the canary analysis and promotion. - -![Flagger Canary Process](https://raw.githubusercontent.com/weaveworks/flagger/master/docs/diagrams/flagger-canary-hpa.png) - -### Canary Custom Resource - -For a deployment named _podinfo_, a canary promotion can be defined using Flagger's custom resource: - -```yaml -apiVersion: flagger.app/v1alpha3 -kind: Canary -metadata: - name: podinfo - namespace: test -spec: - # service mesh provider (optional) - # can be: kubernetes, istio, linkerd, appmesh, nginx, gloo, supergloo - # use the kubernetes provider for Blue/Green style deployments - provider: istio - # deployment reference - targetRef: - apiVersion: apps/v1 - kind: Deployment - name: podinfo - # the maximum time in seconds for the canary deployment - # to make progress before it is rollback (default 600s) - progressDeadlineSeconds: 60 - # HPA reference (optional) - autoscalerRef: - apiVersion: autoscaling/v2beta1 - kind: HorizontalPodAutoscaler - name: podinfo - service: - # container port - port: 9898 - # service port name (optional, will default to "http") - portName: http-podinfo - # Istio gateways (optional) - gateways: - - public-gateway.istio-system.svc.cluster.local - # Istio virtual service host names (optional) - hosts: - - podinfo.example.com - # promote the canary without analysing it (default false) - skipAnalysis: false - # define the canary analysis timing and KPIs - canaryAnalysis: - # schedule interval (default 60s) - interval: 1m - # max number of failed metric checks before rollback - threshold: 10 - # max traffic percentage routed to canary - # percentage (0-100) - maxWeight: 50 - # canary increment step - # percentage (0-100) - stepWeight: 5 - # Prometheus checks - metrics: - - name: request-success-rate - # minimum req success rate (non 5xx responses) - # percentage (0-100) - threshold: 99 - interval: 1m - - name: request-duration - # maximum req duration P99 - # milliseconds - threshold: 500 - interval: 30s - # external checks (optional) - webhooks: - - name: integration-tests - url: http://podinfo.test:9898/echo - timeout: 1m - # key-value pairs (optional) - metadata: - test: "all" - token: "16688eb5e9f289f1991c" -``` - -**Note** that the target deployment must have a single label selector in the format `app: `: - -```yaml -apiVersion: apps/v1 -kind: Deployment -metadata: - name: podinfo -spec: - selector: - matchLabels: - app: podinfo - template: - metadata: - labels: - app: podinfo -``` - -Besides `app` Flagger supports `name` and `app.kubernetes.io/name` selectors. If you use a different -convention you can specify your label with the `-selector-labels` flag. - -The target deployment should expose a TCP port that will be used by Flagger to create the ClusterIP Service and -the Istio Virtual Service. The container port from the target deployment should match the `service.port` value. - -### Canary status - -Get the current status of canary deployments cluster wide: - -```bash -kubectl get canaries --all-namespaces - -NAMESPACE NAME STATUS WEIGHT LASTTRANSITIONTIME -test podinfo Progressing 15 2019-06-30T14:05:07Z -prod frontend Succeeded 0 2019-06-30T16:15:07Z -prod backend Failed 0 2019-06-30T17:05:07Z -``` - -The status condition reflects the last know state of the canary analysis: - -```bash -kubectl -n test get canary/podinfo -oyaml | awk '/status/,0' -``` - -A successful rollout status: - -```yaml -status: - canaryWeight: 0 - failedChecks: 0 - iterations: 0 - lastAppliedSpec: "14788816656920327485" - lastPromotedSpec: "14788816656920327485" - conditions: - - lastTransitionTime: "2019-07-10T08:23:18Z" - lastUpdateTime: "2019-07-10T08:23:18Z" - message: Canary analysis completed successfully, promotion finished. - reason: Succeeded - status: "True" - type: Promoted -``` - -The `Promoted` status condition can have one of the following reasons: -Initialized, Waiting, Progressing, Finalising, Succeeded or Failed. -A failed canary will have the promoted status set to `false`, -the reason to `failed` and the last applied spec will be different to the last promoted one. - -Wait for a successful rollout: - -```bash -kubectl wait canary/podinfo --for=condition=promoted -``` - -### Istio routing - -Flagger creates an Istio Virtual Service and Destination Rules based on the Canary service spec. -The service configuration lets you expose an app inside or outside the mesh. -You can also define traffic policies, HTTP match conditions, URI rewrite rules, CORS policies, timeout and retries. - -The following spec exposes the `frontend` workload inside the mesh on `frontend.test.svc.cluster.local:9898` -and outside the mesh on `frontend.example.com`. You'll have to specify an Istio ingress gateway for external hosts. - -```yaml -apiVersion: flagger.app/v1alpha3 -kind: Canary -metadata: - name: frontend - namespace: test -spec: - service: - # container port - port: 9898 - # service port name (optional, will default to "http") - portName: http-frontend - # Istio gateways (optional) - gateways: - - public-gateway.istio-system.svc.cluster.local - - mesh - # Istio virtual service host names (optional) - hosts: - - frontend.example.com - # Istio traffic policy (optional) - trafficPolicy: - loadBalancer: - simple: LEAST_CONN - # HTTP match conditions (optional) - match: - - uri: - prefix: / - # HTTP rewrite (optional) - rewrite: - uri: / - # Envoy timeout and retry policy (optional) - headers: - request: - add: - x-envoy-upstream-rq-timeout-ms: "15000" - x-envoy-max-retries: "10" - x-envoy-retry-on: "gateway-error,connect-failure,refused-stream" - # cross-origin resource sharing policy (optional) - corsPolicy: - allowOrigin: - - example.com - allowMethods: - - GET - allowCredentials: false - allowHeaders: - - x-some-header - maxAge: 24h -``` - -For the above spec Flagger will generate the following virtual service: - -```yaml -apiVersion: networking.istio.io/v1alpha3 -kind: VirtualService -metadata: - name: frontend - namespace: test - ownerReferences: - - apiVersion: flagger.app/v1alpha3 - blockOwnerDeletion: true - controller: true - kind: Canary - name: podinfo - uid: 3a4a40dd-3875-11e9-8e1d-42010a9c0fd1 -spec: - gateways: - - public-gateway.istio-system.svc.cluster.local - - mesh - hosts: - - frontend.example.com - - frontend - http: - - appendHeaders: - x-envoy-max-retries: "10" - x-envoy-retry-on: gateway-error,connect-failure,refused-stream - x-envoy-upstream-rq-timeout-ms: "15000" - corsPolicy: - allowHeaders: - - x-some-header - allowMethods: - - GET - allowOrigin: - - example.com - maxAge: 24h - match: - - uri: - prefix: / - rewrite: - uri: / - route: - - destination: - host: podinfo-primary - weight: 100 - - destination: - host: podinfo-canary - weight: 0 -``` - -For each destination in the virtual service a rule is generated: - -```yaml -apiVersion: networking.istio.io/v1alpha3 -kind: DestinationRule -metadata: - name: frontend-primary - namespace: test -spec: - host: frontend-primary - trafficPolicy: - loadBalancer: - simple: LEAST_CONN ---- -apiVersion: networking.istio.io/v1alpha3 -kind: DestinationRule -metadata: - name: frontend-canary - namespace: test -spec: - host: frontend-canary - trafficPolicy: - loadBalancer: - simple: LEAST_CONN -``` - -Flagger keeps in sync the virtual service and destination rules with the canary service spec. -Any direct modification to the virtual service spec will be overwritten. - -To expose a workload inside the mesh on `http://backend.test.svc.cluster.local:9898`, -the service spec can contain only the container port: - -```yaml -apiVersion: flagger.app/v1alpha3 -kind: Canary -metadata: - name: backend - namespace: test -spec: - service: - port: 9898 -``` - -Based on the above spec, Flagger will create several ClusterIP services like: - -```yaml -apiVersion: v1 -kind: Service -metadata: - name: backend-primary - ownerReferences: - - apiVersion: flagger.app/v1alpha3 - blockOwnerDeletion: true - controller: true - kind: Canary - name: backend - uid: 2ca1a9c7-2ef6-11e9-bd01-42010a9c0145 -spec: - type: ClusterIP - ports: - - name: http - port: 9898 - protocol: TCP - targetPort: 9898 - selector: - app: backend-primary -``` - -Flagger works for user facing apps exposed outside the cluster via an ingress gateway -and for backend HTTP APIs that are accessible only from inside the mesh. - -### Canary Stages - -![Flagger Canary Stages](https://raw.githubusercontent.com/weaveworks/flagger/master/docs/diagrams/flagger-canary-steps.png) - -A canary deployment is triggered by changes in any of the following objects: - -* Deployment PodSpec (container image, command, ports, env, resources, etc) -* ConfigMaps mounted as volumes or mapped to environment variables -* Secrets mounted as volumes or mapped to environment variables - -Gated canary promotion stages: - -* scan for canary deployments -* check Istio virtual service routes are mapped to primary and canary ClusterIP services -* check primary and canary deployments status - * halt advancement if a rolling update is underway - * halt advancement if pods are unhealthy -* call pre-rollout webhooks are check results - * halt advancement if any hook returned a non HTTP 2xx result - * increment the failed checks counter -* increase canary traffic weight percentage from 0% to 5% (step weight) -* call rollout webhooks and check results -* check canary HTTP request success rate and latency - * halt advancement if any metric is under the specified threshold - * increment the failed checks counter -* check if the number of failed checks reached the threshold - * route all traffic to primary - * scale to zero the canary deployment and mark it as failed - * call post-rollout webhooks - * post the analysis result to Slack - * wait for the canary deployment to be updated and start over -* increase canary traffic weight by 5% (step weight) till it reaches 50% (max weight) - * halt advancement if any webhook call fails - * halt advancement while canary request success rate is under the threshold - * halt advancement while canary request duration P99 is over the threshold - * halt advancement if the primary or canary deployment becomes unhealthy - * halt advancement while canary deployment is being scaled up/down by HPA -* promote canary to primary - * copy ConfigMaps and Secrets from canary to primary - * copy canary deployment spec template over primary -* wait for primary rolling update to finish - * halt advancement if pods are unhealthy -* route all traffic to primary -* scale to zero the canary deployment -* mark rollout as finished -* call post-rollout webhooks -* post the analysis result to Slack -* wait for the canary deployment to be updated and start over - -### Canary Analysis - -The canary analysis runs periodically until it reaches the maximum traffic weight or the failed checks threshold. - -Spec: - -```yaml - canaryAnalysis: - # schedule interval (default 60s) - interval: 1m - # max number of failed metric checks before rollback - threshold: 10 - # max traffic percentage routed to canary - # percentage (0-100) - maxWeight: 50 - # canary increment step - # percentage (0-100) - stepWeight: 2 - # deploy straight to production without - # the metrics and webhook checks - skipAnalysis: false -``` - -The above analysis, if it succeeds, will run for 25 minutes while validating the HTTP metrics and webhooks every minute. -You can determine the minimum time that it takes to validate and promote a canary deployment using this formula: - -``` -interval * (maxWeight / stepWeight) -``` - -And the time it takes for a canary to be rollback when the metrics or webhook checks are failing: - -``` -interval * threshold -``` - -In emergency cases, you may want to skip the analysis phase and ship changes directly to production. -At any time you can set the `spec.skipAnalysis: true`. -When skip analysis is enabled, Flagger checks if the canary deployment is healthy and -promotes it without analysing it. If an analysis is underway, Flagger cancels it and runs the promotion. - -### A/B Testing - -Besides weighted routing, Flagger can be configured to route traffic to the canary based on HTTP match conditions. -In an A/B testing scenario, you'll be using HTTP headers or cookies to target a certain segment of your users. -This is particularly useful for frontend applications that require session affinity. - -You can enable A/B testing by specifying the HTTP match conditions and the number of iterations: - -```yaml - canaryAnalysis: - # schedule interval (default 60s) - interval: 1m - # total number of iterations - iterations: 10 - # max number of failed iterations before rollback - threshold: 2 - # canary match condition - match: - - headers: - user-agent: - regex: "^(?!.*Chrome).*Safari.*" - - headers: - cookie: - regex: "^(.*?;)?(user=test)(;.*)?$" -``` - -If Flagger finds a HTTP match condition, it will ignore the `maxWeight` and `stepWeight` settings. - -The above configuration will run an analysis for ten minutes targeting the Safari users and those that have a test cookie. -You can determine the minimum time that it takes to validate and promote a canary deployment using this formula: - -``` -interval * iterations -``` - -And the time it takes for a canary to be rollback when the metrics or webhook checks are failing: - -``` -interval * threshold -``` - -Make sure that the analysis threshold is lower than the number of iterations. - -### HTTP Metrics - -The canary analysis is using the following Prometheus queries: - -**HTTP requests success rate percentage** - -Spec: - -```yaml - canaryAnalysis: - metrics: - - name: request-success-rate - # minimum req success rate (non 5xx responses) - # percentage (0-100) - threshold: 99 - interval: 1m -``` - -Istio query: - -```javascript -sum( - rate( - istio_requests_total{ - reporter="destination", - destination_workload_namespace=~"$namespace", - destination_workload=~"$workload", - response_code!~"5.*" - }[$interval] - ) -) -/ -sum( - rate( - istio_requests_total{ - reporter="destination", - destination_workload_namespace=~"$namespace", - destination_workload=~"$workload" - }[$interval] - ) -) -``` - -App Mesh query: - -```javascript -sum( - rate( - envoy_cluster_upstream_rq{ - kubernetes_namespace="$namespace", - kubernetes_pod_name=~"$workload", - response_code!~"5.*" - }[$interval] - ) -) -/ -sum( - rate( - envoy_cluster_upstream_rq{ - kubernetes_namespace="$namespace", - kubernetes_pod_name=~"$workload" - }[$interval] - ) -) -``` - -**HTTP requests milliseconds duration P99** - -Spec: - -```yaml - canaryAnalysis: - metrics: - - name: request-duration - # maximum req duration P99 - # milliseconds - threshold: 500 - interval: 1m -``` - -Istio query: - -```javascript -histogram_quantile(0.99, - sum( - irate( - istio_request_duration_seconds_bucket{ - reporter="destination", - destination_workload=~"$workload", - destination_workload_namespace=~"$namespace" - }[$interval] - ) - ) by (le) -) -``` - -App Mesh query: - -```javascript -histogram_quantile(0.99, - sum( - irate( - envoy_cluster_upstream_rq_time_bucket{ - kubernetes_pod_name=~"$workload", - kubernetes_namespace=~"$namespace" - }[$interval] - ) - ) by (le) -) -``` - -> **Note** that the metric interval should be lower or equal to the control loop interval. - -### Custom Metrics - -The canary analysis can be extended with custom Prometheus queries. - -```yaml - canaryAnalysis: - threshold: 1 - maxWeight: 50 - stepWeight: 5 - metrics: - - name: "404s percentage" - threshold: 5 - query: | - 100 - sum( - rate( - istio_requests_total{ - reporter="destination", - destination_workload_namespace="test", - destination_workload="podinfo", - response_code!="404" - }[1m] - ) - ) - / - sum( - rate( - istio_requests_total{ - reporter="destination", - destination_workload_namespace="test", - destination_workload="podinfo" - }[1m] - ) - ) * 100 -``` - -The above configuration validates the canary by checking -if the HTTP 404 req/sec percentage is below 5 percent of the total traffic. -If the 404s rate reaches the 5% threshold, then the canary fails. - -```yaml - canaryAnalysis: - threshold: 1 - maxWeight: 50 - stepWeight: 5 - metrics: - - name: "rpc error rate" - threshold: 5 - query: | - 100 - (sum - rate( - grpc_server_handled_total{ - grpc_service="my.TestService", - grpc_code!="OK" - }[1m] - ) - ) - / - sum( - rate( - grpc_server_started_total{ - grpc_service="my.TestService" - }[1m] - ) - ) * 100 -``` - -The above configuration validates the canary by checking if the percentage of -non-OK GRPC req/sec is below 5 percent of the total requests. If the non-OK -rate reaches the 5% threshold, then the canary fails. - -When specifying a query, Flagger will run the promql query and convert the result to float64. -Then it compares the query result value with the metric threshold value. - -### Webhooks - -The canary analysis can be extended with webhooks. Flagger will call each webhook URL and -determine from the response status code (HTTP 2xx) if the canary is failing or not. - -There are three types of hooks: -* Confirm-rollout hooks are executed before scaling up the canary deployment and ca be used for manual approval. -The rollout is paused until the hook returns a successful HTTP status code. -* Pre-rollout hooks are executed before routing traffic to canary. -The canary advancement is paused if a pre-rollout hook fails and if the number of failures reach the -threshold the canary will be rollback. -* Rollout hooks are executed during the analysis on each iteration before the metric checks. -If a rollout hook call fails the canary advancement is paused and eventfully rolled back. -* Post-rollout hooks are executed after the canary has been promoted or rolled back. -If a post rollout hook fails the error is logged. - -Spec: - -```yaml - canaryAnalysis: - webhooks: - - name: "start gate" - type: confirm-rollout - url: http://flagger-loadtester.test/gate/approve - - name: "smoke test" - type: pre-rollout - url: http://flagger-helmtester.kube-system/ - timeout: 3m - metadata: - type: "helm" - cmd: "test podinfo --cleanup" - - name: "load test" - type: rollout - url: http://flagger-loadtester.test/ - timeout: 15s - metadata: - cmd: "hey -z 1m -q 5 -c 2 http://podinfo-canary.test:9898/" - - name: "notify" - type: post-rollout - url: http://telegram.bot:8080/ - timeout: 5s - metadata: - some: "message" -``` - -> **Note** that the sum of all rollout webhooks timeouts should be lower than the analysis interval. - -Webhook payload (HTTP POST): - -```json -{ - "name": "podinfo", - "namespace": "test", - "phase": "Progressing", - "metadata": { - "test": "all", - "token": "16688eb5e9f289f1991c" - } -} -``` - -Response status codes: - -* 200-202 - advance canary by increasing the traffic weight -* timeout or non-2xx - halt advancement and increment failed checks - -On a non-2xx response Flagger will include the response body (if any) in the failed checks log and Kubernetes events. - -### Load Testing - -For workloads that are not receiving constant traffic Flagger can be configured with a webhook, -that when called, will start a load test for the target workload. -If the target workload doesn't receive any traffic during the canary analysis, -Flagger metric checks will fail with "no values found for metric request-success-rate". - -Flagger comes with a load testing service based on [rakyll/hey](https://github.com/rakyll/hey) -that generates traffic during analysis when configured as a webhook. - -![Flagger Load Testing Webhook](https://raw.githubusercontent.com/weaveworks/flagger/master/docs/diagrams/flagger-load-testing.png) - -First you need to deploy the load test runner in a namespace with sidecar injection enabled: - -```bash -export REPO=https://raw.githubusercontent.com/weaveworks/flagger/master - -kubectl -n test apply -f ${REPO}/artifacts/loadtester/deployment.yaml -kubectl -n test apply -f ${REPO}/artifacts/loadtester/service.yaml -``` - -Or by using Helm: - -```bash -helm repo add flagger https://flagger.app - -helm upgrade -i flagger-loadtester flagger/loadtester \ ---namespace=test \ ---set cmd.timeout=1h -``` - -When deployed the load tester API will be available at `http://flagger-loadtester.test/`. - -Now you can add webhooks to the canary analysis spec: - -```yaml -webhooks: - - name: load-test-get - url: http://flagger-loadtester.test/ - timeout: 5s - metadata: - type: cmd - cmd: "hey -z 1m -q 10 -c 2 http://podinfo-canary.test:9898/" - - name: load-test-post - url: http://flagger-loadtester.test/ - timeout: 5s - metadata: - type: cmd - cmd: "hey -z 1m -q 10 -c 2 -m POST -d '{test: 2}' http://podinfo-canary.test:9898/echo" -``` - -When the canary analysis starts, Flagger will call the webhooks and the load tester will run the `hey` commands -in the background, if they are not already running. This will ensure that during the -analysis, the `podinfo-canary.test` service will receive a steady stream of GET and POST requests. - -If your workload is exposed outside the mesh you can point `hey` to the -public URL and use HTTP2. - -```yaml -webhooks: - - name: load-test-get - url: http://flagger-loadtester.test/ - timeout: 5s - metadata: - type: cmd - cmd: "hey -z 1m -q 10 -c 2 -h2 https://podinfo.example.com/" -``` - -For gRPC services you can use [bojand/ghz](https://github.com/bojand/ghz) which is a similar tool to Hey but for gPRC: - -```yaml -webhooks: - - name: grpc-load-test - url: http://flagger-loadtester.test/ - timeout: 5s - metadata: - type: cmd - cmd: "ghz -z 1m -q 10 -c 2 --insecure podinfo.test:9898" -``` - -The load tester can run arbitrary commands as long as the binary is present in the container image. -For example if you you want to replace `hey` with another CLI, you can create your own Docker image: - -```dockerfile -FROM weaveworks/flagger-loadtester: - -RUN curl -Lo /usr/local/bin/my-cli https://github.com/user/repo/releases/download/ver/my-cli \ - && chmod +x /usr/local/bin/my-cli -``` - -### Load Testing Delegation - -The load tester can also forward testing tasks to external tools, by now [nGrinder](https://github.com/naver/ngrinder) -is supported. - -To use this feature, add a load test task of type 'ngrinder' to the canary analysis spec: - -```yaml -webhooks: - - name: load-test-post - url: http://flagger-loadtester.test/ - timeout: 5s - metadata: - # type of this load test task, cmd or ngrinder - type: ngrinder - # base url of your nGrinder controller server - server: http://ngrinder-server:port - # id of the test to clone from, the test must have been defined. - clone: 100 - # user name and base64 encoded password to authenticate against the nGrinder server - username: admin - passwd: YWRtaW4= - # the interval between between nGrinder test status polling, default to 1s - pollInterval: 5s -``` -When the canary analysis starts, the load tester will initiate a [clone_and_start request](https://github.com/naver/ngrinder/wiki/REST-API-PerfTest) -to the nGrinder server and start a new performance test. the load tester will periodically poll the nGrinder server -for the status of the test, and prevent duplicate requests from being sent in subsequent analysis loops. - -### Integration Testing - -Flagger comes with a testing service that can run Helm tests or Bats tests when configured as a webhook. - -Deploy the Helm test runner in the `kube-system` namespace using the `tiller` service account: - -```bash -helm repo add flagger https://flagger.app - -helm upgrade -i flagger-helmtester flagger/loadtester \ ---namespace=kube-system \ ---set serviceAccountName=tiller -``` - -When deployed the Helm tester API will be available at `http://flagger-helmtester.kube-system/`. - -Now you can add pre-rollout webhooks to the canary analysis spec: - -```yaml - canaryAnalysis: - webhooks: - - name: "smoke test" - type: pre-rollout - url: http://flagger-helmtester.kube-system/ - timeout: 3m - metadata: - type: "helm" - cmd: "test {{ .Release.Name }} --cleanup" -``` - -When the canary analysis starts, Flagger will call the pre-rollout webhooks before routing traffic to the canary. -If the helm test fails, Flagger will retry until the analysis threshold is reached and the canary is rolled back. - -As an alternative to Helm you can use the [Bash Automated Testing System](https://github.com/bats-core/bats-core) to run your tests. - -```yaml - canaryAnalysis: - webhooks: - - name: "acceptance tests" - type: pre-rollout - url: http://flagger-batstester.default/ - timeout: 5m - metadata: - type: "bash" - cmd: "bats /tests/acceptance.bats" -``` - -Note that you should create a ConfigMap with your Bats tests and mount it inside the tester container. - -### Manual Gating - -For manual approval of a canary deployment you can use the `confirm-rollout` webhook. -The confirmation hooks are executed before the pre-rollout hooks. -Flagger will halt the canary traffic shifting and analysis until the confirm webhook returns HTTP status 200. - -Manual gating with Flagger's tester: - -```yaml - canaryAnalysis: - webhooks: - - name: "gate" - type: confirm-rollout - url: http://flagger-loadtester.test/gate/halt -``` - -The `/gate/halt` returns HTTP 403 thus blocking the rollout. - -If you have notifications enabled, Flagger will post a message to Slack or MS Teams if a canary rollout is waiting for approval. - -Change the URL to `/gate/approve` to start the canary analysis: - -```yaml - canaryAnalysis: - webhooks: - - name: "gate" - type: confirm-rollout - url: http://flagger-loadtester.test/gate/approve -``` - -Manual gating can be driven with Flagger's tester API. Set the confirmation URL to `/gate/check`: - -```yaml - canaryAnalysis: - webhooks: - - name: "ask for confirmation" - type: confirm-rollout - url: http://flagger-loadtester.test/gate/check -``` - -By default the gate is closed, you can start or resume the canary rollout with: - -```bash -kubectl -n test exec -it flagger-loadtester-xxxx-xxxx sh - -curl -d '{"name": "podinfo","namespace":"test"}' http://localhost:8080/gate/open -``` - -You can pause the rollout at any time with: - -```bash -curl -d '{"name": "podinfo","namespace":"test"}' http://localhost:8080/gate/close -``` - -If a canary analysis is paused the status will change to waiting: - -```bash -kubectl get canary/podinfo - -NAME STATUS WEIGHT -podinfo Waiting 0 -``` diff --git a/docs/gitbook/install/flagger-install-on-eks-appmesh.md b/docs/gitbook/install/flagger-install-on-eks-appmesh.md deleted file mode 100644 index b8bf41af..00000000 --- a/docs/gitbook/install/flagger-install-on-eks-appmesh.md +++ /dev/null @@ -1,184 +0,0 @@ -# Flagger install on AWS - -This guide walks you through setting up Flagger and AWS App Mesh on EKS. - -### App Mesh - -The App Mesh integration with EKS is made out of the following components: - -* Kubernetes custom resources - * `mesh.appmesh.k8s.aws` defines a logical boundary for network traffic between the services - * `virtualnode.appmesh.k8s.aws` defines a logical pointer to a Kubernetes workload - * `virtualservice.appmesh.k8s.aws` defines the routing rules for a workload inside the mesh -* CRD controller - keeps the custom resources in sync with the App Mesh control plane -* Admission controller - injects the Envoy sidecar and assigns Kubernetes pods to App Mesh virtual nodes -* Metrics server - Prometheus instance that collects and stores Envoy's metrics - -Prerequisites: - -* jq -* homebrew -* openssl -* kubectl -* AWS CLI (default region us-west-2) - -### Create a Kubernetes cluster - -In order to create an EKS cluster you can use [eksctl](https://eksctl.io). -Eksctl is an open source command-line utility made by Weaveworks in collaboration with Amazon, -it’s a Kubernetes-native tool written in Go. - -On MacOS you can install eksctl with Homebrew: - -```bash -brew tap weaveworks/tap -brew install weaveworks/tap/eksctl -``` - -Create an EKS cluster: - -```bash -eksctl create cluster --name=appmesh \ ---region=us-west-2 \ ---appmesh-access -``` - -The above command will create a two nodes cluster with App Mesh -[IAM policy](https://docs.aws.amazon.com/app-mesh/latest/userguide/MESH_IAM_user_policies.html) -attached to the EKS node instance role. - -Verify the install with: - -```bash -kubectl get nodes -``` - -### Install Helm - -Install the [Helm](https://docs.helm.sh/using_helm/#installing-helm) command-line tool: - -```text -brew install kubernetes-helm -``` - -Create a service account and a cluster role binding for Tiller: - -```bash -kubectl -n kube-system create sa tiller - -kubectl create clusterrolebinding tiller-cluster-rule \ ---clusterrole=cluster-admin \ ---serviceaccount=kube-system:tiller -``` - -Deploy Tiller in the `kube-system` namespace: - -```bash -helm init --service-account tiller -``` - -You should consider using SSL between Helm and Tiller, for more information on securing your Helm -installation see [docs.helm.sh](https://docs.helm.sh/using_helm/#securing-your-helm-installation). - -### Enable horizontal pod auto-scaling - -Install the Horizontal Pod Autoscaler (HPA) metrics provider: - -```bash -helm upgrade -i metrics-server stable/metrics-server \ ---namespace kube-system -``` - -After a minute, the metrics API should report CPU and memory usage for pods. -You can very the metrics API with: - -```bash -kubectl -n kube-system top pods -``` - -### Install the App Mesh components - -Run the App Mesh installer: - -```bash -curl -fsSL https://git.io/get-app-mesh-eks.sh | bash - -``` - -The installer does the following: - -* creates the `appmesh-system` namespace -* generates a certificate signed by Kubernetes CA -* registers the App Mesh mutating webhook -* deploys the App Mesh webhook in `appmesh-system` namespace -* deploys the App Mesh CRDs -* deploys the App Mesh controller in `appmesh-system` namespace -* creates a mesh called `global` - -Verify that the global mesh is active: - -```bash -kubectl describe mesh - -Status: - Mesh Condition: - Status: True - Type: MeshActive -``` - -### Install Flagger and Grafana - -Add Flagger Helm repository: - -```bash -helm repo add flagger https://flagger.app -``` - -Install Flagger's Canary CRD: - -```yaml -kubectl apply -f https://raw.githubusercontent.com/weaveworks/flagger/master/artifacts/flagger/crd.yaml -``` - -Deploy Flagger and Prometheus in the _**appmesh-system**_ namespace: - -```bash -helm upgrade -i flagger flagger/flagger \ ---namespace=appmesh-system \ ---set crd.create=false \ ---set meshProvider=appmesh \ ---set prometheus.install=true -``` - -In order to collect the App Mesh metrics that Flagger needs to run the canary analysis, -you'll need to setup a Prometheus instance to scrape the Envoy sidecars. - -You can enable **Slack** notifications with: - -```bash -helm upgrade -i flagger flagger/flagger \ ---namespace=appmesh-system \ ---set crd.create=false \ ---set meshProvider=appmesh \ ---set metricsServer=http://prometheus.appmesh:9090 \ ---set slack.url=https://hooks.slack.com/services/YOUR/SLACK/WEBHOOK \ ---set slack.channel=general \ ---set slack.user=flagger -``` - -Flagger comes with a Grafana dashboard made for monitoring the canary analysis. -Deploy Grafana in the _**appmesh-system**_ namespace: - -```bash -helm upgrade -i flagger-grafana flagger/grafana \ ---namespace=appmesh-system \ ---set url=http://flagger-prometheus.appmesh-system:9090 -``` - -You can access Grafana using port forwarding: - -```bash -kubectl -n appmesh-system port-forward svc/flagger-grafana 3000:80 -``` - -Now that you have Flagger running you can try the -[App Mesh canary deployments tutorial](https://docs.flagger.app/usage/appmesh-progressive-delivery). diff --git a/docs/gitbook/install/flagger-install-on-google-cloud.md b/docs/gitbook/install/flagger-install-on-google-cloud.md deleted file mode 100644 index e1a38452..00000000 --- a/docs/gitbook/install/flagger-install-on-google-cloud.md +++ /dev/null @@ -1,410 +0,0 @@ -# Flagger install on Google Cloud - -This guide walks you through setting up Flagger and Istio on Google Kubernetes Engine. - -![GKE Cluster Overview](https://raw.githubusercontent.com/weaveworks/flagger/master/docs/diagrams/flagger-gke-istio.png) - -### Prerequisites - -You will be creating a cluster on Google’s Kubernetes Engine \(GKE\), -if you don’t have an account you can sign up [here](https://cloud.google.com/free/) for free credits. - -Login into Google Cloud, create a project and enable billing for it. - -Install the [gcloud](https://cloud.google.com/sdk/) command line utility and configure your project with `gcloud init`. - -Set the default project \(replace `PROJECT_ID` with your own project\): - -```text -gcloud config set project PROJECT_ID -``` - -Set the default compute region and zone: - -```text -gcloud config set compute/region us-central1 -gcloud config set compute/zone us-central1-a -``` - -Enable the Kubernetes and Cloud DNS services for your project: - -```text -gcloud services enable container.googleapis.com -gcloud services enable dns.googleapis.com -``` - -Install the kubectl command-line tool: - -```text -gcloud components install kubectl -``` - -### GKE cluster setup - -Create a cluster with the Istio add-on: - -```bash -K8S_VERSION=$(gcloud container get-server-config --format=json \ -| jq -r '.validMasterVersions[0]') - -gcloud beta container clusters create istio \ ---cluster-version=${K8S_VERSION} \ ---zone=us-central1-a \ ---num-nodes=2 \ ---machine-type=n1-highcpu-4 \ ---preemptible \ ---no-enable-cloud-logging \ ---no-enable-cloud-monitoring \ ---disk-size=30 \ ---enable-autorepair \ ---addons=HorizontalPodAutoscaling,Istio \ ---istio-config=auth=MTLS_PERMISSIVE -``` - -The above command will create a default node pool consisting of two `n1-highcpu-4` \(vCPU: 4, RAM 3.60GB, DISK: 30GB\) -preemptible VMs. Preemptible VMs are up to 80% cheaper than regular instances and are terminated and replaced -after a maximum of 24 hours. - -Set up credentials for `kubectl`: - -```bash -gcloud container clusters get-credentials istio -``` - -Create a cluster admin role binding: - -```bash -kubectl create clusterrolebinding "cluster-admin-$(whoami)" \ ---clusterrole=cluster-admin \ ---user="$(gcloud config get-value core/account)" -``` - -Validate your setup with: - -```bash -kubectl -n istio-system get svc -``` - -In a couple of seconds GCP should allocate an external IP to the `istio-ingressgateway` service. - -### Cloud DNS setup - -You will need an internet domain and access to the registrar to change the name servers to Google Cloud DNS. - -Create a managed zone named `istio` in Cloud DNS \(replace `example.com` with your domain\): - -```bash -gcloud dns managed-zones create \ ---dns-name="example.com." \ ---description="Istio zone" "istio" -``` - -Look up your zone's name servers: - -```bash -gcloud dns managed-zones describe istio -``` - -Update your registrar's name server records with the records returned by the above command. - -Wait for the name servers to change \(replace `example.com` with your domain\): - -```bash -watch dig +short NS example.com -``` - -Create a static IP address named `istio-gateway` using the Istio ingress IP: - -```bash -export GATEWAY_IP=$(kubectl -n istio-system get svc/istio-ingressgateway -ojson \ -| jq -r .status.loadBalancer.ingress[0].ip) - -gcloud compute addresses create istio-gateway --addresses ${GATEWAY_IP} --region us-central1 -``` - -Create the following DNS records \(replace `example.com` with your domain\): - -```bash -DOMAIN="example.com" - -gcloud dns record-sets transaction start --zone=istio - -gcloud dns record-sets transaction add --zone=istio \ ---name="${DOMAIN}" --ttl=300 --type=A ${GATEWAY_IP} - -gcloud dns record-sets transaction add --zone=istio \ ---name="www.${DOMAIN}" --ttl=300 --type=A ${GATEWAY_IP} - -gcloud dns record-sets transaction add --zone=istio \ ---name="*.${DOMAIN}" --ttl=300 --type=A ${GATEWAY_IP} - -gcloud dns record-sets transaction execute --zone istio -``` - -Verify that the wildcard DNS is working \(replace `example.com` with your domain\): - -```bash -watch host test.example.com -``` - -### Install Helm - -Install the [Helm](https://docs.helm.sh/using_helm/#installing-helm) command-line tool: - -```text -brew install kubernetes-helm -``` - -Create a service account and a cluster role binding for Tiller: - -```bash -kubectl -n kube-system create sa tiller - -kubectl create clusterrolebinding tiller-cluster-rule \ ---clusterrole=cluster-admin \ ---serviceaccount=kube-system:tiller -``` - -Deploy Tiller in the `kube-system` namespace: - -```bash -helm init --service-account tiller -``` - -You should consider using SSL between Helm and Tiller, for more information on securing your Helm -installation see [docs.helm.sh](https://docs.helm.sh/using_helm/#securing-your-helm-installation). - -### Install cert-manager - -Jetstack's [cert-manager](https://github.com/jetstack/cert-manager) -is a Kubernetes operator that automatically creates and manages TLS certs issued by Let’s Encrypt. - -You'll be using cert-manager to provision a wildcard certificate for the Istio ingress gateway. - -Install cert-manager's CRDs: - -```bash -CERT_REPO=https://raw.githubusercontent.com/jetstack/cert-manager - -kubectl apply -f ${CERT_REPO}/release-0.7/deploy/manifests/00-crds.yaml -``` - -Create the cert-manager namespace and disable resource validation: - -```bash -kubectl create namespace cert-manager - -kubectl label namespace cert-manager certmanager.k8s.io/disable-validation=true -``` - -Install cert-manager with Helm: - -```bash -helm repo add jetstack https://charts.jetstack.io && \ -helm repo update && \ -helm upgrade -i cert-manager \ ---namespace cert-manager \ ---version v0.7.0 \ -jetstack/cert-manager -``` - -### Istio Gateway TLS setup - -![Istio Let's Encrypt](https://raw.githubusercontent.com/weaveworks/flagger/master/docs/diagrams/istio-cert-manager-gke.png) - -Create a generic Istio Gateway to expose services outside the mesh on HTTPS: - -```bash -REPO=https://raw.githubusercontent.com/weaveworks/flagger/master - -kubectl apply -f ${REPO}/artifacts/gke/istio-gateway.yaml -``` - -Create a service account with Cloud DNS admin role \(replace `my-gcp-project` with your project ID\): - -```bash -GCP_PROJECT=my-gcp-project - -gcloud iam service-accounts create dns-admin \ ---display-name=dns-admin \ ---project=${GCP_PROJECT} - -gcloud iam service-accounts keys create ./gcp-dns-admin.json \ ---iam-account=dns-admin@${GCP_PROJECT}.iam.gserviceaccount.com \ ---project=${GCP_PROJECT} - -gcloud projects add-iam-policy-binding ${GCP_PROJECT} \ ---member=serviceAccount:dns-admin@${GCP_PROJECT}.iam.gserviceaccount.com \ ---role=roles/dns.admin -``` - -Create a Kubernetes secret with the GCP Cloud DNS admin key: - -```bash -kubectl create secret generic cert-manager-credentials \ ---from-file=./gcp-dns-admin.json \ ---namespace=istio-system -``` - -Create a letsencrypt issuer for CloudDNS \(replace `email@example.com` with a valid email address and -`my-gcp-project`with your project ID\): - -```yaml -apiVersion: certmanager.k8s.io/v1alpha1 -kind: Issuer -metadata: - name: letsencrypt-prod - namespace: istio-system -spec: - acme: - server: https://acme-v02.api.letsencrypt.org/directory - email: email@example.com - privateKeySecretRef: - name: letsencrypt-prod - dns01: - providers: - - name: cloud-dns - clouddns: - serviceAccountSecretRef: - name: cert-manager-credentials - key: gcp-dns-admin.json - project: my-gcp-project -``` - -Save the above resource as letsencrypt-issuer.yaml and then apply it: - -```text -kubectl apply -f ./letsencrypt-issuer.yaml -``` - -Create a wildcard certificate \(replace `example.com` with your domain\): - -```yaml -apiVersion: certmanager.k8s.io/v1alpha1 -kind: Certificate -metadata: - name: istio-gateway - namespace: istio-system -spec: - secretName: istio-ingressgateway-certs - issuerRef: - name: letsencrypt-prod - commonName: "*.example.com" - acme: - config: - - dns01: - provider: cloud-dns - domains: - - "*.example.com" - - "example.com" -``` - -Save the above resource as istio-gateway-cert.yaml and then apply it: - -```text -kubectl apply -f ./istio-gateway-cert.yaml -``` - -In a couple of seconds cert-manager should fetch a wildcard certificate from letsencrypt.org: - -```text -kubectl -n istio-system describe certificate istio-gateway - -Events: - Type Reason Age From Message - ---- ------ ---- ---- ------- - Normal CertIssued 1m52s cert-manager Certificate issued successfully -``` - -Recreate Istio ingress gateway pods: - -```bash -kubectl -n istio-system get pods -l istio=ingressgateway -``` - -Note that Istio gateway doesn't reload the certificates from the TLS secret on cert-manager renewal. -Since the GKE cluster is made out of preemptible VMs the gateway pods will be replaced once every 24h, -if your not using preemptible nodes then you need to manually delete the gateway pods every two months -before the certificate expires. - -### Install Prometheus - -The GKE Istio add-on does not include a Prometheus instance that scrapes the Istio telemetry service. -Because Flagger uses the Istio HTTP metrics to run the canary analysis you have to deploy the following -Prometheus configuration that's similar to the one that comes with the official Istio Helm chart. - -Find the GKE Istio version with: - -```bash -kubectl -n istio-system get deploy istio-pilot -oyaml | grep image: -``` - -Install Prometheus in istio-system namespace (replace `1.0.6-gke.3` with your version): - -```bash -kubectl -n istio-system apply -f \ -https://storage.googleapis.com/gke-release/istio/release/1.0.6-gke.3/patches/install-prometheus.yaml -``` - -### Install Flagger and Grafana - -Add Flagger Helm repository: - -```bash -helm repo add flagger https://flagger.app -``` - -Install Flagger's Canary CRD: - -```yaml -kubectl apply -f https://raw.githubusercontent.com/weaveworks/flagger/master/artifacts/flagger/crd.yaml -``` - -Deploy Flagger in the `istio-system` namespace with Slack notifications enabled: - -```bash -helm upgrade -i flagger flagger/flagger \ ---namespace=istio-system \ ---set crd.create=false \ ---set metricsServer=http://prometheus.istio-system:9090 \ ---set slack.url=https://hooks.slack.com/services/YOUR/SLACK/WEBHOOK \ ---set slack.channel=general \ ---set slack.user=flagger -``` - -Deploy Grafana in the `istio-system` namespace: - -```bash -helm upgrade -i flagger-grafana flagger/grafana \ ---namespace=istio-system \ ---set url=http://prometheus.istio-system:9090 \ ---set user=admin \ ---set password=replace-me -``` - -Expose Grafana through the public gateway by creating a virtual service \(replace `example.com` with your domain\): - -```yaml -apiVersion: networking.istio.io/v1alpha3 -kind: VirtualService -metadata: - name: grafana - namespace: istio-system -spec: - hosts: - - "grafana.example.com" - gateways: - - public-gateway.istio-system.svc.cluster.local - http: - - route: - - destination: - host: flagger-grafana -``` - -Save the above resource as grafana-virtual-service.yaml and then apply it: - -```bash -kubectl apply -f ./grafana-virtual-service.yaml -``` - -Navigate to `http://grafana.example.com` in your browser and you should be redirected to the HTTPS version. diff --git a/docs/gitbook/install/flagger-install-on-kubernetes.md b/docs/gitbook/install/flagger-install-on-kubernetes.md deleted file mode 100644 index 5800e50f..00000000 --- a/docs/gitbook/install/flagger-install-on-kubernetes.md +++ /dev/null @@ -1,240 +0,0 @@ -# Flagger install on Kubernetes - -This guide walks you through setting up Flagger on a Kubernetes cluster with Helm or Kustomize. - -### Prerequisites - -Flagger requires a Kubernetes cluster **v1.11** or newer with the following admission controllers enabled: - -* MutatingAdmissionWebhook -* ValidatingAdmissionWebhook - -### Install Flagger with Helm - -Add Flagger Helm repository: - -```bash -helm repo add flagger https://flagger.app -``` - -Install Flagger's Canary CRD: - -```yaml -kubectl apply -f https://raw.githubusercontent.com/weaveworks/flagger/master/artifacts/flagger/crd.yaml -``` - -Deploy Flagger for Istio: - -```bash -helm upgrade -i flagger flagger/flagger \ ---namespace=istio-system \ ---set crd.create=false \ ---set meshProvider=istio \ ---set metricsServer=http://prometheus:9090 -``` - -Deploy Flagger for Linkerd: - -```bash -helm upgrade -i flagger flagger/flagger \ ---namespace=linkerd \ ---set crd.create=false \ ---set meshProvider=linkerd \ ---set metricsServer=http://linkerd-prometheus:9090 -``` - -You can install Flagger in any namespace as long as it can talk to the Prometheus service on port 9090. - -Enable **Slack** notifications: - -```bash -helm upgrade -i flagger flagger/flagger \ ---namespace=istio-system \ ---set crd.create=false \ ---set slack.url=https://hooks.slack.com/services/YOUR/SLACK/WEBHOOK \ ---set slack.channel=general \ ---set slack.user=flagger -``` - -Enable **Microsoft Teams** notifications: - -```bash -helm upgrade -i flagger flagger/flagger \ ---namespace=istio-system \ ---set crd.create=false \ ---set msteams.url=https://outlook.office.com/webhook/YOUR/TEAMS/WEBHOOK -``` - -If you don't have Tiller you can use the helm template command and apply the generated yaml with kubectl: - -```bash -# generate -helm fetch --untar --untardir . flagger/flagger && -helm template flagger \ ---name flagger \ ---namespace=istio-system \ ---set metricsServer=http://prometheus.istio-system:9090 \ -> $HOME/flagger.yaml - -# apply -kubectl apply -f $HOME/flagger.yaml -``` - -To uninstall the Flagger release with Helm run: - -```text -helm delete --purge flagger -``` - -The command removes all the Kubernetes components associated with the chart and deletes the release. - -> **Note** that on uninstall the Canary CRD will not be removed. -Deleting the CRD will make Kubernetes remove all the objects owned by Flagger like Istio virtual services, -Kubernetes deployments and ClusterIP services. - -If you want to remove all the objects created by Flagger you have delete the Canary CRD with kubectl: - -```text -kubectl delete crd canaries.flagger.app -``` - -### Install Grafana with Helm - -Flagger comes with a Grafana dashboard made for monitoring the canary analysis. - -Deploy Grafana in the _**istio-system**_ namespace: - -```bash -helm upgrade -i flagger-grafana flagger/grafana \ ---namespace=istio-system \ ---set url=http://prometheus.istio-system:9090 \ ---set user=admin \ ---set password=change-me -``` - -Or use helm template command and apply the generated yaml with kubectl: - -```bash -# generate -helm fetch --untar --untardir . flagger/grafana && -helm template grafana \ ---name flagger-grafana \ ---namespace=istio-system \ -> $HOME/flagger-grafana.yaml - -# apply -kubectl apply -f $HOME/flagger-grafana.yaml -``` - -You can access Grafana using port forwarding: - -```bash -kubectl -n istio-system port-forward svc/flagger-grafana 3000:80 -``` - -### Install Flagger with Kustomize - -As an alternative to Helm, Flagger can be installed with Kustomize. - -**Service mesh specific installers** - -Install Flagger for Istio: - -```bash -kubectl apply -k github.com/weaveworks/flagger//kustomize/istio -``` - -This deploys Flagger in the `istio-system` namespace and sets the metrics server URL to Istio's Prometheus instance. - -Note that you'll need kubectl 1.14 to run the above the command or you can download the -[kustomize binary](https://github.com/kubernetes-sigs/kustomize/releases) and run: - -```bash -kustomize build github.com/weaveworks/flagger//kustomize/istio | kubectl apply -f - -``` - -Install Flagger for Linkerd: - -```bash -kubectl apply -k github.com/weaveworks/flagger//kustomize/linkerd -``` - -This deploys Flagger in the `linkerd` namespace and sets the metrics server URL to Linkerd's Prometheus instance. - -If you want to install a specific Flagger release, add the version number to the URL: - -```bash -kubectl apply -k github.com/weaveworks/flagger//kustomize/linkerd?ref=0.18.0 -``` - -**Generic installer** - -Install Flagger and Prometheus: - -```bash -kubectl apply -k github.com/weaveworks/flagger//kustomize/kubernetes -``` - -This deploys Flagger and Prometheus in the `flagger-system` namespace, -sets the metrics server URL to `http://flagger-prometheus.flagger-system:9090` and the mesh provider to `kubernetes`. - -The Prometheus instance has a two hours data retention and is configured to scrape all pods in your cluster that -have the `prometheus.io/scrape: "true"` annotation. - -To target a different provider you can specify it in the canary custom resource: - -```yaml -apiVersion: flagger.app/v1alpha3 -kind: Canary -metadata: - name: app - namespace: test -spec: - # can be: kubernetes, istio, linkerd, appmesh, nginx, gloo - # use the kubernetes provider for Blue/Green style deployments - provider: nginx -``` - -**Customized installer** - -Create a kustomization file using flagger as base: - -```bash -cat > kustomization.yaml < patch.yaml <.`. - -Optionally you can enable **Slack** notifications: - -```bash -helm upgrade -i flagger flagger/flagger \ ---reuse-values \ ---namespace=istio-system \ ---set slack.url=https://hooks.slack.com/services/YOUR/SLACK/WEBHOOK \ ---set slack.channel=general \ ---set slack.user=flagger -``` - -### Install Grafana - -Flagger comes with a Grafana dashboard made for monitoring the canary analysis. - -Deploy Grafana in the _**istio-system**_ namespace: - -```bash -helm upgrade -i flagger-grafana flagger/grafana \ ---namespace=istio-system \ ---set url=http://prometheus.istio-system:9090 -``` - -You can access Grafana using port forwarding: - -```bash -kubectl -n istio-system port-forward svc/flagger-grafana 3000:80 -``` - -### Install Load Tester - -Flagger comes with an optional load testing service that generates traffic -during canary analysis when configured as a webhook. - -Deploy the load test runner with Helm: - -```bash -helm upgrade -i flagger-loadtester flagger/loadtester \ ---namespace=test \ ---set cmd.timeout=1h -``` - -Deploy with kubectl: - -```bash -helm fetch --untar --untardir . flagger/loadtester && -helm template loadtester \ ---name flagger-loadtester \ ---namespace=test -> $HOME/flagger-loadtester.yaml - -# apply -kubectl apply -f $HOME/flagger-loadtester.yaml -``` - -> **Note** that the load tester should be deployed in a namespace with Istio sidecar injection enabled. diff --git a/docs/gitbook/tutorials/canary-helm-gitops.md b/docs/gitbook/tutorials/canary-helm-gitops.md deleted file mode 100644 index f8527fc6..00000000 --- a/docs/gitbook/tutorials/canary-helm-gitops.md +++ /dev/null @@ -1,373 +0,0 @@ -# Canary Deployments with Helm Charts and GitOps - -This guide shows you how to package a web app into a Helm chart, trigger canary deployments on Helm upgrade -and automate the chart release process with Weave Flux. - -### Packaging - -You'll be using the [podinfo](https://github.com/stefanprodan/k8s-podinfo) chart. -This chart packages a web app made with Go, it's configuration, a horizontal pod autoscaler (HPA) -and the canary configuration file. - -``` -├── Chart.yaml -├── README.md -├── templates -│   ├── NOTES.txt -│   ├── _helpers.tpl -│   ├── canary.yaml -│   ├── configmap.yaml -│   ├── deployment.yaml -│   ├── hpa.yaml -│   ├── service.yaml -│   └── tests -│   ├── test-config.yaml -│   └── test-pod.yaml -└── values.yaml -``` - -You can find the chart source [here](https://github.com/stefanprodan/flagger/tree/master/charts/podinfo). - -### Install - -Create a test namespace with Istio sidecar injection enabled: - -```bash -export REPO=https://raw.githubusercontent.com/weaveworks/flagger/master - -kubectl apply -f ${REPO}/artifacts/namespaces/test.yaml -``` - -Add Flagger Helm repository: - -```bash -helm repo add flagger https://flagger.app -``` - -Install podinfo with the release name `frontend` (replace `example.com` with your own domain): - -```bash -helm upgrade -i frontend flagger/podinfo \ ---namespace test \ ---set nameOverride=frontend \ ---set backend=http://backend.test:9898/echo \ ---set canary.loadtest.enabled=true \ ---set canary.istioIngress.enabled=true \ ---set canary.istioIngress.gateway=public-gateway.istio-system.svc.cluster.local \ ---set canary.istioIngress.host=frontend.istio.example.com -``` - -Flagger takes a Kubernetes deployment and a horizontal pod autoscaler (HPA), -then creates a series of objects (Kubernetes deployments, ClusterIP services and Istio virtual services). -These objects expose the application on the mesh and drive the canary analysis and promotion. - -```bash -# generated by Helm -configmap/frontend -deployment.apps/frontend -horizontalpodautoscaler.autoscaling/frontend -canary.flagger.app/frontend - -# generated by Flagger -configmap/frontend-primary -deployment.apps/frontend-primary -horizontalpodautoscaler.autoscaling/frontend-primary -service/frontend -service/frontend-canary -service/frontend-primary -virtualservice.networking.istio.io/frontend -``` - -When the `frontend-primary` deployment comes online, -Flagger will route all traffic to the primary pods and scale to zero the `frontend` deployment. - -Open your browser and navigate to the frontend URL: - -![Podinfo Frontend](https://raw.githubusercontent.com/weaveworks/flagger/master/docs/screens/demo-frontend.png) - -Now let's install the `backend` release without exposing it outside the mesh: - -```bash -helm upgrade -i backend flagger/podinfo \ ---namespace test \ ---set nameOverride=backend \ ---set canary.loadtest.enabled=true \ ---set canary.istioIngress.enabled=false -``` - -Check if Flagger has successfully deployed the canaries: - -``` -kubectl -n test get canaries - -NAME STATUS WEIGHT LASTTRANSITIONTIME -backend Initialized 0 2019-02-12T18:53:18Z -frontend Initialized 0 2019-02-12T17:50:50Z -``` - -Click on the ping button in the `frontend` UI to trigger a HTTP POST request -that will reach the `backend` app: - -![Jaeger Tracing](https://raw.githubusercontent.com/weaveworks/flagger/master/docs/screens/demo-frontend-jaeger.png) - -We'll use the `/echo` endpoint (same as the one the ping button calls) -to generate load on both apps during a canary deployment. - -### Upgrade - -First let's install a load testing service that will generate traffic during analysis: - -```bash -helm upgrade -i flagger-loadtester flagger/loadtester \ ---namespace=test -``` - -Install Flagger's helm test runner in the `kube-system` using `tiller` service account: - -```bash -helm upgrade -i flagger-helmtester flagger/loadtester \ ---namespace=kube-system \ ---set serviceAccountName=tiller -``` - -Enable the load and helm tester and deploy a new `frontend` version: - -```bash -helm upgrade -i frontend flagger/podinfo/ \ ---namespace test \ ---reuse-values \ ---set canary.loadtest.enabled=true \ ---set canary.helmtest.enabled=true \ ---set image.tag=2.0.1 -``` - -Flagger detects that the deployment revision changed and starts the canary analysis: - -``` -kubectl -n istio-system logs deployment/flagger -f | jq .msg - -New revision detected! Scaling up frontend.test -Halt advancement frontend.test waiting for rollout to finish: 0 of 2 updated replicas are available -Starting canary analysis for frontend.test -Pre-rollout check helm test passed -Advance frontend.test canary weight 5 -Advance frontend.test canary weight 10 -Advance frontend.test canary weight 15 -Advance frontend.test canary weight 20 -Advance frontend.test canary weight 25 -Advance frontend.test canary weight 30 -Advance frontend.test canary weight 35 -Advance frontend.test canary weight 40 -Advance frontend.test canary weight 45 -Advance frontend.test canary weight 50 -Copying frontend.test template spec to frontend-primary.test -Halt advancement frontend-primary.test waiting for rollout to finish: 1 old replicas are pending termination -Promotion completed! Scaling down frontend.test -``` - -You can monitor the canary deployment with Grafana. Open the Flagger dashboard, -select `test` from the namespace dropdown, `frontend-primary` from the primary dropdown and `frontend` from the -canary dropdown. - -![Flagger Grafana Dashboard](https://raw.githubusercontent.com/weaveworks/flagger/master/docs/screens/demo-frontend-dashboard.png) - -Now trigger a canary deployment for the `backend` app, but this time you'll change a value in the configmap: - -```bash -helm upgrade -i backend flagger/podinfo/ \ ---namespace test \ ---reuse-values \ ---set canary.helmtest.enabled=true \ ---set httpServer.timeout=25s -``` - -Generate HTTP 500 errors: - -```bash -kubectl -n test exec -it flagger-loadtester-xxx-yyy sh - -watch curl http://backend-canary:9898/status/500 -``` - -Generate latency: - -```bash -kubectl -n test exec -it flagger-loadtester-xxx-yyy sh - -watch curl http://backend-canary:9898/delay/1 -``` - -Flagger detects the config map change and starts a canary analysis. Flagger will pause the advancement -when the HTTP success rate drops under 99% or when the average request duration in the last minute is over 500ms: - -``` -kubectl -n test describe canary backend - -Events: - -ConfigMap backend has changed -New revision detected! Scaling up backend.test -Starting canary analysis for backend.test -Advance backend.test canary weight 5 -Advance backend.test canary weight 10 -Advance backend.test canary weight 15 -Advance backend.test canary weight 20 -Advance backend.test canary weight 25 -Advance backend.test canary weight 30 -Advance backend.test canary weight 35 -Halt backend.test advancement success rate 62.50% < 99% -Halt backend.test advancement success rate 88.24% < 99% -Advance backend.test canary weight 40 -Advance backend.test canary weight 45 -Halt backend.test advancement request duration 2.415s > 500ms -Halt backend.test advancement request duration 2.42s > 500ms -Advance backend.test canary weight 50 -ConfigMap backend-primary synced -Copying backend.test template spec to backend-primary.test -Promotion completed! Scaling down backend.test -``` - -![Flagger Grafana Dashboard](https://raw.githubusercontent.com/weaveworks/flagger/master/docs/screens/demo-backend-dashboard.png) - -If the number of failed checks reaches the canary analysis threshold, the traffic is routed back to the primary, -the canary is scaled to zero and the rollout is marked as failed. - -```bash -kubectl -n test get canary - -NAME STATUS WEIGHT LASTTRANSITIONTIME -backend Succeeded 0 2019-02-12T19:33:11Z -frontend Failed 0 2019-02-12T19:47:20Z -``` - -If you've enabled the Slack notifications, you'll receive an alert with the reason why the `backend` promotion failed. - -### GitOps automation - -Instead of using Helm CLI from a CI tool to perform the install and upgrade, -you could use a Git based approach. GitOps is a way to do Continuous Delivery, -it works by using Git as a source of truth for declarative infrastructure and workloads. -In the [GitOps model](https://www.weave.works/technologies/gitops/), -any change to production must be committed in source control -prior to being applied on the cluster. This way rollback and audit logs are provided by Git. - -![Helm GitOps Canary Deployment](https://raw.githubusercontent.com/weaveworks/flagger/master/docs/diagrams/flagger-flux-gitops.png) - -In order to apply the GitOps pipeline model to Flagger canary deployments you'll need -a Git repository with your workloads definitions in YAML format, -a container registry where your CI system pushes immutable images and -an operator that synchronizes the Git repo with the cluster state. - -Create a git repository with the following content: - -``` -├── namespaces -│   └── test.yaml -└── releases - └── test - ├── backend.yaml - ├── frontend.yaml - ├── loadtester.yaml - └── helmtester.yaml -``` - -You can find the git source [here](https://github.com/stefanprodan/flagger/tree/master/artifacts/cluster). - -Define the `frontend` release using Flux `HelmRelease` custom resource: - -```yaml -apiVersion: flux.weave.works/v1beta1 -kind: HelmRelease -metadata: - name: frontend - namespace: test - annotations: - flux.weave.works/automated: "true" - flux.weave.works/tag.chart-image: semver:~2.0 -spec: - releaseName: frontend - chart: - git: https://github.com/weaveowrks/flagger - ref: master - path: charts/podinfo - values: - image: - repository: stefanprodan/podinfo - tag: 2.0.0 - backend: http://backend-podinfo:9898/echo - canary: - enabled: true - istioIngress: - enabled: true - gateway: public-gateway.istio-system.svc.cluster.local - host: frontend.istio.example.com - loadtest: - enabled: true - helmtest: - enabled: true -``` - -In the `chart` section I've defined the release source by specifying the Helm repository (hosted on GitHub Pages), chart name and version. -In the `values` section I've overwritten the defaults set in values.yaml. - -With the `flux.weave.works` annotations I instruct Flux to automate this release. -When an image tag in the sem ver range of `2.0.0 - 2.0.99` is pushed to Quay, -Flux will upgrade the Helm release and from there Flagger will pick up the change and start a canary deployment. - -Install [Weave Flux](https://github.com/weaveworks/flux) and its Helm Operator by specifying your Git repo URL: - -```bash -helm repo add fluxcd https://charts.fluxcd.io - -helm install --name flux \ ---set helmOperator.create=true \ ---set helmOperator.createCRD=true \ ---set git.url=git@github.com:/ \ ---namespace fluxcd \ -fluxcd/flux -``` - -At startup Flux generates a SSH key and logs the public key. Find the SSH public key with: - -```bash -kubectl -n fluxcd logs deployment/flux | grep identity.pub | cut -d '"' -f2 -``` - -In order to sync your cluster state with Git you need to copy the public key and create a -deploy key with write access on your GitHub repository. - -Open GitHub, navigate to your fork, go to _Setting > Deploy keys_ click on _Add deploy key_, -check _Allow write access_, paste the Flux public key and click _Add key_. - -After a couple of seconds Flux will apply the Kubernetes resources from Git and Flagger will -launch the `frontend` and `backend` apps. - -A CI/CD pipeline for the `frontend` release could look like this: - -* cut a release from the master branch of the podinfo code repo with the git tag `2.0.1` -* CI builds the image and pushes the `podinfo:2.0.1` image to the container registry -* Flux scans the registry and updates the Helm release `image.tag` to `2.0.1` -* Flux commits and push the change to the cluster repo -* Flux applies the updated Helm release on the cluster -* Flux Helm Operator picks up the change and calls Tiller to upgrade the release -* Flagger detects a revision change and scales up the `frontend` deployment -* Flagger runs the helm test before routing traffic to the canary service -* Flagger starts the load test and runs the canary analysis -* Based on the analysis result the canary deployment is promoted to production or rolled back -* Flagger sends a Slack notification with the canary result - -If the canary fails, fix the bug, do another patch release eg `2.0.2` and the whole process will run again. - -A canary deployment can fail due to any of the following reasons: - -* the container image can't be downloaded -* the deployment replica set is stuck for more then ten minutes (eg. due to a container crash loop) -* the webooks (acceptance tests, helm tests, load tests, etc) are returning a non 2xx response -* the HTTP success rate (non 5xx responses) metric drops under the threshold -* the HTTP average duration metric goes over the threshold -* the Istio telemetry service is unable to collect traffic metrics -* the metrics server (Prometheus) can't be reached - -If you want to find out more about managing Helm releases with Flux here are two in-depth guides: -[gitops-helm](https://github.com/stefanprodan/gitops-helm) and -[gitops-istio](https://github.com/stefanprodan/gitops-istio). diff --git a/docs/gitbook/tutorials/flagger-smi-istio.md b/docs/gitbook/tutorials/flagger-smi-istio.md deleted file mode 100644 index 3fae5dda..00000000 --- a/docs/gitbook/tutorials/flagger-smi-istio.md +++ /dev/null @@ -1,332 +0,0 @@ -# Flagger SMI - -This guide shows you how to use the SMI Istio adapter and Flagger to automate canary deployments. - -### Prerequisites - -Flagger requires a Kubernetes cluster **v1.11** or newer with the following admission controllers enabled: - -* MutatingAdmissionWebhook -* ValidatingAdmissionWebhook - -Flagger depends on [Istio](https://istio.io/docs/setup/kubernetes/quick-start/) **v1.0.3** or newer -with traffic management, telemetry and Prometheus enabled. - -A minimal Istio installation should contain the following services: - -* istio-pilot -* istio-ingressgateway -* istio-sidecar-injector -* istio-telemetry -* prometheus - -### Install Istio and the SMI adapter - -Add Istio Helm repository: - -```bash -helm repo add istio.io https://storage.googleapis.com/istio-release/releases/1.1.5/charts -``` - -Install Istio CRDs: - -```bash -helm upgrade -i istio-init istio.io/istio-init --wait --namespace istio-system - -kubectl -n istio-system wait --for=condition=complete job/istio-init-crd-11 -``` - -Install Istio: - -```bash -helm upgrade -i istio istio.io/istio --wait --namespace istio-system -``` - -Create a generic Istio gateway to expose services outside the mesh on HTTP: - -```yaml -apiVersion: networking.istio.io/v1alpha3 -kind: Gateway -metadata: - name: public-gateway - namespace: istio-system -spec: - selector: - istio: ingressgateway - servers: - - port: - number: 80 - name: http - protocol: HTTP - hosts: - - "*" -``` - -Save the above resource as public-gateway.yaml and then apply it: - -```bash -kubectl apply -f ./public-gateway.yaml -``` - -Find the Gateway load balancer IP and add a DNS record for it: - -```bash -kubectl -n istio-system get svc/istio-ingressgateway -ojson | jq -r .status.loadBalancer.ingress[0].ip -``` - -Install the SMI adapter: - -```bash -REPO=https://raw.githubusercontent.com/weaveworks/flagger/master - -kubectl apply -f ${REPO}/artifacts/smi/istio-adapter.yaml -``` - -### Install Flagger and Grafana - -Add Flagger Helm repository: - -```bash -helm repo add flagger https://flagger.app -``` - -Deploy Flagger in the _**istio-system**_ namespace: - -```bash -helm upgrade -i flagger flagger/flagger \ ---namespace=istio-system \ ---set image.tag=master-12d84b2 \ ---set meshProvider=smi:istio -``` - -Flagger comes with a Grafana dashboard made for monitoring the canary deployments. - -Deploy Grafana in the _**istio-system**_ namespace: - -```bash -helm upgrade -i flagger-grafana flagger/grafana \ ---namespace=istio-system \ ---set url=http://prometheus.istio-system:9090 -``` - -You can access Grafana using port forwarding: - -```bash -kubectl -n istio-system port-forward svc/flagger-grafana 3000:80 -``` - -### Workloads bootstrap - -Create a test namespace with Istio sidecar injection enabled: - -```bash -export REPO=https://raw.githubusercontent.com/weaveworks/flagger/master - -kubectl apply -f ${REPO}/artifacts/namespaces/test.yaml -``` - -Create a deployment and a horizontal pod autoscaler: - -```bash -kubectl apply -f ${REPO}/artifacts/canaries/deployment.yaml -kubectl apply -f ${REPO}/artifacts/canaries/hpa.yaml -``` - -Deploy the load testing service to generate traffic during the canary analysis: - -```bash -kubectl -n test apply -f ${REPO}/artifacts/loadtester/deployment.yaml -kubectl -n test apply -f ${REPO}/artifacts/loadtester/service.yaml -``` - -Create a canary custom resource (replace example.com with your own domain): - -```yaml -apiVersion: flagger.app/v1alpha3 -kind: Canary -metadata: - name: podinfo - namespace: test -spec: - # deployment reference - targetRef: - apiVersion: apps/v1 - kind: Deployment - name: podinfo - # the maximum time in seconds for the canary deployment - # to make progress before it is rollback (default 600s) - progressDeadlineSeconds: 60 - # HPA reference (optional) - autoscalerRef: - apiVersion: autoscaling/v2beta1 - kind: HorizontalPodAutoscaler - name: podinfo - service: - # container port - port: 9898 - # Istio gateways (optional) - gateways: - - public-gateway.istio-system.svc.cluster.local - # Istio virtual service host names (optional) - hosts: - - app.example.com - canaryAnalysis: - # schedule interval (default 60s) - interval: 10s - # max number of failed metric checks before rollback - threshold: 5 - # max traffic percentage routed to canary - # percentage (0-100) - maxWeight: 50 - # canary increment step - # percentage (0-100) - stepWeight: 10 - metrics: - - name: request-success-rate - # minimum req success rate (non 5xx responses) - # percentage (0-100) - threshold: 99 - interval: 1m - - name: request-duration - # maximum req duration P99 - # milliseconds - threshold: 500 - interval: 30s - # generate traffic during analysis - webhooks: - - name: load-test - url: http://flagger-loadtester.test/ - timeout: 5s - metadata: - cmd: "hey -z 1m -q 10 -c 2 http://podinfo.test:9898/" -``` - -Save the above resource as podinfo-canary.yaml and then apply it: - -```bash -kubectl apply -f ./podinfo-canary.yaml -``` - -After a couple of seconds Flagger will create the canary objects: - -```bash -# applied -deployment.apps/podinfo -horizontalpodautoscaler.autoscaling/podinfo -canary.flagger.app/podinfo - -# generated -deployment.apps/podinfo-primary -horizontalpodautoscaler.autoscaling/podinfo-primary -service/podinfo -service/podinfo-canary -service/podinfo-primary -trafficsplits.split.smi-spec.io/podinfo -``` - -### Automated canary promotion - -Flagger implements a control loop that gradually shifts traffic to the canary while measuring key performance indicators -like HTTP requests success rate, requests average duration and pod health. -Based on analysis of the KPIs a canary is promoted or aborted, and the analysis result is published to Slack. - -![Flagger Canary Stages](https://raw.githubusercontent.com/weaveworks/flagger/master/docs/diagrams/flagger-canary-steps.png) - -Trigger a canary deployment by updating the container image: - -```bash -kubectl -n test set image deployment/podinfo \ -podinfod=quay.io/stefanprodan/podinfo:1.7.1 -``` - -Flagger detects that the deployment revision changed and starts a new rollout: - -```text -kubectl -n istio-system logs deployment/flagger -f | jq .msg - - -New revision detected podinfo.test -Scaling up podinfo.test -Waiting for podinfo.test rollout to finish: 0 of 1 updated replicas are available -Advance podinfo.test canary weight 5 -Advance podinfo.test canary weight 10 -Advance podinfo.test canary weight 15 -Advance podinfo.test canary weight 20 -Advance podinfo.test canary weight 25 -Advance podinfo.test canary weight 30 -Advance podinfo.test canary weight 35 -Advance podinfo.test canary weight 40 -Advance podinfo.test canary weight 45 -Advance podinfo.test canary weight 50 -Copying podinfo.test template spec to podinfo-primary.test -Waiting for podinfo-primary.test rollout to finish: 1 of 2 updated replicas are available -Promotion completed! Scaling down podinfo.test -``` - -**Note** that if you apply new changes to the deployment during the canary analysis, Flagger will restart the analysis. - -During the analysis the canary’s progress can be monitored with Grafana. The Istio dashboard URL is -http://localhost:3000/d/flagger-istio/istio-canary?refresh=10s&orgId=1&var-namespace=test&var-primary=podinfo-primary&var-canary=podinfo - -You can monitor all canaries with: - -```bash -watch kubectl get canaries --all-namespaces - -NAMESPACE NAME STATUS WEIGHT LASTTRANSITIONTIME -test podinfo Progressing 15 2019-05-16T14:05:07Z -prod frontend Succeeded 0 2019-05-15T16:15:07Z -prod backend Failed 0 2019-05-14T17:05:07Z -``` - -### Automated rollback - -During the canary analysis you can generate HTTP 500 errors and high latency to test if Flagger pauses the rollout. - -Create a tester pod and exec into it: - -```bash -kubectl -n test run tester \ ---image=quay.io/stefanprodan/podinfo:1.2.1 \ --- ./podinfo --port=9898 - -kubectl -n test exec -it tester-xx-xx sh -``` - -Generate HTTP 500 errors: - -```bash -watch curl http://podinfo-canary:9898/status/500 -``` - -Generate latency: - -```bash -watch curl http://podinfo-canary:9898/delay/1 -``` - -When the number of failed checks reaches the canary analysis threshold, the traffic is routed back to the primary, -the canary is scaled to zero and the rollout is marked as failed. - -```text -kubectl -n test describe canary/podinfo - -Status: - Canary Weight: 0 - Failed Checks: 10 - Phase: Failed -Events: - Type Reason Age From Message - ---- ------ ---- ---- ------- - Normal Synced 3m flagger Starting canary deployment for podinfo.test - Normal Synced 3m flagger Advance podinfo.test canary weight 5 - Normal Synced 3m flagger Advance podinfo.test canary weight 10 - Normal Synced 3m flagger Advance podinfo.test canary weight 15 - Normal Synced 3m flagger Halt podinfo.test advancement success rate 69.17% < 99% - Normal Synced 2m flagger Halt podinfo.test advancement success rate 61.39% < 99% - Normal Synced 2m flagger Halt podinfo.test advancement success rate 55.06% < 99% - Normal Synced 2m flagger Halt podinfo.test advancement success rate 47.00% < 99% - Normal Synced 2m flagger (combined from similar events): Halt podinfo.test advancement success rate 38.08% < 99% - Warning Synced 1m flagger Rolling back podinfo.test failed checks threshold reached 10 - Warning Synced 1m flagger Canary failed! Scaling down podinfo.test -``` diff --git a/docs/gitbook/tutorials/zero-downtime-deployments.md b/docs/gitbook/tutorials/zero-downtime-deployments.md deleted file mode 100644 index 4eb73bb4..00000000 --- a/docs/gitbook/tutorials/zero-downtime-deployments.md +++ /dev/null @@ -1,206 +0,0 @@ -# Zero downtime deployments - -This is a list of things you should consider when dealing with a high traffic production environment if you want to -minimise the impact of rolling updates and downscaling. - -### Deployment strategy - -Limit the number of unavailable pods during a rolling update: - -```yaml -apiVersion: apps/v1 -kind: Deployment -spec: - progressDeadlineSeconds: 120 - strategy: - type: RollingUpdate - rollingUpdate: - maxUnavailable: 0 -``` - -The default progress deadline for a deployment is ten minutes. -You should consider adjusting this value to make the deployment process fail faster. - -### Liveness health check - -You application should expose a HTTP endpoint that Kubernetes can call to determine if -your app transitioned to a broken state from which it can't recover and needs to be restarted. - -```yaml -readinessProbe: - exec: - command: - - wget - - --quiet - - --tries=1 - - --timeout=4 - - --spider - - http://localhost:8080/healthz - timeoutSeconds: 5 - initialDelaySeconds: 5 -``` - -If you've enabled mTLS, you'll have to use `exec` for liveness and readiness checks since -kubelet is not part of the service mesh and doesn't have access to the TLS cert. - -### Readiness health check - -You application should expose a HTTP endpoint that Kubernetes can call to determine if -your app is ready to receive traffic. - -```yaml -livenessProbe: - exec: - command: - - wget - - --quiet - - --tries=1 - - --timeout=4 - - --spider - - http://localhost:8080/readyz - timeoutSeconds: 5 - initialDelaySeconds: 5 - periodSeconds: 5 -``` - -If your app depends on external services, you should check if those services are available before allowing Kubernetes -to route traffic to an app instance. Keep in mind that the Envoy sidecar can have a slower startup than your app. -This means that on application start you should retry for at least a couple of seconds any external connection. - -### Graceful shutdown - -Before a pod gets terminated, Kubernetes sends a `SIGTERM` signal to every container and waits for period of -time (30s by default) for all containers to exit gracefully. If your app doesn't handle the `SIGTERM` signal or if it -doesn't exit within the grace period, Kubernetes will kill the container and any inflight requests that your app is -processing will fail. - -```yaml -apiVersion: apps/v1 -kind: Deployment -spec: - template: - spec: - terminationGracePeriodSeconds: 60 - containers: - - name: app - lifecycle: - preStop: - exec: - command: - - sleep - - "10" -``` - -Your app container should have a `preStop` hook that delays the container shutdown. -This will allow the service mesh to drain the traffic and remove this pod from all other Envoy sidecars before your app -becomes unavailable. - -### Delay Envoy shutdown - -Even if your app reacts to `SIGTERM` and tries to complete the inflight requests before shutdown, that -doesn't mean that the response will make it back to the caller. If the Envoy sidecar shuts down before your app, then -the caller will receive a 503 error. - -To mitigate this issue you can add a `preStop` hook to the Istio proxy and wait for the main app to exist before Envoy exists. - -```bash -#!/bin/bash -set -e -if ! pidof envoy &>/dev/null; then - exit 0 -fi - -if ! pidof pilot-agent &>/dev/null; then - exit 0 -fi - -while [ $(netstat -plunt | grep tcp | grep -v envoy | wc -l | xargs) -ne 0 ]; do - sleep 1; -done - -exit 0 -``` - -You'll have to build your own Envoy docker image with the above script and -modify the Istio injection webhook with the `preStop` directive. - -Thanks to Stono for his excellent [tips](https://github.com/istio/istio/issues/12183) on minimising 503s. - -### Resource requests and limits - -Setting CPU and memory requests/limits for all workloads is a mandatory step if you're running a production system. -Without limits your nodes could run out of memory or become unresponsive due to CPU exhausting. -Without CPU and memory requests, -the Kubernetes scheduler will not be able to make decisions about which nodes to place pods on. - -```yaml -apiVersion: apps/v1 -kind: Deployment -spec: - template: - spec: - containers: - - name: app - resources: - limits: - cpu: 1000m - memory: 1Gi - requests: - cpu: 100m - memory: 128Mi -``` - -Note that without resource requests the horizontal pod autoscaler can't determine when to scale your app. - -### Autoscaling - -A production environment should be able to handle traffic bursts without impacting the quality of service. -This can be achieved with Kubernetes autoscaling capabilities. -Autoscaling in Kubernetes has two dimensions: the Cluster Autoscaler that deals with node scaling operations and -the Horizontal Pod Autoscaler that automatically scales the number of pods in a deployment. - -```yaml -apiVersion: autoscaling/v2beta1 -kind: HorizontalPodAutoscaler -spec: - scaleTargetRef: - apiVersion: apps/v1 - kind: Deployment - name: app - minReplicas: 2 - maxReplicas: 4 - metrics: - - type: Resource - resource: - name: cpu - targetAverageValue: 900m - - type: Resource - resource: - name: memory - targetAverageValue: 768Mi -``` - -The above HPA ensures your app will be scaled up before the pods reach the CPU or memory limits. - -### Ingress retries - -To minimise the impact of downscaling operations you can make use of Envoy retry capabilities. - -```yaml -apiVersion: flagger.app/v1alpha3 -kind: Canary -spec: - service: - port: 9898 - gateways: - - public-gateway.istio-system.svc.cluster.local - hosts: - - app.example.com - appendHeaders: - x-envoy-upstream-rq-timeout-ms: "15000" - x-envoy-max-retries: "10" - x-envoy-retry-on: "gateway-error,connect-failure,refused-stream" -``` - -When the HPA scales down your app, your users could run into 503 errors. -The above configuration will make Envoy retry the HTTP requests that failed due to gateway errors. diff --git a/docs/gitbook/usage/ab-testing.md b/docs/gitbook/usage/ab-testing.md deleted file mode 100644 index d79207a6..00000000 --- a/docs/gitbook/usage/ab-testing.md +++ /dev/null @@ -1,215 +0,0 @@ -# Istio A/B Testing - -This guide shows you how to automate A/B testing with Istio and Flagger. - -Besides weighted routing, Flagger can be configured to route traffic to the canary based on HTTP match conditions. -In an A/B testing scenario, you'll be using HTTP headers or cookies to target a certain segment of your users. -This is particularly useful for frontend applications that require session affinity. - -![Flagger A/B Testing Stages](https://raw.githubusercontent.com/weaveworks/flagger/master/docs/diagrams/flagger-abtest-steps.png) - -### Bootstrap - -Create a test namespace with Istio sidecar injection enabled: - -```bash -export REPO=https://raw.githubusercontent.com/weaveworks/flagger/master - -kubectl apply -f ${REPO}/artifacts/namespaces/test.yaml -``` - -Create a deployment and a horizontal pod autoscaler: - -```bash -kubectl apply -f ${REPO}/artifacts/ab-testing/deployment.yaml -kubectl apply -f ${REPO}/artifacts/ab-testing/hpa.yaml -``` - -Deploy the load testing service to generate traffic during the canary analysis: - -```bash -kubectl -n test apply -f ${REPO}/artifacts/loadtester/deployment.yaml -kubectl -n test apply -f ${REPO}/artifacts/loadtester/service.yaml -``` - -Create a canary custom resource (replace example.com with your own domain): - -```yaml -apiVersion: flagger.app/v1alpha3 -kind: Canary -metadata: - name: abtest - namespace: test -spec: - # deployment reference - targetRef: - apiVersion: apps/v1 - kind: Deployment - name: abtest - # the maximum time in seconds for the canary deployment - # to make progress before it is rollback (default 600s) - progressDeadlineSeconds: 60 - # HPA reference (optional) - autoscalerRef: - apiVersion: autoscaling/v2beta1 - kind: HorizontalPodAutoscaler - name: abtest - service: - # container port - port: 9898 - # Istio gateways (optional) - gateways: - - public-gateway.istio-system.svc.cluster.local - # Istio virtual service host names (optional) - hosts: - - app.example.com - canaryAnalysis: - # schedule interval (default 60s) - interval: 1m - # total number of iterations - iterations: 10 - # max number of failed iterations before rollback - threshold: 2 - # canary match condition - match: - - headers: - user-agent: - regex: "^(?!.*Chrome).*Safari.*" - - headers: - cookie: - regex: "^(.*?;)?(type=insider)(;.*)?$" - metrics: - - name: request-success-rate - # minimum req success rate (non 5xx responses) - # percentage (0-100) - threshold: 99 - interval: 1m - - name: request-duration - # maximum req duration P99 - # milliseconds - threshold: 500 - interval: 30s - # generate traffic during analysis - webhooks: - - name: load-test - url: http://flagger-loadtester.test/ - timeout: 5s - metadata: - cmd: "hey -z 1m -q 10 -c 2 -H 'Cookie: type=insider' http://podinfo.test:9898/" -``` - -The above configuration will run an analysis for ten minutes targeting Safari users and those that have an insider cookie. - -Save the above resource as podinfo-abtest.yaml and then apply it: - -```bash -kubectl apply -f ./podinfo-abtest.yaml -``` - -After a couple of seconds Flagger will create the canary objects: - -```bash -# applied -deployment.apps/abtest -horizontalpodautoscaler.autoscaling/abtest -canary.flagger.app/abtest - -# generated -deployment.apps/abtest-primary -horizontalpodautoscaler.autoscaling/abtest-primary -service/abtest -service/abtest-canary -service/abtest-primary -destinationrule.networking.istio.io/abtest-canary -destinationrule.networking.istio.io/abtest-primary -virtualservice.networking.istio.io/abtest -``` - -### Automated canary promotion - -Trigger a canary deployment by updating the container image: - -```bash -kubectl -n test set image deployment/abtest \ -podinfod=stefanprodan/podinfo:2.0.1 -``` - -Flagger detects that the deployment revision changed and starts a new rollout: - -```text -kubectl -n test describe canary/abtest - -Status: - Failed Checks: 0 - Phase: Succeeded -Events: - Type Reason Age From Message - ---- ------ ---- ---- ------- - Normal Synced 3m flagger New revision detected abtest.test - Normal Synced 3m flagger Scaling up abtest.test - Warning Synced 3m flagger Waiting for abtest.test rollout to finish: 0 of 1 updated replicas are available - Normal Synced 3m flagger Advance abtest.test canary iteration 1/10 - Normal Synced 3m flagger Advance abtest.test canary iteration 2/10 - Normal Synced 3m flagger Advance abtest.test canary iteration 3/10 - Normal Synced 2m flagger Advance abtest.test canary iteration 4/10 - Normal Synced 2m flagger Advance abtest.test canary iteration 5/10 - Normal Synced 1m flagger Advance abtest.test canary iteration 6/10 - Normal Synced 1m flagger Advance abtest.test canary iteration 7/10 - Normal Synced 55s flagger Advance abtest.test canary iteration 8/10 - Normal Synced 45s flagger Advance abtest.test canary iteration 9/10 - Normal Synced 35s flagger Advance abtest.test canary iteration 10/10 - Normal Synced 25s flagger Copying abtest.test template spec to abtest-primary.test - Warning Synced 15s flagger Waiting for abtest-primary.test rollout to finish: 1 of 2 updated replicas are available - Normal Synced 5s flagger Promotion completed! Scaling down abtest.test -``` - -**Note** that if you apply new changes to the deployment during the canary analysis, Flagger will restart the analysis. - -You can monitor all canaries with: - -```bash -watch kubectl get canaries --all-namespaces - -NAMESPACE NAME STATUS WEIGHT LASTTRANSITIONTIME -test abtest Progressing 100 2019-03-16T14:05:07Z -prod frontend Succeeded 0 2019-03-15T16:15:07Z -prod backend Failed 0 2019-03-14T17:05:07Z -``` - -### Automated rollback - -During the canary analysis you can generate HTTP 500 errors and high latency to test Flagger's rollback. - -Generate HTTP 500 errors: - -```bash -watch curl -b 'type=insider' http://app.example.com/status/500 -``` - -Generate latency: - -```bash -watch curl -b 'type=insider' http://app.example.com/delay/1 -``` - -When the number of failed checks reaches the canary analysis threshold, the traffic is routed back to the primary, -the canary is scaled to zero and the rollout is marked as failed. - -```text -kubectl -n test describe canary/abtest - -Status: - Failed Checks: 2 - Phase: Failed -Events: - Type Reason Age From Message - ---- ------ ---- ---- ------- - Normal Synced 3m flagger Starting canary deployment for abtest.test - Normal Synced 3m flagger Advance abtest.test canary iteration 1/10 - Normal Synced 3m flagger Advance abtest.test canary iteration 2/10 - Normal Synced 3m flagger Advance abtest.test canary iteration 3/10 - Normal Synced 3m flagger Halt abtest.test advancement success rate 69.17% < 99% - Normal Synced 2m flagger Halt abtest.test advancement success rate 61.39% < 99% - Warning Synced 2m flagger Rolling back abtest.test failed checks threshold reached 2 - Warning Synced 1m flagger Canary failed! Scaling down abtest.test -``` diff --git a/docs/gitbook/usage/alerting.md b/docs/gitbook/usage/alerting.md deleted file mode 100644 index 17fe1531..00000000 --- a/docs/gitbook/usage/alerting.md +++ /dev/null @@ -1,55 +0,0 @@ -# Alerting - -### Slack - -Flagger can be configured to send Slack notifications: - -```bash -helm upgrade -i flagger flagger/flagger \ ---set slack.url=https://hooks.slack.com/services/YOUR/SLACK/WEBHOOK \ ---set slack.channel=general \ ---set slack.user=flagger -``` - -Once configured with a Slack incoming **webhook**, Flagger will post messages when a canary deployment -has been initialised, when a new revision has been detected and if the canary analysis failed or succeeded. - -![Slack Notifications](https://raw.githubusercontent.com/weaveworks/flagger/master/docs/screens/slack-canary-notifications.png) - -A canary deployment will be rolled back if the progress deadline exceeded or if the analysis reached the -maximum number of failed checks: - -![Slack Notifications](https://raw.githubusercontent.com/weaveworks/flagger/master/docs/screens/slack-canary-failed.png) - -### Microsoft Teams - -Flagger can be configured to send notifications to Microsoft Teams: - -```bash -helm upgrade -i flagger flagger/flagger \ ---set msteams.url=https://outlook.office.com/webhook/YOUR/TEAMS/WEBHOOK -``` - -Flagger will post a message card to MS Teams when a new revision has been detected and if the canary analysis failed or succeeded: - -![MS Teams Notifications](https://raw.githubusercontent.com/weaveworks/flagger/master/docs/screens/flagger-ms-teams-notifications.png) - -And you'll get a notification on rollback: - -![MS Teams Notifications](https://raw.githubusercontent.com/weaveworks/flagger/master/docs/screens/flagger-ms-teams-failed.png) - -### Prometheus Alert Manager - -Besides Slack, you can use Alertmanager to trigger alerts when a canary deployment failed: - -```yaml - - alert: canary_rollback - expr: flagger_canary_status > 1 - for: 1m - labels: - severity: warning - annotations: - summary: "Canary failed" - description: "Workload {{ $labels.name }} namespace {{ $labels.namespace }}" -``` - diff --git a/docs/gitbook/usage/appmesh-progressive-delivery.md b/docs/gitbook/usage/appmesh-progressive-delivery.md deleted file mode 100644 index 4727be49..00000000 --- a/docs/gitbook/usage/appmesh-progressive-delivery.md +++ /dev/null @@ -1,288 +0,0 @@ -# App Mesh Canary Deployments - -This guide shows you how to use App Mesh and Flagger to automate canary deployments. -You'll need an EKS cluster configured with App Mesh, you can find the install guide -[here](https://docs.flagger.app/install/flagger-install-on-eks-appmesh). - -### Bootstrap - -Flagger takes a Kubernetes deployment and optionally a horizontal pod autoscaler (HPA), -then creates a series of objects (Kubernetes deployments, ClusterIP services, App Mesh virtual nodes and services). -These objects expose the application on the mesh and drive the canary analysis and promotion. -The only App Mesh object you need to create by yourself is the mesh resource. - -Create a mesh called `global`: - -```bash -export REPO=https://raw.githubusercontent.com/weaveworks/flagger/master - -kubectl apply -f ${REPO}/artifacts/appmesh/global-mesh.yaml -``` - -Create a test namespace with App Mesh sidecar injection enabled: - -```bash -kubectl apply -f ${REPO}/artifacts/namespaces/test.yaml -``` - -Create a deployment and a horizontal pod autoscaler: - -```bash -kubectl apply -f ${REPO}/artifacts/appmesh/deployment.yaml -kubectl apply -f ${REPO}/artifacts/appmesh/hpa.yaml -``` - -Deploy the load testing service to generate traffic during the canary analysis: - -```bash -helm upgrade -i flagger-loadtester flagger/loadtester \ ---namespace=test \ ---set meshName=global \ ---set "backends[0]=podinfo.test" \ ---set "backends[1]=podinfo-canary.test" \ ---set "backends[2]=podinfo-primary.test" -``` - -Create a canary custom resource: - -```yaml -apiVersion: flagger.app/v1alpha3 -kind: Canary -metadata: - name: podinfo - namespace: test -spec: - # deployment reference - targetRef: - apiVersion: apps/v1 - kind: Deployment - name: podinfo - # the maximum time in seconds for the canary deployment - # to make progress before it is rollback (default 600s) - progressDeadlineSeconds: 60 - # HPA reference (optional) - autoscalerRef: - apiVersion: autoscaling/v2beta1 - kind: HorizontalPodAutoscaler - name: podinfo - service: - # container port - port: 9898 - # App Mesh reference - meshName: global - # App Mesh egress (optional) - backends: - - backend.test - # define the canary analysis timing and KPIs - canaryAnalysis: - # schedule interval (default 60s) - interval: 10s - # max number of failed metric checks before rollback - threshold: 10 - # max traffic percentage routed to canary - # percentage (0-100) - maxWeight: 50 - # canary increment step - # percentage (0-100) - stepWeight: 5 - # App Mesh Prometheus checks - metrics: - - name: request-success-rate - # minimum req success rate (non 5xx responses) - # percentage (0-100) - threshold: 99 - interval: 1m - # external checks (optional) - webhooks: - - name: load-test - url: http://flagger-loadtester.test/ - timeout: 5s - metadata: - cmd: "hey -z 1m -q 10 -c 2 http://podinfo.test:9898/" -``` - -Save the above resource as podinfo-canary.yaml and then apply it: - -```bash -kubectl apply -f ./podinfo-canary.yaml -``` - -After a couple of seconds Flagger will create the canary objects: - -```bash -# applied -deployment.apps/podinfo -horizontalpodautoscaler.autoscaling/podinfo -canary.flagger.app/podinfo - -# generated Kubernetes objects -deployment.apps/podinfo-primary -horizontalpodautoscaler.autoscaling/podinfo-primary -service/podinfo -service/podinfo-canary -service/podinfo-primary - -# generated App Mesh objects -virtualnode.appmesh.k8s.aws/podinfo -virtualnode.appmesh.k8s.aws/podinfo-canary -virtualnode.appmesh.k8s.aws/podinfo-primary -virtualservice.appmesh.k8s.aws/podinfo.test -``` - -The App Mesh specific settings are: - -```yaml - service: - port: 9898 - meshName: global.appmesh-system - backends: - - backend1.test - - backend2.test -``` - -App Mesh blocks all egress traffic by default. If your application needs to call another service, you have to create an -App Mesh virtual service for it and add the virtual service name to the backend list. - -### Setup App Mesh ingress (optional) - -In order to expose the podinfo app outside the mesh you'll be using an Envoy ingress and an AWS classic load balancer. -The ingress binds to an internet domain and forwards the calls into the mesh through the App Mesh sidecar. -If podinfo becomes unavailable due to a HPA downscaling or a node restart, -the ingress will retry the calls for a short period of time. - -Deploy the ingress and the AWS ELB service: - -```bash -kubectl apply -f ${REPO}/artifacts/appmesh/ingress.yaml -``` - -Find the ingress public address: - -```bash -kubectl -n test describe svc/ingress | grep Ingress - -LoadBalancer Ingress: yyy-xx.us-west-2.elb.amazonaws.com -``` - -Wait for the ELB to become active: - -```bash - watch curl -sS ${INGRESS_URL} -``` - -Open your browser and navigate to the ingress address to access podinfo UI. - -### Automated canary promotion - -Trigger a canary deployment by updating the container image: - -```bash -kubectl -n test set image deployment/podinfo \ -podinfod=stefanprodan/podinfo:2.0.1 -``` - -Flagger detects that the deployment revision changed and starts a new rollout: - -```text -kubectl -n test describe canary/podinfo - -Status: - Canary Weight: 0 - Failed Checks: 0 - Phase: Succeeded -Events: - Type Reason Age From Message - ---- ------ ---- ---- ------- - Normal Synced 3m flagger New revision detected podinfo.test - Normal Synced 3m flagger Scaling up podinfo.test - Warning Synced 3m flagger Waiting for podinfo.test rollout to finish: 0 of 1 updated replicas are available - Normal Synced 3m flagger Advance podinfo.test canary weight 5 - Normal Synced 3m flagger Advance podinfo.test canary weight 10 - Normal Synced 3m flagger Advance podinfo.test canary weight 15 - Normal Synced 2m flagger Advance podinfo.test canary weight 20 - Normal Synced 2m flagger Advance podinfo.test canary weight 25 - Normal Synced 1m flagger Advance podinfo.test canary weight 30 - Normal Synced 1m flagger Advance podinfo.test canary weight 35 - Normal Synced 55s flagger Advance podinfo.test canary weight 40 - Normal Synced 45s flagger Advance podinfo.test canary weight 45 - Normal Synced 35s flagger Advance podinfo.test canary weight 50 - Normal Synced 25s flagger Copying podinfo.test template spec to podinfo-primary.test - Warning Synced 15s flagger Waiting for podinfo-primary.test rollout to finish: 1 of 2 updated replicas are available - Normal Synced 5s flagger Promotion completed! Scaling down podinfo.test -``` - -**Note** that if you apply new changes to the deployment during the canary analysis, Flagger will restart the analysis. - -During the analysis the canary’s progress can be monitored with Grafana. The App Mesh dashboard URL is -http://localhost:3000/d/flagger-appmesh/appmesh-canary?refresh=10s&orgId=1&var-namespace=test&var-primary=podinfo-primary&var-canary=podinfo - -![App Mesh Canary Dashboard](https://raw.githubusercontent.com/weaveworks/flagger/master/docs/screens/flagger-grafana-appmesh.png) - -You can monitor all canaries with: - -```bash -watch kubectl get canaries --all-namespaces - -NAMESPACE NAME STATUS WEIGHT LASTTRANSITIONTIME -test podinfo Progressing 15 2019-03-16T14:05:07Z -prod frontend Succeeded 0 2019-03-15T16:15:07Z -prod backend Failed 0 2019-03-14T17:05:07Z -``` - -If you’ve enabled the Slack notifications, you should receive the following messages: - -![Flagger Slack Notifications](https://raw.githubusercontent.com/weaveworks/flagger/master/docs/screens/slack-canary-notifications.png) - -### Automated rollback - -During the canary analysis you can generate HTTP 500 errors to test if Flagger pauses the rollout. - -Trigger a canary deployment: - -```bash -kubectl -n test set image deployment/podinfo \ -podinfod=stefanprodan/podinfo:2.0.2 -``` - -Exec into the load tester pod with: - -```bash -kubectl -n test exec -it flagger-loadtester-xx-xx sh -``` - -Generate HTTP 500 errors: - -```bash -hey -z 1m -c 5 -q 5 http://podinfo.test:9898/status/500 -``` - -When the number of failed checks reaches the canary analysis threshold, the traffic is routed back to the primary, -the canary is scaled to zero and the rollout is marked as failed. - -```text -kubectl -n test describe canary/podinfo - -Status: - Canary Weight: 0 - Failed Checks: 10 - Phase: Failed -Events: - Type Reason Age From Message - ---- ------ ---- ---- ------- - Normal Synced 3m flagger Starting canary deployment for podinfo.test - Normal Synced 3m flagger Advance podinfo.test canary weight 5 - Normal Synced 3m flagger Advance podinfo.test canary weight 10 - Normal Synced 3m flagger Advance podinfo.test canary weight 15 - Normal Synced 3m flagger Halt podinfo.test advancement success rate 69.17% < 99% - Normal Synced 2m flagger Halt podinfo.test advancement success rate 61.39% < 99% - Normal Synced 2m flagger Halt podinfo.test advancement success rate 55.06% < 99% - Normal Synced 2m flagger Halt podinfo.test advancement success rate 47.00% < 99% - Normal Synced 2m flagger (combined from similar events): Halt podinfo.test advancement success rate 38.08% < 99% - Warning Synced 1m flagger Rolling back podinfo.test failed checks threshold reached 10 - Warning Synced 1m flagger Canary failed! Scaling down podinfo.test -``` - -If you’ve enabled the Slack notifications, you’ll receive a message if the progress deadline is exceeded, -or if the analysis reached the maximum number of failed checks: - -![Flagger Slack Notifications](https://raw.githubusercontent.com/weaveworks/flagger/master/docs/screens/slack-canary-failed.png) diff --git a/docs/gitbook/usage/blue-green.md b/docs/gitbook/usage/blue-green.md deleted file mode 100644 index 8702735a..00000000 --- a/docs/gitbook/usage/blue-green.md +++ /dev/null @@ -1,356 +0,0 @@ -# Blue/Green Deployments - -This guide shows you how to automate Blue/Green deployments with Flagger and Kubernetes. - -For applications that are not deployed on a service mesh, Flagger can orchestrate Blue/Green style deployments -with Kubernetes L4 networking. - -![Flagger Blue/Green Stages](https://raw.githubusercontent.com/weaveworks/flagger/master/docs/diagrams/flagger-bluegreen-steps.png) - -### Prerequisites - -Flagger requires a Kubernetes cluster **v1.11** or newer. - -Install Flagger and the Prometheus add-on: - -```bash -helm repo add flagger https://flagger.app - -helm upgrade -i flagger flagger/flagger \ ---namespace flagger \ ---set prometheus.install=true \ ---set meshProvider=kubernetes -``` - -If you already have a Prometheus instance running in your cluster, -you can point Flagger to the ClusterIP service with: - -```bash -helm upgrade -i flagger flagger/flagger \ ---namespace flagger \ ---set metricsServer=http://prometheus.monitoring:9090 -``` - -Optionally you can enable Slack notifications: - -```bash -helm upgrade -i flagger flagger/flagger \ ---reuse-values \ ---namespace flagger \ ---set slack.url=https://hooks.slack.com/services/YOUR/SLACK/WEBHOOK \ ---set slack.channel=general \ ---set slack.user=flagger -``` - -### Bootstrap - -Flagger takes a Kubernetes deployment and optionally a horizontal pod autoscaler (HPA), -then creates a series of objects (Kubernetes deployment and ClusterIP services). -These objects expose the application inside the cluster and drive the canary analysis and Blue/Green promotion. - -Create a test namespace: - -```bash -kubectl create ns test -``` - -Create a deployment and a horizontal pod autoscaler: - -```bash -export REPO=https://raw.githubusercontent.com/weaveworks/flagger/master - -kubectl apply -f ${REPO}/artifacts/canaries/deployment.yaml -kubectl apply -f ${REPO}/artifacts/canaries/hpa.yaml -``` - -Deploy the load testing service to generate traffic during the analysis: - -```bash -kubectl -n test apply -f ${REPO}/artifacts/loadtester/deployment.yaml -kubectl -n test apply -f ${REPO}/artifacts/loadtester/service.yaml -``` - -Create a canary custom resource: - -```yaml -apiVersion: flagger.app/v1alpha3 -kind: Canary -metadata: - name: podinfo - namespace: test -spec: - # service mesh provider can be: kubernetes, istio, appmesh, nginx, gloo - # use the kubernetes provider for Blue/Green style deployments - provider: kubernetes - # deployment reference - targetRef: - apiVersion: apps/v1 - kind: Deployment - name: podinfo - # the maximum time in seconds for the canary deployment - # to make progress before rollback (default 600s) - progressDeadlineSeconds: 60 - # HPA reference (optional) - autoscalerRef: - apiVersion: autoscaling/v2beta1 - kind: HorizontalPodAutoscaler - name: podinfo - service: - # container port - port: 9898 - portDiscovery: true - canaryAnalysis: - # schedule interval (default 60s) - interval: 30s - # max number of failed checks before rollback - threshold: 2 - # number of checks to run before rollback - iterations: 10 - # Prometheus checks based on - # http_request_duration_seconds histogram - metrics: - - name: request-success-rate - # minimum req success rate (non 5xx responses) - # percentage (0-100) - threshold: 99 - interval: 1m - - name: request-duration - # maximum req duration P99 - # milliseconds - threshold: 500 - interval: 30s - # acceptance/load testing hooks - webhooks: - - name: smoke-test - type: pre-rollout - url: http://flagger-loadtester.test/ - timeout: 15s - metadata: - type: bash - cmd: "curl -sd 'anon' http://podinfo-canary.test:9898/token | grep token" - - name: load-test - url: http://flagger-loadtester.test/ - timeout: 5s - metadata: - type: cmd - cmd: "hey -z 1m -q 10 -c 2 http://podinfo-canary.test:9898/" -``` - -The above configuration will run an analysis for five minutes. - -Save the above resource as podinfo-canary.yaml and then apply it: - -```bash -kubectl apply -f ./podinfo-canary.yaml -``` - -After a couple of seconds Flagger will create the canary objects: - -```bash -# applied -deployment.apps/podinfo -horizontalpodautoscaler.autoscaling/podinfo -canary.flagger.app/podinfo - -# generated -deployment.apps/podinfo-primary -horizontalpodautoscaler.autoscaling/podinfo-primary -service/podinfo -service/podinfo-canary -service/podinfo-primary -``` - -Blue/Green scenario: -* on bootstrap, Flagger will create three ClusterIP services (`app-primary`,` app-canary`, `app`) and a shadow deployment named `app-primary` that represents the blue version -* when a new version is detected, Flagger would scale up the green version and run the conformance tests (the tests should target the `app-canary` ClusterIP service to reach the green version) -* if the conformance tests are passing, Flagger would start the load tests and validate them with custom Prometheus queries -* if the load test analysis is successful, Flagger will promote the new version to `app-primary` and scale down the green version - -### Automated Blue/Green promotion - -Trigger a deployment by updating the container image: - -```bash -kubectl -n test set image deployment/podinfo \ -podinfod=stefanprodan/podinfo:2.0.1 -``` - -Flagger detects that the deployment revision changed and starts a new rollout: - -```text -kubectl -n test describe canary/podinfo - -Events: - -New revision detected podinfo.test -Waiting for podinfo.test rollout to finish: 0 of 1 updated replicas are available -Pre-rollout check acceptance-test passed -Advance podinfo.test canary iteration 1/10 -Advance podinfo.test canary iteration 2/10 -Advance podinfo.test canary iteration 3/10 -Advance podinfo.test canary iteration 4/10 -Advance podinfo.test canary iteration 5/10 -Advance podinfo.test canary iteration 6/10 -Advance podinfo.test canary iteration 7/10 -Advance podinfo.test canary iteration 8/10 -Advance podinfo.test canary iteration 9/10 -Advance podinfo.test canary iteration 10/10 -Copying podinfo.test template spec to podinfo-primary.test -Waiting for podinfo-primary.test rollout to finish: 1 of 2 updated replicas are available -Promotion completed! Scaling down podinfo.test -``` - -**Note** that if you apply new changes to the deployment during the canary analysis, Flagger will restart the analysis. - -You can monitor all canaries with: - -```bash -watch kubectl get canaries --all-namespaces - -NAMESPACE NAME STATUS WEIGHT LASTTRANSITIONTIME -test podinfo Progressing 100 2019-06-16T14:05:07Z -prod frontend Succeeded 0 2019-06-15T16:15:07Z -prod backend Failed 0 2019-06-14T17:05:07Z -``` - -### Automated rollback - -During the analysis you can generate HTTP 500 errors and high latency to test Flagger's rollback. - -Exec into the load tester pod with: - -```bash -kubectl -n test exec -it flagger-loadtester-xx-xx sh -``` - -Generate HTTP 500 errors: - -```bash -watch curl http://podinfo-canary.test:9898/status/500 -``` - -Generate latency: - -```bash -watch curl http://podinfo-canary.test:9898/delay/1 -``` - -When the number of failed checks reaches the analysis threshold, -the green version is scaled to zero and the rollout is marked as failed. - -```text -kubectl -n test describe canary/podinfo - -Status: - Failed Checks: 2 - Phase: Failed -Events: - Type Reason Age From Message - ---- ------ ---- ---- ------- - Normal Synced 3m flagger New revision detected podinfo.test - Normal Synced 3m flagger Advance podinfo.test canary iteration 1/10 - Normal Synced 3m flagger Advance podinfo.test canary iteration 2/10 - Normal Synced 3m flagger Advance podinfo.test canary iteration 3/10 - Normal Synced 3m flagger Halt podinfo.test advancement success rate 69.17% < 99% - Normal Synced 2m flagger Halt podinfo.test advancement success rate 61.39% < 99% - Warning Synced 2m flagger Rolling back podinfo.test failed checks threshold reached 2 - Warning Synced 1m flagger Canary failed! Scaling down podinfo.test -``` - -### Custom metrics - -The analysis can be extended with Prometheus queries. The demo app is instrumented with Prometheus -so you can create a custom check that will use the HTTP request duration histogram to validate the canary (green version). - -Edit the canary analysis and add the following metric: - -```yaml - canaryAnalysis: - metrics: - - name: "404s percentage" - threshold: 5 - query: | - 100 - sum( - rate( - http_request_duration_seconds_count{ - kubernetes_namespace="test", - kubernetes_pod_name=~"podinfo-[0-9a-zA-Z]+(-[0-9a-zA-Z]+)" - status!="404" - }[1m] - ) - ) - / - sum( - rate( - http_request_duration_seconds_count{ - kubernetes_namespace="test", - kubernetes_pod_name=~"podinfo-[0-9a-zA-Z]+(-[0-9a-zA-Z]+)" - }[1m] - ) - ) * 100 -``` - -The above configuration validates the canary (green version) by checking if the HTTP 404 req/sec percentage is below 5 -percent of the total traffic. If the 404s rate reaches the 5% threshold, then the rollout is rolled back. - -Trigger a deployment by updating the container image: - -```bash -kubectl -n test set image deployment/podinfo \ -podinfod=stefanprodan/podinfo:2.0.3 -``` - -Generate 404s: - -```bash -watch curl http://podinfo-canary.test:9898/status/400 -``` - -Watch Flagger logs: - -``` -kubectl -n flagger logs deployment/flagger -f | jq .msg - -New revision detected podinfo.test -Scaling up podinfo.test -Advance podinfo.test canary iteration 1/10 -Halt podinfo.test advancement 404s percentage 6.20 > 5 -Halt podinfo.test advancement 404s percentage 6.45 > 5 -Rolling back podinfo.test failed checks threshold reached 2 -Canary failed! Scaling down podinfo.test -``` - -If you have Slack configured, Flagger will send a notification with the reason why the canary failed. - -### Conformance Testing with Helm - -Flagger comes with a testing service that can run Helm tests when configured as a pre-rollout webhook. - -Deploy the Helm test runner in the `kube-system` namespace using the `tiller` service account: - -```bash -helm repo add flagger https://flagger.app - -helm upgrade -i flagger-helmtester flagger/loadtester \ ---namespace=kube-system \ ---set serviceAccountName=tiller -``` - -When deployed the Helm tester API will be available at `http://flagger-helmtester.kube-system/`. - -Add a helm test pre-rollout hook to your chart: - -```yaml - canaryAnalysis: - webhooks: - - name: "conformance testing" - type: pre-rollout - url: http://flagger-helmtester.kube-system/ - timeout: 3m - metadata: - type: "helm" - cmd: "test {{ .Release.Name }} --cleanup" -``` - -When the canary analysis starts, Flagger will call the pre-rollout webhooks. -If the helm test fails, Flagger will retry until the analysis threshold is reached and the canary is rolled back. diff --git a/docs/gitbook/usage/gloo-progressive-delivery.md b/docs/gitbook/usage/gloo-progressive-delivery.md deleted file mode 100644 index 3c5018d7..00000000 --- a/docs/gitbook/usage/gloo-progressive-delivery.md +++ /dev/null @@ -1,365 +0,0 @@ -# NGNIX Ingress Controller Canary Deployments - -This guide shows you how to use the [Gloo](https://gloo.solo.io/) ingress controller and Flagger to automate canary deployments. - -![Flagger Gloo Ingress Controller](https://raw.githubusercontent.com/weaveworks/flagger/master/docs/diagrams/flagger-gloo-overview.png) - -### Prerequisites - -Flagger requires a Kubernetes cluster **v1.11** or newer and Gloo ingress **0.13.29** or newer. - -Install Gloo with Helm: - -```bash -helm repo add gloo https://storage.googleapis.com/solo-public-helm - -helm upgrade -i gloo gloo/gloo \ ---namespace gloo-system -``` - -Install Flagger and the Prometheus add-on in the same namespace as Gloo: - -```bash -helm repo add flagger https://flagger.app - -helm upgrade -i flagger flagger/flagger \ ---namespace gloo-system \ ---set prometheus.install=true \ ---set meshProvider=gloo -``` - -Optionally you can enable Slack notifications: - -```bash -helm upgrade -i flagger flagger/flagger \ ---reuse-values \ ---namespace gloo-system \ ---set slack.url=https://hooks.slack.com/services/YOUR/SLACK/WEBHOOK \ ---set slack.channel=general \ ---set slack.user=flagger -``` - -### Bootstrap - -Flagger takes a Kubernetes deployment and optionally a horizontal pod autoscaler (HPA), -then creates a series of objects (Kubernetes deployments, ClusterIP services and Gloo upstream groups). -These objects expose the application outside the cluster and drive the canary analysis and promotion. - -Create a test namespace: - -```bash -kubectl create ns test -``` - -Create a deployment and a horizontal pod autoscaler: - -```bash -kubectl apply -f ${REPO}/artifacts/gloo/deployment.yaml -kubectl apply -f ${REPO}/artifacts/gloo/hpa.yaml -``` - -Deploy the load testing service to generate traffic during the canary analysis: - -```bash -helm upgrade -i flagger-loadtester flagger/loadtester \ ---namespace=test -``` - -Create an virtual service definition that references an upstream group that will be generated by Flagger -(replace `app.example.com` with your own domain): - -```yaml -apiVersion: gateway.solo.io/v1 -kind: VirtualService -metadata: - name: podinfo - namespace: test -spec: - virtualHost: - domains: - - 'app.example.com' - name: podinfo.test - routes: - - matcher: - prefix: / - routeAction: - upstreamGroup: - name: podinfo - namespace: test -``` - -Save the above resource as podinfo-virtualservice.yaml and then apply it: - -```bash -kubectl apply -f ./podinfo-virtualservice.yaml -``` - -Create a canary custom resource (replace `app.example.com` with your own domain): - -```yaml -apiVersion: flagger.app/v1alpha3 -kind: Canary -metadata: - name: podinfo - namespace: test -spec: - provider: gloo - # deployment reference - targetRef: - apiVersion: apps/v1 - kind: Deployment - name: podinfo - # HPA reference (optional) - autoscalerRef: - apiVersion: autoscaling/v2beta1 - kind: HorizontalPodAutoscaler - name: podinfo - # the maximum time in seconds for the canary deployment - # to make progress before it is rollback (default 600s) - progressDeadlineSeconds: 60 - service: - # container port - port: 9898 - canaryAnalysis: - # schedule interval (default 60s) - interval: 10s - # max number of failed metric checks before rollback - threshold: 5 - # max traffic percentage routed to canary - # percentage (0-100) - maxWeight: 50 - # canary increment step - # percentage (0-100) - stepWeight: 5 - # Gloo Prometheus checks - metrics: - - name: request-success-rate - # minimum req success rate (non 5xx responses) - # percentage (0-100) - threshold: 99 - interval: 1m - - name: request-duration - # maximum req duration P99 - # milliseconds - threshold: 500 - interval: 30s - # load testing (optional) - webhooks: - - name: load-test - url: http://flagger-loadtester.test/ - timeout: 5s - metadata: - type: cmd - cmd: "hey -z 1m -q 10 -c 2 http://app.example.com/" -``` - -Save the above resource as podinfo-canary.yaml and then apply it: - -```bash -kubectl apply -f ./podinfo-canary.yaml -``` - -After a couple of seconds Flagger will create the canary objects: - -```bash -# applied -deployment.apps/podinfo -horizontalpodautoscaler.autoscaling/podinfo -virtualservices.gateway.solo.io/podinfo -canary.flagger.app/podinfo - -# generated -deployment.apps/podinfo-primary -horizontalpodautoscaler.autoscaling/podinfo-primary -service/podinfo -service/podinfo-canary -service/podinfo-primary -upstreamgroups.gloo.solo.io/podinfo -``` - -When the bootstrap finishes Flagger will set the canary status to initialized: - -```bash -kubectl -n test get canary podinfo - -NAME STATUS WEIGHT LASTTRANSITIONTIME -podinfo Initialized 0 2019-05-17T08:09:51Z -``` - -### Automated canary promotion - -Flagger implements a control loop that gradually shifts traffic to the canary while measuring key performance indicators -like HTTP requests success rate, requests average duration and pod health. -Based on analysis of the KPIs a canary is promoted or aborted, and the analysis result is published to Slack. - -![Flagger Canary Stages](https://raw.githubusercontent.com/weaveworks/flagger/master/docs/diagrams/flagger-canary-steps.png) - -Trigger a canary deployment by updating the container image: - -```bash -kubectl -n test set image deployment/podinfo \ -podinfod=stefanprodan/podinfo:2.0.1 -``` - -Flagger detects that the deployment revision changed and starts a new rollout: - -```text -kubectl -n test describe canary/podinfo - -Status: - Canary Weight: 0 - Failed Checks: 0 - Phase: Succeeded -Events: - Type Reason Age From Message - ---- ------ ---- ---- ------- - Normal Synced 3m flagger New revision detected podinfo.test - Normal Synced 3m flagger Scaling up podinfo.test - Warning Synced 3m flagger Waiting for podinfo.test rollout to finish: 0 of 1 updated replicas are available - Normal Synced 3m flagger Advance podinfo.test canary weight 5 - Normal Synced 3m flagger Advance podinfo.test canary weight 10 - Normal Synced 3m flagger Advance podinfo.test canary weight 15 - Normal Synced 2m flagger Advance podinfo.test canary weight 20 - Normal Synced 2m flagger Advance podinfo.test canary weight 25 - Normal Synced 1m flagger Advance podinfo.test canary weight 30 - Normal Synced 1m flagger Advance podinfo.test canary weight 35 - Normal Synced 55s flagger Advance podinfo.test canary weight 40 - Normal Synced 45s flagger Advance podinfo.test canary weight 45 - Normal Synced 35s flagger Advance podinfo.test canary weight 50 - Normal Synced 25s flagger Copying podinfo.test template spec to podinfo-primary.test - Warning Synced 15s flagger Waiting for podinfo-primary.test rollout to finish: 1 of 2 updated replicas are available - Normal Synced 5s flagger Promotion completed! Scaling down podinfo.test -``` - -**Note** that if you apply new changes to the deployment during the canary analysis, Flagger will restart the analysis. - -You can monitor all canaries with: - -```bash -watch kubectl get canaries --all-namespaces - -NAMESPACE NAME STATUS WEIGHT LASTTRANSITIONTIME -test podinfo Progressing 15 2019-05-17T14:05:07Z -prod frontend Succeeded 0 2019-05-17T16:15:07Z -prod backend Failed 0 2019-05-17T17:05:07Z -``` - -### Automated rollback - -During the canary analysis you can generate HTTP 500 errors and high latency to test if Flagger pauses and rolls back the faulted version. - -Trigger another canary deployment: - -```bash -kubectl -n test set image deployment/podinfo \ -podinfod=stefanprodan/podinfo:2.0.2 -``` - -Generate HTTP 500 errors: - -```bash -watch curl http://app.example.com/status/500 -``` - -Generate high latency: - -```bash -watch curl http://app.example.com/delay/2 -``` - -When the number of failed checks reaches the canary analysis threshold, the traffic is routed back to the primary, -the canary is scaled to zero and the rollout is marked as failed. - -```text -kubectl -n test describe canary/podinfo - -Status: - Canary Weight: 0 - Failed Checks: 10 - Phase: Failed -Events: - Type Reason Age From Message - ---- ------ ---- ---- ------- - Normal Synced 3m flagger Starting canary deployment for podinfo.test - Normal Synced 3m flagger Advance podinfo.test canary weight 5 - Normal Synced 3m flagger Advance podinfo.test canary weight 10 - Normal Synced 3m flagger Advance podinfo.test canary weight 15 - Normal Synced 3m flagger Halt podinfo.test advancement success rate 69.17% < 99% - Normal Synced 2m flagger Halt podinfo.test advancement success rate 61.39% < 99% - Normal Synced 2m flagger Halt podinfo.test advancement success rate 55.06% < 99% - Normal Synced 2m flagger Halt podinfo.test advancement success rate 47.00% < 99% - Normal Synced 2m flagger (combined from similar events): Halt podinfo.test advancement success rate 38.08% < 99% - Warning Synced 1m flagger Rolling back podinfo.test failed checks threshold reached 10 - Warning Synced 1m flagger Canary failed! Scaling down podinfo.test -``` - -### Custom metrics - -The canary analysis can be extended with Prometheus queries. - -The demo app is instrumented with Prometheus so you can create a custom check that will use the HTTP request duration -histogram to validate the canary. - -Edit the canary analysis and add the following metric: - -```yaml - canaryAnalysis: - metrics: - - name: "404s percentage" - threshold: 5 - query: | - 100 - sum( - rate( - http_request_duration_seconds_count{ - kubernetes_namespace="test", - kubernetes_pod_name=~"podinfo-[0-9a-zA-Z]+(-[0-9a-zA-Z]+)" - status!="404" - }[1m] - ) - ) - / - sum( - rate( - http_request_duration_seconds_count{ - kubernetes_namespace="test", - kubernetes_pod_name=~"podinfo-[0-9a-zA-Z]+(-[0-9a-zA-Z]+)" - }[1m] - ) - ) * 100 -``` - -The above configuration validates the canary by checking if the HTTP 404 req/sec percentage is below 5 -percent of the total traffic. If the 404s rate reaches the 5% threshold, then the canary fails. - -Trigger a canary deployment by updating the container image: - -```bash -kubectl -n test set image deployment/podinfo \ -podinfod=stefanprodan/podinfo:2.0.3 -``` - -Generate 404s: - -```bash -watch curl http://app.example.com/status/400 -``` - -Watch Flagger logs: - -``` -kubectl -n gloo-system logs deployment/flagger -f | jq .msg - -Starting canary deployment for podinfo.test -Advance podinfo.test canary weight 5 -Advance podinfo.test canary weight 10 -Advance podinfo.test canary weight 15 -Halt podinfo.test advancement 404s percentage 6.20 > 5 -Halt podinfo.test advancement 404s percentage 6.45 > 5 -Halt podinfo.test advancement 404s percentage 7.60 > 5 -Halt podinfo.test advancement 404s percentage 8.69 > 5 -Halt podinfo.test advancement 404s percentage 9.70 > 5 -Rolling back podinfo.test failed checks threshold reached 5 -Canary failed! Scaling down podinfo.test -``` - -If you have Slack configured, Flagger will send a notification with the reason why the canary failed. diff --git a/docs/gitbook/usage/linkerd-progressive-delivery.md b/docs/gitbook/usage/linkerd-progressive-delivery.md deleted file mode 100644 index 815b4856..00000000 --- a/docs/gitbook/usage/linkerd-progressive-delivery.md +++ /dev/null @@ -1,471 +0,0 @@ -# Linkerd Canary Deployments - -This guide shows you how to use Linkerd and Flagger to automate canary deployments. - -![Flagger Linkerd Traffic Split](https://raw.githubusercontent.com/weaveworks/flagger/master/docs/diagrams/flagger-linkerd-traffic-split.png) - -### Prerequisites - -Flagger requires a Kubernetes cluster **v1.11** or newer and Linkerd **2.4** or newer. - -Install Flagger in the linkerd namespace: - -```bash -kubectl apply -k github.com/weaveworks/flagger//kustomize/linkerd -``` - -Note that you'll need kubectl 1.14 or newer to run the above command. - -To enable Slack or MS Teams notifications, -see Flagger's [install docs](https://docs.flagger.app/install/flagger-install-on-kubernetes) for Kustomize or Helm options. - -### Bootstrap - -Flagger takes a Kubernetes deployment and optionally a horizontal pod autoscaler (HPA), -then creates a series of objects (Kubernetes deployments, ClusterIP services and SMI traffic split). -These objects expose the application inside the mesh and drive the canary analysis and promotion. - -Create a test namespace and enable Linkerd proxy injection: - -```bash -kubectl create ns test -kubectl annotate namespace test linkerd.io/inject=enabled -``` - -Install the load testing service to generate traffic during the canary analysis: - -```bash -kubectl apply -k github.com/weaveworks/flagger//kustomize/tester -``` - -Create a deployment and a horizontal pod autoscaler: - -```bash -kubectl apply -k github.com/weaveworks/flagger//kustomize/podinfo -``` - -Create a canary custom resource for the podinfo deployment: - -```yaml -apiVersion: flagger.app/v1alpha3 -kind: Canary -metadata: - name: podinfo - namespace: test -spec: - # deployment reference - targetRef: - apiVersion: apps/v1 - kind: Deployment - name: podinfo - # HPA reference (optional) - autoscalerRef: - apiVersion: autoscaling/v2beta1 - kind: HorizontalPodAutoscaler - name: podinfo - # the maximum time in seconds for the canary deployment - # to make progress before it is rollback (default 600s) - progressDeadlineSeconds: 60 - service: - # container port - port: 9898 - canaryAnalysis: - # schedule interval (default 60s) - interval: 30s - # max number of failed metric checks before rollback - threshold: 5 - # max traffic percentage routed to canary - # percentage (0-100) - maxWeight: 50 - # canary increment step - # percentage (0-100) - stepWeight: 5 - # Linkerd Prometheus checks - metrics: - - name: request-success-rate - # minimum req success rate (non 5xx responses) - # percentage (0-100) - threshold: 99 - interval: 1m - - name: request-duration - # maximum req duration P99 - # milliseconds - threshold: 500 - interval: 30s - # testing (optional) - webhooks: - - name: acceptance-test - type: pre-rollout - url: http://flagger-loadtester.test/ - timeout: 30s - metadata: - type: bash - cmd: "curl -sd 'test' http://podinfo-canary:9898/token | grep token" - - name: load-test - type: rollout - url: http://flagger-loadtester.test/ - metadata: - cmd: "hey -z 2m -q 10 -c 2 http://podinfo:9898/" -``` - -Save the above resource as podinfo-canary.yaml and then apply it: - -```bash -kubectl apply -f ./podinfo-canary.yaml -``` - -When the canary analysis starts, Flagger will call the pre-rollout webhooks before routing traffic to the canary. -The canary analysis will run for five minutes while validating the HTTP metrics and rollout hooks every half a minute. - -After a couple of seconds Flagger will create the canary objects: - -```bash -# applied -deployment.apps/podinfo -horizontalpodautoscaler.autoscaling/podinfo -ingresses.extensions/podinfo -canary.flagger.app/podinfo - -# generated -deployment.apps/podinfo-primary -horizontalpodautoscaler.autoscaling/podinfo-primary -service/podinfo -service/podinfo-canary -service/podinfo-primary -trafficsplits.split.smi-spec.io/podinfo -``` - -After the boostrap, the podinfo deployment will be scaled to zero and the traffic to `podinfo.test` will be routed -to the primary pods. During the canary analysis, the `podinfo-canary.test` address can be used to target directly the canary pods. - -### Automated canary promotion - -Flagger implements a control loop that gradually shifts traffic to the canary while measuring key performance indicators -like HTTP requests success rate, requests average duration and pod health. -Based on analysis of the KPIs a canary is promoted or aborted, and the analysis result is published to Slack. - -![Flagger Canary Stages](https://raw.githubusercontent.com/weaveworks/flagger/master/docs/diagrams/flagger-canary-steps.png) - -Trigger a canary deployment by updating the container image: - -```bash -kubectl -n test set image deployment/podinfo \ -podinfod=stefanprodan/podinfo:2.0.1 -``` - -Flagger detects that the deployment revision changed and starts a new rollout: - -```text -kubectl -n test describe canary/podinfo - -Status: - Canary Weight: 0 - Failed Checks: 0 - Phase: Succeeded -Events: - New revision detected! Scaling up podinfo.test - Waiting for podinfo.test rollout to finish: 0 of 1 updated replicas are available - Pre-rollout check acceptance-test passed - Advance podinfo.test canary weight 5 - Advance podinfo.test canary weight 10 - Advance podinfo.test canary weight 15 - Advance podinfo.test canary weight 20 - Advance podinfo.test canary weight 25 - Waiting for podinfo.test rollout to finish: 1 of 2 updated replicas are available - Advance podinfo.test canary weight 30 - Advance podinfo.test canary weight 35 - Advance podinfo.test canary weight 40 - Advance podinfo.test canary weight 45 - Advance podinfo.test canary weight 50 - Copying podinfo.test template spec to podinfo-primary.test - Waiting for podinfo-primary.test rollout to finish: 1 of 2 updated replicas are available - Promotion completed! Scaling down podinfo.test -``` - -**Note** that if you apply new changes to the deployment during the canary analysis, Flagger will restart the analysis. - -A canary deployment is triggered by changes in any of the following objects: -* Deployment PodSpec (container image, command, ports, env, resources, etc) -* ConfigMaps mounted as volumes or mapped to environment variables -* Secrets mounted as volumes or mapped to environment variables - -You can monitor all canaries with: - -```bash -watch kubectl get canaries --all-namespaces - -NAMESPACE NAME STATUS WEIGHT LASTTRANSITIONTIME -test podinfo Progressing 15 2019-06-30T14:05:07Z -prod frontend Succeeded 0 2019-06-30T16:15:07Z -prod backend Failed 0 2019-06-30T17:05:07Z -``` - -### Automated rollback - -During the canary analysis you can generate HTTP 500 errors and high latency to test if Flagger pauses and rolls back the faulted version. - -Trigger another canary deployment: - -```bash -kubectl -n test set image deployment/podinfo \ -podinfod=stefanprodan/podinfo:2.0.2 -``` - -Exec into the load tester pod with: - -```bash -kubectl -n test exec -it flagger-loadtester-xx-xx sh -``` - -Generate HTTP 500 errors: - -```bash -watch -n 1 curl http://podinfo-canary.test:9898/status/500 -``` - -Generate latency: - -```bash -watch -n 1 curl http://podinfo-canary.test:9898/delay/1 -``` - -When the number of failed checks reaches the canary analysis threshold, the traffic is routed back to the primary, -the canary is scaled to zero and the rollout is marked as failed. - -```text -kubectl -n test describe canary/podinfo - -Status: - Canary Weight: 0 - Failed Checks: 10 - Phase: Failed -Events: - Starting canary analysis for podinfo.test - Pre-rollout check acceptance-test passed - Advance podinfo.test canary weight 5 - Advance podinfo.test canary weight 10 - Advance podinfo.test canary weight 15 - Halt podinfo.test advancement success rate 69.17% < 99% - Halt podinfo.test advancement success rate 61.39% < 99% - Halt podinfo.test advancement success rate 55.06% < 99% - Halt podinfo.test advancement request duration 1.20s > 0.5s - Halt podinfo.test advancement request duration 1.45s > 0.5s - Rolling back podinfo.test failed checks threshold reached 5 - Canary failed! Scaling down podinfo.test -``` - -### Custom metrics - -The canary analysis can be extended with Prometheus queries. - -Let's a define a check for not found errors. Edit the canary analysis and add the following metric: - -```yaml - canaryAnalysis: - metrics: - - name: "404s percentage" - threshold: 3 - query: | - 100 - sum( - rate( - response_total{ - namespace="test", - deployment="podinfo", - status_code!="404", - direction="inbound" - }[1m] - ) - ) - / - sum( - rate( - response_total{ - namespace="test", - deployment="podinfo", - direction="inbound" - }[1m] - ) - ) - * 100 -``` - -The above configuration validates the canary version by checking if the HTTP 404 req/sec percentage is below -three percent of the total traffic. If the 404s rate reaches the 3% threshold, then the analysis is aborted and the -canary is marked as failed. - -Trigger a canary deployment by updating the container image: - -```bash -kubectl -n test set image deployment/podinfo \ -podinfod=stefanprodan/podinfo:2.0.3 -``` - -Generate 404s: - -```bash -watch -n 1 curl http://podinfo-canary:9898/status/404 -``` - -Watch Flagger logs: - -``` -kubectl -n linkerd logs deployment/flagger -f | jq .msg - -Starting canary deployment for podinfo.test -Pre-rollout check acceptance-test passed -Advance podinfo.test canary weight 5 -Halt podinfo.test advancement 404s percentage 6.20 > 3 -Halt podinfo.test advancement 404s percentage 6.45 > 3 -Halt podinfo.test advancement 404s percentage 7.22 > 3 -Halt podinfo.test advancement 404s percentage 6.50 > 3 -Halt podinfo.test advancement 404s percentage 6.34 > 3 -Rolling back podinfo.test failed checks threshold reached 5 -Canary failed! Scaling down podinfo.test -``` - -If you have Slack configured, Flagger will send a notification with the reason why the canary failed. - -### Linkerd Ingress - -There are two ingress controllers that are compatible with both Flagger and Linkerd: NGINX and Gloo. - -Install NGINX: - -```bash -helm upgrade -i nginx-ingress stable/nginx-ingress \ ---namespace ingress-nginx -``` - -Create an ingress definition for podinfo that rewrites the incoming header to the internal service name (required by Linkerd): - -```yaml -apiVersion: extensions/v1beta1 -kind: Ingress -metadata: - name: podinfo - namespace: test - labels: - app: podinfo - annotations: - kubernetes.io/ingress.class: "nginx" - nginx.ingress.kubernetes.io/configuration-snippet: | - proxy_set_header l5d-dst-override $service_name.$namespace.svc.cluster.local:9898; - proxy_hide_header l5d-remote-ip; - proxy_hide_header l5d-server-id; -spec: - rules: - - host: app.example.com - http: - paths: - - backend: - serviceName: podinfo - servicePort: 9898 -``` - -When using an ingress controller, the Linkerd traffic split does not apply to incoming traffic since NGINX in running outside of -the mesh. In order to run a canary analysis for a frontend app, Flagger creates a shadow ingress and sets the NGINX specific annotations. - -### A/B Testing - -Besides weighted routing, Flagger can be configured to route traffic to the canary based on HTTP match conditions. -In an A/B testing scenario, you'll be using HTTP headers or cookies to target a certain segment of your users. -This is particularly useful for frontend applications that require session affinity. - -![Flagger Linkerd Ingress](https://raw.githubusercontent.com/weaveworks/flagger/master/docs/diagrams/flagger-nginx-linkerd.png) - -Edit podinfo canary analysis, set the provider to `nginx`, add the ingress reference, remove the max/step weight and add the match conditions and iterations: - -```yaml -apiVersion: flagger.app/v1alpha3 -kind: Canary -metadata: - name: podinfo - namespace: test -spec: - # ingress reference - provider: nginx - ingressRef: - apiVersion: extensions/v1beta1 - kind: Ingress - name: podinfo - targetRef: - apiVersion: apps/v1 - kind: Deployment - name: podinfo - autoscalerRef: - apiVersion: autoscaling/v2beta1 - kind: HorizontalPodAutoscaler - name: podinfo - service: - # container port - port: 9898 - canaryAnalysis: - interval: 1m - threshold: 10 - iterations: 10 - match: - # curl -H 'X-Canary: always' http://app.example.com - - headers: - x-canary: - exact: "always" - # curl -b 'canary=always' http://app.example.com - - headers: - cookie: - exact: "canary" - # Linkerd Prometheus checks - metrics: - - name: request-success-rate - threshold: 99 - interval: 1m - - name: request-duration - threshold: 500 - interval: 30s - webhooks: - - name: acceptance-test - type: pre-rollout - url: http://flagger-loadtester.test/ - timeout: 30s - metadata: - type: bash - cmd: "curl -sd 'test' http://podinfo-canary:9898/token | grep token" - - name: load-test - type: rollout - url: http://flagger-loadtester.test/ - metadata: - cmd: "hey -z 2m -q 10 -c 2 -H 'Cookie: canary=always' http://app.example.com" -``` - -The above configuration will run an analysis for ten minutes targeting users that have a `canary` cookie set to `always` or -those that call the service using the `X-Canary: always` header. - -**Note** that the load test now targets the external address and uses the canary cookie. - -Trigger a canary deployment by updating the container image: - -```bash -kubectl -n test set image deployment/podinfo \ -podinfod=stefanprodan/podinfo:2.0.4 -``` - -Flagger detects that the deployment revision changed and starts the A/B testing: - -```text -kubectl -n test describe canary/podinfo - -Events: - Starting canary deployment for podinfo.test - Pre-rollout check acceptance-test passed - Advance podinfo.test canary iteration 1/10 - Advance podinfo.test canary iteration 2/10 - Advance podinfo.test canary iteration 3/10 - Advance podinfo.test canary iteration 4/10 - Advance podinfo.test canary iteration 5/10 - Advance podinfo.test canary iteration 6/10 - Advance podinfo.test canary iteration 7/10 - Advance podinfo.test canary iteration 8/10 - Advance podinfo.test canary iteration 9/10 - Advance podinfo.test canary iteration 10/10 - Copying podinfo.test template spec to podinfo-primary.test - Waiting for podinfo-primary.test rollout to finish: 1 of 2 updated replicas are available - Promotion completed! Scaling down podinfo.test -``` diff --git a/docs/gitbook/usage/monitoring.md b/docs/gitbook/usage/monitoring.md deleted file mode 100644 index 4f60151f..00000000 --- a/docs/gitbook/usage/monitoring.md +++ /dev/null @@ -1,70 +0,0 @@ -# Monitoring - -### Grafana - -Flagger comes with a Grafana dashboard made for canary analysis. Install Grafana with Helm: - -```bash -helm upgrade -i flagger-grafana flagger/grafana \ ---namespace=istio-system \ # or appmesh-system ---set url=http://prometheus:9090 -``` - -The dashboard shows the RED and USE metrics for the primary and canary workloads: - -![Canary Dashboard](https://raw.githubusercontent.com/weaveworks/flagger/master/docs/screens/grafana-canary-analysis.png) - -### Logging - -The canary errors and latency spikes have been recorded as Kubernetes events and logged by Flagger in json format: - -```text -kubectl -n istio-system logs deployment/flagger --tail=100 | jq .msg - -Starting canary deployment for podinfo.test -Advance podinfo.test canary weight 5 -Advance podinfo.test canary weight 10 -Advance podinfo.test canary weight 15 -Advance podinfo.test canary weight 20 -Advance podinfo.test canary weight 25 -Advance podinfo.test canary weight 30 -Advance podinfo.test canary weight 35 -Halt podinfo.test advancement success rate 98.69% < 99% -Advance podinfo.test canary weight 40 -Halt podinfo.test advancement request duration 1.515s > 500ms -Advance podinfo.test canary weight 45 -Advance podinfo.test canary weight 50 -Copying podinfo.test template spec to podinfo-primary.test -Halt podinfo-primary.test advancement waiting for rollout to finish: 1 old replicas are pending termination -Scaling down podinfo.test -Promotion completed! podinfo.test -``` - -### Metrics - -Flagger exposes Prometheus metrics that can be used to determine the canary analysis status and -the destination weight values: - -```bash -# Flagger version and mesh provider gauge -flagger_info{version="0.10.0", mesh_provider="istio"} 1 - -# Canaries total gauge -flagger_canary_total{namespace="test"} 1 - -# Canary promotion last known status gauge -# 0 - running, 1 - successful, 2 - failed -flagger_canary_status{name="podinfo" namespace="test"} 1 - -# Canary traffic weight gauge -flagger_canary_weight{workload="podinfo-primary" namespace="test"} 95 -flagger_canary_weight{workload="podinfo" namespace="test"} 5 - -# Seconds spent performing canary analysis histogram -flagger_canary_duration_seconds_bucket{name="podinfo",namespace="test",le="10"} 6 -flagger_canary_duration_seconds_bucket{name="podinfo",namespace="test",le="+Inf"} 6 -flagger_canary_duration_seconds_sum{name="podinfo",namespace="test"} 17.3561329 -flagger_canary_duration_seconds_count{name="podinfo",namespace="test"} 6 -``` - - diff --git a/docs/gitbook/usage/nginx-progressive-delivery.md b/docs/gitbook/usage/nginx-progressive-delivery.md deleted file mode 100644 index 2772850f..00000000 --- a/docs/gitbook/usage/nginx-progressive-delivery.md +++ /dev/null @@ -1,422 +0,0 @@ -# NGNIX Ingress Controller Canary Deployments - -This guide shows you how to use the NGINX ingress controller and Flagger to automate canary deployments and A/B testing. - -![Flagger NGINX Ingress Controller](https://raw.githubusercontent.com/weaveworks/flagger/master/docs/diagrams/flagger-nginx-overview.png) - -### Prerequisites - -Flagger requires a Kubernetes cluster **v1.11** or newer and NGINX ingress **0.24** or newer. - -Install NGINX with Helm: - -```bash -helm upgrade -i nginx-ingress stable/nginx-ingress \ ---namespace ingress-nginx \ ---set controller.stats.enabled=true \ ---set controller.metrics.enabled=true \ ---set controller.podAnnotations."prometheus\.io/scrape"=true \ ---set controller.podAnnotations."prometheus\.io/port"=10254 -``` - -Install Flagger and the Prometheus add-on in the same namespace as NGINX: - -```bash -helm repo add flagger https://flagger.app - -helm upgrade -i flagger flagger/flagger \ ---namespace ingress-nginx \ ---set prometheus.install=true \ ---set meshProvider=nginx -``` - -Optionally you can enable Slack notifications: - -```bash -helm upgrade -i flagger flagger/flagger \ ---reuse-values \ ---namespace ingress-nginx \ ---set slack.url=https://hooks.slack.com/services/YOUR/SLACK/WEBHOOK \ ---set slack.channel=general \ ---set slack.user=flagger -``` - -### Bootstrap - -Flagger takes a Kubernetes deployment and optionally a horizontal pod autoscaler (HPA), -then creates a series of objects (Kubernetes deployments, ClusterIP services and canary ingress). -These objects expose the application outside the cluster and drive the canary analysis and promotion. - -Create a test namespace: - -```bash -kubectl create ns test -``` - -Create a deployment and a horizontal pod autoscaler: - -```bash -kubectl apply -f ${REPO}/artifacts/nginx/deployment.yaml -kubectl apply -f ${REPO}/artifacts/nginx/hpa.yaml -``` - -Deploy the load testing service to generate traffic during the canary analysis: - -```bash -helm upgrade -i flagger-loadtester flagger/loadtester \ ---namespace=test -``` - -Create an ingress definition (replace `app.example.com` with your own domain): - -```yaml -apiVersion: extensions/v1beta1 -kind: Ingress -metadata: - name: podinfo - namespace: test - labels: - app: podinfo - annotations: - kubernetes.io/ingress.class: "nginx" -spec: - rules: - - host: app.example.com - http: - paths: - - backend: - serviceName: podinfo - servicePort: 9898 -``` - -Save the above resource as podinfo-ingress.yaml and then apply it: - -```bash -kubectl apply -f ./podinfo-ingress.yaml -``` - -Create a canary custom resource (replace `app.example.com` with your own domain): - -```yaml -apiVersion: flagger.app/v1alpha3 -kind: Canary -metadata: - name: podinfo - namespace: test -spec: - provider: nginx - # deployment reference - targetRef: - apiVersion: apps/v1 - kind: Deployment - name: podinfo - # ingress reference - ingressRef: - apiVersion: extensions/v1beta1 - kind: Ingress - name: podinfo - # HPA reference (optional) - autoscalerRef: - apiVersion: autoscaling/v2beta1 - kind: HorizontalPodAutoscaler - name: podinfo - # the maximum time in seconds for the canary deployment - # to make progress before it is rollback (default 600s) - progressDeadlineSeconds: 60 - service: - # container port - port: 9898 - canaryAnalysis: - # schedule interval (default 60s) - interval: 10s - # max number of failed metric checks before rollback - threshold: 10 - # max traffic percentage routed to canary - # percentage (0-100) - maxWeight: 50 - # canary increment step - # percentage (0-100) - stepWeight: 5 - # NGINX Prometheus checks - metrics: - - name: request-success-rate - # minimum req success rate (non 5xx responses) - # percentage (0-100) - threshold: 99 - interval: 1m - # load testing (optional) - webhooks: - - name: load-test - url: http://flagger-loadtester.test/ - timeout: 5s - metadata: - type: cmd - cmd: "hey -z 1m -q 10 -c 2 http://app.example.com/" -``` - -Save the above resource as podinfo-canary.yaml and then apply it: - -```bash -kubectl apply -f ./podinfo-canary.yaml -``` - -After a couple of seconds Flagger will create the canary objects: - -```bash -# applied -deployment.apps/podinfo -horizontalpodautoscaler.autoscaling/podinfo -ingresses.extensions/podinfo -canary.flagger.app/podinfo - -# generated -deployment.apps/podinfo-primary -horizontalpodautoscaler.autoscaling/podinfo-primary -service/podinfo -service/podinfo-canary -service/podinfo-primary -ingresses.extensions/podinfo-canary -``` - -### Automated canary promotion - -Flagger implements a control loop that gradually shifts traffic to the canary while measuring key performance indicators -like HTTP requests success rate, requests average duration and pod health. -Based on analysis of the KPIs a canary is promoted or aborted, and the analysis result is published to Slack. - -![Flagger Canary Stages](https://raw.githubusercontent.com/weaveworks/flagger/master/docs/diagrams/flagger-canary-steps.png) - -Trigger a canary deployment by updating the container image: - -```bash -kubectl -n test set image deployment/podinfo \ -podinfod=stefanprodan/podinfo:2.0.1 -``` - -Flagger detects that the deployment revision changed and starts a new rollout: - -```text -kubectl -n test describe canary/podinfo - -Status: - Canary Weight: 0 - Failed Checks: 0 - Phase: Succeeded -Events: - Type Reason Age From Message - ---- ------ ---- ---- ------- - Normal Synced 3m flagger New revision detected podinfo.test - Normal Synced 3m flagger Scaling up podinfo.test - Warning Synced 3m flagger Waiting for podinfo.test rollout to finish: 0 of 1 updated replicas are available - Normal Synced 3m flagger Advance podinfo.test canary weight 5 - Normal Synced 3m flagger Advance podinfo.test canary weight 10 - Normal Synced 3m flagger Advance podinfo.test canary weight 15 - Normal Synced 2m flagger Advance podinfo.test canary weight 20 - Normal Synced 2m flagger Advance podinfo.test canary weight 25 - Normal Synced 1m flagger Advance podinfo.test canary weight 30 - Normal Synced 1m flagger Advance podinfo.test canary weight 35 - Normal Synced 55s flagger Advance podinfo.test canary weight 40 - Normal Synced 45s flagger Advance podinfo.test canary weight 45 - Normal Synced 35s flagger Advance podinfo.test canary weight 50 - Normal Synced 25s flagger Copying podinfo.test template spec to podinfo-primary.test - Warning Synced 15s flagger Waiting for podinfo-primary.test rollout to finish: 1 of 2 updated replicas are available - Normal Synced 5s flagger Promotion completed! Scaling down podinfo.test -``` - -**Note** that if you apply new changes to the deployment during the canary analysis, Flagger will restart the analysis. - -You can monitor all canaries with: - -```bash -watch kubectl get canaries --all-namespaces - -NAMESPACE NAME STATUS WEIGHT LASTTRANSITIONTIME -test podinfo Progressing 15 2019-05-06T14:05:07Z -prod frontend Succeeded 0 2019-05-05T16:15:07Z -prod backend Failed 0 2019-05-04T17:05:07Z -``` - -### Automated rollback - -During the canary analysis you can generate HTTP 500 errors to test if Flagger pauses and rolls back the faulted version. - -Trigger another canary deployment: - -```bash -kubectl -n test set image deployment/podinfo \ -podinfod=stefanprodan/podinfo:2.0.2 -``` - -Generate HTTP 500 errors: - -```bash -watch curl http://app.example.com/status/500 -``` - -When the number of failed checks reaches the canary analysis threshold, the traffic is routed back to the primary, -the canary is scaled to zero and the rollout is marked as failed. - -```text -kubectl -n test describe canary/podinfo - -Status: - Canary Weight: 0 - Failed Checks: 10 - Phase: Failed -Events: - Type Reason Age From Message - ---- ------ ---- ---- ------- - Normal Synced 3m flagger Starting canary deployment for podinfo.test - Normal Synced 3m flagger Advance podinfo.test canary weight 5 - Normal Synced 3m flagger Advance podinfo.test canary weight 10 - Normal Synced 3m flagger Advance podinfo.test canary weight 15 - Normal Synced 3m flagger Halt podinfo.test advancement success rate 69.17% < 99% - Normal Synced 2m flagger Halt podinfo.test advancement success rate 61.39% < 99% - Normal Synced 2m flagger Halt podinfo.test advancement success rate 55.06% < 99% - Normal Synced 2m flagger Halt podinfo.test advancement success rate 47.00% < 99% - Normal Synced 2m flagger (combined from similar events): Halt podinfo.test advancement success rate 38.08% < 99% - Warning Synced 1m flagger Rolling back podinfo.test failed checks threshold reached 10 - Warning Synced 1m flagger Canary failed! Scaling down podinfo.test -``` - -### Custom metrics - -The canary analysis can be extended with Prometheus queries. - -The demo app is instrumented with Prometheus so you can create a custom check that will use the HTTP request duration -histogram to validate the canary. - -Edit the canary analysis and add the following metric: - -```yaml - canaryAnalysis: - metrics: - - name: "latency" - threshold: 0.5 - interval: 1m - query: | - histogram_quantile(0.99, - sum( - rate( - http_request_duration_seconds_bucket{ - kubernetes_namespace="test", - kubernetes_pod_name=~"podinfo-[0-9a-zA-Z]+(-[0-9a-zA-Z]+)" - }[1m] - ) - ) by (le) - ) -``` - -The threshold is set to 500ms so if the average request duration in the last minute -goes over half a second then the analysis will fail and the canary will not be promoted. - -Trigger a canary deployment by updating the container image: - -```bash -kubectl -n test set image deployment/podinfo \ -podinfod=stefanprodan/podinfo:2.0.3 -``` - -Generate high response latency: - -```bash -watch curl http://app.exmaple.com/delay/2 -``` - -Watch Flagger logs: - -``` -kubectl -n nginx-ingress logs deployment/flagger -f | jq .msg - -Starting canary deployment for podinfo.test -Advance podinfo.test canary weight 5 -Advance podinfo.test canary weight 10 -Advance podinfo.test canary weight 15 -Halt podinfo.test advancement latency 1.20 > 0.5 -Halt podinfo.test advancement latency 1.45 > 0.5 -Halt podinfo.test advancement latency 1.60 > 0.5 -Halt podinfo.test advancement latency 1.69 > 0.5 -Halt podinfo.test advancement latency 1.70 > 0.5 -Rolling back podinfo.test failed checks threshold reached 5 -Canary failed! Scaling down podinfo.test -``` - -If you have Slack configured, Flagger will send a notification with the reason why the canary failed. - -### A/B Testing - -Besides weighted routing, Flagger can be configured to route traffic to the canary based on HTTP match conditions. -In an A/B testing scenario, you'll be using HTTP headers or cookies to target a certain segment of your users. -This is particularly useful for frontend applications that require session affinity. - -![Flagger A/B Testing Stages](https://raw.githubusercontent.com/weaveworks/flagger/master/docs/diagrams/flagger-abtest-steps.png) - -Edit the canary analysis, remove the max/step weight and add the match conditions and iterations: - -```yaml - canaryAnalysis: - interval: 1m - threshold: 10 - iterations: 10 - match: - # curl -H 'X-Canary: insider' http://app.example.com - - headers: - x-canary: - exact: "insider" - # curl -b 'canary=always' http://app.example.com - - headers: - cookie: - exact: "canary" - metrics: - - name: request-success-rate - threshold: 99 - interval: 1m - webhooks: - - name: load-test - url: http://localhost:8888/ - timeout: 5s - metadata: - type: cmd - cmd: "hey -z 1m -q 10 -c 2 -H 'Cookie: canary=always' http://app.example.com/" - logCmdOutput: "true" -``` - -The above configuration will run an analysis for ten minutes targeting users that have a `canary` cookie set to `always` or -those that call the service using the `X-Canary: insider` header. - -Trigger a canary deployment by updating the container image: - -```bash -kubectl -n test set image deployment/podinfo \ -podinfod=stefanprodan/podinfo:2.0.4 -``` - -Flagger detects that the deployment revision changed and starts the A/B testing: - -```text -kubectl -n test describe canary/podinfo - -Status: - Failed Checks: 0 - Phase: Succeeded -Events: - Type Reason Age From Message - ---- ------ ---- ---- ------- - Normal Synced 3m flagger New revision detected podinfo.test - Normal Synced 3m flagger Scaling up podinfo.test - Warning Synced 3m flagger Waiting for podinfo.test rollout to finish: 0 of 1 updated replicas are available - Normal Synced 3m flagger Advance podinfo.test canary iteration 1/10 - Normal Synced 3m flagger Advance podinfo.test canary iteration 2/10 - Normal Synced 3m flagger Advance podinfo.test canary iteration 3/10 - Normal Synced 2m flagger Advance podinfo.test canary iteration 4/10 - Normal Synced 2m flagger Advance podinfo.test canary iteration 5/10 - Normal Synced 1m flagger Advance podinfo.test canary iteration 6/10 - Normal Synced 1m flagger Advance podinfo.test canary iteration 7/10 - Normal Synced 55s flagger Advance podinfo.test canary iteration 8/10 - Normal Synced 45s flagger Advance podinfo.test canary iteration 9/10 - Normal Synced 35s flagger Advance podinfo.test canary iteration 10/10 - Normal Synced 25s flagger Copying podinfo.test template spec to podinfo-primary.test - Warning Synced 15s flagger Waiting for podinfo-primary.test rollout to finish: 1 of 2 updated replicas are available - Normal Synced 5s flagger Promotion completed! Scaling down podinfo.test -``` - diff --git a/docs/gitbook/usage/progressive-delivery.md b/docs/gitbook/usage/progressive-delivery.md deleted file mode 100644 index d2ae025f..00000000 --- a/docs/gitbook/usage/progressive-delivery.md +++ /dev/null @@ -1,231 +0,0 @@ -# Istio Canary Deployments - -This guide shows you how to use Istio and Flagger to automate canary deployments. - -### Bootstrap - -Create a test namespace with Istio sidecar injection enabled: - -```bash -export REPO=https://raw.githubusercontent.com/weaveworks/flagger/master - -kubectl apply -f ${REPO}/artifacts/namespaces/test.yaml -``` - -Create a deployment and a horizontal pod autoscaler: - -```bash -kubectl apply -f ${REPO}/artifacts/canaries/deployment.yaml -kubectl apply -f ${REPO}/artifacts/canaries/hpa.yaml -``` - -Deploy the load testing service to generate traffic during the canary analysis: - -```bash -kubectl -n test apply -f ${REPO}/artifacts/loadtester/deployment.yaml -kubectl -n test apply -f ${REPO}/artifacts/loadtester/service.yaml -``` - -Create a canary custom resource (replace example.com with your own domain): - -```yaml -apiVersion: flagger.app/v1alpha3 -kind: Canary -metadata: - name: podinfo - namespace: test -spec: - # deployment reference - targetRef: - apiVersion: apps/v1 - kind: Deployment - name: podinfo - # the maximum time in seconds for the canary deployment - # to make progress before it is rollback (default 600s) - progressDeadlineSeconds: 60 - # HPA reference (optional) - autoscalerRef: - apiVersion: autoscaling/v2beta1 - kind: HorizontalPodAutoscaler - name: podinfo - service: - # container port - port: 9898 - # Istio gateways (optional) - gateways: - - public-gateway.istio-system.svc.cluster.local - # Istio virtual service host names (optional) - hosts: - - app.example.com - canaryAnalysis: - # schedule interval (default 60s) - interval: 1m - # max number of failed metric checks before rollback - threshold: 5 - # max traffic percentage routed to canary - # percentage (0-100) - maxWeight: 50 - # canary increment step - # percentage (0-100) - stepWeight: 10 - metrics: - - name: request-success-rate - # minimum req success rate (non 5xx responses) - # percentage (0-100) - threshold: 99 - interval: 1m - - name: request-duration - # maximum req duration P99 - # milliseconds - threshold: 500 - interval: 30s - # testing (optional) - webhooks: - - name: acceptance-test - type: pre-rollout - url: http://flagger-loadtester.test/ - timeout: 30s - metadata: - type: bash - cmd: "curl -sd 'test' http://podinfo-canary:9898/token | grep token" - - name: load-test - url: http://flagger-loadtester.test/ - timeout: 5s - metadata: - cmd: "hey -z 1m -q 10 -c 2 http://podinfo-canary.test:9898/" -``` - -Save the above resource as podinfo-canary.yaml and then apply it: - -```bash -kubectl apply -f ./podinfo-canary.yaml -``` - -When the canary analysis starts, Flagger will call the pre-rollout webhooks before routing traffic to the canary. -The canary analysis will run for five minutes while validating the HTTP metrics and rollout hooks every minute. - -After a couple of seconds Flagger will create the canary objects: - -```bash -# applied -deployment.apps/podinfo -horizontalpodautoscaler.autoscaling/podinfo -canary.flagger.app/podinfo - -# generated -deployment.apps/podinfo-primary -horizontalpodautoscaler.autoscaling/podinfo-primary -service/podinfo -service/podinfo-canary -service/podinfo-primary -destinationrule.networking.istio.io/podinfo-canary -destinationrule.networking.istio.io/podinfo-primary -virtualservice.networking.istio.io/podinfo -``` - -### Automated canary promotion - -Trigger a canary deployment by updating the container image: - -```bash -kubectl -n test set image deployment/podinfo \ -podinfod=stefanprodan/podinfo:2.0.1 -``` - -Flagger detects that the deployment revision changed and starts a new rollout: - -```text -kubectl -n test describe canary/podinfo - -Status: - Canary Weight: 0 - Failed Checks: 0 - Phase: Succeeded -Events: - Type Reason Age From Message - ---- ------ ---- ---- ------- - Normal Synced 3m flagger New revision detected podinfo.test - Normal Synced 3m flagger Scaling up podinfo.test - Warning Synced 3m flagger Waiting for podinfo.test rollout to finish: 0 of 1 updated replicas are available - Normal Synced 3m flagger Advance podinfo.test canary weight 5 - Normal Synced 3m flagger Advance podinfo.test canary weight 10 - Normal Synced 3m flagger Advance podinfo.test canary weight 15 - Normal Synced 2m flagger Advance podinfo.test canary weight 20 - Normal Synced 2m flagger Advance podinfo.test canary weight 25 - Normal Synced 1m flagger Advance podinfo.test canary weight 30 - Normal Synced 1m flagger Advance podinfo.test canary weight 35 - Normal Synced 55s flagger Advance podinfo.test canary weight 40 - Normal Synced 45s flagger Advance podinfo.test canary weight 45 - Normal Synced 35s flagger Advance podinfo.test canary weight 50 - Normal Synced 25s flagger Copying podinfo.test template spec to podinfo-primary.test - Warning Synced 15s flagger Waiting for podinfo-primary.test rollout to finish: 1 of 2 updated replicas are available - Normal Synced 5s flagger Promotion completed! Scaling down podinfo.test -``` - -**Note** that if you apply new changes to the deployment during the canary analysis, Flagger will restart the analysis. - -You can monitor all canaries with: - -```bash -watch kubectl get canaries --all-namespaces - -NAMESPACE NAME STATUS WEIGHT LASTTRANSITIONTIME -test podinfo Progressing 15 2019-01-16T14:05:07Z -prod frontend Succeeded 0 2019-01-15T16:15:07Z -prod backend Failed 0 2019-01-14T17:05:07Z -``` - -### Automated rollback - -During the canary analysis you can generate HTTP 500 errors and high latency to test if Flagger pauses the rollout. - -Trigger another canary deployment: - -```bash -kubectl -n test set image deployment/podinfo \ -podinfod=stefanprodan/podinfo:2.0.2 -``` - -Exec into the load tester pod with: - -```bash -kubectl -n test exec -it flagger-loadtester-xx-xx sh -``` - -Generate HTTP 500 errors: - -```bash -watch curl http://podinfo-canary:9898/status/500 -``` - -Generate latency: - -```bash -watch curl http://podinfo-canary:9898/delay/1 -``` - -When the number of failed checks reaches the canary analysis threshold, the traffic is routed back to the primary, -the canary is scaled to zero and the rollout is marked as failed. - -```text -kubectl -n test describe canary/podinfo - -Status: - Canary Weight: 0 - Failed Checks: 10 - Phase: Failed -Events: - Type Reason Age From Message - ---- ------ ---- ---- ------- - Normal Synced 3m flagger Starting canary deployment for podinfo.test - Normal Synced 3m flagger Advance podinfo.test canary weight 5 - Normal Synced 3m flagger Advance podinfo.test canary weight 10 - Normal Synced 3m flagger Advance podinfo.test canary weight 15 - Normal Synced 3m flagger Halt podinfo.test advancement success rate 69.17% < 99% - Normal Synced 2m flagger Halt podinfo.test advancement success rate 61.39% < 99% - Normal Synced 2m flagger Halt podinfo.test advancement success rate 55.06% < 99% - Normal Synced 2m flagger Halt podinfo.test advancement success rate 47.00% < 99% - Normal Synced 2m flagger (combined from similar events): Halt podinfo.test advancement success rate 38.08% < 99% - Warning Synced 1m flagger Rolling back podinfo.test failed checks threshold reached 10 - Warning Synced 1m flagger Canary failed! Scaling down podinfo.test -``` diff --git a/docs/logo/flagger-icon.jpg b/docs/logo/flagger-icon.jpg deleted file mode 100644 index 4c3f85f8..00000000 Binary files a/docs/logo/flagger-icon.jpg and /dev/null differ diff --git a/docs/logo/flagger-icon.png b/docs/logo/flagger-icon.png deleted file mode 100644 index bae658d7..00000000 Binary files a/docs/logo/flagger-icon.png and /dev/null differ diff --git a/docs/logo/flagger-weaveworks.svg b/docs/logo/flagger-weaveworks.svg deleted file mode 100644 index 7ccd3cf0..00000000 --- a/docs/logo/flagger-weaveworks.svg +++ /dev/null @@ -1,71 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/docs/screens/demo-backend-dashboard.png b/docs/screens/demo-backend-dashboard.png deleted file mode 100644 index d8303417..00000000 Binary files a/docs/screens/demo-backend-dashboard.png and /dev/null differ diff --git a/docs/screens/demo-frontend-dashboard.png b/docs/screens/demo-frontend-dashboard.png deleted file mode 100644 index febf78cd..00000000 Binary files a/docs/screens/demo-frontend-dashboard.png and /dev/null differ diff --git a/docs/screens/demo-frontend-jaeger.png b/docs/screens/demo-frontend-jaeger.png deleted file mode 100644 index 28da7ff0..00000000 Binary files a/docs/screens/demo-frontend-jaeger.png and /dev/null differ diff --git a/docs/screens/demo-frontend.png b/docs/screens/demo-frontend.png deleted file mode 100644 index 1dbf922c..00000000 Binary files a/docs/screens/demo-frontend.png and /dev/null differ diff --git a/docs/screens/flagger-grafana-appmesh.png b/docs/screens/flagger-grafana-appmesh.png deleted file mode 100644 index b1d30999..00000000 Binary files a/docs/screens/flagger-grafana-appmesh.png and /dev/null differ diff --git a/docs/screens/flagger-grafana-dashboard.png b/docs/screens/flagger-grafana-dashboard.png deleted file mode 100644 index 3d65a7d7..00000000 Binary files a/docs/screens/flagger-grafana-dashboard.png and /dev/null differ diff --git a/docs/screens/flagger-ms-teams-failed.png b/docs/screens/flagger-ms-teams-failed.png deleted file mode 100644 index 5155877f..00000000 Binary files a/docs/screens/flagger-ms-teams-failed.png and /dev/null differ diff --git a/docs/screens/flagger-ms-teams-notifications.png b/docs/screens/flagger-ms-teams-notifications.png deleted file mode 100644 index a592efd3..00000000 Binary files a/docs/screens/flagger-ms-teams-notifications.png and /dev/null differ diff --git a/docs/screens/grafana-canary-analysis.png b/docs/screens/grafana-canary-analysis.png deleted file mode 100644 index a40de5c3..00000000 Binary files a/docs/screens/grafana-canary-analysis.png and /dev/null differ diff --git a/docs/screens/slack-canary-failed.png b/docs/screens/slack-canary-failed.png deleted file mode 100644 index b150f280..00000000 Binary files a/docs/screens/slack-canary-failed.png and /dev/null differ diff --git a/docs/screens/slack-canary-notifications.png b/docs/screens/slack-canary-notifications.png deleted file mode 100644 index 4e948711..00000000 Binary files a/docs/screens/slack-canary-notifications.png and /dev/null differ diff --git a/go.mod b/go.mod deleted file mode 100644 index 59b27495..00000000 --- a/go.mod +++ /dev/null @@ -1,72 +0,0 @@ -module github.com/weaveworks/flagger - -go 1.12 - -require ( - cloud.google.com/go v0.37.4 // indirect - github.com/Masterminds/semver v1.4.2 - github.com/beorn7/perks v1.0.0 // indirect - github.com/bxcodec/faker v2.0.1+incompatible // indirect - github.com/envoyproxy/go-control-plane v0.8.0 // indirect - github.com/gogo/googleapis v1.2.0 // indirect - github.com/gogo/protobuf v1.2.1 - github.com/golang/protobuf v1.3.1 // indirect - github.com/golang/snappy v0.0.1 // indirect - github.com/google/btree v1.0.0 // indirect - github.com/google/go-cmp v0.3.0 - github.com/hashicorp/consul v1.4.4 // indirect - github.com/hashicorp/go-cleanhttp v0.5.1 // indirect - github.com/hashicorp/go-retryablehttp v0.5.3 // indirect - github.com/hashicorp/go-rootcerts v1.0.0 // indirect - github.com/hashicorp/go-sockaddr v1.0.2 // indirect - github.com/hashicorp/hcl v1.0.0 // indirect - github.com/hashicorp/serf v0.8.3 // indirect - github.com/hashicorp/vault v1.1.0 // indirect - github.com/imdario/mergo v0.3.7 // indirect - github.com/k0kubun/pp v3.0.1+incompatible // indirect - github.com/linkerd/linkerd2 v0.0.0-20190221030352-5e47cb150a33 // indirect - github.com/lyft/protoc-gen-validate v0.0.14 // indirect - github.com/mattn/go-colorable v0.1.1 // indirect - github.com/mattn/go-isatty v0.0.7 // indirect - github.com/mitchellh/go-homedir v1.1.0 // indirect - github.com/mitchellh/go-testing-interface v1.0.0 // indirect - github.com/mitchellh/hashstructure v1.0.0 - github.com/pkg/errors v0.8.1 - github.com/prometheus/client_golang v0.9.3-0.20190127221311-3c4408c8b829 - github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90 // indirect - github.com/prometheus/common v0.3.0 // indirect - github.com/prometheus/procfs v0.0.0-20190416084830-8368d24ba045 // indirect - github.com/radovskyb/watcher v1.0.6 // indirect - github.com/ryanuber/go-glob v1.0.0 // indirect - github.com/solo-io/gloo v0.13.17 - github.com/solo-io/go-utils v0.7.11 // indirect - github.com/solo-io/solo-kit v0.6.3 - github.com/solo-io/supergloo v0.3.11 - go.opencensus.io v0.20.2 // indirect - go.uber.org/zap v1.9.1 - golang.org/x/crypto v0.0.0-20190418161225-b43e412143f9 // indirect - golang.org/x/time v0.0.0-20190308202827-9d24e82272b4 // indirect - gopkg.in/h2non/gock.v1 v1.0.14 - k8s.io/api v0.0.0-20190620073856-dcce3486da33 - k8s.io/apiextensions-apiserver v0.0.0-20190315093550-53c4693659ed // indirect - k8s.io/apimachinery v0.0.0-20190620073744-d16981aedf33 - k8s.io/client-go v11.0.0+incompatible - k8s.io/code-generator v0.0.0-20190620073620-d55040311883 - k8s.io/kube-openapi v0.0.0-20190418160015-6b3d3b2d5666 // indirect -) - -replace ( - github.com/google/uuid => github.com/google/uuid v1.0.0 - golang.org/x/crypto => golang.org/x/crypto v0.0.0-20181025213731-e84da0312774 - golang.org/x/net => golang.org/x/net v0.0.0-20190206173232-65e2d4e15006 - golang.org/x/sync => golang.org/x/sync v0.0.0-20181108010431-42b317875d0f - golang.org/x/sys => golang.org/x/sys v0.0.0-20190209173611-3b5209105503 - golang.org/x/tools => golang.org/x/tools v0.0.0-20190313210603-aa82965741a9 - k8s.io/api => k8s.io/api v0.0.0-20190620073856-dcce3486da33 - k8s.io/apimachinery => k8s.io/apimachinery v0.0.0-20190620073744-d16981aedf33 - k8s.io/client-go => k8s.io/client-go v0.0.0-20190620074045-585a16d2e773 - k8s.io/code-generator => k8s.io/code-generator v0.0.0-20190620073620-d55040311883 - k8s.io/component-base => k8s.io/component-base v0.0.0-20190620074451-e5083e713460 -) - -replace k8s.io/klog => github.com/stefanprodan/klog v0.0.0-20190418165334-9cbb78b20423 diff --git a/go.sum b/go.sum deleted file mode 100644 index 465cc6eb..00000000 --- a/go.sum +++ /dev/null @@ -1,518 +0,0 @@ -cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= -cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= -cloud.google.com/go v0.37.4 h1:glPeL3BQJsbF6aIIYfZizMwc5LTYz250bDMjttbBGAU= -cloud.google.com/go v0.37.4/go.mod h1:NHPJ89PdicEuT9hdPXMROBD91xc5uRDxsMtSB16k7hw= -git.apache.org/thrift.git v0.0.0-20180902110319-2566ecd5d999/go.mod h1:fPE2ZNJGynbRyZ4dJvy6G277gSllfV2HJqblrnkyeyg= -github.com/Azure/go-autorest v11.1.2+incompatible/go.mod h1:r+4oMnoxhatjLLJ6zxSWATqVooLgysK6ZNox3g/xq24= -github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= -github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo= -github.com/MakeNowJust/heredoc v0.0.0-20171113091838-e9091a26100e/go.mod h1:64YHyfSL2R96J44Nlwm39UHepQbyR5q10x7iYa1ks2E= -github.com/Masterminds/goutils v1.1.0/go.mod h1:8cTjp+g8YejhMuvIA5y2vz3BpJxksy863GQaJW2MFNU= -github.com/Masterminds/semver v1.4.2 h1:WBLTQ37jOCzSLtXNdoo8bNM8876KhNqOKvrlGITgsTc= -github.com/Masterminds/semver v1.4.2/go.mod h1:MB6lktGJrhw8PrUyiEoblNEGEQ+RzHPF078ddwwvV3Y= -github.com/Masterminds/sprig v2.18.0+incompatible/go.mod h1:y6hNFY5UBTIWBxnzTeuNhlNS5hqE0NB0E6fgfo2Br3o= -github.com/NYTimes/gziphandler v0.0.0-20170623195520-56545f4a5d46/go.mod h1:3wb06e3pkSAbeQ52E9H9iFoQsEEwGN64994WTCIhntQ= -github.com/NYTimes/gziphandler v1.1.1/go.mod h1:n/CVRwUEOgIxrgPvAQhUUr9oeUtvrhMomdKFjzJNB0c= -github.com/Netflix/go-expect v0.0.0-20180615182759-c93bf25de8e8/go.mod h1:oX5x61PbNXchhh0oikYAH+4Pcfw5LKv21+Jnpr6r6Pc= -github.com/Netflix/go-expect v0.0.0-20180928190340-9d1f4485533b/go.mod h1:oX5x61PbNXchhh0oikYAH+4Pcfw5LKv21+Jnpr6r6Pc= -github.com/PuerkitoBio/purell v1.0.0/go.mod h1:c11w/QuzBsJSee3cPx9rAFu61PvFxuPbtSwDGJws/X0= -github.com/PuerkitoBio/purell v1.1.0/go.mod h1:c11w/QuzBsJSee3cPx9rAFu61PvFxuPbtSwDGJws/X0= -github.com/PuerkitoBio/urlesc v0.0.0-20160726150825-5bd2802263f2/go.mod h1:uGdkoq3SwY9Y+13GIhn11/XLaGBb4BfwItxLd5jeuXE= -github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578/go.mod h1:uGdkoq3SwY9Y+13GIhn11/XLaGBb4BfwItxLd5jeuXE= -github.com/Shopify/sarama v1.19.0/go.mod h1:FVkBWblsNy7DGZRfXLU0O9RCGt5g3g3yEuWXgklEdEo= -github.com/Shopify/toxiproxy v2.1.4+incompatible/go.mod h1:OXgGpZ6Cli1/URJOF1DMxUHB2q5Ap20/P/eIdh4G0pI= -github.com/airbrake/gobrake v3.6.1+incompatible/go.mod h1:wM4gu3Cn0W0K7GUuVWnlXZU11AGBXMILnrdOU8Kn00o= -github.com/alcortesm/tgz v0.0.0-20161220082320-9c5fe88206d7/go.mod h1:6zEj6s6u/ghQa61ZWa/C2Aw3RkjiTBOix7dkqa1VLIs= -github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= -github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= -github.com/anmitsu/go-shlex v0.0.0-20161002113705-648efa622239/go.mod h1:2FmKhYUyUczH0OGQWaF5ceTx0UBShxjsH6f8oGKYe2c= -github.com/apache/thrift v0.12.0/go.mod h1:cp2SuWMxlEZw2r+iP2GNCdIi4C1qmUzdZFSVb+bacwQ= -github.com/appscode/jsonpatch v0.0.0-20190108182946-7c0e3b262f30/go.mod h1:4AJxUpXUhv4N+ziTvIcWWXgeorXpxPZOfk9HdEVr96M= -github.com/armon/circbuf v0.0.0-20150827004946-bbbad097214e/go.mod h1:3U/XgcO3hCbHZ8TKRvWD2dDTCfh9M9ya+I9JpbB7O8o= -github.com/armon/go-metrics v0.0.0-20180917152333-f0300d1749da h1:8GUt8eRujhVEGZFFEjBj46YV4rDjvGrNxb0KMWYkL2I= -github.com/armon/go-metrics v0.0.0-20180917152333-f0300d1749da/go.mod h1:Q73ZrmVTwzkszR9V5SSuryQ31EELlFMUz1kKyl939pY= -github.com/armon/go-radix v0.0.0-20180808171621-7fddfc383310 h1:BUAU3CGlLvorLI26FmByPp2eC2qla6E1Tw+scpcg/to= -github.com/armon/go-radix v0.0.0-20180808171621-7fddfc383310/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8= -github.com/asaskevich/govalidator v0.0.0-20180720115003-f9ffefc3facf/go.mod h1:lB+ZfQJz7igIIfQNfa7Ml4HSf2uFQQRzpGGRXenZAgY= -github.com/avast/retry-go v2.2.0+incompatible h1:m+w7mVLWa/oKqX2xYqiEKQQkeGH8DDEXB/XnjS54Wyw= -github.com/avast/retry-go v2.2.0+incompatible/go.mod h1:XtSnn+n/sHqQIpZ10K1qAevBhOOCWBLXXy3hyiqqBrY= -github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q= -github.com/beorn7/perks v1.0.0 h1:HWo1m869IqiPhD389kmkxeTalrjNbbJTC8LXupb+sl0= -github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8= -github.com/bgentry/speakeasy v0.1.0 h1:ByYyxL9InA1OWqxJqqp2A5pYHUrCiAL6K3J+LKSsQkY= -github.com/bgentry/speakeasy v0.1.0/go.mod h1:+zsyZBPWlz7T6j88CTgSN5bM796AkVf0kBD4zp0CCIs= -github.com/bitly/go-simplejson v0.5.0/go.mod h1:cXHtHw4XUPsvGaxgjIAn8PhEWG9NfngEKAMDJEczWVA= -github.com/bmizerany/assert v0.0.0-20160611221934-b7ed37b82869/go.mod h1:Ekp36dRnpXw/yCqJaO+ZrUyxD+3VXMFFr56k5XYrpB4= -github.com/bugsnag/bugsnag-go v1.4.0/go.mod h1:2oa8nejYd4cQ/b0hMIopN0lCRxU0bueqREvZLWFrtK8= -github.com/bugsnag/panicwrap v1.2.0/go.mod h1:D/8v3kj0zr8ZAKg1AQ6crr+5VwKN5eIywRkfhyM/+dE= -github.com/bxcodec/faker v2.0.1+incompatible h1:P0KUpUw5w6WJXwrPfv35oc91i4d8nf40Nwln+M/+faA= -github.com/bxcodec/faker v2.0.1+incompatible/go.mod h1:BNzfpVdTwnFJ6GtfYTcQu6l6rHShT+veBxNCnjCx5XM= -github.com/certifi/gocertifi v0.0.0-20190105021004-abcd57078448/go.mod h1:GJKEexRPVJrBSOjoqN5VNOIKJ5Q3RViH6eu3puDRwx4= -github.com/chai2010/gettext-go v0.0.0-20170215093142-bf70f2a70fb1/go.mod h1:/iP1qXHoty45bqomnu2LM+VVyAEdWN+vtSHGlQgyxbw= -github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= -github.com/coreos/etcd v3.3.12+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE= -github.com/coreos/go-systemd v0.0.0-20190321100706-95778dfbb74e/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4= -github.com/cpuguy83/go-md2man v1.0.8/go.mod h1:N6JayAiVKtlHSnuTCeuLSQVs75hb8q+dYQLjr7cDsKY= -github.com/cyphar/filepath-securejoin v0.2.2/go.mod h1:FpkQEhXnPnOthhzymB7CGsFk2G9VLXONKD9G7QGMM+4= -github.com/davecgh/go-spew v0.0.0-20151105211317-5215b55f46b2/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= -github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/dgrijalva/jwt-go v0.0.0-20160705203006-01aeca54ebda/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ= -github.com/docker/distribution v2.7.1+incompatible/go.mod h1:J2gT2udsDAN96Uj4KfcMRqY0/ypR+oyYUYmja8H+y+w= -github.com/docker/docker v1.13.1/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk= -github.com/docker/spdystream v0.0.0-20160310174837-449fdfce4d96/go.mod h1:Qh8CwZgvJUkLughtfhJv5dyTYa91l1fOUCrgjqmcifM= -github.com/docker/spdystream v0.0.0-20181023171402-6480d4af844c/go.mod h1:Qh8CwZgvJUkLughtfhJv5dyTYa91l1fOUCrgjqmcifM= -github.com/eapache/go-resiliency v1.1.0/go.mod h1:kFI+JgMyC7bLPUVY133qvEBtVayf5mFgVsvEsIPBvNs= -github.com/eapache/go-xerial-snappy v0.0.0-20180814174437-776d5712da21/go.mod h1:+020luEh2TKB4/GOp8oxxtq0Daoen/Cii55CzbTV6DU= -github.com/eapache/queue v1.1.0/go.mod h1:6eCeP0CKFpHLu8blIFXhExK/dRa7WDZfr6jVFPTqq+I= -github.com/elazarl/go-bindata-assetfs v1.0.0/go.mod h1:v+YaWX3bdea5J/mo8dSETolEo7R71Vk1u8bnjau5yw4= -github.com/elazarl/goproxy v0.0.0-20170405201442-c4fc26588b6e/go.mod h1:/Zj4wYkgs4iZTTu3o/KG3Itv/qCCa8VVMlb3i9OVuzc= -github.com/emicklei/go-restful v0.0.0-20170410110728-ff4f55a20633/go.mod h1:otzb+WCGbkyDHkqmQmT5YD2WR4BBwUdeQoFo8l/7tVs= -github.com/emicklei/go-restful v2.9.3+incompatible/go.mod h1:otzb+WCGbkyDHkqmQmT5YD2WR4BBwUdeQoFo8l/7tVs= -github.com/emicklei/go-restful-swagger12 v0.0.0-20170926063155-7524189396c6/go.mod h1:qr0VowGBT4CS4Q8vFF8BSeKz34PuqKGxs/L0IAQA9DQ= -github.com/emirpasic/gods v1.9.0/go.mod h1:YfzfFFoVP/catgzJb4IKIqXjX78Ha8FMSDh3ymbK86o= -github.com/envoyproxy/go-control-plane v0.8.0 h1:uE6Fp4fOcAJdc1wTQXLJ+SYistkbG1dNoi6Zs1+Ybvk= -github.com/envoyproxy/go-control-plane v0.8.0/go.mod h1:GSSbY9P1neVhdY7G4wu+IK1rk/dqhiCC/4ExuWJZVuk= -github.com/envoyproxy/protoc-gen-validate v0.0.14 h1:YBW6/cKy9prEGRYLnaGa4IDhzxZhRCtKsax8srGKDnM= -github.com/envoyproxy/protoc-gen-validate v0.0.14/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= -github.com/evanphx/json-patch v4.0.0+incompatible/go.mod h1:50XU6AFN0ol/bzJsmQLiYLvXMP4fmwYFNcr97nuDLSk= -github.com/evanphx/json-patch v4.1.0+incompatible h1:K1MDoo4AZ4wU0GIU/fPmtZg7VpzLjCxu+UwBD1FvwOc= -github.com/evanphx/json-patch v4.1.0+incompatible/go.mod h1:50XU6AFN0ol/bzJsmQLiYLvXMP4fmwYFNcr97nuDLSk= -github.com/evanphx/json-patch v4.2.0+incompatible h1:fUDGZCv/7iAN7u0puUVhvKCcsR6vRfwrJatElLBEf0I= -github.com/evanphx/json-patch v4.2.0+incompatible/go.mod h1:50XU6AFN0ol/bzJsmQLiYLvXMP4fmwYFNcr97nuDLSk= -github.com/exponent-io/jsonpath v0.0.0-20151013193312-d6023ce2651d/go.mod h1:ZZMPRZwes7CROmyNKgQzC3XPs6L/G2EJLHddWejkmf4= -github.com/fatih/color v1.7.0 h1:DkWD4oS2D8LGGgTQ6IvwJJXSL5Vp2ffcQg58nFV38Ys= -github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4= -github.com/fgrosse/zaptest v1.1.0 h1:sK9hP0/xBoNX5qfFo3KWFluDXfc809APomI1QXuYELA= -github.com/fgrosse/zaptest v1.1.0/go.mod h1:vMnRSul6kW7kIUXZgnZZcDwyTn8k49ODfAULL8nmL5w= -github.com/flynn/go-shlex v0.0.0-20150515145356-3f9db97f8568/go.mod h1:xEzjJPgXI435gkrCt3MPfRiAkVrwSbHsst4LCFVfpJc= -github.com/fsnotify/fsnotify v1.4.7 h1:IXs+QLmnXW2CcXuY+8Mzv/fWEsPGWxqefPtCP5CnV9I= -github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= -github.com/getsentry/raven-go v0.2.0/go.mod h1:KungGk8q33+aIAZUIVWZDr2OfAEBsO49PX4NzFV5kcQ= -github.com/ghodss/yaml v0.0.0-20150909031657-73d445a93680/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= -github.com/ghodss/yaml v1.0.0 h1:wQHKEahhL6wmXdzwWG11gIVCkOv05bNOh+Rxn0yngAk= -github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= -github.com/gliderlabs/ssh v0.1.1/go.mod h1:U7qILu1NlMHj9FlMhZLlkCdDnU1DBEAqr0aevW3Awn0= -github.com/globalsign/mgo v0.0.0-20180905125535-1ca0a4f7cbcb/go.mod h1:xkRDCp4j0OGD1HRkm4kmhM+pmpv3AKq5SU7GMg4oO/Q= -github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= -github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE= -github.com/go-logr/logr v0.1.0/go.mod h1:ixOQHD9gLJUVQQ2ZOR7zLEifBX6tGkNJF4QyIY7sIas= -github.com/go-logr/zapr v0.1.1/go.mod h1:tabnROwaDl0UNxkVeFRbY8bwB37GwRv0P8lg6aAiEnk= -github.com/go-openapi/analysis v0.0.0-20180825180245-b006789cd277/go.mod h1:k70tL6pCuVxPJOHXQ+wIac1FUrvNkHolPie/cLEU6hI= -github.com/go-openapi/analysis v0.17.0/go.mod h1:IowGgpVeD0vNm45So8nr+IcQ3pxVtpRoBWb8PVZO0ik= -github.com/go-openapi/errors v0.17.0/go.mod h1:LcZQpmvG4wyF5j4IhA73wkLFQg+QJXOQHVjmcZxhka0= -github.com/go-openapi/jsonpointer v0.0.0-20160704185906-46af16f9f7b1/go.mod h1:+35s3my2LFTysnkMfxsJBAMHj/DoqoB9knIWoYG/Vk0= -github.com/go-openapi/jsonpointer v0.17.0/go.mod h1:cOnomiV+CVVwFLk0A/MExoFMjwdsUdVpsRhURCKh+3M= -github.com/go-openapi/jsonreference v0.0.0-20160704190145-13c6e3589ad9/go.mod h1:W3Z9FmVs9qj+KR4zFKmDPGiLdk1D9Rlm7cyMvf57TTg= -github.com/go-openapi/jsonreference v0.17.0/go.mod h1:g4xxGn04lDIRh0GJb5QlpE3HfopLOL6uZrK/VgnsK9I= -github.com/go-openapi/loads v0.17.0/go.mod h1:72tmFy5wsWx89uEVddd0RjRWPZm92WRLhf7AC+0+OOU= -github.com/go-openapi/runtime v0.0.0-20180920151709-4f900dc2ade9/go.mod h1:6v9a6LTXWQCdL8k1AO3cvqx5OtZY/Y9wKTgaoP6YRfA= -github.com/go-openapi/spec v0.0.0-20160808142527-6aced65f8501/go.mod h1:J8+jY1nAiCcj+friV/PDoE1/3eeccG9LYBs0tYvLOWc= -github.com/go-openapi/spec v0.17.0/go.mod h1:XkF/MOi14NmjsfZ8VtAKf8pIlbZzyoTvZsdfssdxcBI= -github.com/go-openapi/spec v0.19.0/go.mod h1:XkF/MOi14NmjsfZ8VtAKf8pIlbZzyoTvZsdfssdxcBI= -github.com/go-openapi/strfmt v0.17.0/go.mod h1:P82hnJI0CXkErkXi8IKjPbNBM6lV6+5pLP5l494TcyU= -github.com/go-openapi/strfmt v0.19.0/go.mod h1:+uW+93UVvGGq2qGaZxdDeJqSAqBqBdl+ZPMF/cC8nDY= -github.com/go-openapi/swag v0.0.0-20160704191624-1d0bd113de87/go.mod h1:DXUve3Dpr1UfpPtxFw+EFuQ41HhCWZfha5jSVRG7C7I= -github.com/go-openapi/swag v0.17.0/go.mod h1:AByQ+nYG6gQg71GINrmuDXCPWdL640yX49/kXLo40Tg= -github.com/go-openapi/validate v0.19.0/go.mod h1:Uh4HdOzKt19xGIGm1qHf/ofbX1YQ4Y+MYsct2VUrAJ4= -github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY= -github.com/gobwas/glob v0.2.3/go.mod h1:d3Ez4x06l9bZtSvzIay5+Yzi0fmZzPgnTbPcKjJAkT8= -github.com/gofrs/uuid v3.2.0+incompatible/go.mod h1:b2aQJv3Z4Fp6yNu3cdSllBxTCLRxnplIgP/c0N/04lM= -github.com/gogo/googleapis v1.1.0/go.mod h1:gf4bu3Q80BeJ6H1S1vYPm8/ELATdvryBaNFGgqEef3s= -github.com/gogo/googleapis v1.2.0 h1:Z0v3OJDotX9ZBpdz2V+AI7F4fITSZhVE5mg6GQppwMM= -github.com/gogo/googleapis v1.2.0/go.mod h1:Njal3psf3qN6dwBtQfUmBZh2ybovJ0tlu3o/AC7HYjU= -github.com/gogo/protobuf v0.0.0-20171007142547-342cbe0a0415/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ= -github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ= -github.com/gogo/protobuf v1.2.0/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ= -github.com/gogo/protobuf v1.2.1 h1:/s5zKNz0uPFCZ5hddgPdo2TK2TVrUNMn0OOX8/aZMTE= -github.com/gogo/protobuf v1.2.1/go.mod h1:hp+jE20tsWTFYpLwKvXlhS1hjn+gTNwPg2I6zVXpSg4= -github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b h1:VKtxabqXZkF25pY9ekfRL6a582T4P37/31XEstQ5p58= -github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= -github.com/golang/groupcache v0.0.0-20160516000752-02826c3e7903/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= -github.com/golang/groupcache v0.0.0-20190129154638-5b532d6fd5ef h1:veQD95Isof8w9/WXiA+pa3tz3fJXkt5B7QaRBrM62gk= -github.com/golang/groupcache v0.0.0-20190129154638-5b532d6fd5ef/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= -github.com/golang/lint v0.0.0-20180702182130-06c8688daad7/go.mod h1:tluoj9z5200jBnyusfRPU2LqT6J+DAorxEvtC7LHB+E= -github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= -github.com/golang/mock v1.2.0/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= -github.com/golang/protobuf v0.0.0-20161109072736-4bd1920723d7/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= -github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= -github.com/golang/protobuf v1.3.1 h1:YF8+flBXS5eO826T4nzqPrxfhQThhXl0YzfuUPu4SBg= -github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= -github.com/golang/snappy v0.0.0-20180518054509-2e65f85255db/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= -github.com/golang/snappy v0.0.1 h1:Qgr9rKW7uDUkrbSmQeiDsGa8SjGyCOGtuasMWwvp2P4= -github.com/golang/snappy v0.0.1/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= -github.com/google/btree v0.0.0-20160524151835-7d79101e329e/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= -github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= -github.com/google/btree v1.0.0 h1:0udJVsspx3VBr5FwtLhQQtuAsVc79tTq0ocGIPAU6qo= -github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= -github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= -github.com/google/go-cmp v0.3.0 h1:crn/baboCvb5fXaQ0IJ1SGTsTVrWpDsCWC8EGETZijY= -github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= -github.com/google/go-github v17.0.0+incompatible/go.mod h1:zLgOLi98H3fifZn+44m+umXrS52loVEgC2AApnigrVQ= -github.com/google/go-querystring v1.0.0/go.mod h1:odCYkC5MyYFN7vkCjXpyrEuKhc/BUO6wN/zVPAxq5ck= -github.com/google/gofuzz v0.0.0-20161122191042-44d81051d367/go.mod h1:HP5RmnzzSNb993RKQDq4+1A4ia9nllfqcQFTQJedwGI= -github.com/google/gofuzz v0.0.0-20170612174753-24818f796faf/go.mod h1:HP5RmnzzSNb993RKQDq4+1A4ia9nllfqcQFTQJedwGI= -github.com/google/gofuzz v1.0.0 h1:A8PeW59pxE9IoFRqBp37U+mSNaQoZ46F1f0f863XSXw= -github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= -github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs= -github.com/google/pprof v0.0.0-20181206194817-3ea8567a2e57/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc= -github.com/google/uuid v1.0.0 h1:b4Gk+7WdP/d3HZH8EJsZpvV7EtDOgaZLtnaNGIu1adA= -github.com/google/uuid v1.0.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= -github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg= -github.com/googleapis/gnostic v0.0.0-20170426233943-68f4ded48ba9/go.mod h1:sJBsCZ4ayReDTBIg8b9dl28c5xFWyhBTVRp3pOg5EKY= -github.com/googleapis/gnostic v0.0.0-20170729233727-0c5108395e2d/go.mod h1:sJBsCZ4ayReDTBIg8b9dl28c5xFWyhBTVRp3pOg5EKY= -github.com/googleapis/gnostic v0.2.0 h1:l6N3VoaVzTncYYW+9yOz2LJJammFZGBO13sqgEhpy9g= -github.com/googleapis/gnostic v0.2.0/go.mod h1:sJBsCZ4ayReDTBIg8b9dl28c5xFWyhBTVRp3pOg5EKY= -github.com/goph/emperror v0.17.1/go.mod h1:+ZbQ+fUNO/6FNiUo0ujtMjhgad9Xa6fQL9KhH4LNHic= -github.com/gophercloud/gophercloud v0.0.0-20190126172459-c818fa66e4c8/go.mod h1:3WdhXV3rUYy9p6AUW8d94kr+HS62Y4VL9mBnFxsD8q4= -github.com/gorilla/context v1.1.1/go.mod h1:kBGZzfjB9CEq2AlWe17Uuf7NDRt0dE0s8S51q0aT7Yg= -github.com/gorilla/mux v1.6.2/go.mod h1:1lud6UwP+6orDFRuTfBEV8e9/aOM/c4fVVCaMa2zaAs= -github.com/gregjones/httpcache v0.0.0-20170728041850-787624de3eb7/go.mod h1:FecbI9+v66THATjSRHfNgh1IVFe/9kFxbXtjV0ctIMA= -github.com/gregjones/httpcache v0.0.0-20181110185634-c63ab54fda8f h1:ShTPMJQes6tubcjzGMODIVG5hlrCeImaBnZzKF2N8SM= -github.com/gregjones/httpcache v0.0.0-20181110185634-c63ab54fda8f/go.mod h1:FecbI9+v66THATjSRHfNgh1IVFe/9kFxbXtjV0ctIMA= -github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0/go.mod h1:8NvIoxWQoOIhqOTXgfV/d3M/q6VIi02HzZEHgUlZvzk= -github.com/grpc-ecosystem/grpc-gateway v1.5.0/go.mod h1:RSKVYQBd5MCa4OVpNdGskqpgL2+G+NZTnrVHpWWfpdw= -github.com/h2non/parth v0.0.0-20190131123155-b4df798d6542 h1:2VTzZjLZBgl62/EtslCrtky5vbi9dd7HrQPQIx6wqiw= -github.com/h2non/parth v0.0.0-20190131123155-b4df798d6542/go.mod h1:Ow0tF8D4Kplbc8s8sSb3V2oUCygFHVp8gC3Dn6U4MNI= -github.com/hashicorp/consul v1.4.4 h1:DR1+5EGgnPsd/LIsK3c9RDvajcsV5GOkGQBSNd3dpn8= -github.com/hashicorp/consul v1.4.4/go.mod h1:mFrjN1mfidgJfYP1xrJCF+AfRhr6Eaqhb2+sfyn/OOI= -github.com/hashicorp/errwrap v1.0.0 h1:hLrqtEDnRye3+sgx6z4qVLNuviH3MR5aQ0ykNJa/UYA= -github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= -github.com/hashicorp/go-cleanhttp v0.5.0/go.mod h1:JpRdi6/HCYpAwUzNwuwqhbovhLtngrth3wmdIIUrZ80= -github.com/hashicorp/go-cleanhttp v0.5.1 h1:dH3aiDG9Jvb5r5+bYHsikaOUIpcM0xvgMXVoDkXMzJM= -github.com/hashicorp/go-cleanhttp v0.5.1/go.mod h1:JpRdi6/HCYpAwUzNwuwqhbovhLtngrth3wmdIIUrZ80= -github.com/hashicorp/go-immutable-radix v1.0.0 h1:AKDB1HM5PWEA7i4nhcpwOrO2byshxBjXVn/J/3+z5/0= -github.com/hashicorp/go-immutable-radix v1.0.0/go.mod h1:0y9vanUI8NX6FsYoO3zeMjhV/C5i9g4Q3DwcSNZ4P60= -github.com/hashicorp/go-msgpack v0.5.3 h1:zKjpN5BK/P5lMYrLmBHdBULWbJ0XpYR+7NGzqkZzoD4= -github.com/hashicorp/go-msgpack v0.5.3/go.mod h1:ahLV/dePpqEmjfWmKiqvPkv/twdG7iPBM1vqhUKIvfM= -github.com/hashicorp/go-multierror v1.0.0 h1:iVjPR7a6H0tWELX5NxNe7bYopibicUzc7uPribsnS6o= -github.com/hashicorp/go-multierror v1.0.0/go.mod h1:dHtQlpGsu+cZNNAkkCN/P3hoUDHhCYQXV3UM06sGGrk= -github.com/hashicorp/go-retryablehttp v0.5.3 h1:QlWt0KvWT0lq8MFppF9tsJGF+ynG7ztc2KIPhzRGk7s= -github.com/hashicorp/go-retryablehttp v0.5.3/go.mod h1:9B5zBasrRhHXnJnui7y6sL7es7NDiJgTc6Er0maI1Xs= -github.com/hashicorp/go-rootcerts v1.0.0 h1:Rqb66Oo1X/eSV1x66xbDccZjhJigjg0+e82kpwzSwCI= -github.com/hashicorp/go-rootcerts v1.0.0/go.mod h1:K6zTfqpRlCUIjkwsN4Z+hiSfzSTQa6eBIzfwKfwNnHU= -github.com/hashicorp/go-sockaddr v1.0.0/go.mod h1:7Xibr9yA9JjQq1JpNB2Vw7kxv8xerXegt+ozgdvDeDU= -github.com/hashicorp/go-sockaddr v1.0.2 h1:ztczhD1jLxIRjVejw8gFomI1BQZOe2WoVOu0SyteCQc= -github.com/hashicorp/go-sockaddr v1.0.2/go.mod h1:rB4wwRAUzs07qva3c5SdrY/NEtAUjGlgmH/UkBUC97A= -github.com/hashicorp/go-syslog v1.0.0 h1:KaodqZuhUoZereWVIYmpUgZysurB1kBLX2j0MwMrUAE= -github.com/hashicorp/go-syslog v1.0.0/go.mod h1:qPfqrKkXGihmCqbJM2mZgkZGvKG1dFdvsLplgctolz4= -github.com/hashicorp/go-uuid v1.0.0/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro= -github.com/hashicorp/go-uuid v1.0.1 h1:fv1ep09latC32wFoVwnqcnKJGnMSdBanPczbHAYm1BE= -github.com/hashicorp/go-uuid v1.0.1/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro= -github.com/hashicorp/go.net v0.0.1/go.mod h1:hjKkEWcCURg++eb33jQU7oqQcI9XDCnUzHA0oac0k90= -github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= -github.com/hashicorp/golang-lru v0.5.1 h1:0hERBMJE1eitiLkihrMvRVBYAkpHzc/J3QdDN+dAcgU= -github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= -github.com/hashicorp/hcl v1.0.0 h1:0Anlzjpi4vEasTeNFn2mLJgTSwt0+6sfsiTG8qcWGx4= -github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ= -github.com/hashicorp/logutils v1.0.0 h1:dLEQVugN8vlakKOUE3ihGLTZJRB4j+M2cdTm/ORI65Y= -github.com/hashicorp/logutils v1.0.0/go.mod h1:QIAnNjmIWmVIIkWDTG1z5v++HQmx9WQRO+LraFDTW64= -github.com/hashicorp/mdns v1.0.0/go.mod h1:tL+uN++7HEJ6SQLQ2/p+z2pH24WQKWjBPkE0mNTz8vQ= -github.com/hashicorp/memberlist v0.1.3 h1:EmmoJme1matNzb+hMpDuR/0sbJSUisxyqBGG676r31M= -github.com/hashicorp/memberlist v0.1.3/go.mod h1:ajVTdAv/9Im8oMAAj5G31PhhMCZJV2pPBoIllUwCN7I= -github.com/hashicorp/serf v0.8.3 h1:MWYcmct5EtKz0efYooPcL0yNkem+7kWxqXDi/UIh+8k= -github.com/hashicorp/serf v0.8.3/go.mod h1:UpNcs7fFbpKIyZaUuSW6EPiH+eZC7OuyFD+wc1oal+k= -github.com/hashicorp/vault v1.1.0 h1:v79NUgO5xCZnXVzUkIqFOXtP8YhpnHAi1fk3eo9cuOE= -github.com/hashicorp/vault v1.1.0/go.mod h1:KfSyffbKxoVyspOdlaGVjIuwLobi07qD1bAbosPMpP0= -github.com/hinshun/vt10x v0.0.0-20180616224451-1954e6464174/go.mod h1:DqJ97dSdRW1W22yXSB90986pcOyQ7r45iio1KN2ez1A= -github.com/hinshun/vt10x v0.0.0-20180809195222-d55458df857c/go.mod h1:DqJ97dSdRW1W22yXSB90986pcOyQ7r45iio1KN2ez1A= -github.com/hpcloud/tail v1.0.0 h1:nfCOvKYfkgYP8hkirhJocXT2+zOD8yUNjXaWfTlyFKI= -github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU= -github.com/huandu/xstrings v1.2.0/go.mod h1:DvyZB1rfVYsBIigL8HwpZgxHwXozlTgGqn63UyNX5k4= -github.com/imdario/mergo v0.3.5/go.mod h1:2EnlNZ0deacrJVfApfmtdGgDfMuh/nq6Ok1EcJh5FfA= -github.com/imdario/mergo v0.3.6/go.mod h1:2EnlNZ0deacrJVfApfmtdGgDfMuh/nq6Ok1EcJh5FfA= -github.com/imdario/mergo v0.3.7 h1:Y+UAYTZ7gDEuOfhxKWy+dvb5dRQ6rJjFSdX2HZY1/gI= -github.com/imdario/mergo v0.3.7/go.mod h1:2EnlNZ0deacrJVfApfmtdGgDfMuh/nq6Ok1EcJh5FfA= -github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8= -github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99/go.mod h1:1lJo3i6rXxKeerYnT8Nvf0QmHCRC1n8sfWVwXF2Frvo= -github.com/jessevdk/go-flags v1.4.0/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI= -github.com/json-iterator/go v0.0.0-20180612202835-f2b4162afba3/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU= -github.com/json-iterator/go v1.1.5/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU= -github.com/json-iterator/go v1.1.6 h1:MrUvLMLTMxbqFJ9kzlvat/rYZqZnW3u4wkLzWTaFwKs= -github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU= -github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU= -github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w= -github.com/k0kubun/colorstring v0.0.0-20150214042306-9440f1994b88 h1:uC1QfSlInpQF+M0ao65imhwqKnz3Q2z/d8PWZRMQvDM= -github.com/k0kubun/colorstring v0.0.0-20150214042306-9440f1994b88/go.mod h1:3w7q1U84EfirKl04SVQ/s7nPm1ZPhiXd34z40TNz36k= -github.com/k0kubun/pp v2.3.0+incompatible/go.mod h1:GWse8YhT0p8pT4ir3ZgBbfZild3tgzSScAn6HmfYukg= -github.com/k0kubun/pp v3.0.1+incompatible h1:3tqvf7QgUnZ5tXO6pNAZlrvHgl6DvifjDrd9g2S9Z40= -github.com/k0kubun/pp v3.0.1+incompatible/go.mod h1:GWse8YhT0p8pT4ir3ZgBbfZild3tgzSScAn6HmfYukg= -github.com/kardianos/osext v0.0.0-20190222173326-2bc1f35cddc0/go.mod h1:1NbS8ALrpOvjt0rHPNLyCIeMtbizbir8U//inJ+zuB8= -github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51/go.mod h1:CzGEWj7cYgsdH8dAjBGEr58BoE7ScuLd+fwFZ44+/x8= -github.com/kevinburke/ssh_config v0.0.0-20180830205328-81db2a75821e/go.mod h1:CT57kijsi8u/K/BOFA39wgDQJ9CxiF4nAY/ojJ6r6mM= -github.com/kisielk/errcheck v1.1.0/go.mod h1:EZBBE59ingxPouuu3KfxchcWSUPOHkagtvWXihfKN4Q= -github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= -github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= -github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc= -github.com/kr/pretty v0.1.0 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI= -github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= -github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= -github.com/kr/pty v1.1.3/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= -github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE= -github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= -github.com/linkerd/linkerd2 v0.0.0-20190221030352-5e47cb150a33 h1:+eM/rkJK2iCSi0fDzp218TzJSAglSGeI985YYgRS/mY= -github.com/linkerd/linkerd2 v0.0.0-20190221030352-5e47cb150a33/go.mod h1:n9QnL65Uv2gAG97S0t1q2aYmP33wPQ3oAh0+DJhQSSw= -github.com/lyft/protoc-gen-validate v0.0.14 h1:xbdDVIHd0Xq5Bfzu+8JR9s7mFmJPMvNLmfGhgcHJdFU= -github.com/lyft/protoc-gen-validate v0.0.14/go.mod h1:XbGvPuh87YZc5TdIa2/I4pLk0QoUACkjt2znoq26NVQ= -github.com/mailru/easyjson v0.0.0-20160728113105-d5b7844b561a/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= -github.com/mailru/easyjson v0.0.0-20180823135443-60711f1a8329/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= -github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU= -github.com/mattn/go-colorable v0.1.1 h1:G1f5SKeVxmagw/IyvzvtZE4Gybcc4Tr1tf7I8z0XgOg= -github.com/mattn/go-colorable v0.1.1/go.mod h1:FuOcm+DKB9mbwrcAfNl7/TZVBZ6rcnceauSikq3lYCQ= -github.com/mattn/go-isatty v0.0.3/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4= -github.com/mattn/go-isatty v0.0.4/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4= -github.com/mattn/go-isatty v0.0.5/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s= -github.com/mattn/go-isatty v0.0.7 h1:UvyT9uN+3r7yLEYSlJsbQGdsaB/a0DlgWP3pql6iwOc= -github.com/mattn/go-isatty v0.0.7/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s= -github.com/matttproud/golang_protobuf_extensions v1.0.1 h1:4hp9jkHxhMHkqkrB3Ix0jegS5sx/RkqARlsWZ6pIwiU= -github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0= -github.com/mgutz/ansi v0.0.0-20170206155736-9520e82c474b/go.mod h1:01TrycV0kFyexm33Z7vhZRXopbI8J3TDReVlkTgMUxE= -github.com/miekg/dns v1.0.14 h1:9jZdLNd/P4+SfEJ0TNyxYpsK8N4GtfylBLqtbYN1sbA= -github.com/miekg/dns v1.0.14/go.mod h1:W1PPwlIAgtquWBMBEV9nkV9Cazfe8ScdGz/Lj7v3Nrg= -github.com/mitchellh/cli v1.0.0 h1:iGBIsUe3+HZ/AD/Vd7DErOt5sU9fa8Uj7A2s1aggv1Y= -github.com/mitchellh/cli v1.0.0/go.mod h1:hNIlj7HEI86fIcpObd7a0FcrxTWetlwJDGcceTlRvqc= -github.com/mitchellh/go-homedir v1.0.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= -github.com/mitchellh/go-homedir v1.1.0 h1:lukF9ziXFxDFPkA1vsr5zpc1XuPDn/wFntq5mG+4E0Y= -github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= -github.com/mitchellh/go-testing-interface v1.0.0 h1:fzU/JVNcaqHQEcVFAKeR41fkiLdIPrefOvVG1VZ96U0= -github.com/mitchellh/go-testing-interface v1.0.0/go.mod h1:kRemZodwjscx+RGhAo8eIhFbs2+BFgRtFPeD/KE+zxI= -github.com/mitchellh/go-wordwrap v1.0.0/go.mod h1:ZXFpozHsX6DPmq2I0TCekCxypsnAUbP2oI0UX1GXzOo= -github.com/mitchellh/hashstructure v1.0.0 h1:ZkRJX1CyOoTkar7p/mLS5TZU4nJ1Rn/F8u9dGS02Q3Y= -github.com/mitchellh/hashstructure v1.0.0/go.mod h1:QjSHrPWS+BGUVBYkbTZWEnOh3G1DutKwClXU/ABz6AQ= -github.com/mitchellh/mapstructure v0.0.0-20160808181253-ca63d7c062ee/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= -github.com/mitchellh/mapstructure v1.1.2 h1:fmNYVwqnSfB9mZU6OS2O6GsXM+wcskZDuKQzvN1EDeE= -github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= -github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg= -github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= -github.com/modern-go/reflect2 v0.0.0-20180320133207-05fbef0ca5da/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= -github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= -github.com/modern-go/reflect2 v1.0.1 h1:9f412s+6RmYXLWZSEzVVgPGK7C2PphHj5RJrvfx9AWI= -github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= -github.com/munnerz/goautoneg v0.0.0-20120707110453-a547fc61f48d/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ= -github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= -github.com/mxk/go-flowrate v0.0.0-20140419014527-cca7078d478f/go.mod h1:ZdcZmHo+o7JKHSa8/e818NopupXU1YMK5fe1lsApnBw= -github.com/nbio/st v0.0.0-20140626010706-e9e8d9816f32 h1:W6apQkHrMkS0Muv8G/TipAy/FJl/rCYT0+EuS8+Z0z4= -github.com/nbio/st v0.0.0-20140626010706-e9e8d9816f32/go.mod h1:9wM+0iRr9ahx58uYLpLIr5fm8diHn0JbqRycJi6w0Ms= -github.com/onsi/ginkgo v0.0.0-20170829012221-11459a886d9c/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= -github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= -github.com/onsi/ginkgo v1.7.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= -github.com/onsi/ginkgo v1.8.0 h1:VkHVNpR4iVnU8XQR6DBm8BqYjN7CRzw+xKUbVVbbW9w= -github.com/onsi/ginkgo v1.8.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= -github.com/onsi/gomega v0.0.0-20170829124025-dcabb60a477c/go.mod h1:C1qb7wdrVGGVU+Z6iS04AVkA3Q65CEZX59MT0QO5uiA= -github.com/onsi/gomega v1.4.3/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY= -github.com/onsi/gomega v1.5.0 h1:izbySO9zDPmjJ8rDjLvkA2zJHIo+HkYXHnf7eN7SSyo= -github.com/onsi/gomega v1.5.0/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY= -github.com/opencontainers/go-digest v1.0.0-rc1/go.mod h1:cMLVZDEM3+U2I4VmLI6N8jQYUd2OVphdqWwCJHrFt2s= -github.com/openzipkin/zipkin-go v0.1.1/go.mod h1:NtoC/o8u3JlF1lSlyPNswIbeQH9bJTmOf0Erfk+hxe8= -github.com/openzipkin/zipkin-go v0.1.6/go.mod h1:QgAqvLzwWbR/WpD4A3cGpPtJrZXNIiJc5AZX7/PBEpw= -github.com/pascaldekloe/goe v0.0.0-20180627143212-57f6aae5913c h1:Lgl0gzECD8GnQ5QCWA8o6BtfL6mDH5rQgM4/fX3avOs= -github.com/pascaldekloe/goe v0.0.0-20180627143212-57f6aae5913c/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc= -github.com/pborman/uuid v1.2.0/go.mod h1:X/NO0urCmaxf9VXbdlT7C2Yzkj2IKimNn4k+gtPdI/k= -github.com/pelletier/go-buffruneio v0.2.0/go.mod h1:JkE26KsDizTr40EUHkXVtNPvgGtbSNq5BcowyYOWdKo= -github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic= -github.com/peterbourgon/diskv v2.0.1+incompatible h1:UBdAOUP5p4RWqPBg048CAvpKN+vxiaj6gdUUzhl4XmI= -github.com/peterbourgon/diskv v2.0.1+incompatible/go.mod h1:uqqh8zWWbv1HBMNONnaR/tNboyR3/BZd58JJSHlUSCU= -github.com/pierrec/lz4 v2.0.5+incompatible h1:2xWsjqPFWcplujydGg4WmhC/6fZqK42wMM8aXeqhl0I= -github.com/pierrec/lz4 v2.0.5+incompatible/go.mod h1:pdkljMzZIN41W+lC3N2tnIh5sFi+IEE17M5jbnwPHcY= -github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= -github.com/pkg/errors v0.8.1 h1:iURUrRGxPUNPdy5/HRSm+Yj6okJ6UtLINN0Q9M4+h3I= -github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= -github.com/pmezard/go-difflib v0.0.0-20151028094244-d8ed2627bdf0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= -github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= -github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= -github.com/posener/complete v1.1.1 h1:ccV59UEOTzVDnDUEFdT95ZzHVZ+5+158q8+SJb2QV5w= -github.com/posener/complete v1.1.1/go.mod h1:em0nMJCgc9GFtwrmVmEMR/ZL6WyhyjMBndrE9hABlRI= -github.com/prometheus/client_golang v0.8.0/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw= -github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw= -github.com/prometheus/client_golang v0.9.2/go.mod h1:OsXs2jCmiKlQ1lTBmv21f2mNfw4xf/QclQDMrYNZzcM= -github.com/prometheus/client_golang v0.9.3-0.20190127221311-3c4408c8b829 h1:D+CiwcpGTW6pL6bv6KI3KbyEyCKyS+1JWS2h8PNDnGA= -github.com/prometheus/client_golang v0.9.3-0.20190127221311-3c4408c8b829/go.mod h1:p2iRAGwDERtqlqzRXnrOVns+ignqQo//hLXqYxZYVNs= -github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo= -github.com/prometheus/client_model v0.0.0-20190115171406-56726106282f/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo= -github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90 h1:S/YWwWx/RA8rT8tKFRuGUZhuA90OyIBpPCXkcbwU8DE= -github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= -github.com/prometheus/common v0.0.0-20180801064454-c7de2306084e/go.mod h1:daVV7qP5qjZbuso7PdcryaAu0sAZbrN9i7WWcTMWvro= -github.com/prometheus/common v0.0.0-20181126121408-4724e9255275/go.mod h1:daVV7qP5qjZbuso7PdcryaAu0sAZbrN9i7WWcTMWvro= -github.com/prometheus/common v0.0.0-20190104105734-b1c43a6df3ae/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4= -github.com/prometheus/common v0.2.0/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4= -github.com/prometheus/common v0.3.0 h1:taZ4h8Tkxv2kNyoSctBvfXEHmBmxrwmIidZTIaHons4= -github.com/prometheus/common v0.3.0/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4= -github.com/prometheus/procfs v0.0.0-20180725123919-05ee40e3a273/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= -github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= -github.com/prometheus/procfs v0.0.0-20181204211112-1dc9a6cbc91a/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= -github.com/prometheus/procfs v0.0.0-20190104112138-b1a0a9a36d74/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= -github.com/prometheus/procfs v0.0.0-20190117184657-bf6a532e95b1/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= -github.com/prometheus/procfs v0.0.0-20190416084830-8368d24ba045 h1:Raos9GP+3BlCBicScEQ+SjTLpYYac34fZMoeqj9McSM= -github.com/prometheus/procfs v0.0.0-20190416084830-8368d24ba045/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA= -github.com/radovskyb/watcher v1.0.6 h1:8WIQ9UxEYMZjem1OwU7dVH94DXXk9mAIE1i8eqHD+IY= -github.com/radovskyb/watcher v1.0.6/go.mod h1:78okwvY5wPdzcb1UYnip1pvrZNIVEIh/Cm+ZuvsUYIg= -github.com/rcrowley/go-metrics v0.0.0-20181016184325-3113b8401b8a/go.mod h1:bCqnVzQkZxMG4s8nGwiZ5l3QUCyqpo9Y+/ZMZ9VjZe4= -github.com/remyoudompheng/bigfft v0.0.0-20170806203942-52369c62f446/go.mod h1:uYEyJGbgTkfkS4+E/PavXkNJcbFIpEtjt2B0KDQ5+9M= -github.com/rollbar/rollbar-go v1.0.2/go.mod h1:AcFs5f0I+c71bpHlXNNDbOWJiKwjFDtISeXco0L5PKQ= -github.com/russross/blackfriday v1.5.2/go.mod h1:JO/DiYxRf+HjHt06OyowR9PTA263kcR/rfWxYHBV53g= -github.com/ryanuber/columnize v0.0.0-20160712163229-9b3edd62028f/go.mod h1:sm1tb6uqfes/u+d4ooFouqFdy9/2g9QGwK3SQygK0Ts= -github.com/ryanuber/columnize v2.1.0+incompatible/go.mod h1:sm1tb6uqfes/u+d4ooFouqFdy9/2g9QGwK3SQygK0Ts= -github.com/ryanuber/go-glob v1.0.0 h1:iQh3xXAumdQ+4Ufa5b25cRpC5TYKlno6hsv6Cb3pkBk= -github.com/ryanuber/go-glob v1.0.0/go.mod h1:807d1WSdnB0XRJzKNil9Om6lcp/3a0v4qIHxIXzX/Yc= -github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529 h1:nn5Wsu0esKSJiIVhscUtVbo7ada43DJhG55ua/hjS5I= -github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529/go.mod h1:DxrIzT+xaE7yg65j358z/aeFdxmN0P9QXhEzd20vsDc= -github.com/sergi/go-diff v1.0.0/go.mod h1:0CfEIISq7TuYL3j771MWULgwwjU+GofnZX9QAmXWZgo= -github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo= -github.com/solo-io/gloo v0.13.17 h1:rbNmO7e5+0vEq5krkO9/Rcp16PqqvepyGD0j3xPdmhg= -github.com/solo-io/gloo v0.13.17/go.mod h1:dNnxchbq5F4ITJhX/0fy5brfbsj6vlW+AwgnTlqZN0A= -github.com/solo-io/go-utils v0.7.11 h1:3Kmk50e6nYqyf7MBY1473XkH5L7qO/Nigjx+t6jEOQo= -github.com/solo-io/go-utils v0.7.11/go.mod h1:7r+dFKdqJNOjx+odeLFqg8SOwVHyVVG1P0EPt6rNLN8= -github.com/solo-io/solo-kit v0.6.3 h1:s/SxcgG7YSjW7wu7iQER5MCHSzeXg1b/lCZRazQ0IMw= -github.com/solo-io/solo-kit v0.6.3/go.mod h1:oBaQ6tOwuO97u7w+s3TeI08YLHcbiWemInx0XkDfKFw= -github.com/solo-io/supergloo v0.3.11 h1:IwnrL2xojowzb7k+V2wCG3I6WrelzXsezqJiraaVxIM= -github.com/solo-io/supergloo v0.3.11/go.mod h1:hJuUwop5IMBL9Qc2/G+f+/PfIWPt/2nGr66fDcuhrn8= -github.com/spf13/afero v1.2.1/go.mod h1:9ZxEEn6pIJ8Rxe320qSDBk6AsU0r9pR7Q4OcevTdifk= -github.com/spf13/cobra v0.0.3/go.mod h1:1l0Ry5zgKvJasoi3XT1TypsSe7PqH0Sj9dhYf7v3XqQ= -github.com/spf13/pflag v0.0.0-20170130214245-9ff6c6923cff/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= -github.com/spf13/pflag v1.0.3 h1:zPAT6CGy6wXeQ7NtTnaTerfKOsV6V6F8agHXFiazDkg= -github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= -github.com/src-d/gcfg v1.4.0/go.mod h1:p/UMsR43ujA89BJY9duynAwIpvqEujIH/jFlfL7jWoI= -github.com/stefanprodan/klog v0.0.0-20190418165334-9cbb78b20423 h1:qTtUiiNM+iq4IXOwHofKW5+jzvkvnNVz0GFRxwukUlY= -github.com/stefanprodan/klog v0.0.0-20190418165334-9cbb78b20423/go.mod h1:TYstY5LQfzxFVm9MiiMg7kZ39sc5cue/6CFoY5KgXn8= -github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= -github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= -github.com/stretchr/testify v0.0.0-20151208002404-e3a8ff8ce365/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= -github.com/stretchr/testify v1.2.1/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= -github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= -github.com/stretchr/testify v1.3.0 h1:TivCn/peBQ7UY8ooIcPgZFpTNSz0Q2U6UrFlUfqbe0Q= -github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= -github.com/technosophos/moniker v0.0.0-20180509230615-a5dbd03a2245/go.mod h1:O1c8HleITsZqzNZDjSNzirUGsMT0oGu9LhHKoJrqO+A= -github.com/xanzy/ssh-agent v0.2.0/go.mod h1:0NyE30eGUDliuLEHJgYte/zncp2zdTStcOnWhgSqHD8= -go.opencensus.io v0.18.0/go.mod h1:vKdFvxhtzZ9onBp9VKHK8z/sRpBMnKAsufL7wlDrCOA= -go.opencensus.io v0.20.1/go.mod h1:6WKK9ahsWS3RSO+PY9ZHZUfv2irvY6gN279GOPZjmmk= -go.opencensus.io v0.20.2 h1:NAfh7zF0/3/HqtMvJNZ/RFrSlCE6ZTlHmKfhL/Dm1Jk= -go.opencensus.io v0.20.2/go.mod h1:6WKK9ahsWS3RSO+PY9ZHZUfv2irvY6gN279GOPZjmmk= -go.uber.org/atomic v1.3.2 h1:2Oa65PReHzfn29GpvgsYwloV9AVFHPDk8tYxt2c2tr4= -go.uber.org/atomic v1.3.2/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE= -go.uber.org/multierr v1.1.0 h1:HoEmRHQPVSqub6w2z2d2EOVs2fjyFRGyofhKuyDq0QI= -go.uber.org/multierr v1.1.0/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/0= -go.uber.org/zap v1.9.1 h1:XCJQEf3W6eZaVwhRBof6ImoYGJSITeKWsyeh3HFu/5o= -go.uber.org/zap v1.9.1/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q= -golang.org/x/crypto v0.0.0-20181025213731-e84da0312774 h1:a4tQYYYuK9QdeO/+kEvNYyuR21S+7ve5EANok6hABhI= -golang.org/x/crypto v0.0.0-20181025213731-e84da0312774/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= -golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= -golang.org/x/exp v0.0.0-20190125153040-c74c464bbbf2/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= -golang.org/x/exp v0.0.0-20190312203227-4b39c73a6495 h1:I6A9Ag9FpEKOjcKrRNjQkPHawoXIhKyTGfvvjFAiiAk= -golang.org/x/exp v0.0.0-20190312203227-4b39c73a6495/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8= -golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js= -golang.org/x/lint v0.0.0-20180702182130-06c8688daad7/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= -golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= -golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU= -golang.org/x/lint v0.0.0-20190301231843-5614ed5bae6f/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= -golang.org/x/mobile v0.0.0-20190312151609-d3739f865fa6/go.mod h1:z+o9i4GpDbdi3rU15maQ/Ox0txvL9dWGYEHz965HBQE= -golang.org/x/net v0.0.0-20190206173232-65e2d4e15006 h1:bfLnR+k0tq5Lqt6dflRLcZiz6UaXCMt3vhYJ1l4FQ80= -golang.org/x/net v0.0.0-20190206173232-65e2d4e15006/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= -golang.org/x/oauth2 v0.0.0-20181203162652-d668ce993890/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= -golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421 h1:Wo7BWFiOk0QRFMLYMqJGFMd9CgUAcGx7V+qEg/h5IBI= -golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= -golang.org/x/oauth2 v0.0.0-20190402181905-9f3314589c9a h1:tImsplftrFpALCYumobsd0K86vlAs/eXGFms2txfJfA= -golang.org/x/oauth2 v0.0.0-20190402181905-9f3314589c9a/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= -golang.org/x/sync v0.0.0-20181108010431-42b317875d0f h1:Bl/8QSvNqXvPGPGXa2z5xUTmV7VDcZyvRZ+QQXkXTZQ= -golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sys v0.0.0-20190209173611-3b5209105503 h1:5SvYFrOM3W8Mexn9/oA44Ji7vhXAZQ9hiP+1Q/DMrWg= -golang.org/x/sys v0.0.0-20190209173611-3b5209105503/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/text v0.0.0-20160726164857-2910a502d2bf/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= -golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= -golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= -golang.org/x/text v0.3.1-0.20181227161524-e6919f6577db/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= -golang.org/x/text v0.3.2 h1:tW2bmiBqwgJj/UpqtC8EpXEZVYOwU0yG4iWbprSVAcs= -golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= -golang.org/x/time v0.0.0-20161028155119-f51c12702a4d/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= -golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= -golang.org/x/time v0.0.0-20190308202827-9d24e82272b4 h1:SvFZT6jyqRaOeXpc5h/JSfZenJ2O330aBsf7JfSUXmQ= -golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= -golang.org/x/tools v0.0.0-20190313210603-aa82965741a9 h1:7Pf/N3ln54fsGsAPsSwSfFhxXGKWHMIRUI/T5x1GP90= -golang.org/x/tools v0.0.0-20190313210603-aa82965741a9/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= -gonum.org/v1/gonum v0.0.0-20190331200053-3d26580ed485 h1:OB/uP/Puiu5vS5QMRPrXCDWUPb+kt8f1KW8oQzFejQw= -gonum.org/v1/gonum v0.0.0-20190331200053-3d26580ed485/go.mod h1:2ltnJ7xHfj0zHS40VVPYEAAMTa3ZGguvHGBSJeRWqE0= -gonum.org/v1/netlib v0.0.0-20190313105609-8cb42192e0e0/go.mod h1:wa6Ws7BG/ESfp6dHfk7C6KdzKA7wR7u/rKwOGE66zvw= -gonum.org/v1/netlib v0.0.0-20190331212654-76723241ea4e h1:jRyg0XfpwWlhEV8mDfdNGBeSJM2fuyh9Yjrnd8kF2Ts= -gonum.org/v1/netlib v0.0.0-20190331212654-76723241ea4e/go.mod h1:kS+toOQn6AQKjmKJ7gzohV1XkqsFehRA2FbsbkopSuQ= -google.golang.org/api v0.0.0-20180910000450-7ca32eb868bf/go.mod h1:4mhQ8q/RsB7i+udVvVy5NUi08OU8ZlA0gRVgrF7VFY0= -google.golang.org/api v0.3.1/go.mod h1:6wY9I6uQWHQ8EM57III9mq/AjF+i8G65rmVagqKMtkk= -google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= -google.golang.org/appengine v1.4.0 h1:/wp5JvzpHIxhs/dumFmF7BXTf3Z+dd4uXta4kVyO508= -google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= -google.golang.org/appengine v1.5.0 h1:KxkO13IPW4Lslp2bz+KHP2E3gtFlrIGNThxkZQ3g+4c= -google.golang.org/appengine v1.5.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= -google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= -google.golang.org/genproto v0.0.0-20180831171423-11092d34479b/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= -google.golang.org/genproto v0.0.0-20181221175505-bd9b4fb69e2f/go.mod h1:7Ep/1NZk928CDR8SjdVbjWNpdIf6nzjE3BTgJDr2Atg= -google.golang.org/genproto v0.0.0-20190307195333-5fe7a883aa19/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= -google.golang.org/genproto v0.0.0-20190404172233-64821d5d2107 h1:xtNn7qFlagY2mQNFHMSRPjT2RkOV4OXM7P5TVy9xATo= -google.golang.org/genproto v0.0.0-20190404172233-64821d5d2107/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= -google.golang.org/grpc v1.14.0/go.mod h1:yo6s7OP7yaDglbqo1J04qKzAhqBH6lvTonzMVmEdcZw= -google.golang.org/grpc v1.16.0/go.mod h1:0JHn/cJsOMiMfNA9+DeHDlAU7KAAB5GDlYFpa9MZMio= -google.golang.org/grpc v1.17.0/go.mod h1:6QZJwpn2B+Zp71q/5VxRsJ6NXXVCE5NRUHRo+f3cWCs= -google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= -google.golang.org/grpc v1.19.1 h1:TrBcJ1yqAl1G++wO39nD/qtgpsW9/1+QGrluyMGEYgM= -google.golang.org/grpc v1.19.1/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= -gopkg.in/AlecAivazis/survey.v1 v1.8.2/go.mod h1:iBNOmqKz/NUbZx3bA+4hAGLRC7fSK7tgtVDT4tB22XA= -gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw= -gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 h1:qIbj1fsPNlZgppZ+VLlY7N33q108Sa+fhmuc+sWQYwY= -gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/fsnotify.v1 v1.4.7 h1:xOHLXZwVvI9hhs+cLKq5+I5onOuwQLhQwiu63xxlHs4= -gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys= -gopkg.in/h2non/gock.v1 v1.0.14 h1:fTeu9fcUvSnLNacYvYI54h+1/XEteDyHvrVCZEEEYNM= -gopkg.in/h2non/gock.v1 v1.0.14/go.mod h1:sX4zAkdYX1TRGJ2JY156cFspQn4yRWn6p9EMdODlynE= -gopkg.in/inf.v0 v0.9.0/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw= -gopkg.in/inf.v0 v0.9.1 h1:73M5CoZyi3ZLMOyDlQh031Cx6N9NDJ2Vvfl76EDAgDc= -gopkg.in/inf.v0 v0.9.1/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw= -gopkg.in/natefinch/lumberjack.v2 v2.0.0/go.mod h1:l0ndWWf7gzL7RNwBG7wST/UCcT4T24xpD6X8LsfU/+k= -gopkg.in/square/go-jose.v2 v2.3.0/go.mod h1:M9dMgbHiYLoDGQrXy7OpJDJWiKiU//h+vD76mk0e1AI= -gopkg.in/src-d/go-billy.v4 v4.2.1/go.mod h1:tm33zBoOwxjYHZIE+OV8bxTWFMJLrconzFMd38aARFk= -gopkg.in/src-d/go-git-fixtures.v3 v3.1.1/go.mod h1:dLBcvytrw/TYZsNTWCnkNF2DSIlzWYqTe3rJR56Ac7g= -gopkg.in/src-d/go-git.v4 v4.10.0/go.mod h1:Vtut8izDyrM8BUVQnzJ+YvmNcem2J89EmfZYCkLokZk= -gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ= -gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw= -gopkg.in/warnings.v0 v0.1.2/go.mod h1:jksf8JmL6Qr/oQM2OXTHunEvvTAsrWBLb6OOjuVWRNI= -gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= -gopkg.in/yaml.v2 v2.2.2 h1:ZCJp+EgiOT7lHqUV2J862kp8Qj64Jo6az82+3Td9dZw= -gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= -honnef.co/go/tools v0.0.0-20180728063816-88497007e858/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= -honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= -honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= -istio.io/gogo-genproto v0.0.0-20190124151557-6d926a6e6feb/go.mod h1:eIDJ6jNk/IeJz6ODSksHl5Aiczy5JUq6vFhJWI5OtiI= -k8s.io/api v0.0.0-20190620073856-dcce3486da33 h1:aC/EvF9PT1h8NeMEOVwTel8xxbZwq0SZnxXNThEROnE= -k8s.io/api v0.0.0-20190620073856-dcce3486da33/go.mod h1:ldk709UQo/iedNLOW7J06V9QSSGY5heETKeWqnPoqF8= -k8s.io/apiextensions-apiserver v0.0.0-20190111034747-7d26de67f177+incompatible/go.mod h1:IxkesAMoaCRoLrPJdZNZUQp9NfZnzqaVzLhb2VEQzXE= -k8s.io/apiextensions-apiserver v0.0.0-20190315093550-53c4693659ed h1:rCteec//ELIjZMfjIGQbVtZooyaofqDJwsmWwWKItNs= -k8s.io/apiextensions-apiserver v0.0.0-20190315093550-53c4693659ed/go.mod h1:IxkesAMoaCRoLrPJdZNZUQp9NfZnzqaVzLhb2VEQzXE= -k8s.io/apimachinery v0.0.0-20190620073744-d16981aedf33 h1:Lkd+QNFOB3DqrDyWo796aodJgFJautn/M+t9IGearPc= -k8s.io/apimachinery v0.0.0-20190620073744-d16981aedf33/go.mod h1:9q5NW/mMno/nwbRZd/Ks2TECgi2PTZ9cwarf4q+ze6Q= -k8s.io/apiserver v0.0.0-20190111033246-d50e9ac5404f+incompatible/go.mod h1:6bqaTSOSJavUIXUtfaR9Os9JtTCm8ZqH2SUl2S60C4w= -k8s.io/cli-runtime v0.0.0-20190111035321-c7263d800665+incompatible/go.mod h1:qWnH3/b8sp/l7EvlDh7ulDU3UWA4P4N1NFbEEP791tM= -k8s.io/client-go v0.0.0-20190620074045-585a16d2e773 h1:XyjDnwRO9icfyrN7HRSa8o3NqdPOEQoVW8vWizuqyQQ= -k8s.io/client-go v0.0.0-20190620074045-585a16d2e773/go.mod h1:miKCC7C/WGwJqcDctyJtAnP3Gss0Y5KwURqJ7q5pfEw= -k8s.io/code-generator v0.0.0-20190620073620-d55040311883 h1:NWWNvN6IdpmQvZ43rVccCI8GPUrheK8XNdqeKycw0DI= -k8s.io/code-generator v0.0.0-20190620073620-d55040311883/go.mod h1:+a+9g9W0llgbgvx6qOb+VbeZPH5km1FrVyMQe9/jkQY= -k8s.io/gengo v0.0.0-20190116091435-f8a0810f38af/go.mod h1:ezvh/TsK7cY6rbqRK0oQQ8IAqLxYwwyPxAX1Pzy0ii0= -k8s.io/gengo v0.0.0-20190128074634-0689ccc1d7d6 h1:4s3/R4+OYYYUKptXPhZKjQ04WJ6EhQQVFdjOFvCazDk= -k8s.io/gengo v0.0.0-20190128074634-0689ccc1d7d6/go.mod h1:ezvh/TsK7cY6rbqRK0oQQ8IAqLxYwwyPxAX1Pzy0ii0= -k8s.io/helm v2.13.0+incompatible/go.mod h1:LZzlS4LQBHfciFOurYBFkCMTaZ0D1l+p0teMg7TSULI= -k8s.io/kube-openapi v0.0.0-20190228160746-b3a7cee44a30/go.mod h1:BXM9ceUBTj2QnfH2MK1odQs778ajze1RxcmP6S8RVVc= -k8s.io/kube-openapi v0.0.0-20190401085232-94e1e7b7574c/go.mod h1:BXM9ceUBTj2QnfH2MK1odQs778ajze1RxcmP6S8RVVc= -k8s.io/kube-openapi v0.0.0-20190418160015-6b3d3b2d5666 h1:hlzz2EvLPcefAcG/j0tOZpds4LWSElZzxpZuhxbblbc= -k8s.io/kube-openapi v0.0.0-20190418160015-6b3d3b2d5666/go.mod h1:jqYp7BKXW0Jl+F1dWXBieUmcHKMPpGHGWA0uqfpOZZ4= -k8s.io/kubernetes v1.13.2/go.mod h1:ocZa8+6APFNC2tX1DZASIbocyYT5jHzqFVsY5aoB7Jk= -k8s.io/utils v0.0.0-20190221042446-c2654d5206da/go.mod h1:8k8uAuAQ0rXslZKaEWd0c3oVhZz7sSzSiPnVZayjIX0= -k8s.io/utils v0.0.0-20190308190857-21c4ce38f2a7 h1:8r+l4bNWjRlsFYlQJnKJ2p7s1YQPj4XyXiJVqDHRx7c= -k8s.io/utils v0.0.0-20190308190857-21c4ce38f2a7/go.mod h1:8k8uAuAQ0rXslZKaEWd0c3oVhZz7sSzSiPnVZayjIX0= -modernc.org/cc v1.0.0/go.mod h1:1Sk4//wdnYJiUIxnW8ddKpaOJCF37yAdqYnkxUpaYxw= -modernc.org/golex v1.0.0/go.mod h1:b/QX9oBD/LhixY6NDh+IdGv17hgB+51fET1i2kPSmvk= -modernc.org/mathutil v1.0.0/go.mod h1:wU0vUrJsVWBZ4P6e7xtFJEhFSNsfRLJ8H458uRjg03k= -modernc.org/strutil v1.0.0/go.mod h1:lstksw84oURvj9y3tn8lGvRxyRC1S2+g5uuIzNfIOBs= -modernc.org/xc v1.0.0/go.mod h1:mRNCo0bvLjGhHO9WsyuKVU4q0ceiDDDoEeWDJHrNx8I= -sigs.k8s.io/controller-runtime v0.1.10/go.mod h1:HFAYoOh6XMV+jKF1UjFwrknPbowfyHEHHRdJMf2jMX8= -sigs.k8s.io/structured-merge-diff v0.0.0-20181214233322-d43a45b8663b/go.mod h1:wWxsB5ozmmv/SG7nM11ayaAW51xMvak/t1r0CSlcokI= -sigs.k8s.io/yaml v1.1.0 h1:4A07+ZFc2wgJwo8YNlQpr1rVlgUDlxXHhPJciaPY5gs= -sigs.k8s.io/yaml v1.1.0/go.mod h1:UJmg0vDUVViEyp3mgSv9WPwZCDxu4rQW1olrI1uml+o= -vbom.ml/util v0.0.0-20180919145318-efcd4e0f9787/go.mod h1:so/NYdZXCz+E3ZpW0uAoCj6uzU2+8OWDFv/HxUSs7kI= diff --git a/hack/boilerplate.go.txt b/hack/boilerplate.go.txt deleted file mode 100755 index ccbde0e2..00000000 --- a/hack/boilerplate.go.txt +++ /dev/null @@ -1,16 +0,0 @@ -/* -Copyright The Flagger Authors. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - diff --git a/hack/update-codegen.sh b/hack/update-codegen.sh deleted file mode 100755 index 2913d77e..00000000 --- a/hack/update-codegen.sh +++ /dev/null @@ -1,38 +0,0 @@ -#!/usr/bin/env bash - -set -o errexit -set -o nounset -set -o pipefail - -SCRIPT_ROOT=$(realpath $(dirname ${BASH_SOURCE})/..) - -# Grab code-generator version from go.sum. -CODEGEN_VERSION=$(grep 'k8s.io/code-generator' go.sum | awk '{print $2}' | head -1) -CODEGEN_PKG=$(echo `go env GOPATH`"/pkg/mod/k8s.io/code-generator@${CODEGEN_VERSION}") - -echo ">> Using ${CODEGEN_PKG}" - -# code-generator does work with go.mod but makes assumptions about -# the project living in `$GOPATH/src`. To work around this and support -# any location; create a temporary directory, use this as an output -# base, and copy everything back once generated. -TEMP_DIR=$(mktemp -d) -cleanup() { - echo ">> Removing ${TEMP_DIR}" - rm -rf ${TEMP_DIR} -} -trap "cleanup" EXIT SIGINT - -echo ">> Temporary output directory ${TEMP_DIR}" - -# Ensure we can execute. -chmod +x ${CODEGEN_PKG}/generate-groups.sh - -${CODEGEN_PKG}/generate-groups.sh all \ - github.com/weaveworks/flagger/pkg/client github.com/weaveworks/flagger/pkg/apis \ - "appmesh:v1beta1 istio:v1alpha3 flagger:v1alpha3 smi:v1alpha1" \ - --output-base "${TEMP_DIR}" \ - --go-header-file ${SCRIPT_ROOT}/hack/boilerplate.go.txt - -# Copy everything back. -cp -r "${TEMP_DIR}/github.com/weaveworks/flagger/." "${SCRIPT_ROOT}/" diff --git a/hack/verify-codegen.sh b/hack/verify-codegen.sh deleted file mode 100755 index d02a6fa3..00000000 --- a/hack/verify-codegen.sh +++ /dev/null @@ -1,48 +0,0 @@ -#!/usr/bin/env bash - -# Copyright 2017 The Kubernetes Authors. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -set -o errexit -set -o nounset -set -o pipefail - -SCRIPT_ROOT=$(dirname "${BASH_SOURCE}")/.. - -DIFFROOT="${SCRIPT_ROOT}/pkg" -TMP_DIFFROOT="${SCRIPT_ROOT}/_tmp/pkg" -_tmp="${SCRIPT_ROOT}/_tmp" - -cleanup() { - rm -rf "${_tmp}" -} -trap "cleanup" EXIT SIGINT - -cleanup - -mkdir -p "${TMP_DIFFROOT}" -cp -a "${DIFFROOT}"/* "${TMP_DIFFROOT}" - -"${SCRIPT_ROOT}/hack/update-codegen.sh" -echo "diffing ${DIFFROOT} against freshly generated codegen" -ret=0 -diff -Naupr "${DIFFROOT}" "${TMP_DIFFROOT}" || ret=$? -cp -a "${TMP_DIFFROOT}"/* "${DIFFROOT}" -if [[ $ret -eq 0 ]] -then - echo "${DIFFROOT} up to date." -else - echo "${DIFFROOT} is out of date. Please run hack/update-codegen.sh" - exit 1 -fi diff --git a/kustomize/README.md b/kustomize/README.md deleted file mode 100644 index b9bdf981..00000000 --- a/kustomize/README.md +++ /dev/null @@ -1,145 +0,0 @@ -# Flagger Kustomize installer - -As an alternative to Helm, Flagger can be installed with [Kustomize](https://kustomize.io/). - -## Service mesh specific installers - -Install Flagger for Istio: - -```bash -kubectl apply -k github.com/weaveworks/flagger//kustomize/istio -``` - -This deploys Flagger in the `istio-system` namespace and sets the metrics server URL to Istio's Prometheus instance. - -Note that you'll need kubectl 1.14 to run the above the command or you can download the -[kustomize binary](https://github.com/kubernetes-sigs/kustomize/releases) and run: - -```bash -kustomize build github.com/weaveworks/flagger//kustomize/istio | kubectl apply -f - -``` - -Install Flagger for Linkerd: - -```bash -kubectl apply -k github.com/weaveworks/flagger//kustomize/linkerd -``` - -This deploys Flagger in the `linkerd` namespace and sets the metrics server URL to Linkerd's Prometheus instance. - -If you want to install a specific Flagger release, add the version number to the URL: - -```bash -kubectl apply -k github.com/weaveworks/flagger//kustomize/linkerd?ref=0.18.0 -``` - -## Generic installer - -Install Flagger and Prometheus: - -```bash -kubectl apply -k github.com/weaveworks/flagger//kustomize/kubernetes -``` - -This deploys Flagger and Prometheus in the `flagger-system` namespace, -sets the metrics server URL to `http://flagger-prometheus.flagger-system:9090` and the mesh provider to `kubernetes`. - -To target a different provider you can specify it in the canary custom resource: - -```yaml -apiVersion: flagger.app/v1alpha3 -kind: Canary -metadata: - name: app - namespace: test -spec: - # can be: kubernetes, istio, linkerd, appmesh, nginx, gloo - # use the kubernetes provider for Blue/Green style deployments - provider: nginx -``` - -You'll need Prometheus when using Flagger with AWS App Mesh, Gloo or NGINX ingress controller. -The Prometheus instance has a two hours data retention and is configured to scrape all pods in your cluster that -have the `prometheus.io/scrape: "true"` annotation. - -## Configure Slack notifications - -Create a kustomization file using flagger as base: - -```bash -cat > kustomization.yaml < patch.yaml < kustomization.yaml < patch.yaml <-. - Port PortSelector `json:"port"` - - // Settings controlling the load balancer algorithms. - LoadBalancer *LoadBalancerSettings `json:"loadBalancer,omitempty"` - - // Settings controlling the volume of connections to an upstream service - ConnectionPool *ConnectionPoolSettings `json:"connectionPool,omitempty"` - - // Settings controlling eviction of unhealthy hosts from the load balancing pool - OutlierDetection *OutlierDetection `json:"outlierDetection,omitempty"` - - // TLS related settings for connections to the upstream service. - TLS *TLSSettings `json:"tls,omitempty"` -} - -// A subset of endpoints of a service. Subsets can be used for scenarios -// like A/B testing, or routing to a specific version of a service. Refer -// to [VirtualService](#VirtualService) documentation for examples of using -// subsets in these scenarios. In addition, traffic policies defined at the -// service-level can be overridden at a subset-level. The following rule -// uses a round robin load balancing policy for all traffic going to a -// subset named testversion that is composed of endpoints (e.g., pods) with -// labels (version:v3). -// -// apiVersion: networking.istio.io/v1alpha3 -// kind: DestinationRule -// metadata: -// name: bookinfo-ratings -// spec: -// host: ratings.prod.svc.cluster.local -// trafficPolicy: -// loadBalancer: -// simple: LEAST_CONN -// subsets: -// - name: testversion -// labels: -// version: v3 -// trafficPolicy: -// loadBalancer: -// simple: ROUND_ROBIN -// -// **Note:** Policies specified for subsets will not take effect until -// a route rule explicitly sends traffic to this subset. -type Subset struct { - // REQUIRED. Name of the subset. The service name and the subset name can - // be used for traffic splitting in a route rule. - Name string `json:"name"` - - // REQUIRED. Labels apply a filter over the endpoints of a service in the - // service registry. See route rules for examples of usage. - Labels map[string]string `json:"labels"` - - // Traffic policies that apply to this subset. Subsets inherit the - // traffic policies specified at the DestinationRule level. Settings - // specified at the subset level will override the corresponding settings - // specified at the DestinationRule level. - TrafficPolicy *TrafficPolicy `json:"trafficPolicy,omitempty"` -} - -// Load balancing policies to apply for a specific destination. See Envoy's -// load balancing -// [documentation](https://www.envoyproxy.io/docs/envoy/latest/intro/arch_overview/load_balancing.html) -// for more details. -// -// For example, the following rule uses a round robin load balancing policy -// for all traffic going to the ratings service. -// -// apiVersion: networking.istio.io/v1alpha3 -// kind: DestinationRule -// metadata: -// name: bookinfo-ratings -// spec: -// host: ratings.prod.svc.cluster.local -// trafficPolicy: -// loadBalancer: -// simple: ROUND_ROBIN -// -// The following example sets up sticky sessions for the ratings service -// hashing-based load balancer for the same ratings service using the -// the User cookie as the hash key. -// -// apiVersion: networking.istio.io/v1alpha3 -// kind: DestinationRule -// metadata: -// name: bookinfo-ratings -// spec: -// host: ratings.prod.svc.cluster.local -// trafficPolicy: -// loadBalancer: -// consistentHash: -// httpCookie: -// name: user -// ttl: 0s -type LoadBalancerSettings struct { - // It is required to specify exactly one of the fields: - // Simple or ConsistentHash - Simple SimpleLB `json:"simple,omitempty"` - ConsistentHash *ConsistentHashLB `json:"consistentHash,omitempty"` -} - -// Standard load balancing algorithms that require no tuning. -type SimpleLB string - -const ( - // Round Robin policy. Default - SimpleLBRoundRobin SimpleLB = "ROUND_ROBIN" - - // The least request load balancer uses an O(1) algorithm which selects - // two random healthy hosts and picks the host which has fewer active - // requests. - SimpleLBLeastConn SimpleLB = "LEAST_CONN" - - // The random load balancer selects a random healthy host. The random - // load balancer generally performs better than round robin if no health - // checking policy is configured. - SimpleLBRandom SimpleLB = "RANDOM" - - // This option will forward the connection to the original IP address - // requested by the caller without doing any form of load - // balancing. This option must be used with care. It is meant for - // advanced use cases. Refer to Original Destination load balancer in - // Envoy for further details. - SimpleLBPassthrough SimpleLB = "PASSTHROUGH" -) - -// Consistent Hash-based load balancing can be used to provide soft -// session affinity based on HTTP headers, cookies or other -// properties. This load balancing policy is applicable only for HTTP -// connections. The affinity to a particular destination host will be -// lost when one or more hosts are added/removed from the destination -// service. -type ConsistentHashLB struct { - - // It is required to specify exactly one of the fields as hash key: - // HTTPHeaderName, HTTPCookie, or UseSourceIP. - // Hash based on a specific HTTP header. - HTTPHeaderName string `json:"httpHeaderName,omitempty"` - - // Hash based on HTTP cookie. - HTTPCookie *HTTPCookie `json:"httpCookie,omitempty"` - - // Hash based on the source IP address. - UseSourceIP bool `json:"useSourceIp,omitempty"` - - // The minimum number of virtual nodes to use for the hash - // ring. Defaults to 1024. Larger ring sizes result in more granular - // load distributions. If the number of hosts in the load balancing - // pool is larger than the ring size, each host will be assigned a - // single virtual node. - MinimumRingSize uint64 `json:"minimumRingSize,omitempty"` -} - -// Describes a HTTP cookie that will be used as the hash key for the -// Consistent Hash load balancer. If the cookie is not present, it will -// be generated. -type HTTPCookie struct { - // REQUIRED. Name of the cookie. - Name string `json:"name"` - - // Path to set for the cookie. - Path string `json:"path,omitempty"` - - // REQUIRED. Lifetime of the cookie. - TTL string `json:"ttl"` -} - -// Connection pool settings for an upstream host. The settings apply to -// each individual host in the upstream service. See Envoy's [circuit -// breaker](https://www.envoyproxy.io/docs/envoy/latest/intro/arch_overview/circuit_breaking) -// for more details. Connection pool settings can be applied at the TCP -// level as well as at HTTP level. -// -// For example, the following rule sets a limit of 100 connections to redis -// service called myredissrv with a connect timeout of 30ms -// -// apiVersion: networking.istio.io/v1alpha3 -// kind: DestinationRule -// metadata: -// name: bookinfo-redis -// spec: -// host: myredissrv.prod.svc.cluster.local -// trafficPolicy: -// connectionPool: -// tcp: -// maxConnections: 100 -// connectTimeout: 30ms -type ConnectionPoolSettings struct { - - // Settings common to both HTTP and TCP upstream connections. - TCP *TCPSettings `json:"tcp,omitempty"` - - // HTTP connection pool settings. - HTTP *HTTPSettings `json:"http,omitempty"` -} - -// Settings common to both HTTP and TCP upstream connections. -type TCPSettings struct { - // Maximum number of HTTP1 /TCP connections to a destination host. - MaxConnections int32 `json:"maxConnections,omitempty"` - - // TCP connection timeout. - ConnectTimeout string `json:"connectTimeout,omitempty"` -} - -// Settings applicable to HTTP1.1/HTTP2/GRPC connections. -type HTTPSettings struct { - // Maximum number of pending HTTP requests to a destination. Default 1024. - HTTP1MaxPendingRequests int32 `json:"http1MaxPendingRequests,omitempty"` - - // Maximum number of requests to a backend. Default 1024. - HTTP2MaxRequests int32 `json:"http2MaxRequests,omitempty"` - - // Maximum number of requests per connection to a backend. Setting this - // parameter to 1 disables keep alive. - MaxRequestsPerConnection int32 `json:"maxRequestsPerConnection,omitempty"` - - // Maximum number of retries that can be outstanding to all hosts in a - // cluster at a given time. Defaults to 3. - MaxRetries int32 `json:"maxRetries,omitempty"` -} - -// A Circuit breaker implementation that tracks the status of each -// individual host in the upstream service. Applicable to both HTTP and -// TCP services. For HTTP services, hosts that continually return 5xx -// errors for API calls are ejected from the pool for a pre-defined period -// of time. For TCP services, connection timeouts or connection -// failures to a given host counts as an error when measuring the -// consecutive errors metric. See Envoy's [outlier -// detection](https://www.envoyproxy.io/docs/envoy/latest/intro/arch_overview/outlier) -// for more details. -// -// The following rule sets a connection pool size of 100 connections and -// 1000 concurrent HTTP2 requests, with no more than 10 req/connection to -// "reviews" service. In addition, it configures upstream hosts to be -// scanned every 5 mins, such that any host that fails 7 consecutive times -// with 5XX error code will be ejected for 15 minutes. -// -// apiVersion: networking.istio.io/v1alpha3 -// kind: DestinationRule -// metadata: -// name: reviews-cb-policy -// spec: -// host: reviews.prod.svc.cluster.local -// trafficPolicy: -// connectionPool: -// tcp: -// maxConnections: 100 -// http: -// http2MaxRequests: 1000 -// maxRequestsPerConnection: 10 -// outlierDetection: -// consecutiveErrors: 7 -// interval: 5m -// baseEjectionTime: 15m -type OutlierDetection struct { - // Number of errors before a host is ejected from the connection - // pool. Defaults to 5. When the upstream host is accessed over HTTP, a - // 5xx return code qualifies as an error. When the upstream host is - // accessed over an opaque TCP connection, connect timeouts and - // connection error/failure events qualify as an error. - ConsecutiveErrors int32 `json:"consecutiveErrors,omitempty"` - - // Time interval between ejection sweep analysis. format: - // 1h/1m/1s/1ms. MUST BE >=1ms. Default is 10s. - Interval string `json:"interval,omitempty"` - - // Minimum ejection duration. A host will remain ejected for a period - // equal to the product of minimum ejection duration and the number of - // times the host has been ejected. This technique allows the system to - // automatically increase the ejection period for unhealthy upstream - // servers. format: 1h/1m/1s/1ms. MUST BE >=1ms. Default is 30s. - BaseEjectionTime string `json:"baseEjectionTime,omitempty"` - - // Maximum % of hosts in the load balancing pool for the upstream - // service that can be ejected. Defaults to 10%. - MaxEjectionPercent int32 `json:"maxEjectionPercent,omitempty"` -} - -// SSL/TLS related settings for upstream connections. See Envoy's [TLS -// context](https://www.envoyproxy.io/docs/envoy/latest/api-v1/cluster_manager/cluster_ssl.html#config-cluster-manager-cluster-ssl) -// for more details. These settings are common to both HTTP and TCP upstreams. -// -// For example, the following rule configures a client to use mutual TLS -// for connections to upstream database cluster. -// -// apiVersion: networking.istio.io/v1alpha3 -// kind: DestinationRule -// metadata: -// name: db-mtls -// spec: -// host: mydbserver.prod.svc.cluster.local -// trafficPolicy: -// tls: -// mode: MUTUAL -// clientCertificate: /etc/certs/myclientcert.pem -// privateKey: /etc/certs/client_private_key.pem -// caCertificates: /etc/certs/rootcacerts.pem -// -// The following rule configures a client to use TLS when talking to a -// foreign service whose domain matches *.foo.com. -// -// apiVersion: networking.istio.io/v1alpha3 -// kind: DestinationRule -// metadata: -// name: tls-foo -// spec: -// host: "*.foo.com" -// trafficPolicy: -// tls: -// mode: SIMPLE -// -// The following rule configures a client to use Istio mutual TLS when talking -// to rating services. -// -// apiVersion: networking.istio.io/v1alpha3 -// kind: DestinationRule -// metadata: -// name: ratings-istio-mtls -// spec: -// host: ratings.prod.svc.cluster.local -// trafficPolicy: -// tls: -// mode: ISTIO_MUTUAL -type TLSSettings struct { - - // REQUIRED: Indicates whether connections to this port should be secured - // using TLS. The value of this field determines how TLS is enforced. - Mode TLSmode `json:"mode"` - - // REQUIRED if mode is `MUTUAL`. The path to the file holding the - // client-side TLS certificate to use. - // Should be empty if mode is `ISTIO_MUTUAL`. - ClientCertificate string `json:"clientCertificate,omitempty"` - - // REQUIRED if mode is `MUTUAL`. The path to the file holding the - // client's private key. - // Should be empty if mode is `ISTIO_MUTUAL`. - PrivateKey string `json:"privateKey,omitempty"` - - // OPTIONAL: The path to the file containing certificate authority - // certificates to use in verifying a presented server certificate. If - // omitted, the proxy will not verify the server's certificate. - // Should be empty if mode is `ISTIO_MUTUAL`. - CaCertificates string `json:"caCertificates,omitempty"` - - // A list of alternate names to verify the subject identity in the - // certificate. If specified, the proxy will verify that the server - // certificate's subject alt name matches one of the specified values. - // Should be empty if mode is `ISTIO_MUTUAL`. - SubjectAltNames []string `json:"subjectAltNames,omitempty"` - - // SNI string to present to the server during TLS handshake. - // Should be empty if mode is `ISTIO_MUTUAL`. - Sni string `json:"sni,omitempty"` -} - -// TLS connection mode -type TLSmode string - -const ( - // Do not setup a TLS connection to the upstream endpoint. - TLSmodeDisable TLSmode = "DISABLE" - - // Originate a TLS connection to the upstream endpoint. - TLSmodeSimple TLSmode = "SIMPLE" - - // Secure connections to the upstream using mutual TLS by presenting - // client certificates for authentication. - TLSmodeMutual TLSmode = "MUTUAL" - - // Secure connections to the upstream using mutual TLS by presenting - // client certificates for authentication. - // Compared to Mutual mode, this mode uses certificates generated - // automatically by Istio for mTLS authentication. When this mode is - // used, all other fields in `TLSSettings` should be empty. - TLSmodeIstioMutual TLSmode = "ISTIO_MUTUAL" -) - -// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object -// DestinationRuleList is a list of DestinationRule resources -type DestinationRuleList struct { - metav1.TypeMeta `json:",inline"` - metav1.ListMeta `json:"metadata"` - Items []DestinationRule `json:"items"` -} diff --git a/pkg/apis/istio/v1alpha3/doc.go b/pkg/apis/istio/v1alpha3/doc.go deleted file mode 100644 index 4e7f04ef..00000000 --- a/pkg/apis/istio/v1alpha3/doc.go +++ /dev/null @@ -1,7 +0,0 @@ -// Api versions allow the api contract for a resource to be changed while keeping -// backward compatibility by support multiple concurrent versions -// of the same resource - -// +k8s:deepcopy-gen=package -// +groupName=networking.istio.io -package v1alpha3 diff --git a/pkg/apis/istio/v1alpha3/register.go b/pkg/apis/istio/v1alpha3/register.go deleted file mode 100644 index a7bd3abd..00000000 --- a/pkg/apis/istio/v1alpha3/register.go +++ /dev/null @@ -1,38 +0,0 @@ -package v1alpha3 - -import ( - "github.com/weaveworks/flagger/pkg/apis/istio" - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - "k8s.io/apimachinery/pkg/runtime" - "k8s.io/apimachinery/pkg/runtime/schema" -) - -// SchemeGroupVersion is group version used to register these objects -var SchemeGroupVersion = schema.GroupVersion{Group: istio.GroupName, Version: "v1alpha3"} - -// Kind takes an unqualified kind and returns back a Group qualified GroupKind -func Kind(kind string) schema.GroupKind { - return SchemeGroupVersion.WithKind(kind).GroupKind() -} - -// Resource takes an unqualified resource and returns a Group qualified GroupResource -func Resource(resource string) schema.GroupResource { - return SchemeGroupVersion.WithResource(resource).GroupResource() -} - -var ( - SchemeBuilder = runtime.NewSchemeBuilder(addKnownTypes) - AddToScheme = SchemeBuilder.AddToScheme -) - -// Adds the list of known types to Scheme. -func addKnownTypes(scheme *runtime.Scheme) error { - scheme.AddKnownTypes(SchemeGroupVersion, - &VirtualService{}, - &VirtualServiceList{}, - &DestinationRule{}, - &DestinationRuleList{}, - ) - metav1.AddToGroupVersion(scheme, SchemeGroupVersion) - return nil -} diff --git a/pkg/apis/istio/v1alpha3/virtual_service.go b/pkg/apis/istio/v1alpha3/virtual_service.go deleted file mode 100644 index e911f8ee..00000000 --- a/pkg/apis/istio/v1alpha3/virtual_service.go +++ /dev/null @@ -1,829 +0,0 @@ -// proto: https://github.com/istio/api/blob/master/networking/v1alpha3/virtual_service.proto -package v1alpha3 - -import ( - "github.com/weaveworks/flagger/pkg/apis/istio/common/v1alpha1" - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" -) - -// +genclient -// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object - -// VirtualService -type VirtualService struct { - metav1.TypeMeta `json:",inline"` - metav1.ObjectMeta `json:"metadata,omitempty"` - - Spec VirtualServiceSpec `json:"spec"` -} - -// VirtualServiceSpec defines a set of traffic routing rules to apply when a host is -// addressed. Each routing rule defines matching criteria for traffic of a specific -// protocol. If the traffic is matched, then it is sent to a named destination service -// (or subset/version of it) defined in the registry. -// -// The source of traffic can also be matched in a routing rule. This allows routing -// to be customized for specific client contexts. -// -// The following example on Kubernetes, routes all HTTP traffic by default to -// pods of the reviews service with label "version: v1". In addition, -// HTTP requests with path starting with /wpcatalog/ or /consumercatalog/ will -// be rewritten to /newcatalog and sent to pods with label "version: v2". -// -// -// ```yaml -// apiVersion: networking.istio.io/v1alpha3 -// kind: VirtualService -// metadata: -// name: reviews-route -// spec: -// hosts: -// - reviews.prod.svc.cluster.local -// http: -// - match: -// - uri: -// prefix: "/wpcatalog" -// - uri: -// prefix: "/consumercatalog" -// rewrite: -// uri: "/newcatalog" -// route: -// - destination: -// host: reviews.prod.svc.cluster.local -// subset: v2 -// - route: -// - destination: -// host: reviews.prod.svc.cluster.local -// subset: v1 -// ``` -// -// A subset/version of a route destination is identified with a reference -// to a named service subset which must be declared in a corresponding -// `DestinationRule`. -// -// ```yaml -// apiVersion: networking.istio.io/v1alpha3 -// kind: DestinationRule -// metadata: -// name: reviews-destination -// spec: -// host: reviews.prod.svc.cluster.local -// subsets: -// - name: v1 -// labels: -// version: v1 -// - name: v2 -// labels: -// version: v2 -// ``` -// -type VirtualServiceSpec struct { - // REQUIRED. The destination hosts to which traffic is being sent. Could - // be a DNS name with wildcard prefix or an IP address. Depending on the - // platform, short-names can also be used instead of a FQDN (i.e. has no - // dots in the name). In such a scenario, the FQDN of the host would be - // derived based on the underlying platform. - // - // **A host name can be defined by only one VirtualService**. A single - // VirtualService can be used to describe traffic properties for multiple - // HTTP and TCP ports. - // - // *Note for Kubernetes users*: When short names are used (e.g. "reviews" - // instead of "reviews.default.svc.cluster.local"), Istio will interpret - // the short name based on the namespace of the rule, not the service. A - // rule in the "default" namespace containing a host "reviews will be - // interpreted as "reviews.default.svc.cluster.local", irrespective of - // the actual namespace associated with the reviews service. _To avoid - // potential misconfigurations, it is recommended to always use fully - // qualified domain names over short names._ - // - // The hosts field applies to both HTTP and TCP services. Service inside - // the mesh, i.e., those found in the service registry, must always be - // referred to using their alphanumeric names. IP addresses are allowed - // only for services defined via the Gateway. - Hosts []string `json:"hosts"` - - // The names of gateways and sidecars that should apply these routes. A - // single VirtualService is used for sidecars inside the mesh as well as - // for one or more gateways. The selection condition imposed by this - // field can be overridden using the source field in the match conditions - // of protocol-specific routes. The reserved word `mesh` is used to imply - // all the sidecars in the mesh. When this field is omitted, the default - // gateway (`mesh`) will be used, which would apply the rule to all - // sidecars in the mesh. If a list of gateway names is provided, the - // rules will apply only to the gateways. To apply the rules to both - // gateways and sidecars, specify `mesh` as one of the gateway names. - Gateways []string `json:"gateways,omitempty"` - - // An ordered list of route rules for HTTP traffic. HTTP routes will be - // applied to platform service ports named 'http-*'/'http2-*'/'grpc-*', gateway - // ports with protocol HTTP/HTTP2/GRPC/ TLS-terminated-HTTPS and service - // entry ports using HTTP/HTTP2/GRPC protocols. The first rule matching - // an incoming request is used. - Http []HTTPRoute `json:"http,omitempty"` - - // An ordered list of route rules for opaque TCP traffic. TCP routes will - // be applied to any port that is not a HTTP or TLS port. The first rule - // matching an incoming request is used. - Tcp []TCPRoute `json:"tcp,omitempty"` -} - -// Destination indicates the network addressable service to which the -// request/connection will be sent after processing a routing rule. The -// destination.host should unambiguously refer to a service in the service -// registry. Istio's service registry is composed of all the services found -// in the platform's service registry (e.g., Kubernetes services, Consul -// services), as well as services declared through the -// [ServiceEntry](#ServiceEntry) resource. -// -// *Note for Kubernetes users*: When short names are used (e.g. "reviews" -// instead of "reviews.default.svc.cluster.local"), Istio will interpret -// the short name based on the namespace of the rule, not the service. A -// rule in the "default" namespace containing a host "reviews will be -// interpreted as "reviews.default.svc.cluster.local", irrespective of the -// actual namespace associated with the reviews service. _To avoid potential -// misconfigurations, it is recommended to always use fully qualified -// domain names over short names._ -// -// The following Kubernetes example routes all traffic by default to pods -// of the reviews service with label "version: v1" (i.e., subset v1), and -// some to subset v2, in a kubernetes environment. -// -// ```yaml -// apiVersion: networking.istio.io/v1alpha3 -// kind: VirtualService -// metadata: -// name: reviews-route -// namespace: foo -// spec: -// hosts: -// - reviews # interpreted as reviews.foo.svc.cluster.local -// http: -// - match: -// - uri: -// prefix: "/wpcatalog" -// - uri: -// prefix: "/consumercatalog" -// rewrite: -// uri: "/newcatalog" -// route: -// - destination: -// host: reviews # interpreted as reviews.foo.svc.cluster.local -// subset: v2 -// - route: -// - destination: -// host: reviews # interpreted as reviews.foo.svc.cluster.local -// subset: v1 -// ``` -// -// And the associated DestinationRule -// -// ```yaml -// apiVersion: networking.istio.io/v1alpha3 -// kind: DestinationRule -// metadata: -// name: reviews-destination -// namespace: foo -// spec: -// host: reviews # interpreted as reviews.foo.svc.cluster.local -// subsets: -// - name: v1 -// labels: -// version: v1 -// - name: v2 -// labels: -// version: v2 -// ``` -// -// The following VirtualService sets a timeout of 5s for all calls to -// productpage.prod.svc.cluster.local service in Kubernetes. Notice that -// there are no subsets defined in this rule. Istio will fetch all -// instances of productpage.prod.svc.cluster.local service from the service -// registry and populate the sidecar's load balancing pool. Also, notice -// that this rule is set in the istio-system namespace but uses the fully -// qualified domain name of the productpage service, -// productpage.prod.svc.cluster.local. Therefore the rule's namespace does -// not have an impact in resolving the name of the productpage service. -// -// ```yaml -// apiVersion: networking.istio.io/v1alpha3 -// kind: VirtualService -// metadata: -// name: my-productpage-rule -// namespace: istio-system -// spec: -// hosts: -// - productpage.prod.svc.cluster.local # ignores rule namespace -// http: -// - timeout: 5s -// route: -// - destination: -// host: productpage.prod.svc.cluster.local -// ``` -// -// To control routing for traffic bound to services outside the mesh, external -// services must first be added to Istio's internal service registry using the -// ServiceEntry resource. VirtualServices can then be defined to control traffic -// bound to these external services. For example, the following rules define a -// Service for wikipedia.org and set a timeout of 5s for http requests. -// -// ```yaml -// apiVersion: networking.istio.io/v1alpha3 -// kind: ServiceEntry -// metadata: -// name: external-svc-wikipedia -// spec: -// hosts: -// - wikipedia.org -// location: MESH_EXTERNAL -// ports: -// - number: 80 -// name: example-http -// protocol: HTTP -// resolution: DNS -// -// apiVersion: networking.istio.io/v1alpha3 -// kind: VirtualService -// metadata: -// name: my-wiki-rule -// spec: -// hosts: -// - wikipedia.org -// http: -// - timeout: 5s -// route: -// - destination: -// host: wikipedia.org -// ``` -type Destination struct { - // REQUIRED. The name of a service from the service registry. Service - // names are looked up from the platform's service registry (e.g., - // Kubernetes services, Consul services, etc.) and from the hosts - // declared by [ServiceEntry](#ServiceEntry). Traffic forwarded to - // destinations that are not found in either of the two, will be dropped. - // - // *Note for Kubernetes users*: When short names are used (e.g. "reviews" - // instead of "reviews.default.svc.cluster.local"), Istio will interpret - // the short name based on the namespace of the rule, not the service. A - // rule in the "default" namespace containing a host "reviews will be - // interpreted as "reviews.default.svc.cluster.local", irrespective of - // the actual namespace associated with the reviews service. _To avoid - // potential misconfigurations, it is recommended to always use fully - // qualified domain names over short names._ - Host string `json:"host"` - - // The name of a subset within the service. Applicable only to services - // within the mesh. The subset must be defined in a corresponding - // DestinationRule. - Subset string `json:"subset,omitempty"` - - // Specifies the port on the host that is being addressed. If a service - // exposes only a single port it is not required to explicitly select the - // port. - Port *PortSelector `json:"port,omitempty"` -} - -// Describes match conditions and actions for routing HTTP/1.1, HTTP2, and -// gRPC traffic. See VirtualService for usage examples. -type HTTPRoute struct { - // Match conditions to be satisfied for the rule to be - // activated. All conditions inside a single match block have AND - // semantics, while the list of match blocks have OR semantics. The rule - // is matched if any one of the match blocks succeed. - Match []HTTPMatchRequest `json:"match,omitempty"` - - // A http rule can either redirect or forward (default) traffic. The - // forwarding target can be one of several versions of a service (see - // glossary in beginning of document). Weights associated with the - // service version determine the proportion of traffic it receives. - Route []DestinationWeight `json:"route,omitempty"` - - // A http rule can either redirect or forward (default) traffic. If - // traffic passthrough option is specified in the rule, - // route/redirect will be ignored. The redirect primitive can be used to - // send a HTTP 302 redirect to a different URI or Authority. - Redirect *HTTPRedirect `json:"redirect,omitempty"` - - // Rewrite HTTP URIs and Authority headers. Rewrite cannot be used with - // Redirect primitive. Rewrite will be performed before forwarding. - Rewrite *HTTPRewrite `json:"rewrite,omitempty"` - - // Timeout for HTTP requests. - Timeout string `json:"timeout,omitempty"` - - // Retry policy for HTTP requests. - Retries *HTTPRetry `json:"retries,omitempty"` - - // Fault injection policy to apply on HTTP traffic. - Fault *HTTPFaultInjection `json:"fault,omitempty"` - - // Mirror HTTP traffic to a another destination in addition to forwarding - // the requests to the intended destination. Mirrored traffic is on a - // best effort basis where the sidecar/gateway will not wait for the - // mirrored cluster to respond before returning the response from the - // original destination. Statistics will be generated for the mirrored - // destination. - Mirror *Destination `json:"mirror,omitempty"` - - // Cross-Origin Resource Sharing policy (CORS). Refer to - // https://developer.mozilla.org/en-US/docs/Web/HTTP/CORS - // for further details about cross origin resource sharing. - CorsPolicy *CorsPolicy `json:"corsPolicy,omitempty"` - - // Additional HTTP headers to add before forwarding a request to the - // destination service. - AppendHeaders map[string]string `json:"appendHeaders,omitempty"` - - // Http headers to remove before returning the response to the caller - RemoveResponseHeaders map[string]string `json:"removeResponseHeaders,omitempty"` - - // Header manipulation rules - Headers *Headers `json:"headers,omitempty"` -} - -// Header manipulation rules -type Headers struct { - // Header manipulation rules to apply before forwarding a request - // to the destination service - Request *HeaderOperations `json:"request,omitempty"` - - // Header manipulation rules to apply before returning a response - // to the caller - Response *HeaderOperations `json:"response,omitempty"` -} - -// HeaderOperations Describes the header manipulations to apply -type HeaderOperations struct { - // Overwrite the headers specified by key with the given values - Set map[string]string `json:"set"` - - // Append the given values to the headers specified by keys - // (will create a comma-separated list of values) - Add map[string]string `json:"add"` - - // Remove the specified headers - Remove []string `json:"remove"` -} - -// HttpMatchRequest specifies a set of criterion to be met in order for the -// rule to be applied to the HTTP request. For example, the following -// restricts the rule to match only requests where the URL path -// starts with /ratings/v2/ and the request contains a "cookie" with value -// "user=jason". -// -// apiVersion: networking.istio.io/v1alpha3 -// kind: VirtualService -// metadata: -// name: ratings-route -// spec: -// hosts: -// - ratings -// http: -// - match: -// - headers: -// cookie: -// regex: "^(.*?;)?(user=jason)(;.*)?" -// uri: -// prefix: "/ratings/v2/" -// route: -// - destination: -// host: ratings -// -// HTTPMatchRequest CANNOT be empty. -type HTTPMatchRequest struct { - // URI to match - // values are case-sensitive and formatted as follows: - // - // - `exact: "value"` for exact string match - // - // - `prefix: "value"` for prefix-based match - // - // - `regex: "value"` for ECMAscript style regex-based match - // - Uri *v1alpha1.StringMatch `json:"uri,omitempty"` - - // URI Scheme - // values are case-sensitive and formatted as follows: - // - // - `exact: "value"` for exact string match - // - // - `prefix: "value"` for prefix-based match - // - // - `regex: "value"` for ECMAscript style regex-based match - // - Scheme *v1alpha1.StringMatch `json:"scheme,omitempty"` - - // HTTP Method - // values are case-sensitive and formatted as follows: - // - // - `exact: "value"` for exact string match - // - // - `prefix: "value"` for prefix-based match - // - // - `regex: "value"` for ECMAscript style regex-based match - // - Method *v1alpha1.StringMatch `json:"method,omitempty"` - - // HTTP Authority - // values are case-sensitive and formatted as follows: - // - // - `exact: "value"` for exact string match - // - // - `prefix: "value"` for prefix-based match - // - // - `regex: "value"` for ECMAscript style regex-based match - // - Authority *v1alpha1.StringMatch `json:"authority,omitempty"` - - // The header keys must be lowercase and use hyphen as the separator, - // e.g. _x-request-id_. - // - // Header values are case-sensitive and formatted as follows: - // - // - `exact: "value"` for exact string match - // - // - `prefix: "value"` for prefix-based match - // - // - `regex: "value"` for ECMAscript style regex-based match - // - // **Note:** The keys `uri`, `scheme`, `method`, and `authority` will be ignored. - Headers map[string]v1alpha1.StringMatch `json:"headers,omitempty"` - - // Specifies the ports on the host that is being addressed. Many services - // only expose a single port or label ports with the protocols they support, - // in these cases it is not required to explicitly select the port. - Port uint32 `json:"port,omitempty"` - - // One or more labels that constrain the applicability of a rule to - // workloads with the given labels. If the VirtualService has a list of - // gateways specified at the top, it should include the reserved gateway - // `mesh` in order for this field to be applicable. - SourceLabels map[string]string `json:"sourceLabels,omitempty"` - - // Names of gateways where the rule should be applied to. Gateway names - // at the top of the VirtualService (if any) are overridden. The gateway match is - // independent of sourceLabels. - Gateways []string `json:"gateways,omitempty"` -} - -type DestinationWeight struct { - // REQUIRED. Destination uniquely identifies the instances of a service - // to which the request/connection should be forwarded to. - Destination Destination `json:"destination"` - - // REQUIRED. The proportion of traffic to be forwarded to the service - // version. (0-100). Sum of weights across destinations SHOULD BE == 100. - // If there is only destination in a rule, the weight value is assumed to - // be 100. - Weight int `json:"weight"` -} - -// PortSelector specifies the number of a port to be used for -// matching or selection for final routing. -type PortSelector struct { - // Choose one of the fields below. - - // Valid port number - Number uint32 `json:"number,omitempty"` - - // Valid port name - Name string `json:"name,omitempty"` -} - -// Describes match conditions and actions for routing TCP traffic. The -// following routing rule forwards traffic arriving at port 27017 for -// mongo.prod.svc.cluster.local from 172.17.16.* subnet to another Mongo -// server on port 5555. -// -// ```yaml -// apiVersion: networking.istio.io/v1alpha3 -// kind: VirtualService -// metadata: -// name: bookinfo-Mongo -// spec: -// hosts: -// - mongo.prod.svc.cluster.local -// tcp: -// - match: -// - port: 27017 -// sourceSubnet: "172.17.16.0/24" -// route: -// - destination: -// host: mongo.backup.svc.cluster.local -// port: -// number: 5555 -// ``` -type TCPRoute struct { - // Match conditions to be satisfied for the rule to be - // activated. All conditions inside a single match block have AND - // semantics, while the list of match blocks have OR semantics. The rule - // is matched if any one of the match blocks succeed. - Match []L4MatchAttributes `json:"match"` - - // The destination to which the connection should be forwarded to. - // Currently, only one destination is allowed for TCP services. When TCP - // weighted routing support is introduced in Envoy, multiple destinations - // with weights can be specified. - Route DestinationWeight `json:"route"` -} - -// L4 connection match attributes. Note that L4 connection matching support -// is incomplete. -type L4MatchAttributes struct { - // IPv4 or IPv6 ip address of destination with optional subnet. E.g., - // a.b.c.d/xx form or just a.b.c.d. This is only valid when the - // destination service has several IPs and the application explicitly - // specifies a particular IP. - DestinationSubnet string `json:"destinationSubnet,omitempty"` - - // Specifies the port on the host that is being addressed. Many services - // only expose a single port or label ports with the protocols they support, - // in these cases it is not required to explicitly select the port. - Port int `json:"port,omitempty"` - - // IPv4 or IPv6 ip address of source with optional subnet. E.g., a.b.c.d/xx - // form or just a.b.c.d - SourceSubnet string `json:"sourceSubnet,omitempty"` - - // One or more labels that constrain the applicability of a rule to - // workloads with the given labels. If the VirtualService has a list of - // gateways specified at the top, it should include the reserved gateway - // `mesh` in order for this field to be applicable. - SourceLabel map[string]string `json:"sourceLabel,omitempty"` - - // Names of gateways where the rule should be applied to. Gateway names - // at the top of the VirtualService (if any) are overridden. The gateway match is - // independent of sourceLabels. - Gateways []string `json:"gateways,omitempty"` -} - -// HTTPRedirect can be used to send a 302 redirect response to the caller, -// where the Authority/Host and the URI in the response can be swapped with -// the specified values. For example, the following rule redirects -// requests for /v1/getProductRatings API on the ratings service to -// /v1/bookRatings provided by the bookratings service. -// -// apiVersion: networking.istio.io/v1alpha3 -// kind: VirtualService -// metadata: -// name: ratings-route -// spec: -// hosts: -// - ratings -// http: -// - match: -// - uri: -// exact: /v1/getProductRatings -// redirect: -// uri: /v1/bookRatings -// authority: bookratings.default.svc.cluster.local -// ... -// -type HTTPRedirect struct { - // On a redirect, overwrite the Path portion of the URL with this - // value. Note that the entire path will be replaced, irrespective of the - // request URI being matched as an exact path or prefix. - Uri string `json:"uri,omitempty"` - - // On a redirect, overwrite the Authority/Host portion of the URL with - // this value. - Authority string `json:"authority,omitempty"` -} - -// HTTPRewrite can be used to rewrite specific parts of a HTTP request -// before forwarding the request to the destination. Rewrite primitive can -// be used only with the DestinationWeights. The following example -// demonstrates how to rewrite the URL prefix for api call (/ratings) to -// ratings service before making the actual API call. -// -// apiVersion: networking.istio.io/v1alpha3 -// kind: VirtualService -// metadata: -// name: ratings-route -// spec: -// hosts: -// - ratings -// http: -// - match: -// - uri: -// prefix: /ratings -// rewrite: -// uri: /v1/bookRatings -// route: -// - destination: -// host: ratings -// subset: v1 -// -type HTTPRewrite struct { - // rewrite the path (or the prefix) portion of the URI with this - // value. If the original URI was matched based on prefix, the value - // provided in this field will replace the corresponding matched prefix. - Uri string `json:"uri,omitempty"` - - // rewrite the Authority/Host header with this value. - Authority string `json:"authority,omitempty"` -} - -// Describes the retry policy to use when a HTTP request fails. For -// example, the following rule sets the maximum number of retries to 3 when -// calling ratings:v1 service, with a 2s timeout per retry attempt. -// -// ```yaml -// apiVersion: networking.istio.io/v1alpha3 -// kind: VirtualService -// metadata: -// name: ratings-route -// spec: -// hosts: -// - ratings.prod.svc.cluster.local -// http: -// - route: -// - destination: -// host: ratings.prod.svc.cluster.local -// subset: v1 -// retries: -// attempts: 3 -// perTryTimeout: 2s -// retryOn: gateway-error,connect-failure,refused-stream -// ``` -// -type HTTPRetry struct { - // REQUIRED. Number of retries for a given request. The interval - // between retries will be determined automatically (25ms+). Actual - // number of retries attempted depends on the httpReqTimeout. - Attempts int `json:"attempts"` - - // Timeout per retry attempt for a given request. format: 1h/1m/1s/1ms. MUST BE >=1ms. - PerTryTimeout string `json:"perTryTimeout"` - - // Specifies the conditions under which retry takes place. - // One or more policies can be specified using a ‘,’ delimited list. - // The supported policies can be found in - // - // and - RetryOn string `json:"retryOn"` -} - -// Describes the Cross-Origin Resource Sharing (CORS) policy, for a given -// service. Refer to -// https://developer.mozilla.org/en-US/docs/Web/HTTP/Access_control_CORS -// for further details about cross origin resource sharing. For example, -// the following rule restricts cross origin requests to those originating -// from example.com domain using HTTP POST/GET, and sets the -// Access-Control-Allow-Credentials header to false. In addition, it only -// exposes X-Foo-bar header and sets an expiry period of 1 day. -// -// apiVersion: networking.istio.io/v1alpha3 -// kind: VirtualService -// metadata: -// name: ratings-route -// spec: -// hosts: -// - ratings -// http: -// - route: -// - destination: -// host: ratings -// subset: v1 -// corsPolicy: -// allowOrigin: -// - example.com -// allowMethods: -// - POST -// - GET -// allowCredentials: false -// allowHeaders: -// - X-Foo-Bar -// maxAge: "1d" -// -type CorsPolicy struct { - // The list of origins that are allowed to perform CORS requests. The - // content will be serialized into the Access-Control-Allow-Origin - // header. Wildcard * will allow all origins. - AllowOrigin []string `json:"allowOrigin,omitempty"` - - // List of HTTP methods allowed to access the resource. The content will - // be serialized into the Access-Control-Allow-Methods header. - AllowMethods []string `json:"allowMethods,omitempty"` - - // List of HTTP headers that can be used when requesting the - // resource. Serialized to Access-Control-Allow-Methods header. - AllowHeaders []string `json:"allowHeaders,omitempty"` - - // A white list of HTTP headers that the browsers are allowed to - // access. Serialized into Access-Control-Expose-Headers header. - ExposeHeaders []string `json:"exposeHeaders,omitempty"` - - // Specifies how long the the results of a preflight request can be - // cached. Translates to the Access-Control-Max-Age header. - MaxAge string `json:"maxAge,omitempty"` - - // Indicates whether the caller is allowed to send the actual request - // (not the preflight) using credentials. Translates to - // Access-Control-Allow-Credentials header. - AllowCredentials bool `json:"allowCredentials,omitempty"` -} - -// HTTPFaultInjection can be used to specify one or more faults to inject -// while forwarding http requests to the destination specified in a route. -// Fault specification is part of a VirtualService rule. Faults include -// aborting the Http request from downstream service, and/or delaying -// proxying of requests. A fault rule MUST HAVE delay or abort or both. -// -// *Note:* Delay and abort faults are independent of one another, even if -// both are specified simultaneously. -type HTTPFaultInjection struct { - // Delay requests before forwarding, emulating various failures such as - // network issues, overloaded upstream service, etc. - Delay *InjectDelay `json:"delay,omitempty"` - - // Abort Http request attempts and return error codes back to downstream - // service, giving the impression that the upstream service is faulty. - Abort *InjectAbort `json:"abort,omitempty"` -} - -// Delay specification is used to inject latency into the request -// forwarding path. The following example will introduce a 5 second delay -// in 10% of the requests to the "v1" version of the "reviews" -// service from all pods with label env: prod -// -// apiVersion: networking.istio.io/v1alpha3 -// kind: VirtualService -// metadata: -// name: reviews-route -// spec: -// hosts: -// - reviews -// http: -// - match: -// - sourceLabels: -// env: prod -// route: -// - destination: -// host: reviews -// subset: v1 -// fault: -// delay: -// percent: 10 -// fixedDelay: 5s -// -// The _fixedDelay_ field is used to indicate the amount of delay in -// seconds. An optional _percent_ field, a value between 0 and 100, can -// be used to only delay a certain percentage of requests. If left -// unspecified, all request will be delayed. -type InjectDelay struct { - // Percentage of requests on which the delay will be injected (0-100). - Percent int `json:"percent,omitempty"` - - // REQUIRED. Add a fixed delay before forwarding the request. Format: - // 1h/1m/1s/1ms. MUST be >=1ms. - FixedDelay string `json:"fixedDelay"` - - // (-- Add a delay (based on an exponential function) before forwarding - // the request. mean delay needed to derive the exponential delay - // values --) - ExponentialDelay string `json:"exponentialDelay,omitempty"` -} - -// Abort specification is used to prematurely abort a request with a -// pre-specified error code. The following example will return an HTTP -// 400 error code for 10% of the requests to the "ratings" service "v1". -// -// apiVersion: networking.istio.io/v1alpha3 -// kind: VirtualService -// metadata: -// name: ratings-route -// spec: -// hosts: -// - ratings -// http: -// - route: -// - destination: -// host: ratings -// subset: v1 -// fault: -// abort: -// percent: 10 -// httpStatus: 400 -// -// The _httpStatus_ field is used to indicate the HTTP status code to -// return to the caller. The optional _percent_ field, a value between 0 -// and 100, is used to only abort a certain percentage of requests. If -// not specified, all requests are aborted. -type InjectAbort struct { - // Percentage of requests to be aborted with the error code provided (0-100). - Perecent int `json:"percent,omitempty"` - - // REQUIRED. HTTP status code to use to abort the Http request. - HttpStatus int `json:"httpStatus"` -} - -// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object - -// VirtualServiceList is a list of VirtualService resources -type VirtualServiceList struct { - metav1.TypeMeta `json:",inline"` - metav1.ListMeta `json:"metadata"` - - Items []VirtualService `json:"items"` -} diff --git a/pkg/apis/istio/v1alpha3/zz_generated.deepcopy.go b/pkg/apis/istio/v1alpha3/zz_generated.deepcopy.go deleted file mode 100644 index 86f585a3..00000000 --- a/pkg/apis/istio/v1alpha3/zz_generated.deepcopy.go +++ /dev/null @@ -1,918 +0,0 @@ -// +build !ignore_autogenerated - -/* -Copyright The Flagger Authors. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -// Code generated by deepcopy-gen. DO NOT EDIT. - -package v1alpha3 - -import ( - v1alpha1 "github.com/weaveworks/flagger/pkg/apis/istio/common/v1alpha1" - runtime "k8s.io/apimachinery/pkg/runtime" -) - -// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *ConnectionPoolSettings) DeepCopyInto(out *ConnectionPoolSettings) { - *out = *in - if in.TCP != nil { - in, out := &in.TCP, &out.TCP - *out = new(TCPSettings) - **out = **in - } - if in.HTTP != nil { - in, out := &in.HTTP, &out.HTTP - *out = new(HTTPSettings) - **out = **in - } - return -} - -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ConnectionPoolSettings. -func (in *ConnectionPoolSettings) DeepCopy() *ConnectionPoolSettings { - if in == nil { - return nil - } - out := new(ConnectionPoolSettings) - in.DeepCopyInto(out) - return out -} - -// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *ConsistentHashLB) DeepCopyInto(out *ConsistentHashLB) { - *out = *in - if in.HTTPCookie != nil { - in, out := &in.HTTPCookie, &out.HTTPCookie - *out = new(HTTPCookie) - **out = **in - } - return -} - -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ConsistentHashLB. -func (in *ConsistentHashLB) DeepCopy() *ConsistentHashLB { - if in == nil { - return nil - } - out := new(ConsistentHashLB) - in.DeepCopyInto(out) - return out -} - -// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *CorsPolicy) DeepCopyInto(out *CorsPolicy) { - *out = *in - if in.AllowOrigin != nil { - in, out := &in.AllowOrigin, &out.AllowOrigin - *out = make([]string, len(*in)) - copy(*out, *in) - } - if in.AllowMethods != nil { - in, out := &in.AllowMethods, &out.AllowMethods - *out = make([]string, len(*in)) - copy(*out, *in) - } - if in.AllowHeaders != nil { - in, out := &in.AllowHeaders, &out.AllowHeaders - *out = make([]string, len(*in)) - copy(*out, *in) - } - if in.ExposeHeaders != nil { - in, out := &in.ExposeHeaders, &out.ExposeHeaders - *out = make([]string, len(*in)) - copy(*out, *in) - } - return -} - -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new CorsPolicy. -func (in *CorsPolicy) DeepCopy() *CorsPolicy { - if in == nil { - return nil - } - out := new(CorsPolicy) - in.DeepCopyInto(out) - return out -} - -// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *Destination) DeepCopyInto(out *Destination) { - *out = *in - if in.Port != nil { - in, out := &in.Port, &out.Port - *out = new(PortSelector) - **out = **in - } - return -} - -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Destination. -func (in *Destination) DeepCopy() *Destination { - if in == nil { - return nil - } - out := new(Destination) - in.DeepCopyInto(out) - return out -} - -// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *DestinationRule) DeepCopyInto(out *DestinationRule) { - *out = *in - out.TypeMeta = in.TypeMeta - in.ObjectMeta.DeepCopyInto(&out.ObjectMeta) - in.Spec.DeepCopyInto(&out.Spec) - return -} - -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new DestinationRule. -func (in *DestinationRule) DeepCopy() *DestinationRule { - if in == nil { - return nil - } - out := new(DestinationRule) - in.DeepCopyInto(out) - return out -} - -// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. -func (in *DestinationRule) DeepCopyObject() runtime.Object { - if c := in.DeepCopy(); c != nil { - return c - } - return nil -} - -// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *DestinationRuleList) DeepCopyInto(out *DestinationRuleList) { - *out = *in - out.TypeMeta = in.TypeMeta - in.ListMeta.DeepCopyInto(&out.ListMeta) - if in.Items != nil { - in, out := &in.Items, &out.Items - *out = make([]DestinationRule, len(*in)) - for i := range *in { - (*in)[i].DeepCopyInto(&(*out)[i]) - } - } - return -} - -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new DestinationRuleList. -func (in *DestinationRuleList) DeepCopy() *DestinationRuleList { - if in == nil { - return nil - } - out := new(DestinationRuleList) - in.DeepCopyInto(out) - return out -} - -// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. -func (in *DestinationRuleList) DeepCopyObject() runtime.Object { - if c := in.DeepCopy(); c != nil { - return c - } - return nil -} - -// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *DestinationRuleSpec) DeepCopyInto(out *DestinationRuleSpec) { - *out = *in - if in.TrafficPolicy != nil { - in, out := &in.TrafficPolicy, &out.TrafficPolicy - *out = new(TrafficPolicy) - (*in).DeepCopyInto(*out) - } - if in.Subsets != nil { - in, out := &in.Subsets, &out.Subsets - *out = make([]Subset, len(*in)) - for i := range *in { - (*in)[i].DeepCopyInto(&(*out)[i]) - } - } - return -} - -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new DestinationRuleSpec. -func (in *DestinationRuleSpec) DeepCopy() *DestinationRuleSpec { - if in == nil { - return nil - } - out := new(DestinationRuleSpec) - in.DeepCopyInto(out) - return out -} - -// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *DestinationWeight) DeepCopyInto(out *DestinationWeight) { - *out = *in - in.Destination.DeepCopyInto(&out.Destination) - return -} - -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new DestinationWeight. -func (in *DestinationWeight) DeepCopy() *DestinationWeight { - if in == nil { - return nil - } - out := new(DestinationWeight) - in.DeepCopyInto(out) - return out -} - -// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *HTTPCookie) DeepCopyInto(out *HTTPCookie) { - *out = *in - return -} - -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new HTTPCookie. -func (in *HTTPCookie) DeepCopy() *HTTPCookie { - if in == nil { - return nil - } - out := new(HTTPCookie) - in.DeepCopyInto(out) - return out -} - -// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *HTTPFaultInjection) DeepCopyInto(out *HTTPFaultInjection) { - *out = *in - if in.Delay != nil { - in, out := &in.Delay, &out.Delay - *out = new(InjectDelay) - **out = **in - } - if in.Abort != nil { - in, out := &in.Abort, &out.Abort - *out = new(InjectAbort) - **out = **in - } - return -} - -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new HTTPFaultInjection. -func (in *HTTPFaultInjection) DeepCopy() *HTTPFaultInjection { - if in == nil { - return nil - } - out := new(HTTPFaultInjection) - in.DeepCopyInto(out) - return out -} - -// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *HTTPMatchRequest) DeepCopyInto(out *HTTPMatchRequest) { - *out = *in - if in.Uri != nil { - in, out := &in.Uri, &out.Uri - *out = new(v1alpha1.StringMatch) - **out = **in - } - if in.Scheme != nil { - in, out := &in.Scheme, &out.Scheme - *out = new(v1alpha1.StringMatch) - **out = **in - } - if in.Method != nil { - in, out := &in.Method, &out.Method - *out = new(v1alpha1.StringMatch) - **out = **in - } - if in.Authority != nil { - in, out := &in.Authority, &out.Authority - *out = new(v1alpha1.StringMatch) - **out = **in - } - if in.Headers != nil { - in, out := &in.Headers, &out.Headers - *out = make(map[string]v1alpha1.StringMatch, len(*in)) - for key, val := range *in { - (*out)[key] = val - } - } - if in.SourceLabels != nil { - in, out := &in.SourceLabels, &out.SourceLabels - *out = make(map[string]string, len(*in)) - for key, val := range *in { - (*out)[key] = val - } - } - if in.Gateways != nil { - in, out := &in.Gateways, &out.Gateways - *out = make([]string, len(*in)) - copy(*out, *in) - } - return -} - -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new HTTPMatchRequest. -func (in *HTTPMatchRequest) DeepCopy() *HTTPMatchRequest { - if in == nil { - return nil - } - out := new(HTTPMatchRequest) - in.DeepCopyInto(out) - return out -} - -// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *HTTPRedirect) DeepCopyInto(out *HTTPRedirect) { - *out = *in - return -} - -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new HTTPRedirect. -func (in *HTTPRedirect) DeepCopy() *HTTPRedirect { - if in == nil { - return nil - } - out := new(HTTPRedirect) - in.DeepCopyInto(out) - return out -} - -// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *HTTPRetry) DeepCopyInto(out *HTTPRetry) { - *out = *in - return -} - -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new HTTPRetry. -func (in *HTTPRetry) DeepCopy() *HTTPRetry { - if in == nil { - return nil - } - out := new(HTTPRetry) - in.DeepCopyInto(out) - return out -} - -// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *HTTPRewrite) DeepCopyInto(out *HTTPRewrite) { - *out = *in - return -} - -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new HTTPRewrite. -func (in *HTTPRewrite) DeepCopy() *HTTPRewrite { - if in == nil { - return nil - } - out := new(HTTPRewrite) - in.DeepCopyInto(out) - return out -} - -// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *HTTPRoute) DeepCopyInto(out *HTTPRoute) { - *out = *in - if in.Match != nil { - in, out := &in.Match, &out.Match - *out = make([]HTTPMatchRequest, len(*in)) - for i := range *in { - (*in)[i].DeepCopyInto(&(*out)[i]) - } - } - if in.Route != nil { - in, out := &in.Route, &out.Route - *out = make([]DestinationWeight, len(*in)) - for i := range *in { - (*in)[i].DeepCopyInto(&(*out)[i]) - } - } - if in.Redirect != nil { - in, out := &in.Redirect, &out.Redirect - *out = new(HTTPRedirect) - **out = **in - } - if in.Rewrite != nil { - in, out := &in.Rewrite, &out.Rewrite - *out = new(HTTPRewrite) - **out = **in - } - if in.Retries != nil { - in, out := &in.Retries, &out.Retries - *out = new(HTTPRetry) - **out = **in - } - if in.Fault != nil { - in, out := &in.Fault, &out.Fault - *out = new(HTTPFaultInjection) - (*in).DeepCopyInto(*out) - } - if in.Mirror != nil { - in, out := &in.Mirror, &out.Mirror - *out = new(Destination) - (*in).DeepCopyInto(*out) - } - if in.CorsPolicy != nil { - in, out := &in.CorsPolicy, &out.CorsPolicy - *out = new(CorsPolicy) - (*in).DeepCopyInto(*out) - } - if in.AppendHeaders != nil { - in, out := &in.AppendHeaders, &out.AppendHeaders - *out = make(map[string]string, len(*in)) - for key, val := range *in { - (*out)[key] = val - } - } - if in.RemoveResponseHeaders != nil { - in, out := &in.RemoveResponseHeaders, &out.RemoveResponseHeaders - *out = make(map[string]string, len(*in)) - for key, val := range *in { - (*out)[key] = val - } - } - if in.Headers != nil { - in, out := &in.Headers, &out.Headers - *out = new(Headers) - (*in).DeepCopyInto(*out) - } - return -} - -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new HTTPRoute. -func (in *HTTPRoute) DeepCopy() *HTTPRoute { - if in == nil { - return nil - } - out := new(HTTPRoute) - in.DeepCopyInto(out) - return out -} - -// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *HTTPSettings) DeepCopyInto(out *HTTPSettings) { - *out = *in - return -} - -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new HTTPSettings. -func (in *HTTPSettings) DeepCopy() *HTTPSettings { - if in == nil { - return nil - } - out := new(HTTPSettings) - in.DeepCopyInto(out) - return out -} - -// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *HeaderOperations) DeepCopyInto(out *HeaderOperations) { - *out = *in - if in.Set != nil { - in, out := &in.Set, &out.Set - *out = make(map[string]string, len(*in)) - for key, val := range *in { - (*out)[key] = val - } - } - if in.Add != nil { - in, out := &in.Add, &out.Add - *out = make(map[string]string, len(*in)) - for key, val := range *in { - (*out)[key] = val - } - } - if in.Remove != nil { - in, out := &in.Remove, &out.Remove - *out = make([]string, len(*in)) - copy(*out, *in) - } - return -} - -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new HeaderOperations. -func (in *HeaderOperations) DeepCopy() *HeaderOperations { - if in == nil { - return nil - } - out := new(HeaderOperations) - in.DeepCopyInto(out) - return out -} - -// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *Headers) DeepCopyInto(out *Headers) { - *out = *in - if in.Request != nil { - in, out := &in.Request, &out.Request - *out = new(HeaderOperations) - (*in).DeepCopyInto(*out) - } - if in.Response != nil { - in, out := &in.Response, &out.Response - *out = new(HeaderOperations) - (*in).DeepCopyInto(*out) - } - return -} - -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Headers. -func (in *Headers) DeepCopy() *Headers { - if in == nil { - return nil - } - out := new(Headers) - in.DeepCopyInto(out) - return out -} - -// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *InjectAbort) DeepCopyInto(out *InjectAbort) { - *out = *in - return -} - -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new InjectAbort. -func (in *InjectAbort) DeepCopy() *InjectAbort { - if in == nil { - return nil - } - out := new(InjectAbort) - in.DeepCopyInto(out) - return out -} - -// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *InjectDelay) DeepCopyInto(out *InjectDelay) { - *out = *in - return -} - -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new InjectDelay. -func (in *InjectDelay) DeepCopy() *InjectDelay { - if in == nil { - return nil - } - out := new(InjectDelay) - in.DeepCopyInto(out) - return out -} - -// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *L4MatchAttributes) DeepCopyInto(out *L4MatchAttributes) { - *out = *in - if in.SourceLabel != nil { - in, out := &in.SourceLabel, &out.SourceLabel - *out = make(map[string]string, len(*in)) - for key, val := range *in { - (*out)[key] = val - } - } - if in.Gateways != nil { - in, out := &in.Gateways, &out.Gateways - *out = make([]string, len(*in)) - copy(*out, *in) - } - return -} - -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new L4MatchAttributes. -func (in *L4MatchAttributes) DeepCopy() *L4MatchAttributes { - if in == nil { - return nil - } - out := new(L4MatchAttributes) - in.DeepCopyInto(out) - return out -} - -// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *LoadBalancerSettings) DeepCopyInto(out *LoadBalancerSettings) { - *out = *in - if in.ConsistentHash != nil { - in, out := &in.ConsistentHash, &out.ConsistentHash - *out = new(ConsistentHashLB) - (*in).DeepCopyInto(*out) - } - return -} - -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new LoadBalancerSettings. -func (in *LoadBalancerSettings) DeepCopy() *LoadBalancerSettings { - if in == nil { - return nil - } - out := new(LoadBalancerSettings) - in.DeepCopyInto(out) - return out -} - -// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *OutlierDetection) DeepCopyInto(out *OutlierDetection) { - *out = *in - return -} - -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new OutlierDetection. -func (in *OutlierDetection) DeepCopy() *OutlierDetection { - if in == nil { - return nil - } - out := new(OutlierDetection) - in.DeepCopyInto(out) - return out -} - -// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *PortSelector) DeepCopyInto(out *PortSelector) { - *out = *in - return -} - -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new PortSelector. -func (in *PortSelector) DeepCopy() *PortSelector { - if in == nil { - return nil - } - out := new(PortSelector) - in.DeepCopyInto(out) - return out -} - -// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *PortTrafficPolicy) DeepCopyInto(out *PortTrafficPolicy) { - *out = *in - out.Port = in.Port - if in.LoadBalancer != nil { - in, out := &in.LoadBalancer, &out.LoadBalancer - *out = new(LoadBalancerSettings) - (*in).DeepCopyInto(*out) - } - if in.ConnectionPool != nil { - in, out := &in.ConnectionPool, &out.ConnectionPool - *out = new(ConnectionPoolSettings) - (*in).DeepCopyInto(*out) - } - if in.OutlierDetection != nil { - in, out := &in.OutlierDetection, &out.OutlierDetection - *out = new(OutlierDetection) - **out = **in - } - if in.TLS != nil { - in, out := &in.TLS, &out.TLS - *out = new(TLSSettings) - (*in).DeepCopyInto(*out) - } - return -} - -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new PortTrafficPolicy. -func (in *PortTrafficPolicy) DeepCopy() *PortTrafficPolicy { - if in == nil { - return nil - } - out := new(PortTrafficPolicy) - in.DeepCopyInto(out) - return out -} - -// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *Subset) DeepCopyInto(out *Subset) { - *out = *in - if in.Labels != nil { - in, out := &in.Labels, &out.Labels - *out = make(map[string]string, len(*in)) - for key, val := range *in { - (*out)[key] = val - } - } - if in.TrafficPolicy != nil { - in, out := &in.TrafficPolicy, &out.TrafficPolicy - *out = new(TrafficPolicy) - (*in).DeepCopyInto(*out) - } - return -} - -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Subset. -func (in *Subset) DeepCopy() *Subset { - if in == nil { - return nil - } - out := new(Subset) - in.DeepCopyInto(out) - return out -} - -// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *TCPRoute) DeepCopyInto(out *TCPRoute) { - *out = *in - if in.Match != nil { - in, out := &in.Match, &out.Match - *out = make([]L4MatchAttributes, len(*in)) - for i := range *in { - (*in)[i].DeepCopyInto(&(*out)[i]) - } - } - in.Route.DeepCopyInto(&out.Route) - return -} - -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new TCPRoute. -func (in *TCPRoute) DeepCopy() *TCPRoute { - if in == nil { - return nil - } - out := new(TCPRoute) - in.DeepCopyInto(out) - return out -} - -// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *TCPSettings) DeepCopyInto(out *TCPSettings) { - *out = *in - return -} - -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new TCPSettings. -func (in *TCPSettings) DeepCopy() *TCPSettings { - if in == nil { - return nil - } - out := new(TCPSettings) - in.DeepCopyInto(out) - return out -} - -// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *TLSSettings) DeepCopyInto(out *TLSSettings) { - *out = *in - if in.SubjectAltNames != nil { - in, out := &in.SubjectAltNames, &out.SubjectAltNames - *out = make([]string, len(*in)) - copy(*out, *in) - } - return -} - -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new TLSSettings. -func (in *TLSSettings) DeepCopy() *TLSSettings { - if in == nil { - return nil - } - out := new(TLSSettings) - in.DeepCopyInto(out) - return out -} - -// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *TrafficPolicy) DeepCopyInto(out *TrafficPolicy) { - *out = *in - if in.LoadBalancer != nil { - in, out := &in.LoadBalancer, &out.LoadBalancer - *out = new(LoadBalancerSettings) - (*in).DeepCopyInto(*out) - } - if in.ConnectionPool != nil { - in, out := &in.ConnectionPool, &out.ConnectionPool - *out = new(ConnectionPoolSettings) - (*in).DeepCopyInto(*out) - } - if in.OutlierDetection != nil { - in, out := &in.OutlierDetection, &out.OutlierDetection - *out = new(OutlierDetection) - **out = **in - } - if in.TLS != nil { - in, out := &in.TLS, &out.TLS - *out = new(TLSSettings) - (*in).DeepCopyInto(*out) - } - if in.PortLevelSettings != nil { - in, out := &in.PortLevelSettings, &out.PortLevelSettings - *out = make([]PortTrafficPolicy, len(*in)) - for i := range *in { - (*in)[i].DeepCopyInto(&(*out)[i]) - } - } - return -} - -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new TrafficPolicy. -func (in *TrafficPolicy) DeepCopy() *TrafficPolicy { - if in == nil { - return nil - } - out := new(TrafficPolicy) - in.DeepCopyInto(out) - return out -} - -// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *VirtualService) DeepCopyInto(out *VirtualService) { - *out = *in - out.TypeMeta = in.TypeMeta - in.ObjectMeta.DeepCopyInto(&out.ObjectMeta) - in.Spec.DeepCopyInto(&out.Spec) - return -} - -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new VirtualService. -func (in *VirtualService) DeepCopy() *VirtualService { - if in == nil { - return nil - } - out := new(VirtualService) - in.DeepCopyInto(out) - return out -} - -// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. -func (in *VirtualService) DeepCopyObject() runtime.Object { - if c := in.DeepCopy(); c != nil { - return c - } - return nil -} - -// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *VirtualServiceList) DeepCopyInto(out *VirtualServiceList) { - *out = *in - out.TypeMeta = in.TypeMeta - in.ListMeta.DeepCopyInto(&out.ListMeta) - if in.Items != nil { - in, out := &in.Items, &out.Items - *out = make([]VirtualService, len(*in)) - for i := range *in { - (*in)[i].DeepCopyInto(&(*out)[i]) - } - } - return -} - -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new VirtualServiceList. -func (in *VirtualServiceList) DeepCopy() *VirtualServiceList { - if in == nil { - return nil - } - out := new(VirtualServiceList) - in.DeepCopyInto(out) - return out -} - -// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. -func (in *VirtualServiceList) DeepCopyObject() runtime.Object { - if c := in.DeepCopy(); c != nil { - return c - } - return nil -} - -// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *VirtualServiceSpec) DeepCopyInto(out *VirtualServiceSpec) { - *out = *in - if in.Hosts != nil { - in, out := &in.Hosts, &out.Hosts - *out = make([]string, len(*in)) - copy(*out, *in) - } - if in.Gateways != nil { - in, out := &in.Gateways, &out.Gateways - *out = make([]string, len(*in)) - copy(*out, *in) - } - if in.Http != nil { - in, out := &in.Http, &out.Http - *out = make([]HTTPRoute, len(*in)) - for i := range *in { - (*in)[i].DeepCopyInto(&(*out)[i]) - } - } - if in.Tcp != nil { - in, out := &in.Tcp, &out.Tcp - *out = make([]TCPRoute, len(*in)) - for i := range *in { - (*in)[i].DeepCopyInto(&(*out)[i]) - } - } - return -} - -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new VirtualServiceSpec. -func (in *VirtualServiceSpec) DeepCopy() *VirtualServiceSpec { - if in == nil { - return nil - } - out := new(VirtualServiceSpec) - in.DeepCopyInto(out) - return out -} diff --git a/pkg/apis/smi/register.go b/pkg/apis/smi/register.go deleted file mode 100644 index 67cbbf79..00000000 --- a/pkg/apis/smi/register.go +++ /dev/null @@ -1,5 +0,0 @@ -package smi - -const ( - GroupName = "split.smi-spec.io" -) diff --git a/pkg/apis/smi/v1alpha1/doc.go b/pkg/apis/smi/v1alpha1/doc.go deleted file mode 100644 index 7792b7f6..00000000 --- a/pkg/apis/smi/v1alpha1/doc.go +++ /dev/null @@ -1,4 +0,0 @@ -// +k8s:deepcopy-gen=package -// +groupName=split.smi-spec.io - -package v1alpha1 diff --git a/pkg/apis/smi/v1alpha1/register.go b/pkg/apis/smi/v1alpha1/register.go deleted file mode 100644 index c9868ec5..00000000 --- a/pkg/apis/smi/v1alpha1/register.go +++ /dev/null @@ -1,48 +0,0 @@ -package v1alpha1 - -import ( - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - "k8s.io/apimachinery/pkg/runtime" - "k8s.io/apimachinery/pkg/runtime/schema" - - ts "github.com/weaveworks/flagger/pkg/apis/smi" -) - -// SchemeGroupVersion is the identifier for the API which includes -// the name of the group and the version of the API -var SchemeGroupVersion = schema.GroupVersion{ - Group: ts.GroupName, - Version: "v1alpha1", -} - -// Kind takes an unqualified kind and returns back a Group qualified GroupKind -func Kind(kind string) schema.GroupKind { - return SchemeGroupVersion.WithKind(kind).GroupKind() -} - -// Resource takes an unqualified resource and returns a Group qualified GroupResource -func Resource(resource string) schema.GroupResource { - return SchemeGroupVersion.WithResource(resource).GroupResource() -} - -var ( - // SchemeBuilder collects functions that add things to a scheme. It's to allow - // code to compile without explicitly referencing generated types. You should - // declare one in each package that will have generated deep copy or conversion - // functions. - SchemeBuilder = runtime.NewSchemeBuilder(addKnownTypes) - - // AddToScheme applies all the stored functions to the scheme. A non-nil error - // indicates that one function failed and the attempt was abandoned. - AddToScheme = SchemeBuilder.AddToScheme -) - -// Adds the list of known types to Scheme. -func addKnownTypes(scheme *runtime.Scheme) error { - scheme.AddKnownTypes(SchemeGroupVersion, - &TrafficSplit{}, - &TrafficSplitList{}, - ) - metav1.AddToGroupVersion(scheme, SchemeGroupVersion) - return nil -} diff --git a/pkg/apis/smi/v1alpha1/traffic_split.go b/pkg/apis/smi/v1alpha1/traffic_split.go deleted file mode 100644 index 72574832..00000000 --- a/pkg/apis/smi/v1alpha1/traffic_split.go +++ /dev/null @@ -1,56 +0,0 @@ -package v1alpha1 - -import ( - "k8s.io/apimachinery/pkg/api/resource" - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" -) - -// +genclient -// +genclient:noStatus -// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object - -// TrafficSplit allows users to incrementally direct percentages of traffic -// between various services. It will be used by clients such as ingress -// controllers or service mesh sidecars to split the outgoing traffic to -// different destinations. -type TrafficSplit struct { - metav1.TypeMeta `json:",inline"` - // Standard object's metadata. - // More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#metadata - // +optional - metav1.ObjectMeta `json:"metadata,omitempty" protobuf:"bytes,1,opt,name=metadata"` - - // Specification of the desired behavior of the traffic split. - // More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#spec-and-status - // +optional - Spec TrafficSplitSpec `json:"spec,omitempty" protobuf:"bytes,2,opt,name=spec"` - - // Most recently observed status of the pod. - // This data may not be up to date. - // Populated by the system. - // Read-only. - // More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#spec-and-status - // +optional - //Status Status `json:"status,omitempty" protobuf:"bytes,3,opt,name=status"` -} - -// TrafficSplitSpec is the specification for a TrafficSplit -type TrafficSplitSpec struct { - Service string `json:"service,omitempty"` - Backends []TrafficSplitBackend `json:"backends,omitempty"` -} - -// TrafficSplitBackend defines a backend -type TrafficSplitBackend struct { - Service string `json:"service,omitempty"` - Weight *resource.Quantity `json:"weight,omitempty"` -} - -// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object - -type TrafficSplitList struct { - metav1.TypeMeta `json:",inline"` - metav1.ListMeta `json:"metadata"` - - Items []TrafficSplit `json:"items"` -} diff --git a/pkg/apis/smi/v1alpha1/zz_generated.deepcopy.go b/pkg/apis/smi/v1alpha1/zz_generated.deepcopy.go deleted file mode 100644 index b7b5f657..00000000 --- a/pkg/apis/smi/v1alpha1/zz_generated.deepcopy.go +++ /dev/null @@ -1,129 +0,0 @@ -// +build !ignore_autogenerated - -/* -Copyright The Flagger Authors. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -// Code generated by deepcopy-gen. DO NOT EDIT. - -package v1alpha1 - -import ( - runtime "k8s.io/apimachinery/pkg/runtime" -) - -// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *TrafficSplit) DeepCopyInto(out *TrafficSplit) { - *out = *in - out.TypeMeta = in.TypeMeta - in.ObjectMeta.DeepCopyInto(&out.ObjectMeta) - in.Spec.DeepCopyInto(&out.Spec) - return -} - -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new TrafficSplit. -func (in *TrafficSplit) DeepCopy() *TrafficSplit { - if in == nil { - return nil - } - out := new(TrafficSplit) - in.DeepCopyInto(out) - return out -} - -// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. -func (in *TrafficSplit) DeepCopyObject() runtime.Object { - if c := in.DeepCopy(); c != nil { - return c - } - return nil -} - -// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *TrafficSplitBackend) DeepCopyInto(out *TrafficSplitBackend) { - *out = *in - if in.Weight != nil { - in, out := &in.Weight, &out.Weight - x := (*in).DeepCopy() - *out = &x - } - return -} - -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new TrafficSplitBackend. -func (in *TrafficSplitBackend) DeepCopy() *TrafficSplitBackend { - if in == nil { - return nil - } - out := new(TrafficSplitBackend) - in.DeepCopyInto(out) - return out -} - -// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *TrafficSplitList) DeepCopyInto(out *TrafficSplitList) { - *out = *in - out.TypeMeta = in.TypeMeta - in.ListMeta.DeepCopyInto(&out.ListMeta) - if in.Items != nil { - in, out := &in.Items, &out.Items - *out = make([]TrafficSplit, len(*in)) - for i := range *in { - (*in)[i].DeepCopyInto(&(*out)[i]) - } - } - return -} - -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new TrafficSplitList. -func (in *TrafficSplitList) DeepCopy() *TrafficSplitList { - if in == nil { - return nil - } - out := new(TrafficSplitList) - in.DeepCopyInto(out) - return out -} - -// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. -func (in *TrafficSplitList) DeepCopyObject() runtime.Object { - if c := in.DeepCopy(); c != nil { - return c - } - return nil -} - -// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *TrafficSplitSpec) DeepCopyInto(out *TrafficSplitSpec) { - *out = *in - if in.Backends != nil { - in, out := &in.Backends, &out.Backends - *out = make([]TrafficSplitBackend, len(*in)) - for i := range *in { - (*in)[i].DeepCopyInto(&(*out)[i]) - } - } - return -} - -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new TrafficSplitSpec. -func (in *TrafficSplitSpec) DeepCopy() *TrafficSplitSpec { - if in == nil { - return nil - } - out := new(TrafficSplitSpec) - in.DeepCopyInto(out) - return out -} diff --git a/pkg/canary/deployer.go b/pkg/canary/deployer.go deleted file mode 100644 index 070b72b1..00000000 --- a/pkg/canary/deployer.go +++ /dev/null @@ -1,442 +0,0 @@ -package canary - -import ( - "crypto/rand" - "fmt" - "io" - - "github.com/google/go-cmp/cmp" - "github.com/mitchellh/hashstructure" - flaggerv1 "github.com/weaveworks/flagger/pkg/apis/flagger/v1alpha3" - clientset "github.com/weaveworks/flagger/pkg/client/clientset/versioned" - "go.uber.org/zap" - appsv1 "k8s.io/api/apps/v1" - hpav1 "k8s.io/api/autoscaling/v2beta1" - corev1 "k8s.io/api/core/v1" - "k8s.io/apimachinery/pkg/api/errors" - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - "k8s.io/apimachinery/pkg/runtime/schema" - "k8s.io/client-go/kubernetes" -) - -// Deployer is managing the operations for Kubernetes deployment kind -type Deployer struct { - KubeClient kubernetes.Interface - FlaggerClient clientset.Interface - Logger *zap.SugaredLogger - ConfigTracker ConfigTracker - Labels []string -} - -// Initialize creates the primary deployment, hpa, -// scales to zero the canary deployment and returns the pod selector label and container ports -func (c *Deployer) Initialize(cd *flaggerv1.Canary, skipLivenessChecks bool) (label string, ports *map[string]int32, err error) { - primaryName := fmt.Sprintf("%s-primary", cd.Spec.TargetRef.Name) - label, ports, err = c.createPrimaryDeployment(cd) - if err != nil { - return "", ports, fmt.Errorf("creating deployment %s.%s failed: %v", primaryName, cd.Namespace, err) - } - - if cd.Status.Phase == "" || cd.Status.Phase == flaggerv1.CanaryPhaseInitializing { - if !skipLivenessChecks { - _, readyErr := c.IsPrimaryReady(cd) - if readyErr != nil { - return "", ports, readyErr - } - } - - c.Logger.With("canary", fmt.Sprintf("%s.%s", cd.Name, cd.Namespace)).Infof("Scaling down %s.%s", cd.Spec.TargetRef.Name, cd.Namespace) - if err := c.Scale(cd, 0); err != nil { - return "", ports, err - } - } - - if cd.Spec.AutoscalerRef != nil && cd.Spec.AutoscalerRef.Kind == "HorizontalPodAutoscaler" { - if err := c.reconcilePrimaryHpa(cd, true); err != nil { - return "", ports, fmt.Errorf("creating HorizontalPodAutoscaler %s.%s failed: %v", primaryName, cd.Namespace, err) - } - } - return label, ports, nil -} - -// Promote copies the pod spec, secrets and config maps from canary to primary -func (c *Deployer) Promote(cd *flaggerv1.Canary) error { - targetName := cd.Spec.TargetRef.Name - primaryName := fmt.Sprintf("%s-primary", targetName) - - canary, err := c.KubeClient.AppsV1().Deployments(cd.Namespace).Get(targetName, metav1.GetOptions{}) - if err != nil { - if errors.IsNotFound(err) { - return fmt.Errorf("deployment %s.%s not found", targetName, cd.Namespace) - } - return fmt.Errorf("deployment %s.%s query error %v", targetName, cd.Namespace, err) - } - - label, err := c.getSelectorLabel(canary) - if err != nil { - return fmt.Errorf("invalid label selector! Deployment %s.%s spec.selector.matchLabels must contain selector 'app: %s'", - targetName, cd.Namespace, targetName) - } - - primary, err := c.KubeClient.AppsV1().Deployments(cd.Namespace).Get(primaryName, metav1.GetOptions{}) - if err != nil { - if errors.IsNotFound(err) { - return fmt.Errorf("deployment %s.%s not found", primaryName, cd.Namespace) - } - return fmt.Errorf("deployment %s.%s query error %v", primaryName, cd.Namespace, err) - } - - // promote secrets and config maps - configRefs, err := c.ConfigTracker.GetTargetConfigs(cd) - if err != nil { - return err - } - if err := c.ConfigTracker.CreatePrimaryConfigs(cd, configRefs); err != nil { - return err - } - - primaryCopy := primary.DeepCopy() - primaryCopy.Spec.ProgressDeadlineSeconds = canary.Spec.ProgressDeadlineSeconds - primaryCopy.Spec.MinReadySeconds = canary.Spec.MinReadySeconds - primaryCopy.Spec.RevisionHistoryLimit = canary.Spec.RevisionHistoryLimit - primaryCopy.Spec.Strategy = canary.Spec.Strategy - - // update spec with primary secrets and config maps - primaryCopy.Spec.Template.Spec = c.ConfigTracker.ApplyPrimaryConfigs(canary.Spec.Template.Spec, configRefs) - - // update pod annotations to ensure a rolling update - annotations, err := c.makeAnnotations(canary.Spec.Template.Annotations) - if err != nil { - return err - } - primaryCopy.Spec.Template.Annotations = annotations - - primaryCopy.Spec.Template.Labels = makePrimaryLabels(canary.Spec.Template.Labels, primaryName, label) - - // apply update - _, err = c.KubeClient.AppsV1().Deployments(cd.Namespace).Update(primaryCopy) - if err != nil { - return fmt.Errorf("updating deployment %s.%s template spec failed: %v", - primaryCopy.GetName(), primaryCopy.Namespace, err) - } - - // update HPA - if cd.Spec.AutoscalerRef != nil && cd.Spec.AutoscalerRef.Kind == "HorizontalPodAutoscaler" { - if err := c.reconcilePrimaryHpa(cd, false); err != nil { - return fmt.Errorf("updating HorizontalPodAutoscaler %s.%s failed: %v", primaryName, cd.Namespace, err) - } - } - - return nil -} - -// HasDeploymentChanged returns true if the canary deployment pod spec has changed -func (c *Deployer) HasDeploymentChanged(cd *flaggerv1.Canary) (bool, error) { - targetName := cd.Spec.TargetRef.Name - canary, err := c.KubeClient.AppsV1().Deployments(cd.Namespace).Get(targetName, metav1.GetOptions{}) - if err != nil { - if errors.IsNotFound(err) { - return false, fmt.Errorf("deployment %s.%s not found", targetName, cd.Namespace) - } - return false, fmt.Errorf("deployment %s.%s query error %v", targetName, cd.Namespace, err) - } - - if cd.Status.LastAppliedSpec == "" { - return true, nil - } - - newHash, err := hashstructure.Hash(canary.Spec.Template, nil) - if err != nil { - return false, fmt.Errorf("hash error %v", err) - } - - // do not trigger a canary deployment on manual rollback - if cd.Status.LastPromotedSpec == fmt.Sprintf("%d", newHash) { - return false, nil - } - - if cd.Status.LastAppliedSpec != fmt.Sprintf("%d", newHash) { - return true, nil - } - - return false, nil -} - -// Scale sets the canary deployment replicas -func (c *Deployer) Scale(cd *flaggerv1.Canary, replicas int32) error { - targetName := cd.Spec.TargetRef.Name - dep, err := c.KubeClient.AppsV1().Deployments(cd.Namespace).Get(targetName, metav1.GetOptions{}) - if err != nil { - if errors.IsNotFound(err) { - return fmt.Errorf("deployment %s.%s not found", targetName, cd.Namespace) - } - return fmt.Errorf("deployment %s.%s query error %v", targetName, cd.Namespace, err) - } - - depCopy := dep.DeepCopy() - depCopy.Spec.Replicas = int32p(replicas) - - _, err = c.KubeClient.AppsV1().Deployments(dep.Namespace).Update(depCopy) - if err != nil { - return fmt.Errorf("scaling %s.%s to %v failed: %v", depCopy.GetName(), depCopy.Namespace, replicas, err) - } - return nil -} - -func (c *Deployer) createPrimaryDeployment(cd *flaggerv1.Canary) (string, *map[string]int32, error) { - targetName := cd.Spec.TargetRef.Name - primaryName := fmt.Sprintf("%s-primary", cd.Spec.TargetRef.Name) - - canaryDep, err := c.KubeClient.AppsV1().Deployments(cd.Namespace).Get(targetName, metav1.GetOptions{}) - if err != nil { - if errors.IsNotFound(err) { - return "", nil, fmt.Errorf("deployment %s.%s not found, retrying", targetName, cd.Namespace) - } - return "", nil, err - } - - label, err := c.getSelectorLabel(canaryDep) - if err != nil { - return "", nil, fmt.Errorf("invalid label selector! Deployment %s.%s spec.selector.matchLabels must contain selector 'app: %s'", - targetName, cd.Namespace, targetName) - } - - var ports *map[string]int32 - if cd.Spec.Service.PortDiscovery { - p, err := c.getPorts(canaryDep, cd.Spec.Service.Port) - if err != nil { - return "", nil, fmt.Errorf("port discovery failed with error: %v", err) - } - ports = &p - } - - primaryDep, err := c.KubeClient.AppsV1().Deployments(cd.Namespace).Get(primaryName, metav1.GetOptions{}) - if errors.IsNotFound(err) { - // create primary secrets and config maps - configRefs, err := c.ConfigTracker.GetTargetConfigs(cd) - if err != nil { - return "", nil, err - } - if err := c.ConfigTracker.CreatePrimaryConfigs(cd, configRefs); err != nil { - return "", nil, err - } - annotations, err := c.makeAnnotations(canaryDep.Spec.Template.Annotations) - if err != nil { - return "", nil, err - } - - replicas := int32(1) - if canaryDep.Spec.Replicas != nil && *canaryDep.Spec.Replicas > 0 { - replicas = *canaryDep.Spec.Replicas - } - - // create primary deployment - primaryDep = &appsv1.Deployment{ - ObjectMeta: metav1.ObjectMeta{ - Name: primaryName, - Namespace: cd.Namespace, - OwnerReferences: []metav1.OwnerReference{ - *metav1.NewControllerRef(cd, schema.GroupVersionKind{ - Group: flaggerv1.SchemeGroupVersion.Group, - Version: flaggerv1.SchemeGroupVersion.Version, - Kind: flaggerv1.CanaryKind, - }), - }, - }, - Spec: appsv1.DeploymentSpec{ - ProgressDeadlineSeconds: canaryDep.Spec.ProgressDeadlineSeconds, - MinReadySeconds: canaryDep.Spec.MinReadySeconds, - RevisionHistoryLimit: canaryDep.Spec.RevisionHistoryLimit, - Replicas: int32p(replicas), - Strategy: canaryDep.Spec.Strategy, - Selector: &metav1.LabelSelector{ - MatchLabels: map[string]string{ - label: primaryName, - }, - }, - Template: corev1.PodTemplateSpec{ - ObjectMeta: metav1.ObjectMeta{ - Labels: makePrimaryLabels(canaryDep.Spec.Template.Labels, primaryName, label), - Annotations: annotations, - }, - // update spec with the primary secrets and config maps - Spec: c.ConfigTracker.ApplyPrimaryConfigs(canaryDep.Spec.Template.Spec, configRefs), - }, - }, - } - - _, err = c.KubeClient.AppsV1().Deployments(cd.Namespace).Create(primaryDep) - if err != nil { - return "", nil, err - } - - c.Logger.With("canary", fmt.Sprintf("%s.%s", cd.Name, cd.Namespace)).Infof("Deployment %s.%s created", primaryDep.GetName(), cd.Namespace) - } - - return label, ports, nil -} - -func (c *Deployer) reconcilePrimaryHpa(cd *flaggerv1.Canary, init bool) error { - primaryName := fmt.Sprintf("%s-primary", cd.Spec.TargetRef.Name) - hpa, err := c.KubeClient.AutoscalingV2beta1().HorizontalPodAutoscalers(cd.Namespace).Get(cd.Spec.AutoscalerRef.Name, metav1.GetOptions{}) - if err != nil { - if errors.IsNotFound(err) { - return fmt.Errorf("HorizontalPodAutoscaler %s.%s not found, retrying", - cd.Spec.AutoscalerRef.Name, cd.Namespace) - } - return err - } - - hpaSpec := hpav1.HorizontalPodAutoscalerSpec{ - ScaleTargetRef: hpav1.CrossVersionObjectReference{ - Name: primaryName, - Kind: hpa.Spec.ScaleTargetRef.Kind, - APIVersion: hpa.Spec.ScaleTargetRef.APIVersion, - }, - MinReplicas: hpa.Spec.MinReplicas, - MaxReplicas: hpa.Spec.MaxReplicas, - Metrics: hpa.Spec.Metrics, - } - - primaryHpaName := fmt.Sprintf("%s-primary", cd.Spec.AutoscalerRef.Name) - primaryHpa, err := c.KubeClient.AutoscalingV2beta1().HorizontalPodAutoscalers(cd.Namespace).Get(primaryHpaName, metav1.GetOptions{}) - - // create HPA - if errors.IsNotFound(err) { - primaryHpa = &hpav1.HorizontalPodAutoscaler{ - ObjectMeta: metav1.ObjectMeta{ - Name: primaryHpaName, - Namespace: cd.Namespace, - Labels: hpa.Labels, - OwnerReferences: []metav1.OwnerReference{ - *metav1.NewControllerRef(cd, schema.GroupVersionKind{ - Group: flaggerv1.SchemeGroupVersion.Group, - Version: flaggerv1.SchemeGroupVersion.Version, - Kind: flaggerv1.CanaryKind, - }), - }, - }, - Spec: hpaSpec, - } - - _, err = c.KubeClient.AutoscalingV2beta1().HorizontalPodAutoscalers(cd.Namespace).Create(primaryHpa) - if err != nil { - return err - } - c.Logger.With("canary", fmt.Sprintf("%s.%s", cd.Name, cd.Namespace)).Infof("HorizontalPodAutoscaler %s.%s created", primaryHpa.GetName(), cd.Namespace) - return nil - } - - if err != nil { - return err - } - - // update HPA - if !init && primaryHpa != nil { - diff := cmp.Diff(hpaSpec.Metrics, primaryHpa.Spec.Metrics) - if diff != "" || int32Default(hpaSpec.MinReplicas) != int32Default(primaryHpa.Spec.MinReplicas) || hpaSpec.MaxReplicas != primaryHpa.Spec.MaxReplicas { - fmt.Println(diff, hpaSpec.MinReplicas, primaryHpa.Spec.MinReplicas, hpaSpec.MaxReplicas, primaryHpa.Spec.MaxReplicas) - hpaClone := primaryHpa.DeepCopy() - hpaClone.Spec.MaxReplicas = hpaSpec.MaxReplicas - hpaClone.Spec.MinReplicas = hpaSpec.MinReplicas - hpaClone.Spec.Metrics = hpaSpec.Metrics - - _, upErr := c.KubeClient.AutoscalingV2beta1().HorizontalPodAutoscalers(cd.Namespace).Update(hpaClone) - if upErr != nil { - return upErr - } - c.Logger.With("canary", fmt.Sprintf("%s.%s", cd.Name, cd.Namespace)).Infof("HorizontalPodAutoscaler %s.%s updated", primaryHpa.GetName(), cd.Namespace) - } - } - - return nil -} - -// makeAnnotations appends an unique ID to annotations map -func (c *Deployer) makeAnnotations(annotations map[string]string) (map[string]string, error) { - idKey := "flagger-id" - res := make(map[string]string) - uuid := make([]byte, 16) - n, err := io.ReadFull(rand.Reader, uuid) - if n != len(uuid) || err != nil { - return res, err - } - uuid[8] = uuid[8]&^0xc0 | 0x80 - uuid[6] = uuid[6]&^0xf0 | 0x40 - id := fmt.Sprintf("%x-%x-%x-%x-%x", uuid[0:4], uuid[4:6], uuid[6:8], uuid[8:10], uuid[10:]) - - for k, v := range annotations { - if k != idKey { - res[k] = v - } - } - res[idKey] = id - - return res, nil -} - -// getSelectorLabel returns the selector match label -func (c *Deployer) getSelectorLabel(deployment *appsv1.Deployment) (string, error) { - for _, l := range c.Labels { - if _, ok := deployment.Spec.Selector.MatchLabels[l]; ok { - return l, nil - } - } - - return "", fmt.Errorf("selector not found") -} - -var sidecars = map[string]bool{ - "istio-proxy": true, - "envoy": true, -} - -// getPorts returns a list of all container ports -func (c *Deployer) getPorts(deployment *appsv1.Deployment, canaryPort int32) (map[string]int32, error) { - ports := make(map[string]int32) - - for _, container := range deployment.Spec.Template.Spec.Containers { - // exclude service mesh proxies based on container name - if _, ok := sidecars[container.Name]; ok { - continue - } - for i, p := range container.Ports { - // exclude canary.service.port - if p.ContainerPort == canaryPort { - continue - } - name := fmt.Sprintf("tcp-%s-%v", container.Name, i) - if p.Name != "" { - name = p.Name - } - - ports[name] = p.ContainerPort - } - } - - return ports, nil -} - -func makePrimaryLabels(labels map[string]string, primaryName string, label string) map[string]string { - res := make(map[string]string) - for k, v := range labels { - if k != label { - res[k] = v - } - } - res[label] = primaryName - - return res -} - -func int32p(i int32) *int32 { - return &i -} - -func int32Default(i *int32) int32 { - if i == nil { - return 1 - } - - return *i -} diff --git a/pkg/canary/deployer_test.go b/pkg/canary/deployer_test.go deleted file mode 100644 index ec545aa9..00000000 --- a/pkg/canary/deployer_test.go +++ /dev/null @@ -1,303 +0,0 @@ -package canary - -import ( - "testing" - - "github.com/weaveworks/flagger/pkg/apis/flagger/v1alpha3" - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" -) - -func TestCanaryDeployer_Sync(t *testing.T) { - mocks := SetupMocks() - _, _, err := mocks.deployer.Initialize(mocks.canary, true) - if err != nil { - t.Fatal(err.Error()) - } - - depPrimary, err := mocks.kubeClient.AppsV1().Deployments("default").Get("podinfo-primary", metav1.GetOptions{}) - if err != nil { - t.Fatal(err.Error()) - } - - dep := newTestDeployment() - configMap := NewTestConfigMap() - secret := NewTestSecret() - - primaryImage := depPrimary.Spec.Template.Spec.Containers[0].Image - sourceImage := dep.Spec.Template.Spec.Containers[0].Image - if primaryImage != sourceImage { - t.Errorf("Got image %s wanted %s", primaryImage, sourceImage) - } - - hpaPrimary, err := mocks.kubeClient.AutoscalingV2beta1().HorizontalPodAutoscalers("default").Get("podinfo-primary", metav1.GetOptions{}) - if err != nil { - t.Fatal(err.Error()) - } - - if hpaPrimary.Spec.ScaleTargetRef.Name != depPrimary.Name { - t.Errorf("Got HPA target %s wanted %s", hpaPrimary.Spec.ScaleTargetRef.Name, depPrimary.Name) - } - - configPrimary, err := mocks.kubeClient.CoreV1().ConfigMaps("default").Get("podinfo-config-env-primary", metav1.GetOptions{}) - if err != nil { - t.Fatal(err.Error()) - } - - if configPrimary.Data["color"] != configMap.Data["color"] { - t.Errorf("Got ConfigMap color %s wanted %s", configPrimary.Data["color"], configMap.Data["color"]) - } - - configPrimaryEnv, err := mocks.kubeClient.CoreV1().ConfigMaps("default").Get("podinfo-config-all-env-primary", metav1.GetOptions{}) - if err != nil { - t.Fatal(err.Error()) - } - - if configPrimaryEnv.Data["color"] != configMap.Data["color"] { - t.Errorf("Got ConfigMap %s wanted %s", configPrimaryEnv.Data["a"], configMap.Data["color"]) - } - - configPrimaryVol, err := mocks.kubeClient.CoreV1().ConfigMaps("default").Get("podinfo-config-vol-primary", metav1.GetOptions{}) - if err != nil { - t.Fatal(err.Error()) - } - - if configPrimaryVol.Data["color"] != configMap.Data["color"] { - t.Errorf("Got ConfigMap color %s wanted %s", configPrimary.Data["color"], configMap.Data["color"]) - } - - secretPrimary, err := mocks.kubeClient.CoreV1().Secrets("default").Get("podinfo-secret-env-primary", metav1.GetOptions{}) - if err != nil { - t.Fatal(err.Error()) - } - - if string(secretPrimary.Data["apiKey"]) != string(secret.Data["apiKey"]) { - t.Errorf("Got primary secret %s wanted %s", secretPrimary.Data["apiKey"], secret.Data["apiKey"]) - } - - secretPrimaryEnv, err := mocks.kubeClient.CoreV1().Secrets("default").Get("podinfo-secret-all-env-primary", metav1.GetOptions{}) - if err != nil { - t.Fatal(err.Error()) - } - - if string(secretPrimaryEnv.Data["apiKey"]) != string(secret.Data["apiKey"]) { - t.Errorf("Got primary secret %s wanted %s", secretPrimary.Data["apiKey"], secret.Data["apiKey"]) - } - - secretPrimaryVol, err := mocks.kubeClient.CoreV1().Secrets("default").Get("podinfo-secret-vol-primary", metav1.GetOptions{}) - if err != nil { - t.Fatal(err.Error()) - } - - if string(secretPrimaryVol.Data["apiKey"]) != string(secret.Data["apiKey"]) { - t.Errorf("Got primary secret %s wanted %s", secretPrimary.Data["apiKey"], secret.Data["apiKey"]) - } -} - -func TestCanaryDeployer_IsNewSpec(t *testing.T) { - mocks := SetupMocks() - _, _, err := mocks.deployer.Initialize(mocks.canary, true) - if err != nil { - t.Fatal(err.Error()) - } - - dep2 := newTestDeploymentV2() - _, err = mocks.kubeClient.AppsV1().Deployments("default").Update(dep2) - if err != nil { - t.Fatal(err.Error()) - } - - isNew, err := mocks.deployer.HasDeploymentChanged(mocks.canary) - if err != nil { - t.Fatal(err.Error()) - } - - if !isNew { - t.Errorf("Got %v wanted %v", isNew, true) - } -} - -func TestCanaryDeployer_Promote(t *testing.T) { - mocks := SetupMocks() - _, _, err := mocks.deployer.Initialize(mocks.canary, true) - if err != nil { - t.Fatal(err.Error()) - } - - dep2 := newTestDeploymentV2() - _, err = mocks.kubeClient.AppsV1().Deployments("default").Update(dep2) - if err != nil { - t.Fatal(err.Error()) - } - - config2 := NewTestConfigMapV2() - _, err = mocks.kubeClient.CoreV1().ConfigMaps("default").Update(config2) - if err != nil { - t.Fatal(err.Error()) - } - - hpa, err := mocks.kubeClient.AutoscalingV2beta1().HorizontalPodAutoscalers("default").Get("podinfo", metav1.GetOptions{}) - if err != nil { - t.Fatal(err.Error()) - } - hpaClone := hpa.DeepCopy() - hpaClone.Spec.MaxReplicas = 2 - - _, err = mocks.kubeClient.AutoscalingV2beta1().HorizontalPodAutoscalers("default").Update(hpaClone) - if err != nil { - t.Fatal(err.Error()) - } - - err = mocks.deployer.Promote(mocks.canary) - if err != nil { - t.Fatal(err.Error()) - } - - depPrimary, err := mocks.kubeClient.AppsV1().Deployments("default").Get("podinfo-primary", metav1.GetOptions{}) - if err != nil { - t.Fatal(err.Error()) - } - - primaryImage := depPrimary.Spec.Template.Spec.Containers[0].Image - sourceImage := dep2.Spec.Template.Spec.Containers[0].Image - if primaryImage != sourceImage { - t.Errorf("Got image %s wanted %s", primaryImage, sourceImage) - } - - configPrimary, err := mocks.kubeClient.CoreV1().ConfigMaps("default").Get("podinfo-config-env-primary", metav1.GetOptions{}) - if err != nil { - t.Fatal(err.Error()) - } - - if configPrimary.Data["color"] != config2.Data["color"] { - t.Errorf("Got primary ConfigMap color %s wanted %s", configPrimary.Data["color"], config2.Data["color"]) - } - - hpaPrimary, err := mocks.kubeClient.AutoscalingV2beta1().HorizontalPodAutoscalers("default").Get("podinfo-primary", metav1.GetOptions{}) - if err != nil { - t.Fatal(err.Error()) - } - - if hpaPrimary.Spec.MaxReplicas != 2 { - t.Errorf("Got primary HPA MaxReplicas %v wanted %v", hpaPrimary.Spec.MaxReplicas, 2) - } -} - -func TestCanaryDeployer_IsReady(t *testing.T) { - mocks := SetupMocks() - _, _, err := mocks.deployer.Initialize(mocks.canary, true) - if err != nil { - t.Error("Expected primary readiness check to fail") - } - - _, err = mocks.deployer.IsPrimaryReady(mocks.canary) - if err == nil { - t.Fatal(err.Error()) - } - - _, err = mocks.deployer.IsCanaryReady(mocks.canary) - if err != nil { - t.Fatal(err.Error()) - } -} - -func TestCanaryDeployer_SetFailedChecks(t *testing.T) { - mocks := SetupMocks() - _, _, err := mocks.deployer.Initialize(mocks.canary, true) - if err != nil { - t.Fatal(err.Error()) - } - - err = mocks.deployer.SetStatusFailedChecks(mocks.canary, 1) - if err != nil { - t.Fatal(err.Error()) - } - - res, err := mocks.flaggerClient.FlaggerV1alpha3().Canaries("default").Get("podinfo", metav1.GetOptions{}) - if err != nil { - t.Fatal(err.Error()) - } - - if res.Status.FailedChecks != 1 { - t.Errorf("Got %v wanted %v", res.Status.FailedChecks, 1) - } -} - -func TestCanaryDeployer_SetState(t *testing.T) { - mocks := SetupMocks() - _, _, err := mocks.deployer.Initialize(mocks.canary, true) - if err != nil { - t.Fatal(err.Error()) - } - - err = mocks.deployer.SetStatusPhase(mocks.canary, v1alpha3.CanaryPhaseProgressing) - if err != nil { - t.Fatal(err.Error()) - } - - res, err := mocks.flaggerClient.FlaggerV1alpha3().Canaries("default").Get("podinfo", metav1.GetOptions{}) - if err != nil { - t.Fatal(err.Error()) - } - - if res.Status.Phase != v1alpha3.CanaryPhaseProgressing { - t.Errorf("Got %v wanted %v", res.Status.Phase, v1alpha3.CanaryPhaseProgressing) - } -} - -func TestCanaryDeployer_SyncStatus(t *testing.T) { - mocks := SetupMocks() - _, _, err := mocks.deployer.Initialize(mocks.canary, true) - if err != nil { - t.Fatal(err.Error()) - } - - status := v1alpha3.CanaryStatus{ - Phase: v1alpha3.CanaryPhaseProgressing, - FailedChecks: 2, - } - err = mocks.deployer.SyncStatus(mocks.canary, status) - if err != nil { - t.Fatal(err.Error()) - } - - res, err := mocks.flaggerClient.FlaggerV1alpha3().Canaries("default").Get("podinfo", metav1.GetOptions{}) - if err != nil { - t.Fatal(err.Error()) - } - - if res.Status.Phase != status.Phase { - t.Errorf("Got state %v wanted %v", res.Status.Phase, status.Phase) - } - - if res.Status.FailedChecks != status.FailedChecks { - t.Errorf("Got failed checks %v wanted %v", res.Status.FailedChecks, status.FailedChecks) - } - - if res.Status.TrackedConfigs == nil { - t.Fatalf("Status tracking configs are empty") - } - configs := *res.Status.TrackedConfigs - secret := NewTestSecret() - if _, exists := configs["secret/"+secret.GetName()]; !exists { - t.Errorf("Secret %s not found in status", secret.GetName()) - } -} - -func TestCanaryDeployer_Scale(t *testing.T) { - mocks := SetupMocks() - _, _, err := mocks.deployer.Initialize(mocks.canary, true) - if err != nil { - t.Fatal(err.Error()) - } - - err = mocks.deployer.Scale(mocks.canary, 2) - - c, err := mocks.kubeClient.AppsV1().Deployments("default").Get("podinfo", metav1.GetOptions{}) - if err != nil { - t.Fatal(err.Error()) - } - - if *c.Spec.Replicas != 2 { - t.Errorf("Got replicas %v wanted %v", *c.Spec.Replicas, 2) - } -} diff --git a/pkg/canary/mock.go b/pkg/canary/mock.go deleted file mode 100644 index f60444e8..00000000 --- a/pkg/canary/mock.go +++ /dev/null @@ -1,471 +0,0 @@ -package canary - -import ( - "github.com/weaveworks/flagger/pkg/apis/flagger/v1alpha3" - clientset "github.com/weaveworks/flagger/pkg/client/clientset/versioned" - fakeFlagger "github.com/weaveworks/flagger/pkg/client/clientset/versioned/fake" - "github.com/weaveworks/flagger/pkg/logger" - "go.uber.org/zap" - appsv1 "k8s.io/api/apps/v1" - hpav1 "k8s.io/api/autoscaling/v1" - hpav2 "k8s.io/api/autoscaling/v2beta1" - corev1 "k8s.io/api/core/v1" - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - "k8s.io/client-go/kubernetes" - "k8s.io/client-go/kubernetes/fake" -) - -type Mocks struct { - canary *v1alpha3.Canary - kubeClient kubernetes.Interface - flaggerClient clientset.Interface - deployer Deployer - logger *zap.SugaredLogger -} - -func SetupMocks() Mocks { - // init canary - canary := newTestCanary() - flaggerClient := fakeFlagger.NewSimpleClientset(canary) - - // init kube clientset and register mock objects - kubeClient := fake.NewSimpleClientset( - newTestDeployment(), - newTestHPA(), - NewTestConfigMap(), - NewTestConfigMapEnv(), - NewTestConfigMapVol(), - NewTestSecret(), - NewTestSecretEnv(), - NewTestSecretVol(), - ) - - logger, _ := logger.NewLogger("debug") - - deployer := Deployer{ - FlaggerClient: flaggerClient, - KubeClient: kubeClient, - Logger: logger, - Labels: []string{"app", "name"}, - ConfigTracker: ConfigTracker{ - Logger: logger, - KubeClient: kubeClient, - FlaggerClient: flaggerClient, - }, - } - - return Mocks{ - canary: canary, - deployer: deployer, - logger: logger, - flaggerClient: flaggerClient, - kubeClient: kubeClient, - } -} - -func NewTestConfigMap() *corev1.ConfigMap { - return &corev1.ConfigMap{ - TypeMeta: metav1.TypeMeta{APIVersion: corev1.SchemeGroupVersion.String()}, - ObjectMeta: metav1.ObjectMeta{ - Namespace: "default", - Name: "podinfo-config-env", - }, - Data: map[string]string{ - "color": "red", - }, - } -} - -func NewTestConfigMapV2() *corev1.ConfigMap { - return &corev1.ConfigMap{ - TypeMeta: metav1.TypeMeta{APIVersion: corev1.SchemeGroupVersion.String()}, - ObjectMeta: metav1.ObjectMeta{ - Namespace: "default", - Name: "podinfo-config-env", - }, - Data: map[string]string{ - "color": "blue", - "output": "console", - }, - } -} - -func NewTestConfigMapEnv() *corev1.ConfigMap { - return &corev1.ConfigMap{ - TypeMeta: metav1.TypeMeta{APIVersion: corev1.SchemeGroupVersion.String()}, - ObjectMeta: metav1.ObjectMeta{ - Namespace: "default", - Name: "podinfo-config-all-env", - }, - Data: map[string]string{ - "color": "red", - }, - } -} - -func NewTestConfigMapVol() *corev1.ConfigMap { - return &corev1.ConfigMap{ - TypeMeta: metav1.TypeMeta{APIVersion: corev1.SchemeGroupVersion.String()}, - ObjectMeta: metav1.ObjectMeta{ - Namespace: "default", - Name: "podinfo-config-vol", - }, - Data: map[string]string{ - "color": "red", - }, - } -} - -func NewTestSecret() *corev1.Secret { - return &corev1.Secret{ - TypeMeta: metav1.TypeMeta{APIVersion: corev1.SchemeGroupVersion.String()}, - ObjectMeta: metav1.ObjectMeta{ - Namespace: "default", - Name: "podinfo-secret-env", - }, - Type: corev1.SecretTypeOpaque, - Data: map[string][]byte{ - "apiKey": []byte("test"), - }, - } -} - -func NewTestSecretV2() *corev1.Secret { - return &corev1.Secret{ - TypeMeta: metav1.TypeMeta{APIVersion: corev1.SchemeGroupVersion.String()}, - ObjectMeta: metav1.ObjectMeta{ - Namespace: "default", - Name: "podinfo-secret-env", - }, - Type: corev1.SecretTypeOpaque, - Data: map[string][]byte{ - "apiKey": []byte("test2"), - }, - } -} - -func NewTestSecretEnv() *corev1.Secret { - return &corev1.Secret{ - TypeMeta: metav1.TypeMeta{APIVersion: corev1.SchemeGroupVersion.String()}, - ObjectMeta: metav1.ObjectMeta{ - Namespace: "default", - Name: "podinfo-secret-all-env", - }, - Type: corev1.SecretTypeOpaque, - Data: map[string][]byte{ - "apiKey": []byte("test"), - }, - } -} - -func NewTestSecretVol() *corev1.Secret { - return &corev1.Secret{ - TypeMeta: metav1.TypeMeta{APIVersion: corev1.SchemeGroupVersion.String()}, - ObjectMeta: metav1.ObjectMeta{ - Namespace: "default", - Name: "podinfo-secret-vol", - }, - Type: corev1.SecretTypeOpaque, - Data: map[string][]byte{ - "apiKey": []byte("test"), - }, - } -} - -func newTestCanary() *v1alpha3.Canary { - cd := &v1alpha3.Canary{ - TypeMeta: metav1.TypeMeta{APIVersion: v1alpha3.SchemeGroupVersion.String()}, - ObjectMeta: metav1.ObjectMeta{ - Namespace: "default", - Name: "podinfo", - }, - Spec: v1alpha3.CanarySpec{ - TargetRef: hpav1.CrossVersionObjectReference{ - Name: "podinfo", - APIVersion: "apps/v1", - Kind: "Deployment", - }, - AutoscalerRef: &hpav1.CrossVersionObjectReference{ - Name: "podinfo", - APIVersion: "autoscaling/v2beta1", - Kind: "HorizontalPodAutoscaler", - }, Service: v1alpha3.CanaryService{ - Port: 9898, - }, CanaryAnalysis: v1alpha3.CanaryAnalysis{ - Threshold: 10, - StepWeight: 10, - MaxWeight: 50, - Metrics: []v1alpha3.CanaryMetric{ - { - Name: "istio_requests_total", - Threshold: 99, - Interval: "1m", - }, - { - Name: "istio_request_duration_seconds_bucket", - Threshold: 500, - Interval: "1m", - }, - }, - }, - }, - } - return cd -} - -func newTestDeployment() *appsv1.Deployment { - d := &appsv1.Deployment{ - TypeMeta: metav1.TypeMeta{APIVersion: appsv1.SchemeGroupVersion.String()}, - ObjectMeta: metav1.ObjectMeta{ - Namespace: "default", - Name: "podinfo", - }, - Spec: appsv1.DeploymentSpec{ - Selector: &metav1.LabelSelector{ - MatchLabels: map[string]string{ - "name": "podinfo", - }, - }, - Template: corev1.PodTemplateSpec{ - ObjectMeta: metav1.ObjectMeta{ - Labels: map[string]string{ - "name": "podinfo", - }, - }, - Spec: corev1.PodSpec{ - Containers: []corev1.Container{ - { - Name: "podinfo", - Image: "quay.io/stefanprodan/podinfo:1.2.0", - Command: []string{ - "./podinfo", - "--port=9898", - }, - Args: nil, - WorkingDir: "", - Ports: []corev1.ContainerPort{ - { - Name: "http", - ContainerPort: 9898, - Protocol: corev1.ProtocolTCP, - }, - }, - Env: []corev1.EnvVar{ - { - Name: "PODINFO_UI_COLOR", - ValueFrom: &corev1.EnvVarSource{ - ConfigMapKeyRef: &corev1.ConfigMapKeySelector{ - LocalObjectReference: corev1.LocalObjectReference{ - Name: "podinfo-config-env", - }, - Key: "color", - }, - }, - }, - { - Name: "API_KEY", - ValueFrom: &corev1.EnvVarSource{ - SecretKeyRef: &corev1.SecretKeySelector{ - LocalObjectReference: corev1.LocalObjectReference{ - Name: "podinfo-secret-env", - }, - Key: "apiKey", - }, - }, - }, - }, - EnvFrom: []corev1.EnvFromSource{ - { - ConfigMapRef: &corev1.ConfigMapEnvSource{ - LocalObjectReference: corev1.LocalObjectReference{ - Name: "podinfo-config-all-env", - }, - }, - }, - { - SecretRef: &corev1.SecretEnvSource{ - LocalObjectReference: corev1.LocalObjectReference{ - Name: "podinfo-secret-all-env", - }, - }, - }, - }, - VolumeMounts: []corev1.VolumeMount{ - { - Name: "config", - MountPath: "/etc/podinfo/config", - ReadOnly: true, - }, - { - Name: "secret", - MountPath: "/etc/podinfo/secret", - ReadOnly: true, - }, - }, - }, - }, - Volumes: []corev1.Volume{ - { - Name: "config", - VolumeSource: corev1.VolumeSource{ - ConfigMap: &corev1.ConfigMapVolumeSource{ - LocalObjectReference: corev1.LocalObjectReference{ - Name: "podinfo-config-vol", - }, - }, - }, - }, - { - Name: "secret", - VolumeSource: corev1.VolumeSource{ - Secret: &corev1.SecretVolumeSource{ - SecretName: "podinfo-secret-vol", - }, - }, - }, - }, - }, - }, - }, - } - - return d -} - -func newTestDeploymentV2() *appsv1.Deployment { - d := &appsv1.Deployment{ - TypeMeta: metav1.TypeMeta{APIVersion: appsv1.SchemeGroupVersion.String()}, - ObjectMeta: metav1.ObjectMeta{ - Namespace: "default", - Name: "podinfo", - }, - Spec: appsv1.DeploymentSpec{ - Selector: &metav1.LabelSelector{ - MatchLabels: map[string]string{ - "name": "podinfo", - }, - }, - Template: corev1.PodTemplateSpec{ - ObjectMeta: metav1.ObjectMeta{ - Labels: map[string]string{ - "name": "podinfo", - }, - }, - Spec: corev1.PodSpec{ - Containers: []corev1.Container{ - { - Name: "podinfo", - Image: "quay.io/stefanprodan/podinfo:1.2.1", - Ports: []corev1.ContainerPort{ - { - Name: "http", - ContainerPort: 9898, - Protocol: corev1.ProtocolTCP, - }, - }, - Command: []string{ - "./podinfo", - "--port=9898", - }, - Env: []corev1.EnvVar{ - { - Name: "PODINFO_UI_COLOR", - ValueFrom: &corev1.EnvVarSource{ - ConfigMapKeyRef: &corev1.ConfigMapKeySelector{ - LocalObjectReference: corev1.LocalObjectReference{ - Name: "podinfo-config-env", - }, - Key: "color", - }, - }, - }, - { - Name: "API_KEY", - ValueFrom: &corev1.EnvVarSource{ - SecretKeyRef: &corev1.SecretKeySelector{ - LocalObjectReference: corev1.LocalObjectReference{ - Name: "podinfo-secret-env", - }, - Key: "apiKey", - }, - }, - }, - }, - EnvFrom: []corev1.EnvFromSource{ - { - ConfigMapRef: &corev1.ConfigMapEnvSource{ - LocalObjectReference: corev1.LocalObjectReference{ - Name: "podinfo-config-all-env", - }, - }, - }, - }, - VolumeMounts: []corev1.VolumeMount{ - { - Name: "config", - MountPath: "/etc/podinfo/config", - ReadOnly: true, - }, - { - Name: "secret", - MountPath: "/etc/podinfo/secret", - ReadOnly: true, - }, - }, - }, - }, - Volumes: []corev1.Volume{ - { - Name: "config", - VolumeSource: corev1.VolumeSource{ - ConfigMap: &corev1.ConfigMapVolumeSource{ - LocalObjectReference: corev1.LocalObjectReference{ - Name: "podinfo-config-vol", - }, - }, - }, - }, - { - Name: "secret", - VolumeSource: corev1.VolumeSource{ - Secret: &corev1.SecretVolumeSource{ - SecretName: "podinfo-secret-vol", - }, - }, - }, - }, - }, - }, - }, - } - - return d -} - -func newTestHPA() *hpav2.HorizontalPodAutoscaler { - h := &hpav2.HorizontalPodAutoscaler{ - TypeMeta: metav1.TypeMeta{APIVersion: hpav2.SchemeGroupVersion.String()}, - ObjectMeta: metav1.ObjectMeta{ - Namespace: "default", - Name: "podinfo", - }, - Spec: hpav2.HorizontalPodAutoscalerSpec{ - ScaleTargetRef: hpav2.CrossVersionObjectReference{ - Name: "podinfo", - APIVersion: "apps/v1", - Kind: "Deployment", - }, - Metrics: []hpav2.MetricSpec{ - { - Type: "Resource", - Resource: &hpav2.ResourceMetricSource{ - Name: "cpu", - TargetAverageUtilization: int32p(99), - }, - }, - }, - }, - } - - return h -} diff --git a/pkg/canary/ready.go b/pkg/canary/ready.go deleted file mode 100644 index c7884ba4..00000000 --- a/pkg/canary/ready.go +++ /dev/null @@ -1,112 +0,0 @@ -package canary - -import ( - "fmt" - "time" - - flaggerv1 "github.com/weaveworks/flagger/pkg/apis/flagger/v1alpha3" - appsv1 "k8s.io/api/apps/v1" - "k8s.io/apimachinery/pkg/api/errors" - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" -) - -// IsPrimaryReady checks the primary deployment status and returns an error if -// the deployment is in the middle of a rolling update or if the pods are unhealthy -// it will return a non retriable error if the rolling update is stuck -func (c *Deployer) IsPrimaryReady(cd *flaggerv1.Canary) (bool, error) { - primaryName := fmt.Sprintf("%s-primary", cd.Spec.TargetRef.Name) - primary, err := c.KubeClient.AppsV1().Deployments(cd.Namespace).Get(primaryName, metav1.GetOptions{}) - if err != nil { - if errors.IsNotFound(err) { - return true, fmt.Errorf("deployment %s.%s not found", primaryName, cd.Namespace) - } - return true, fmt.Errorf("deployment %s.%s query error %v", primaryName, cd.Namespace, err) - } - - retriable, err := c.isDeploymentReady(primary, cd.GetProgressDeadlineSeconds()) - if err != nil { - return retriable, fmt.Errorf("Halt advancement %s.%s %s", primaryName, cd.Namespace, err.Error()) - } - - if primary.Spec.Replicas == int32p(0) { - return true, fmt.Errorf("Halt %s.%s advancement primary deployment is scaled to zero", - cd.Name, cd.Namespace) - } - return true, nil -} - -// IsCanaryReady checks the primary deployment status and returns an error if -// the deployment is in the middle of a rolling update or if the pods are unhealthy -// it will return a non retriable error if the rolling update is stuck -func (c *Deployer) IsCanaryReady(cd *flaggerv1.Canary) (bool, error) { - targetName := cd.Spec.TargetRef.Name - canary, err := c.KubeClient.AppsV1().Deployments(cd.Namespace).Get(targetName, metav1.GetOptions{}) - if err != nil { - if errors.IsNotFound(err) { - return true, fmt.Errorf("deployment %s.%s not found", targetName, cd.Namespace) - } - return true, fmt.Errorf("deployment %s.%s query error %v", targetName, cd.Namespace, err) - } - - retriable, err := c.isDeploymentReady(canary, cd.GetProgressDeadlineSeconds()) - if err != nil { - if retriable { - return retriable, fmt.Errorf("Halt advancement %s.%s %s", targetName, cd.Namespace, err.Error()) - } else { - return retriable, fmt.Errorf("deployment does not have minimum availability for more than %vs", - cd.GetProgressDeadlineSeconds()) - } - } - - return true, nil -} - -// isDeploymentReady determines if a deployment is ready by checking the status conditions -// if a deployment has exceeded the progress deadline it returns a non retriable error -func (c *Deployer) isDeploymentReady(deployment *appsv1.Deployment, deadline int) (bool, error) { - retriable := true - if deployment.Generation <= deployment.Status.ObservedGeneration { - progress := c.getDeploymentCondition(deployment.Status, appsv1.DeploymentProgressing) - if progress != nil { - // Determine if the deployment is stuck by checking if there is a minimum replicas unavailable condition - // and if the last update time exceeds the deadline - available := c.getDeploymentCondition(deployment.Status, appsv1.DeploymentAvailable) - if available != nil && available.Status == "False" && available.Reason == "MinimumReplicasUnavailable" { - from := available.LastUpdateTime - delta := time.Duration(deadline) * time.Second - retriable = !from.Add(delta).Before(time.Now()) - } - } - - if progress != nil && progress.Reason == "ProgressDeadlineExceeded" { - return false, fmt.Errorf("deployment %q exceeded its progress deadline", deployment.GetName()) - } else if deployment.Spec.Replicas != nil && deployment.Status.UpdatedReplicas < *deployment.Spec.Replicas { - return retriable, fmt.Errorf("waiting for rollout to finish: %d out of %d new replicas have been updated", - deployment.Status.UpdatedReplicas, *deployment.Spec.Replicas) - } else if deployment.Status.Replicas > deployment.Status.UpdatedReplicas { - return retriable, fmt.Errorf("waiting for rollout to finish: %d old replicas are pending termination", - deployment.Status.Replicas-deployment.Status.UpdatedReplicas) - } else if deployment.Status.AvailableReplicas < deployment.Status.UpdatedReplicas { - return retriable, fmt.Errorf("waiting for rollout to finish: %d of %d updated replicas are available", - deployment.Status.AvailableReplicas, deployment.Status.UpdatedReplicas) - } - - } else { - return true, fmt.Errorf("waiting for rollout to finish: observed deployment generation less then desired generation") - } - - return true, nil -} - -func (c *Deployer) getDeploymentCondition( - status appsv1.DeploymentStatus, - conditionType appsv1.DeploymentConditionType, -) *appsv1.DeploymentCondition { - for i := range status.Conditions { - c := status.Conditions[i] - if c.Type == conditionType { - return &c - } - } - return nil -} diff --git a/pkg/canary/status.go b/pkg/canary/status.go deleted file mode 100644 index e68d911e..00000000 --- a/pkg/canary/status.go +++ /dev/null @@ -1,245 +0,0 @@ -package canary - -import ( - "fmt" - "k8s.io/client-go/util/retry" - - "github.com/mitchellh/hashstructure" - ex "github.com/pkg/errors" - flaggerv1 "github.com/weaveworks/flagger/pkg/apis/flagger/v1alpha3" - corev1 "k8s.io/api/core/v1" - "k8s.io/apimachinery/pkg/api/errors" - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" -) - -// SyncStatus encodes the canary pod spec and updates the canary status -func (c *Deployer) SyncStatus(cd *flaggerv1.Canary, status flaggerv1.CanaryStatus) error { - dep, err := c.KubeClient.AppsV1().Deployments(cd.Namespace).Get(cd.Spec.TargetRef.Name, metav1.GetOptions{}) - if err != nil { - if errors.IsNotFound(err) { - return fmt.Errorf("deployment %s.%s not found", cd.Spec.TargetRef.Name, cd.Namespace) - } - return ex.Wrap(err, "SyncStatus deployment query error") - } - - configs, err := c.ConfigTracker.GetConfigRefs(cd) - if err != nil { - return ex.Wrap(err, "SyncStatus configs query error") - } - - hash, err := hashstructure.Hash(dep.Spec.Template, nil) - if err != nil { - return ex.Wrap(err, "SyncStatus hash error") - } - - firstTry := true - err = retry.RetryOnConflict(retry.DefaultBackoff, func() (err error) { - var selErr error - if !firstTry { - cd, selErr = c.FlaggerClient.FlaggerV1alpha3().Canaries(cd.Namespace).Get(cd.GetName(), metav1.GetOptions{}) - if selErr != nil { - return selErr - } - } - cdCopy := cd.DeepCopy() - cdCopy.Status.Phase = status.Phase - cdCopy.Status.CanaryWeight = status.CanaryWeight - cdCopy.Status.FailedChecks = status.FailedChecks - cdCopy.Status.Iterations = status.Iterations - cdCopy.Status.LastAppliedSpec = fmt.Sprintf("%d", hash) - cdCopy.Status.LastTransitionTime = metav1.Now() - cdCopy.Status.TrackedConfigs = configs - - if ok, conditions := c.MakeStatusConditions(cd.Status, status.Phase); ok { - cdCopy.Status.Conditions = conditions - } - - _, err = c.FlaggerClient.FlaggerV1alpha3().Canaries(cd.Namespace).UpdateStatus(cdCopy) - firstTry = false - return - }) - if err != nil { - return ex.Wrap(err, "SyncStatus") - } - return nil -} - -// SetStatusFailedChecks updates the canary failed checks counter -func (c *Deployer) SetStatusFailedChecks(cd *flaggerv1.Canary, val int) error { - firstTry := true - err := retry.RetryOnConflict(retry.DefaultBackoff, func() (err error) { - var selErr error - if !firstTry { - cd, selErr = c.FlaggerClient.FlaggerV1alpha3().Canaries(cd.Namespace).Get(cd.GetName(), metav1.GetOptions{}) - if selErr != nil { - return selErr - } - } - cdCopy := cd.DeepCopy() - cdCopy.Status.FailedChecks = val - cdCopy.Status.LastTransitionTime = metav1.Now() - - _, err = c.FlaggerClient.FlaggerV1alpha3().Canaries(cd.Namespace).UpdateStatus(cdCopy) - firstTry = false - return - }) - if err != nil { - return ex.Wrap(err, "SetStatusFailedChecks") - } - return nil -} - -// SetStatusWeight updates the canary status weight value -func (c *Deployer) SetStatusWeight(cd *flaggerv1.Canary, val int) error { - firstTry := true - err := retry.RetryOnConflict(retry.DefaultBackoff, func() (err error) { - var selErr error - if !firstTry { - cd, selErr = c.FlaggerClient.FlaggerV1alpha3().Canaries(cd.Namespace).Get(cd.GetName(), metav1.GetOptions{}) - if selErr != nil { - return selErr - } - } - cdCopy := cd.DeepCopy() - cdCopy.Status.CanaryWeight = val - cdCopy.Status.LastTransitionTime = metav1.Now() - - _, err = c.FlaggerClient.FlaggerV1alpha3().Canaries(cd.Namespace).UpdateStatus(cdCopy) - firstTry = false - return - }) - if err != nil { - return ex.Wrap(err, "SetStatusWeight") - } - return nil -} - -// SetStatusIterations updates the canary status iterations value -func (c *Deployer) SetStatusIterations(cd *flaggerv1.Canary, val int) error { - firstTry := true - err := retry.RetryOnConflict(retry.DefaultBackoff, func() (err error) { - var selErr error - if !firstTry { - cd, selErr = c.FlaggerClient.FlaggerV1alpha3().Canaries(cd.Namespace).Get(cd.GetName(), metav1.GetOptions{}) - if selErr != nil { - return selErr - } - } - - cdCopy := cd.DeepCopy() - cdCopy.Status.Iterations = val - cdCopy.Status.LastTransitionTime = metav1.Now() - - _, err = c.FlaggerClient.FlaggerV1alpha3().Canaries(cd.Namespace).UpdateStatus(cdCopy) - firstTry = false - return - }) - - if err != nil { - return ex.Wrap(err, "SetStatusIterations") - } - return nil -} - -// SetStatusPhase updates the canary status phase -func (c *Deployer) SetStatusPhase(cd *flaggerv1.Canary, phase flaggerv1.CanaryPhase) error { - firstTry := true - err := retry.RetryOnConflict(retry.DefaultBackoff, func() (err error) { - var selErr error - if !firstTry { - cd, selErr = c.FlaggerClient.FlaggerV1alpha3().Canaries(cd.Namespace).Get(cd.GetName(), metav1.GetOptions{}) - if selErr != nil { - return selErr - } - } - cdCopy := cd.DeepCopy() - cdCopy.Status.Phase = phase - cdCopy.Status.LastTransitionTime = metav1.Now() - - if phase != flaggerv1.CanaryPhaseProgressing && phase != flaggerv1.CanaryPhaseWaiting { - cdCopy.Status.CanaryWeight = 0 - cdCopy.Status.Iterations = 0 - } - - // on promotion set primary spec hash - if phase == flaggerv1.CanaryPhaseInitialized || phase == flaggerv1.CanaryPhaseSucceeded { - cdCopy.Status.LastPromotedSpec = cd.Status.LastAppliedSpec - } - - if ok, conditions := c.MakeStatusConditions(cdCopy.Status, phase); ok { - cdCopy.Status.Conditions = conditions - } - - _, err = c.FlaggerClient.FlaggerV1alpha3().Canaries(cd.Namespace).UpdateStatus(cdCopy) - firstTry = false - return - }) - if err != nil { - return ex.Wrap(err, "SetStatusPhase") - } - return nil -} - -// GetStatusCondition returns a condition based on type -func (c *Deployer) getStatusCondition(status flaggerv1.CanaryStatus, conditionType flaggerv1.CanaryConditionType) *flaggerv1.CanaryCondition { - for i := range status.Conditions { - c := status.Conditions[i] - if c.Type == conditionType { - return &c - } - } - return nil -} - -// MakeStatusCondition updates the canary status conditions based on canary phase -func (c *Deployer) MakeStatusConditions(canaryStatus flaggerv1.CanaryStatus, - phase flaggerv1.CanaryPhase) (bool, []flaggerv1.CanaryCondition) { - currentCondition := c.getStatusCondition(canaryStatus, flaggerv1.PromotedType) - - message := "New deployment detected, starting initialization." - status := corev1.ConditionUnknown - switch phase { - case flaggerv1.CanaryPhaseInitializing: - status = corev1.ConditionUnknown - message = "New deployment detected, starting initialization." - case flaggerv1.CanaryPhaseInitialized: - status = corev1.ConditionTrue - message = "Deployment initialization completed." - case flaggerv1.CanaryPhaseWaiting: - status = corev1.ConditionUnknown - message = "Waiting for approval." - case flaggerv1.CanaryPhaseProgressing: - status = corev1.ConditionUnknown - message = "New revision detected, starting canary analysis." - case flaggerv1.CanaryPhaseFinalising: - status = corev1.ConditionUnknown - message = "Canary analysis completed, routing all traffic to primary." - case flaggerv1.CanaryPhaseSucceeded: - status = corev1.ConditionTrue - message = "Canary analysis completed successfully, promotion finished." - case flaggerv1.CanaryPhaseFailed: - status = corev1.ConditionFalse - message = "Canary analysis failed, deployment scaled to zero." - } - - newCondition := &flaggerv1.CanaryCondition{ - Type: flaggerv1.PromotedType, - Status: status, - LastUpdateTime: metav1.Now(), - LastTransitionTime: metav1.Now(), - Message: message, - Reason: string(phase), - } - - if currentCondition != nil && - currentCondition.Status == newCondition.Status && - currentCondition.Reason == newCondition.Reason { - return false, nil - } - - if currentCondition != nil && currentCondition.Status == newCondition.Status { - newCondition.LastTransitionTime = currentCondition.LastTransitionTime - } - - return true, []flaggerv1.CanaryCondition{*newCondition} -} diff --git a/pkg/canary/tracker.go b/pkg/canary/tracker.go deleted file mode 100644 index f5c41327..00000000 --- a/pkg/canary/tracker.go +++ /dev/null @@ -1,374 +0,0 @@ -package canary - -import ( - "crypto/sha256" - "encoding/json" - "fmt" - flaggerv1 "github.com/weaveworks/flagger/pkg/apis/flagger/v1alpha3" - clientset "github.com/weaveworks/flagger/pkg/client/clientset/versioned" - "go.uber.org/zap" - corev1 "k8s.io/api/core/v1" - "k8s.io/apimachinery/pkg/api/errors" - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - "k8s.io/apimachinery/pkg/runtime/schema" - "k8s.io/client-go/kubernetes" -) - -// ConfigTracker is managing the operations for Kubernetes ConfigMaps and Secrets -type ConfigTracker struct { - KubeClient kubernetes.Interface - FlaggerClient clientset.Interface - Logger *zap.SugaredLogger -} - -type ConfigRefType string - -const ( - ConfigRefMap ConfigRefType = "configmap" - ConfigRefSecret ConfigRefType = "secret" -) - -// ConfigRef holds the reference to a tracked Kubernetes ConfigMap or Secret -type ConfigRef struct { - Name string - Type ConfigRefType - Checksum string -} - -// GetName returns the config ref type and name -func (c *ConfigRef) GetName() string { - return fmt.Sprintf("%s/%s", c.Type, c.Name) -} - -func checksum(data interface{}) string { - jsonBytes, _ := json.Marshal(data) - hashBytes := sha256.Sum256(jsonBytes) - - return fmt.Sprintf("%x", hashBytes[:8]) -} - -// getRefFromConfigMap transforms a Kubernetes ConfigMap into a ConfigRef -// and computes the checksum of the ConfigMap data -func (ct *ConfigTracker) getRefFromConfigMap(name string, namespace string) (*ConfigRef, error) { - config, err := ct.KubeClient.CoreV1().ConfigMaps(namespace).Get(name, metav1.GetOptions{}) - if err != nil { - return nil, err - } - - return &ConfigRef{ - Name: config.Name, - Type: ConfigRefMap, - Checksum: checksum(config.Data), - }, nil -} - -// getRefFromConfigMap transforms a Kubernetes Secret into a ConfigRef -// and computes the checksum of the Secret data -func (ct *ConfigTracker) getRefFromSecret(name string, namespace string) (*ConfigRef, error) { - secret, err := ct.KubeClient.CoreV1().Secrets(namespace).Get(name, metav1.GetOptions{}) - if err != nil { - return nil, err - } - - // ignore registry secrets (those should be set via service account) - if secret.Type != corev1.SecretTypeOpaque && - secret.Type != corev1.SecretTypeBasicAuth && - secret.Type != corev1.SecretTypeSSHAuth && - secret.Type != corev1.SecretTypeTLS { - ct.Logger.Debugf("ignoring secret %s.%s type not supported %v", name, namespace, secret.Type) - return nil, nil - } - - return &ConfigRef{ - Name: secret.Name, - Type: ConfigRefSecret, - Checksum: checksum(secret.Data), - }, nil -} - -// GetTargetConfigs scans the target deployment for Kubernetes ConfigMaps and Secretes -// and returns a list of config references -func (ct *ConfigTracker) GetTargetConfigs(cd *flaggerv1.Canary) (map[string]ConfigRef, error) { - res := make(map[string]ConfigRef) - targetName := cd.Spec.TargetRef.Name - targetDep, err := ct.KubeClient.AppsV1().Deployments(cd.Namespace).Get(targetName, metav1.GetOptions{}) - if err != nil { - if errors.IsNotFound(err) { - return res, fmt.Errorf("deployment %s.%s not found", targetName, cd.Namespace) - } - return res, fmt.Errorf("deployment %s.%s query error %v", targetName, cd.Namespace, err) - } - - // scan volumes - for _, volume := range targetDep.Spec.Template.Spec.Volumes { - if cmv := volume.ConfigMap; cmv != nil { - config, err := ct.getRefFromConfigMap(cmv.Name, cd.Namespace) - if err != nil { - ct.Logger.Errorf("configMap %s.%s query error %v", cmv.Name, cd.Namespace, err) - continue - } - if config != nil { - res[config.GetName()] = *config - } - } - - if sv := volume.Secret; sv != nil { - secret, err := ct.getRefFromSecret(sv.SecretName, cd.Namespace) - if err != nil { - ct.Logger.Errorf("secret %s.%s query error %v", sv.SecretName, cd.Namespace, err) - continue - } - if secret != nil { - res[secret.GetName()] = *secret - } - } - } - // scan containers - for _, container := range targetDep.Spec.Template.Spec.Containers { - // scan env - for _, env := range container.Env { - if env.ValueFrom != nil { - switch { - case env.ValueFrom.ConfigMapKeyRef != nil: - name := env.ValueFrom.ConfigMapKeyRef.LocalObjectReference.Name - config, err := ct.getRefFromConfigMap(name, cd.Namespace) - if err != nil { - ct.Logger.Errorf("configMap %s.%s query error %v", name, cd.Namespace, err) - continue - } - if config != nil { - res[config.GetName()] = *config - } - case env.ValueFrom.SecretKeyRef != nil: - name := env.ValueFrom.SecretKeyRef.LocalObjectReference.Name - secret, err := ct.getRefFromSecret(name, cd.Namespace) - if err != nil { - ct.Logger.Errorf("secret %s.%s query error %v", name, cd.Namespace, err) - continue - } - if secret != nil { - res[secret.GetName()] = *secret - } - } - } - } - // scan envFrom - for _, envFrom := range container.EnvFrom { - switch { - case envFrom.ConfigMapRef != nil: - name := envFrom.ConfigMapRef.LocalObjectReference.Name - config, err := ct.getRefFromConfigMap(name, cd.Namespace) - if err != nil { - ct.Logger.Errorf("configMap %s.%s query error %v", name, cd.Namespace, err) - continue - } - if config != nil { - res[config.GetName()] = *config - } - case envFrom.SecretRef != nil: - name := envFrom.SecretRef.LocalObjectReference.Name - secret, err := ct.getRefFromSecret(name, cd.Namespace) - if err != nil { - ct.Logger.Errorf("secret %s.%s query error %v", name, cd.Namespace, err) - continue - } - if secret != nil { - res[secret.GetName()] = *secret - } - } - } - } - - return res, nil -} - -// GetConfigRefs returns a map of configs and their checksum -func (ct *ConfigTracker) GetConfigRefs(cd *flaggerv1.Canary) (*map[string]string, error) { - res := make(map[string]string) - configs, err := ct.GetTargetConfigs(cd) - if err != nil { - return nil, err - } - - for _, cfg := range configs { - res[cfg.GetName()] = cfg.Checksum - } - - return &res, nil -} - -// HasConfigChanged checks for changes in ConfigMaps and Secretes by comparing -// the checksum for each ConfigRef stored in Canary.Status.TrackedConfigs -func (ct *ConfigTracker) HasConfigChanged(cd *flaggerv1.Canary) (bool, error) { - configs, err := ct.GetTargetConfigs(cd) - if err != nil { - return false, err - } - - if len(configs) == 0 && cd.Status.TrackedConfigs == nil { - return false, nil - } - - if len(configs) > 0 && cd.Status.TrackedConfigs == nil { - return true, nil - } - - trackedConfigs := *cd.Status.TrackedConfigs - - if len(configs) != len(trackedConfigs) { - return true, nil - } - - for _, cfg := range configs { - if trackedConfigs[cfg.GetName()] != cfg.Checksum { - ct.Logger.With("canary", fmt.Sprintf("%s.%s", cd.Name, cd.Namespace)). - Infof("%s %s has changed", cfg.Type, cfg.Name) - return true, nil - } - } - - return false, nil -} - -// CreatePrimaryConfigs syncs the primary Kubernetes ConfigMaps and Secretes -// with those found in the target deployment -func (ct *ConfigTracker) CreatePrimaryConfigs(cd *flaggerv1.Canary, refs map[string]ConfigRef) error { - for _, ref := range refs { - switch ref.Type { - case ConfigRefMap: - config, err := ct.KubeClient.CoreV1().ConfigMaps(cd.Namespace).Get(ref.Name, metav1.GetOptions{}) - if err != nil { - return err - } - primaryName := fmt.Sprintf("%s-primary", config.GetName()) - primaryConfigMap := &corev1.ConfigMap{ - ObjectMeta: metav1.ObjectMeta{ - Name: primaryName, - Namespace: cd.Namespace, - Labels: config.Labels, - OwnerReferences: []metav1.OwnerReference{ - *metav1.NewControllerRef(cd, schema.GroupVersionKind{ - Group: flaggerv1.SchemeGroupVersion.Group, - Version: flaggerv1.SchemeGroupVersion.Version, - Kind: flaggerv1.CanaryKind, - }), - }, - }, - Data: config.Data, - } - - // update or insert primary ConfigMap - _, err = ct.KubeClient.CoreV1().ConfigMaps(cd.Namespace).Update(primaryConfigMap) - if err != nil { - if errors.IsNotFound(err) { - _, err = ct.KubeClient.CoreV1().ConfigMaps(cd.Namespace).Create(primaryConfigMap) - if err != nil { - return err - } - } else { - return err - } - } - - ct.Logger.With("canary", fmt.Sprintf("%s.%s", cd.Name, cd.Namespace)). - Infof("ConfigMap %s synced", primaryConfigMap.GetName()) - case ConfigRefSecret: - secret, err := ct.KubeClient.CoreV1().Secrets(cd.Namespace).Get(ref.Name, metav1.GetOptions{}) - if err != nil { - return err - } - primaryName := fmt.Sprintf("%s-primary", secret.GetName()) - primarySecret := &corev1.Secret{ - ObjectMeta: metav1.ObjectMeta{ - Name: primaryName, - Namespace: cd.Namespace, - Labels: secret.Labels, - OwnerReferences: []metav1.OwnerReference{ - *metav1.NewControllerRef(cd, schema.GroupVersionKind{ - Group: flaggerv1.SchemeGroupVersion.Group, - Version: flaggerv1.SchemeGroupVersion.Version, - Kind: flaggerv1.CanaryKind, - }), - }, - }, - Type: secret.Type, - Data: secret.Data, - } - - // update or insert primary Secret - _, err = ct.KubeClient.CoreV1().Secrets(cd.Namespace).Update(primarySecret) - if err != nil { - if errors.IsNotFound(err) { - _, err = ct.KubeClient.CoreV1().Secrets(cd.Namespace).Create(primarySecret) - if err != nil { - return err - } - } else { - return err - } - } - - ct.Logger.With("canary", fmt.Sprintf("%s.%s", cd.Name, cd.Namespace)). - Infof("Secret %s synced", primarySecret.GetName()) - } - } - - return nil -} - -// ApplyPrimaryConfigs appends the primary suffix to all ConfigMaps and Secretes found in the PodSpec -func (ct *ConfigTracker) ApplyPrimaryConfigs(spec corev1.PodSpec, refs map[string]ConfigRef) corev1.PodSpec { - // update volumes - for i, volume := range spec.Volumes { - if cmv := volume.ConfigMap; cmv != nil { - name := fmt.Sprintf("%s/%s", ConfigRefMap, cmv.Name) - if _, exists := refs[name]; exists { - spec.Volumes[i].ConfigMap.Name += "-primary" - } - } - - if sv := volume.Secret; sv != nil { - name := fmt.Sprintf("%s/%s", ConfigRefSecret, sv.SecretName) - if _, exists := refs[name]; exists { - spec.Volumes[i].Secret.SecretName += "-primary" - } - } - } - // update containers - for _, container := range spec.Containers { - // update env - for i, env := range container.Env { - if env.ValueFrom != nil { - switch { - case env.ValueFrom.ConfigMapKeyRef != nil: - name := fmt.Sprintf("%s/%s", ConfigRefMap, env.ValueFrom.ConfigMapKeyRef.Name) - if _, exists := refs[name]; exists { - container.Env[i].ValueFrom.ConfigMapKeyRef.Name += "-primary" - } - case env.ValueFrom.SecretKeyRef != nil: - name := fmt.Sprintf("%s/%s", ConfigRefSecret, env.ValueFrom.SecretKeyRef.Name) - if _, exists := refs[name]; exists { - container.Env[i].ValueFrom.SecretKeyRef.Name += "-primary" - } - } - } - } - // update envFrom - for i, envFrom := range container.EnvFrom { - switch { - case envFrom.ConfigMapRef != nil: - name := fmt.Sprintf("%s/%s", ConfigRefMap, envFrom.ConfigMapRef.Name) - if _, exists := refs[name]; exists { - container.EnvFrom[i].ConfigMapRef.Name += "-primary" - } - case envFrom.SecretRef != nil: - name := fmt.Sprintf("%s/%s", ConfigRefSecret, envFrom.SecretRef.Name) - if _, exists := refs[name]; exists { - container.EnvFrom[i].SecretRef.Name += "-primary" - } - } - } - } - - return spec -} diff --git a/pkg/client/clientset/versioned/clientset.go b/pkg/client/clientset/versioned/clientset.go deleted file mode 100644 index 65e60a62..00000000 --- a/pkg/client/clientset/versioned/clientset.go +++ /dev/null @@ -1,132 +0,0 @@ -/* -Copyright The Flagger Authors. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -// Code generated by client-gen. DO NOT EDIT. - -package versioned - -import ( - appmeshv1beta1 "github.com/weaveworks/flagger/pkg/client/clientset/versioned/typed/appmesh/v1beta1" - flaggerv1alpha3 "github.com/weaveworks/flagger/pkg/client/clientset/versioned/typed/flagger/v1alpha3" - networkingv1alpha3 "github.com/weaveworks/flagger/pkg/client/clientset/versioned/typed/istio/v1alpha3" - splitv1alpha1 "github.com/weaveworks/flagger/pkg/client/clientset/versioned/typed/smi/v1alpha1" - discovery "k8s.io/client-go/discovery" - rest "k8s.io/client-go/rest" - flowcontrol "k8s.io/client-go/util/flowcontrol" -) - -type Interface interface { - Discovery() discovery.DiscoveryInterface - AppmeshV1beta1() appmeshv1beta1.AppmeshV1beta1Interface - FlaggerV1alpha3() flaggerv1alpha3.FlaggerV1alpha3Interface - NetworkingV1alpha3() networkingv1alpha3.NetworkingV1alpha3Interface - SplitV1alpha1() splitv1alpha1.SplitV1alpha1Interface -} - -// Clientset contains the clients for groups. Each group has exactly one -// version included in a Clientset. -type Clientset struct { - *discovery.DiscoveryClient - appmeshV1beta1 *appmeshv1beta1.AppmeshV1beta1Client - flaggerV1alpha3 *flaggerv1alpha3.FlaggerV1alpha3Client - networkingV1alpha3 *networkingv1alpha3.NetworkingV1alpha3Client - splitV1alpha1 *splitv1alpha1.SplitV1alpha1Client -} - -// AppmeshV1beta1 retrieves the AppmeshV1beta1Client -func (c *Clientset) AppmeshV1beta1() appmeshv1beta1.AppmeshV1beta1Interface { - return c.appmeshV1beta1 -} - -// FlaggerV1alpha3 retrieves the FlaggerV1alpha3Client -func (c *Clientset) FlaggerV1alpha3() flaggerv1alpha3.FlaggerV1alpha3Interface { - return c.flaggerV1alpha3 -} - -// NetworkingV1alpha3 retrieves the NetworkingV1alpha3Client -func (c *Clientset) NetworkingV1alpha3() networkingv1alpha3.NetworkingV1alpha3Interface { - return c.networkingV1alpha3 -} - -// SplitV1alpha1 retrieves the SplitV1alpha1Client -func (c *Clientset) SplitV1alpha1() splitv1alpha1.SplitV1alpha1Interface { - return c.splitV1alpha1 -} - -// Discovery retrieves the DiscoveryClient -func (c *Clientset) Discovery() discovery.DiscoveryInterface { - if c == nil { - return nil - } - return c.DiscoveryClient -} - -// NewForConfig creates a new Clientset for the given config. -func NewForConfig(c *rest.Config) (*Clientset, error) { - configShallowCopy := *c - if configShallowCopy.RateLimiter == nil && configShallowCopy.QPS > 0 { - configShallowCopy.RateLimiter = flowcontrol.NewTokenBucketRateLimiter(configShallowCopy.QPS, configShallowCopy.Burst) - } - var cs Clientset - var err error - cs.appmeshV1beta1, err = appmeshv1beta1.NewForConfig(&configShallowCopy) - if err != nil { - return nil, err - } - cs.flaggerV1alpha3, err = flaggerv1alpha3.NewForConfig(&configShallowCopy) - if err != nil { - return nil, err - } - cs.networkingV1alpha3, err = networkingv1alpha3.NewForConfig(&configShallowCopy) - if err != nil { - return nil, err - } - cs.splitV1alpha1, err = splitv1alpha1.NewForConfig(&configShallowCopy) - if err != nil { - return nil, err - } - - cs.DiscoveryClient, err = discovery.NewDiscoveryClientForConfig(&configShallowCopy) - if err != nil { - return nil, err - } - return &cs, nil -} - -// NewForConfigOrDie creates a new Clientset for the given config and -// panics if there is an error in the config. -func NewForConfigOrDie(c *rest.Config) *Clientset { - var cs Clientset - cs.appmeshV1beta1 = appmeshv1beta1.NewForConfigOrDie(c) - cs.flaggerV1alpha3 = flaggerv1alpha3.NewForConfigOrDie(c) - cs.networkingV1alpha3 = networkingv1alpha3.NewForConfigOrDie(c) - cs.splitV1alpha1 = splitv1alpha1.NewForConfigOrDie(c) - - cs.DiscoveryClient = discovery.NewDiscoveryClientForConfigOrDie(c) - return &cs -} - -// New creates a new Clientset for the given RESTClient. -func New(c rest.Interface) *Clientset { - var cs Clientset - cs.appmeshV1beta1 = appmeshv1beta1.New(c) - cs.flaggerV1alpha3 = flaggerv1alpha3.New(c) - cs.networkingV1alpha3 = networkingv1alpha3.New(c) - cs.splitV1alpha1 = splitv1alpha1.New(c) - - cs.DiscoveryClient = discovery.NewDiscoveryClient(c) - return &cs -} diff --git a/pkg/client/clientset/versioned/doc.go b/pkg/client/clientset/versioned/doc.go deleted file mode 100644 index a095dc95..00000000 --- a/pkg/client/clientset/versioned/doc.go +++ /dev/null @@ -1,20 +0,0 @@ -/* -Copyright The Flagger Authors. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -// Code generated by client-gen. DO NOT EDIT. - -// This package has the automatically generated clientset. -package versioned diff --git a/pkg/client/clientset/versioned/fake/clientset_generated.go b/pkg/client/clientset/versioned/fake/clientset_generated.go deleted file mode 100644 index 5419a6b1..00000000 --- a/pkg/client/clientset/versioned/fake/clientset_generated.go +++ /dev/null @@ -1,103 +0,0 @@ -/* -Copyright The Flagger Authors. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -// Code generated by client-gen. DO NOT EDIT. - -package fake - -import ( - clientset "github.com/weaveworks/flagger/pkg/client/clientset/versioned" - appmeshv1beta1 "github.com/weaveworks/flagger/pkg/client/clientset/versioned/typed/appmesh/v1beta1" - fakeappmeshv1beta1 "github.com/weaveworks/flagger/pkg/client/clientset/versioned/typed/appmesh/v1beta1/fake" - flaggerv1alpha3 "github.com/weaveworks/flagger/pkg/client/clientset/versioned/typed/flagger/v1alpha3" - fakeflaggerv1alpha3 "github.com/weaveworks/flagger/pkg/client/clientset/versioned/typed/flagger/v1alpha3/fake" - networkingv1alpha3 "github.com/weaveworks/flagger/pkg/client/clientset/versioned/typed/istio/v1alpha3" - fakenetworkingv1alpha3 "github.com/weaveworks/flagger/pkg/client/clientset/versioned/typed/istio/v1alpha3/fake" - splitv1alpha1 "github.com/weaveworks/flagger/pkg/client/clientset/versioned/typed/smi/v1alpha1" - fakesplitv1alpha1 "github.com/weaveworks/flagger/pkg/client/clientset/versioned/typed/smi/v1alpha1/fake" - "k8s.io/apimachinery/pkg/runtime" - "k8s.io/apimachinery/pkg/watch" - "k8s.io/client-go/discovery" - fakediscovery "k8s.io/client-go/discovery/fake" - "k8s.io/client-go/testing" -) - -// NewSimpleClientset returns a clientset that will respond with the provided objects. -// It's backed by a very simple object tracker that processes creates, updates and deletions as-is, -// without applying any validations and/or defaults. It shouldn't be considered a replacement -// for a real clientset and is mostly useful in simple unit tests. -func NewSimpleClientset(objects ...runtime.Object) *Clientset { - o := testing.NewObjectTracker(scheme, codecs.UniversalDecoder()) - for _, obj := range objects { - if err := o.Add(obj); err != nil { - panic(err) - } - } - - cs := &Clientset{tracker: o} - cs.discovery = &fakediscovery.FakeDiscovery{Fake: &cs.Fake} - cs.AddReactor("*", "*", testing.ObjectReaction(o)) - cs.AddWatchReactor("*", func(action testing.Action) (handled bool, ret watch.Interface, err error) { - gvr := action.GetResource() - ns := action.GetNamespace() - watch, err := o.Watch(gvr, ns) - if err != nil { - return false, nil, err - } - return true, watch, nil - }) - - return cs -} - -// Clientset implements clientset.Interface. Meant to be embedded into a -// struct to get a default implementation. This makes faking out just the method -// you want to test easier. -type Clientset struct { - testing.Fake - discovery *fakediscovery.FakeDiscovery - tracker testing.ObjectTracker -} - -func (c *Clientset) Discovery() discovery.DiscoveryInterface { - return c.discovery -} - -func (c *Clientset) Tracker() testing.ObjectTracker { - return c.tracker -} - -var _ clientset.Interface = &Clientset{} - -// AppmeshV1beta1 retrieves the AppmeshV1beta1Client -func (c *Clientset) AppmeshV1beta1() appmeshv1beta1.AppmeshV1beta1Interface { - return &fakeappmeshv1beta1.FakeAppmeshV1beta1{Fake: &c.Fake} -} - -// FlaggerV1alpha3 retrieves the FlaggerV1alpha3Client -func (c *Clientset) FlaggerV1alpha3() flaggerv1alpha3.FlaggerV1alpha3Interface { - return &fakeflaggerv1alpha3.FakeFlaggerV1alpha3{Fake: &c.Fake} -} - -// NetworkingV1alpha3 retrieves the NetworkingV1alpha3Client -func (c *Clientset) NetworkingV1alpha3() networkingv1alpha3.NetworkingV1alpha3Interface { - return &fakenetworkingv1alpha3.FakeNetworkingV1alpha3{Fake: &c.Fake} -} - -// SplitV1alpha1 retrieves the SplitV1alpha1Client -func (c *Clientset) SplitV1alpha1() splitv1alpha1.SplitV1alpha1Interface { - return &fakesplitv1alpha1.FakeSplitV1alpha1{Fake: &c.Fake} -} diff --git a/pkg/client/clientset/versioned/fake/doc.go b/pkg/client/clientset/versioned/fake/doc.go deleted file mode 100644 index 90e25643..00000000 --- a/pkg/client/clientset/versioned/fake/doc.go +++ /dev/null @@ -1,20 +0,0 @@ -/* -Copyright The Flagger Authors. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -// Code generated by client-gen. DO NOT EDIT. - -// This package has the automatically generated fake clientset. -package fake diff --git a/pkg/client/clientset/versioned/fake/register.go b/pkg/client/clientset/versioned/fake/register.go deleted file mode 100644 index b5910b2e..00000000 --- a/pkg/client/clientset/versioned/fake/register.go +++ /dev/null @@ -1,62 +0,0 @@ -/* -Copyright The Flagger Authors. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -// Code generated by client-gen. DO NOT EDIT. - -package fake - -import ( - appmeshv1beta1 "github.com/weaveworks/flagger/pkg/apis/appmesh/v1beta1" - flaggerv1alpha3 "github.com/weaveworks/flagger/pkg/apis/flagger/v1alpha3" - networkingv1alpha3 "github.com/weaveworks/flagger/pkg/apis/istio/v1alpha3" - splitv1alpha1 "github.com/weaveworks/flagger/pkg/apis/smi/v1alpha1" - v1 "k8s.io/apimachinery/pkg/apis/meta/v1" - runtime "k8s.io/apimachinery/pkg/runtime" - schema "k8s.io/apimachinery/pkg/runtime/schema" - serializer "k8s.io/apimachinery/pkg/runtime/serializer" - utilruntime "k8s.io/apimachinery/pkg/util/runtime" -) - -var scheme = runtime.NewScheme() -var codecs = serializer.NewCodecFactory(scheme) -var parameterCodec = runtime.NewParameterCodec(scheme) -var localSchemeBuilder = runtime.SchemeBuilder{ - appmeshv1beta1.AddToScheme, - flaggerv1alpha3.AddToScheme, - networkingv1alpha3.AddToScheme, - splitv1alpha1.AddToScheme, -} - -// AddToScheme adds all types of this clientset into the given scheme. This allows composition -// of clientsets, like in: -// -// import ( -// "k8s.io/client-go/kubernetes" -// clientsetscheme "k8s.io/client-go/kubernetes/scheme" -// aggregatorclientsetscheme "k8s.io/kube-aggregator/pkg/client/clientset_generated/clientset/scheme" -// ) -// -// kclientset, _ := kubernetes.NewForConfig(c) -// _ = aggregatorclientsetscheme.AddToScheme(clientsetscheme.Scheme) -// -// After this, RawExtensions in Kubernetes types will serialize kube-aggregator types -// correctly. -var AddToScheme = localSchemeBuilder.AddToScheme - -func init() { - v1.AddToGroupVersion(scheme, schema.GroupVersion{Version: "v1"}) - utilruntime.Must(AddToScheme(scheme)) -} diff --git a/pkg/client/clientset/versioned/scheme/doc.go b/pkg/client/clientset/versioned/scheme/doc.go deleted file mode 100644 index 52ad2210..00000000 --- a/pkg/client/clientset/versioned/scheme/doc.go +++ /dev/null @@ -1,20 +0,0 @@ -/* -Copyright The Flagger Authors. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -// Code generated by client-gen. DO NOT EDIT. - -// This package contains the scheme of the automatically generated clientset. -package scheme diff --git a/pkg/client/clientset/versioned/scheme/register.go b/pkg/client/clientset/versioned/scheme/register.go deleted file mode 100644 index 7337f87a..00000000 --- a/pkg/client/clientset/versioned/scheme/register.go +++ /dev/null @@ -1,62 +0,0 @@ -/* -Copyright The Flagger Authors. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -// Code generated by client-gen. DO NOT EDIT. - -package scheme - -import ( - appmeshv1beta1 "github.com/weaveworks/flagger/pkg/apis/appmesh/v1beta1" - flaggerv1alpha3 "github.com/weaveworks/flagger/pkg/apis/flagger/v1alpha3" - networkingv1alpha3 "github.com/weaveworks/flagger/pkg/apis/istio/v1alpha3" - splitv1alpha1 "github.com/weaveworks/flagger/pkg/apis/smi/v1alpha1" - v1 "k8s.io/apimachinery/pkg/apis/meta/v1" - runtime "k8s.io/apimachinery/pkg/runtime" - schema "k8s.io/apimachinery/pkg/runtime/schema" - serializer "k8s.io/apimachinery/pkg/runtime/serializer" - utilruntime "k8s.io/apimachinery/pkg/util/runtime" -) - -var Scheme = runtime.NewScheme() -var Codecs = serializer.NewCodecFactory(Scheme) -var ParameterCodec = runtime.NewParameterCodec(Scheme) -var localSchemeBuilder = runtime.SchemeBuilder{ - appmeshv1beta1.AddToScheme, - flaggerv1alpha3.AddToScheme, - networkingv1alpha3.AddToScheme, - splitv1alpha1.AddToScheme, -} - -// AddToScheme adds all types of this clientset into the given scheme. This allows composition -// of clientsets, like in: -// -// import ( -// "k8s.io/client-go/kubernetes" -// clientsetscheme "k8s.io/client-go/kubernetes/scheme" -// aggregatorclientsetscheme "k8s.io/kube-aggregator/pkg/client/clientset_generated/clientset/scheme" -// ) -// -// kclientset, _ := kubernetes.NewForConfig(c) -// _ = aggregatorclientsetscheme.AddToScheme(clientsetscheme.Scheme) -// -// After this, RawExtensions in Kubernetes types will serialize kube-aggregator types -// correctly. -var AddToScheme = localSchemeBuilder.AddToScheme - -func init() { - v1.AddToGroupVersion(Scheme, schema.GroupVersion{Version: "v1"}) - utilruntime.Must(AddToScheme(Scheme)) -} diff --git a/pkg/client/clientset/versioned/typed/appmesh/v1beta1/appmesh_client.go b/pkg/client/clientset/versioned/typed/appmesh/v1beta1/appmesh_client.go deleted file mode 100644 index 6a891c57..00000000 --- a/pkg/client/clientset/versioned/typed/appmesh/v1beta1/appmesh_client.go +++ /dev/null @@ -1,99 +0,0 @@ -/* -Copyright The Flagger Authors. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -// Code generated by client-gen. DO NOT EDIT. - -package v1beta1 - -import ( - v1beta1 "github.com/weaveworks/flagger/pkg/apis/appmesh/v1beta1" - "github.com/weaveworks/flagger/pkg/client/clientset/versioned/scheme" - rest "k8s.io/client-go/rest" -) - -type AppmeshV1beta1Interface interface { - RESTClient() rest.Interface - MeshesGetter - VirtualNodesGetter - VirtualServicesGetter -} - -// AppmeshV1beta1Client is used to interact with features provided by the appmesh.k8s.aws group. -type AppmeshV1beta1Client struct { - restClient rest.Interface -} - -func (c *AppmeshV1beta1Client) Meshes() MeshInterface { - return newMeshes(c) -} - -func (c *AppmeshV1beta1Client) VirtualNodes(namespace string) VirtualNodeInterface { - return newVirtualNodes(c, namespace) -} - -func (c *AppmeshV1beta1Client) VirtualServices(namespace string) VirtualServiceInterface { - return newVirtualServices(c, namespace) -} - -// NewForConfig creates a new AppmeshV1beta1Client for the given config. -func NewForConfig(c *rest.Config) (*AppmeshV1beta1Client, error) { - config := *c - if err := setConfigDefaults(&config); err != nil { - return nil, err - } - client, err := rest.RESTClientFor(&config) - if err != nil { - return nil, err - } - return &AppmeshV1beta1Client{client}, nil -} - -// NewForConfigOrDie creates a new AppmeshV1beta1Client for the given config and -// panics if there is an error in the config. -func NewForConfigOrDie(c *rest.Config) *AppmeshV1beta1Client { - client, err := NewForConfig(c) - if err != nil { - panic(err) - } - return client -} - -// New creates a new AppmeshV1beta1Client for the given RESTClient. -func New(c rest.Interface) *AppmeshV1beta1Client { - return &AppmeshV1beta1Client{c} -} - -func setConfigDefaults(config *rest.Config) error { - gv := v1beta1.SchemeGroupVersion - config.GroupVersion = &gv - config.APIPath = "/apis" - config.NegotiatedSerializer = scheme.Codecs.WithoutConversion() - - if config.UserAgent == "" { - config.UserAgent = rest.DefaultKubernetesUserAgent() - } - - return nil -} - -// RESTClient returns a RESTClient that is used to communicate -// with API server by this client implementation. -func (c *AppmeshV1beta1Client) RESTClient() rest.Interface { - if c == nil { - return nil - } - return c.restClient -} diff --git a/pkg/client/clientset/versioned/typed/appmesh/v1beta1/doc.go b/pkg/client/clientset/versioned/typed/appmesh/v1beta1/doc.go deleted file mode 100644 index 4796e182..00000000 --- a/pkg/client/clientset/versioned/typed/appmesh/v1beta1/doc.go +++ /dev/null @@ -1,20 +0,0 @@ -/* -Copyright The Flagger Authors. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -// Code generated by client-gen. DO NOT EDIT. - -// This package has the automatically generated typed clients. -package v1beta1 diff --git a/pkg/client/clientset/versioned/typed/appmesh/v1beta1/fake/doc.go b/pkg/client/clientset/versioned/typed/appmesh/v1beta1/fake/doc.go deleted file mode 100644 index 7a3b19cb..00000000 --- a/pkg/client/clientset/versioned/typed/appmesh/v1beta1/fake/doc.go +++ /dev/null @@ -1,20 +0,0 @@ -/* -Copyright The Flagger Authors. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -// Code generated by client-gen. DO NOT EDIT. - -// Package fake has the automatically generated clients. -package fake diff --git a/pkg/client/clientset/versioned/typed/appmesh/v1beta1/fake/fake_appmesh_client.go b/pkg/client/clientset/versioned/typed/appmesh/v1beta1/fake/fake_appmesh_client.go deleted file mode 100644 index 8e53acda..00000000 --- a/pkg/client/clientset/versioned/typed/appmesh/v1beta1/fake/fake_appmesh_client.go +++ /dev/null @@ -1,48 +0,0 @@ -/* -Copyright The Flagger Authors. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -// Code generated by client-gen. DO NOT EDIT. - -package fake - -import ( - v1beta1 "github.com/weaveworks/flagger/pkg/client/clientset/versioned/typed/appmesh/v1beta1" - rest "k8s.io/client-go/rest" - testing "k8s.io/client-go/testing" -) - -type FakeAppmeshV1beta1 struct { - *testing.Fake -} - -func (c *FakeAppmeshV1beta1) Meshes() v1beta1.MeshInterface { - return &FakeMeshes{c} -} - -func (c *FakeAppmeshV1beta1) VirtualNodes(namespace string) v1beta1.VirtualNodeInterface { - return &FakeVirtualNodes{c, namespace} -} - -func (c *FakeAppmeshV1beta1) VirtualServices(namespace string) v1beta1.VirtualServiceInterface { - return &FakeVirtualServices{c, namespace} -} - -// RESTClient returns a RESTClient that is used to communicate -// with API server by this client implementation. -func (c *FakeAppmeshV1beta1) RESTClient() rest.Interface { - var ret *rest.RESTClient - return ret -} diff --git a/pkg/client/clientset/versioned/typed/appmesh/v1beta1/fake/fake_mesh.go b/pkg/client/clientset/versioned/typed/appmesh/v1beta1/fake/fake_mesh.go deleted file mode 100644 index d35a1a54..00000000 --- a/pkg/client/clientset/versioned/typed/appmesh/v1beta1/fake/fake_mesh.go +++ /dev/null @@ -1,131 +0,0 @@ -/* -Copyright The Flagger Authors. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -// Code generated by client-gen. DO NOT EDIT. - -package fake - -import ( - v1beta1 "github.com/weaveworks/flagger/pkg/apis/appmesh/v1beta1" - v1 "k8s.io/apimachinery/pkg/apis/meta/v1" - labels "k8s.io/apimachinery/pkg/labels" - schema "k8s.io/apimachinery/pkg/runtime/schema" - types "k8s.io/apimachinery/pkg/types" - watch "k8s.io/apimachinery/pkg/watch" - testing "k8s.io/client-go/testing" -) - -// FakeMeshes implements MeshInterface -type FakeMeshes struct { - Fake *FakeAppmeshV1beta1 -} - -var meshesResource = schema.GroupVersionResource{Group: "appmesh.k8s.aws", Version: "v1beta1", Resource: "meshes"} - -var meshesKind = schema.GroupVersionKind{Group: "appmesh.k8s.aws", Version: "v1beta1", Kind: "Mesh"} - -// Get takes name of the mesh, and returns the corresponding mesh object, and an error if there is any. -func (c *FakeMeshes) Get(name string, options v1.GetOptions) (result *v1beta1.Mesh, err error) { - obj, err := c.Fake. - Invokes(testing.NewRootGetAction(meshesResource, name), &v1beta1.Mesh{}) - if obj == nil { - return nil, err - } - return obj.(*v1beta1.Mesh), err -} - -// List takes label and field selectors, and returns the list of Meshes that match those selectors. -func (c *FakeMeshes) List(opts v1.ListOptions) (result *v1beta1.MeshList, err error) { - obj, err := c.Fake. - Invokes(testing.NewRootListAction(meshesResource, meshesKind, opts), &v1beta1.MeshList{}) - if obj == nil { - return nil, err - } - - label, _, _ := testing.ExtractFromListOptions(opts) - if label == nil { - label = labels.Everything() - } - list := &v1beta1.MeshList{ListMeta: obj.(*v1beta1.MeshList).ListMeta} - for _, item := range obj.(*v1beta1.MeshList).Items { - if label.Matches(labels.Set(item.Labels)) { - list.Items = append(list.Items, item) - } - } - return list, err -} - -// Watch returns a watch.Interface that watches the requested meshes. -func (c *FakeMeshes) Watch(opts v1.ListOptions) (watch.Interface, error) { - return c.Fake. - InvokesWatch(testing.NewRootWatchAction(meshesResource, opts)) -} - -// Create takes the representation of a mesh and creates it. Returns the server's representation of the mesh, and an error, if there is any. -func (c *FakeMeshes) Create(mesh *v1beta1.Mesh) (result *v1beta1.Mesh, err error) { - obj, err := c.Fake. - Invokes(testing.NewRootCreateAction(meshesResource, mesh), &v1beta1.Mesh{}) - if obj == nil { - return nil, err - } - return obj.(*v1beta1.Mesh), err -} - -// Update takes the representation of a mesh and updates it. Returns the server's representation of the mesh, and an error, if there is any. -func (c *FakeMeshes) Update(mesh *v1beta1.Mesh) (result *v1beta1.Mesh, err error) { - obj, err := c.Fake. - Invokes(testing.NewRootUpdateAction(meshesResource, mesh), &v1beta1.Mesh{}) - if obj == nil { - return nil, err - } - return obj.(*v1beta1.Mesh), err -} - -// UpdateStatus was generated because the type contains a Status member. -// Add a +genclient:noStatus comment above the type to avoid generating UpdateStatus(). -func (c *FakeMeshes) UpdateStatus(mesh *v1beta1.Mesh) (*v1beta1.Mesh, error) { - obj, err := c.Fake. - Invokes(testing.NewRootUpdateSubresourceAction(meshesResource, "status", mesh), &v1beta1.Mesh{}) - if obj == nil { - return nil, err - } - return obj.(*v1beta1.Mesh), err -} - -// Delete takes name of the mesh and deletes it. Returns an error if one occurs. -func (c *FakeMeshes) Delete(name string, options *v1.DeleteOptions) error { - _, err := c.Fake. - Invokes(testing.NewRootDeleteAction(meshesResource, name), &v1beta1.Mesh{}) - return err -} - -// DeleteCollection deletes a collection of objects. -func (c *FakeMeshes) DeleteCollection(options *v1.DeleteOptions, listOptions v1.ListOptions) error { - action := testing.NewRootDeleteCollectionAction(meshesResource, listOptions) - - _, err := c.Fake.Invokes(action, &v1beta1.MeshList{}) - return err -} - -// Patch applies the patch and returns the patched mesh. -func (c *FakeMeshes) Patch(name string, pt types.PatchType, data []byte, subresources ...string) (result *v1beta1.Mesh, err error) { - obj, err := c.Fake. - Invokes(testing.NewRootPatchSubresourceAction(meshesResource, name, pt, data, subresources...), &v1beta1.Mesh{}) - if obj == nil { - return nil, err - } - return obj.(*v1beta1.Mesh), err -} diff --git a/pkg/client/clientset/versioned/typed/appmesh/v1beta1/fake/fake_virtualnode.go b/pkg/client/clientset/versioned/typed/appmesh/v1beta1/fake/fake_virtualnode.go deleted file mode 100644 index a253cdc8..00000000 --- a/pkg/client/clientset/versioned/typed/appmesh/v1beta1/fake/fake_virtualnode.go +++ /dev/null @@ -1,140 +0,0 @@ -/* -Copyright The Flagger Authors. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -// Code generated by client-gen. DO NOT EDIT. - -package fake - -import ( - v1beta1 "github.com/weaveworks/flagger/pkg/apis/appmesh/v1beta1" - v1 "k8s.io/apimachinery/pkg/apis/meta/v1" - labels "k8s.io/apimachinery/pkg/labels" - schema "k8s.io/apimachinery/pkg/runtime/schema" - types "k8s.io/apimachinery/pkg/types" - watch "k8s.io/apimachinery/pkg/watch" - testing "k8s.io/client-go/testing" -) - -// FakeVirtualNodes implements VirtualNodeInterface -type FakeVirtualNodes struct { - Fake *FakeAppmeshV1beta1 - ns string -} - -var virtualnodesResource = schema.GroupVersionResource{Group: "appmesh.k8s.aws", Version: "v1beta1", Resource: "virtualnodes"} - -var virtualnodesKind = schema.GroupVersionKind{Group: "appmesh.k8s.aws", Version: "v1beta1", Kind: "VirtualNode"} - -// Get takes name of the virtualNode, and returns the corresponding virtualNode object, and an error if there is any. -func (c *FakeVirtualNodes) Get(name string, options v1.GetOptions) (result *v1beta1.VirtualNode, err error) { - obj, err := c.Fake. - Invokes(testing.NewGetAction(virtualnodesResource, c.ns, name), &v1beta1.VirtualNode{}) - - if obj == nil { - return nil, err - } - return obj.(*v1beta1.VirtualNode), err -} - -// List takes label and field selectors, and returns the list of VirtualNodes that match those selectors. -func (c *FakeVirtualNodes) List(opts v1.ListOptions) (result *v1beta1.VirtualNodeList, err error) { - obj, err := c.Fake. - Invokes(testing.NewListAction(virtualnodesResource, virtualnodesKind, c.ns, opts), &v1beta1.VirtualNodeList{}) - - if obj == nil { - return nil, err - } - - label, _, _ := testing.ExtractFromListOptions(opts) - if label == nil { - label = labels.Everything() - } - list := &v1beta1.VirtualNodeList{ListMeta: obj.(*v1beta1.VirtualNodeList).ListMeta} - for _, item := range obj.(*v1beta1.VirtualNodeList).Items { - if label.Matches(labels.Set(item.Labels)) { - list.Items = append(list.Items, item) - } - } - return list, err -} - -// Watch returns a watch.Interface that watches the requested virtualNodes. -func (c *FakeVirtualNodes) Watch(opts v1.ListOptions) (watch.Interface, error) { - return c.Fake. - InvokesWatch(testing.NewWatchAction(virtualnodesResource, c.ns, opts)) - -} - -// Create takes the representation of a virtualNode and creates it. Returns the server's representation of the virtualNode, and an error, if there is any. -func (c *FakeVirtualNodes) Create(virtualNode *v1beta1.VirtualNode) (result *v1beta1.VirtualNode, err error) { - obj, err := c.Fake. - Invokes(testing.NewCreateAction(virtualnodesResource, c.ns, virtualNode), &v1beta1.VirtualNode{}) - - if obj == nil { - return nil, err - } - return obj.(*v1beta1.VirtualNode), err -} - -// Update takes the representation of a virtualNode and updates it. Returns the server's representation of the virtualNode, and an error, if there is any. -func (c *FakeVirtualNodes) Update(virtualNode *v1beta1.VirtualNode) (result *v1beta1.VirtualNode, err error) { - obj, err := c.Fake. - Invokes(testing.NewUpdateAction(virtualnodesResource, c.ns, virtualNode), &v1beta1.VirtualNode{}) - - if obj == nil { - return nil, err - } - return obj.(*v1beta1.VirtualNode), err -} - -// UpdateStatus was generated because the type contains a Status member. -// Add a +genclient:noStatus comment above the type to avoid generating UpdateStatus(). -func (c *FakeVirtualNodes) UpdateStatus(virtualNode *v1beta1.VirtualNode) (*v1beta1.VirtualNode, error) { - obj, err := c.Fake. - Invokes(testing.NewUpdateSubresourceAction(virtualnodesResource, "status", c.ns, virtualNode), &v1beta1.VirtualNode{}) - - if obj == nil { - return nil, err - } - return obj.(*v1beta1.VirtualNode), err -} - -// Delete takes name of the virtualNode and deletes it. Returns an error if one occurs. -func (c *FakeVirtualNodes) Delete(name string, options *v1.DeleteOptions) error { - _, err := c.Fake. - Invokes(testing.NewDeleteAction(virtualnodesResource, c.ns, name), &v1beta1.VirtualNode{}) - - return err -} - -// DeleteCollection deletes a collection of objects. -func (c *FakeVirtualNodes) DeleteCollection(options *v1.DeleteOptions, listOptions v1.ListOptions) error { - action := testing.NewDeleteCollectionAction(virtualnodesResource, c.ns, listOptions) - - _, err := c.Fake.Invokes(action, &v1beta1.VirtualNodeList{}) - return err -} - -// Patch applies the patch and returns the patched virtualNode. -func (c *FakeVirtualNodes) Patch(name string, pt types.PatchType, data []byte, subresources ...string) (result *v1beta1.VirtualNode, err error) { - obj, err := c.Fake. - Invokes(testing.NewPatchSubresourceAction(virtualnodesResource, c.ns, name, pt, data, subresources...), &v1beta1.VirtualNode{}) - - if obj == nil { - return nil, err - } - return obj.(*v1beta1.VirtualNode), err -} diff --git a/pkg/client/clientset/versioned/typed/appmesh/v1beta1/fake/fake_virtualservice.go b/pkg/client/clientset/versioned/typed/appmesh/v1beta1/fake/fake_virtualservice.go deleted file mode 100644 index 17ee309c..00000000 --- a/pkg/client/clientset/versioned/typed/appmesh/v1beta1/fake/fake_virtualservice.go +++ /dev/null @@ -1,140 +0,0 @@ -/* -Copyright The Flagger Authors. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -// Code generated by client-gen. DO NOT EDIT. - -package fake - -import ( - v1beta1 "github.com/weaveworks/flagger/pkg/apis/appmesh/v1beta1" - v1 "k8s.io/apimachinery/pkg/apis/meta/v1" - labels "k8s.io/apimachinery/pkg/labels" - schema "k8s.io/apimachinery/pkg/runtime/schema" - types "k8s.io/apimachinery/pkg/types" - watch "k8s.io/apimachinery/pkg/watch" - testing "k8s.io/client-go/testing" -) - -// FakeVirtualServices implements VirtualServiceInterface -type FakeVirtualServices struct { - Fake *FakeAppmeshV1beta1 - ns string -} - -var virtualservicesResource = schema.GroupVersionResource{Group: "appmesh.k8s.aws", Version: "v1beta1", Resource: "virtualservices"} - -var virtualservicesKind = schema.GroupVersionKind{Group: "appmesh.k8s.aws", Version: "v1beta1", Kind: "VirtualService"} - -// Get takes name of the virtualService, and returns the corresponding virtualService object, and an error if there is any. -func (c *FakeVirtualServices) Get(name string, options v1.GetOptions) (result *v1beta1.VirtualService, err error) { - obj, err := c.Fake. - Invokes(testing.NewGetAction(virtualservicesResource, c.ns, name), &v1beta1.VirtualService{}) - - if obj == nil { - return nil, err - } - return obj.(*v1beta1.VirtualService), err -} - -// List takes label and field selectors, and returns the list of VirtualServices that match those selectors. -func (c *FakeVirtualServices) List(opts v1.ListOptions) (result *v1beta1.VirtualServiceList, err error) { - obj, err := c.Fake. - Invokes(testing.NewListAction(virtualservicesResource, virtualservicesKind, c.ns, opts), &v1beta1.VirtualServiceList{}) - - if obj == nil { - return nil, err - } - - label, _, _ := testing.ExtractFromListOptions(opts) - if label == nil { - label = labels.Everything() - } - list := &v1beta1.VirtualServiceList{ListMeta: obj.(*v1beta1.VirtualServiceList).ListMeta} - for _, item := range obj.(*v1beta1.VirtualServiceList).Items { - if label.Matches(labels.Set(item.Labels)) { - list.Items = append(list.Items, item) - } - } - return list, err -} - -// Watch returns a watch.Interface that watches the requested virtualServices. -func (c *FakeVirtualServices) Watch(opts v1.ListOptions) (watch.Interface, error) { - return c.Fake. - InvokesWatch(testing.NewWatchAction(virtualservicesResource, c.ns, opts)) - -} - -// Create takes the representation of a virtualService and creates it. Returns the server's representation of the virtualService, and an error, if there is any. -func (c *FakeVirtualServices) Create(virtualService *v1beta1.VirtualService) (result *v1beta1.VirtualService, err error) { - obj, err := c.Fake. - Invokes(testing.NewCreateAction(virtualservicesResource, c.ns, virtualService), &v1beta1.VirtualService{}) - - if obj == nil { - return nil, err - } - return obj.(*v1beta1.VirtualService), err -} - -// Update takes the representation of a virtualService and updates it. Returns the server's representation of the virtualService, and an error, if there is any. -func (c *FakeVirtualServices) Update(virtualService *v1beta1.VirtualService) (result *v1beta1.VirtualService, err error) { - obj, err := c.Fake. - Invokes(testing.NewUpdateAction(virtualservicesResource, c.ns, virtualService), &v1beta1.VirtualService{}) - - if obj == nil { - return nil, err - } - return obj.(*v1beta1.VirtualService), err -} - -// UpdateStatus was generated because the type contains a Status member. -// Add a +genclient:noStatus comment above the type to avoid generating UpdateStatus(). -func (c *FakeVirtualServices) UpdateStatus(virtualService *v1beta1.VirtualService) (*v1beta1.VirtualService, error) { - obj, err := c.Fake. - Invokes(testing.NewUpdateSubresourceAction(virtualservicesResource, "status", c.ns, virtualService), &v1beta1.VirtualService{}) - - if obj == nil { - return nil, err - } - return obj.(*v1beta1.VirtualService), err -} - -// Delete takes name of the virtualService and deletes it. Returns an error if one occurs. -func (c *FakeVirtualServices) Delete(name string, options *v1.DeleteOptions) error { - _, err := c.Fake. - Invokes(testing.NewDeleteAction(virtualservicesResource, c.ns, name), &v1beta1.VirtualService{}) - - return err -} - -// DeleteCollection deletes a collection of objects. -func (c *FakeVirtualServices) DeleteCollection(options *v1.DeleteOptions, listOptions v1.ListOptions) error { - action := testing.NewDeleteCollectionAction(virtualservicesResource, c.ns, listOptions) - - _, err := c.Fake.Invokes(action, &v1beta1.VirtualServiceList{}) - return err -} - -// Patch applies the patch and returns the patched virtualService. -func (c *FakeVirtualServices) Patch(name string, pt types.PatchType, data []byte, subresources ...string) (result *v1beta1.VirtualService, err error) { - obj, err := c.Fake. - Invokes(testing.NewPatchSubresourceAction(virtualservicesResource, c.ns, name, pt, data, subresources...), &v1beta1.VirtualService{}) - - if obj == nil { - return nil, err - } - return obj.(*v1beta1.VirtualService), err -} diff --git a/pkg/client/clientset/versioned/typed/appmesh/v1beta1/generated_expansion.go b/pkg/client/clientset/versioned/typed/appmesh/v1beta1/generated_expansion.go deleted file mode 100644 index 66f15ec1..00000000 --- a/pkg/client/clientset/versioned/typed/appmesh/v1beta1/generated_expansion.go +++ /dev/null @@ -1,25 +0,0 @@ -/* -Copyright The Flagger Authors. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -// Code generated by client-gen. DO NOT EDIT. - -package v1beta1 - -type MeshExpansion interface{} - -type VirtualNodeExpansion interface{} - -type VirtualServiceExpansion interface{} diff --git a/pkg/client/clientset/versioned/typed/appmesh/v1beta1/mesh.go b/pkg/client/clientset/versioned/typed/appmesh/v1beta1/mesh.go deleted file mode 100644 index 4b1c71cd..00000000 --- a/pkg/client/clientset/versioned/typed/appmesh/v1beta1/mesh.go +++ /dev/null @@ -1,180 +0,0 @@ -/* -Copyright The Flagger Authors. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -// Code generated by client-gen. DO NOT EDIT. - -package v1beta1 - -import ( - "time" - - v1beta1 "github.com/weaveworks/flagger/pkg/apis/appmesh/v1beta1" - scheme "github.com/weaveworks/flagger/pkg/client/clientset/versioned/scheme" - v1 "k8s.io/apimachinery/pkg/apis/meta/v1" - types "k8s.io/apimachinery/pkg/types" - watch "k8s.io/apimachinery/pkg/watch" - rest "k8s.io/client-go/rest" -) - -// MeshesGetter has a method to return a MeshInterface. -// A group's client should implement this interface. -type MeshesGetter interface { - Meshes() MeshInterface -} - -// MeshInterface has methods to work with Mesh resources. -type MeshInterface interface { - Create(*v1beta1.Mesh) (*v1beta1.Mesh, error) - Update(*v1beta1.Mesh) (*v1beta1.Mesh, error) - UpdateStatus(*v1beta1.Mesh) (*v1beta1.Mesh, error) - Delete(name string, options *v1.DeleteOptions) error - DeleteCollection(options *v1.DeleteOptions, listOptions v1.ListOptions) error - Get(name string, options v1.GetOptions) (*v1beta1.Mesh, error) - List(opts v1.ListOptions) (*v1beta1.MeshList, error) - Watch(opts v1.ListOptions) (watch.Interface, error) - Patch(name string, pt types.PatchType, data []byte, subresources ...string) (result *v1beta1.Mesh, err error) - MeshExpansion -} - -// meshes implements MeshInterface -type meshes struct { - client rest.Interface -} - -// newMeshes returns a Meshes -func newMeshes(c *AppmeshV1beta1Client) *meshes { - return &meshes{ - client: c.RESTClient(), - } -} - -// Get takes name of the mesh, and returns the corresponding mesh object, and an error if there is any. -func (c *meshes) Get(name string, options v1.GetOptions) (result *v1beta1.Mesh, err error) { - result = &v1beta1.Mesh{} - err = c.client.Get(). - Resource("meshes"). - Name(name). - VersionedParams(&options, scheme.ParameterCodec). - Do(). - Into(result) - return -} - -// List takes label and field selectors, and returns the list of Meshes that match those selectors. -func (c *meshes) List(opts v1.ListOptions) (result *v1beta1.MeshList, err error) { - var timeout time.Duration - if opts.TimeoutSeconds != nil { - timeout = time.Duration(*opts.TimeoutSeconds) * time.Second - } - result = &v1beta1.MeshList{} - err = c.client.Get(). - Resource("meshes"). - VersionedParams(&opts, scheme.ParameterCodec). - Timeout(timeout). - Do(). - Into(result) - return -} - -// Watch returns a watch.Interface that watches the requested meshes. -func (c *meshes) Watch(opts v1.ListOptions) (watch.Interface, error) { - var timeout time.Duration - if opts.TimeoutSeconds != nil { - timeout = time.Duration(*opts.TimeoutSeconds) * time.Second - } - opts.Watch = true - return c.client.Get(). - Resource("meshes"). - VersionedParams(&opts, scheme.ParameterCodec). - Timeout(timeout). - Watch() -} - -// Create takes the representation of a mesh and creates it. Returns the server's representation of the mesh, and an error, if there is any. -func (c *meshes) Create(mesh *v1beta1.Mesh) (result *v1beta1.Mesh, err error) { - result = &v1beta1.Mesh{} - err = c.client.Post(). - Resource("meshes"). - Body(mesh). - Do(). - Into(result) - return -} - -// Update takes the representation of a mesh and updates it. Returns the server's representation of the mesh, and an error, if there is any. -func (c *meshes) Update(mesh *v1beta1.Mesh) (result *v1beta1.Mesh, err error) { - result = &v1beta1.Mesh{} - err = c.client.Put(). - Resource("meshes"). - Name(mesh.Name). - Body(mesh). - Do(). - Into(result) - return -} - -// UpdateStatus was generated because the type contains a Status member. -// Add a +genclient:noStatus comment above the type to avoid generating UpdateStatus(). - -func (c *meshes) UpdateStatus(mesh *v1beta1.Mesh) (result *v1beta1.Mesh, err error) { - result = &v1beta1.Mesh{} - err = c.client.Put(). - Resource("meshes"). - Name(mesh.Name). - SubResource("status"). - Body(mesh). - Do(). - Into(result) - return -} - -// Delete takes name of the mesh and deletes it. Returns an error if one occurs. -func (c *meshes) Delete(name string, options *v1.DeleteOptions) error { - return c.client.Delete(). - Resource("meshes"). - Name(name). - Body(options). - Do(). - Error() -} - -// DeleteCollection deletes a collection of objects. -func (c *meshes) DeleteCollection(options *v1.DeleteOptions, listOptions v1.ListOptions) error { - var timeout time.Duration - if listOptions.TimeoutSeconds != nil { - timeout = time.Duration(*listOptions.TimeoutSeconds) * time.Second - } - return c.client.Delete(). - Resource("meshes"). - VersionedParams(&listOptions, scheme.ParameterCodec). - Timeout(timeout). - Body(options). - Do(). - Error() -} - -// Patch applies the patch and returns the patched mesh. -func (c *meshes) Patch(name string, pt types.PatchType, data []byte, subresources ...string) (result *v1beta1.Mesh, err error) { - result = &v1beta1.Mesh{} - err = c.client.Patch(pt). - Resource("meshes"). - SubResource(subresources...). - Name(name). - Body(data). - Do(). - Into(result) - return -} diff --git a/pkg/client/clientset/versioned/typed/appmesh/v1beta1/virtualnode.go b/pkg/client/clientset/versioned/typed/appmesh/v1beta1/virtualnode.go deleted file mode 100644 index fb97d12f..00000000 --- a/pkg/client/clientset/versioned/typed/appmesh/v1beta1/virtualnode.go +++ /dev/null @@ -1,191 +0,0 @@ -/* -Copyright The Flagger Authors. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -// Code generated by client-gen. DO NOT EDIT. - -package v1beta1 - -import ( - "time" - - v1beta1 "github.com/weaveworks/flagger/pkg/apis/appmesh/v1beta1" - scheme "github.com/weaveworks/flagger/pkg/client/clientset/versioned/scheme" - v1 "k8s.io/apimachinery/pkg/apis/meta/v1" - types "k8s.io/apimachinery/pkg/types" - watch "k8s.io/apimachinery/pkg/watch" - rest "k8s.io/client-go/rest" -) - -// VirtualNodesGetter has a method to return a VirtualNodeInterface. -// A group's client should implement this interface. -type VirtualNodesGetter interface { - VirtualNodes(namespace string) VirtualNodeInterface -} - -// VirtualNodeInterface has methods to work with VirtualNode resources. -type VirtualNodeInterface interface { - Create(*v1beta1.VirtualNode) (*v1beta1.VirtualNode, error) - Update(*v1beta1.VirtualNode) (*v1beta1.VirtualNode, error) - UpdateStatus(*v1beta1.VirtualNode) (*v1beta1.VirtualNode, error) - Delete(name string, options *v1.DeleteOptions) error - DeleteCollection(options *v1.DeleteOptions, listOptions v1.ListOptions) error - Get(name string, options v1.GetOptions) (*v1beta1.VirtualNode, error) - List(opts v1.ListOptions) (*v1beta1.VirtualNodeList, error) - Watch(opts v1.ListOptions) (watch.Interface, error) - Patch(name string, pt types.PatchType, data []byte, subresources ...string) (result *v1beta1.VirtualNode, err error) - VirtualNodeExpansion -} - -// virtualNodes implements VirtualNodeInterface -type virtualNodes struct { - client rest.Interface - ns string -} - -// newVirtualNodes returns a VirtualNodes -func newVirtualNodes(c *AppmeshV1beta1Client, namespace string) *virtualNodes { - return &virtualNodes{ - client: c.RESTClient(), - ns: namespace, - } -} - -// Get takes name of the virtualNode, and returns the corresponding virtualNode object, and an error if there is any. -func (c *virtualNodes) Get(name string, options v1.GetOptions) (result *v1beta1.VirtualNode, err error) { - result = &v1beta1.VirtualNode{} - err = c.client.Get(). - Namespace(c.ns). - Resource("virtualnodes"). - Name(name). - VersionedParams(&options, scheme.ParameterCodec). - Do(). - Into(result) - return -} - -// List takes label and field selectors, and returns the list of VirtualNodes that match those selectors. -func (c *virtualNodes) List(opts v1.ListOptions) (result *v1beta1.VirtualNodeList, err error) { - var timeout time.Duration - if opts.TimeoutSeconds != nil { - timeout = time.Duration(*opts.TimeoutSeconds) * time.Second - } - result = &v1beta1.VirtualNodeList{} - err = c.client.Get(). - Namespace(c.ns). - Resource("virtualnodes"). - VersionedParams(&opts, scheme.ParameterCodec). - Timeout(timeout). - Do(). - Into(result) - return -} - -// Watch returns a watch.Interface that watches the requested virtualNodes. -func (c *virtualNodes) Watch(opts v1.ListOptions) (watch.Interface, error) { - var timeout time.Duration - if opts.TimeoutSeconds != nil { - timeout = time.Duration(*opts.TimeoutSeconds) * time.Second - } - opts.Watch = true - return c.client.Get(). - Namespace(c.ns). - Resource("virtualnodes"). - VersionedParams(&opts, scheme.ParameterCodec). - Timeout(timeout). - Watch() -} - -// Create takes the representation of a virtualNode and creates it. Returns the server's representation of the virtualNode, and an error, if there is any. -func (c *virtualNodes) Create(virtualNode *v1beta1.VirtualNode) (result *v1beta1.VirtualNode, err error) { - result = &v1beta1.VirtualNode{} - err = c.client.Post(). - Namespace(c.ns). - Resource("virtualnodes"). - Body(virtualNode). - Do(). - Into(result) - return -} - -// Update takes the representation of a virtualNode and updates it. Returns the server's representation of the virtualNode, and an error, if there is any. -func (c *virtualNodes) Update(virtualNode *v1beta1.VirtualNode) (result *v1beta1.VirtualNode, err error) { - result = &v1beta1.VirtualNode{} - err = c.client.Put(). - Namespace(c.ns). - Resource("virtualnodes"). - Name(virtualNode.Name). - Body(virtualNode). - Do(). - Into(result) - return -} - -// UpdateStatus was generated because the type contains a Status member. -// Add a +genclient:noStatus comment above the type to avoid generating UpdateStatus(). - -func (c *virtualNodes) UpdateStatus(virtualNode *v1beta1.VirtualNode) (result *v1beta1.VirtualNode, err error) { - result = &v1beta1.VirtualNode{} - err = c.client.Put(). - Namespace(c.ns). - Resource("virtualnodes"). - Name(virtualNode.Name). - SubResource("status"). - Body(virtualNode). - Do(). - Into(result) - return -} - -// Delete takes name of the virtualNode and deletes it. Returns an error if one occurs. -func (c *virtualNodes) Delete(name string, options *v1.DeleteOptions) error { - return c.client.Delete(). - Namespace(c.ns). - Resource("virtualnodes"). - Name(name). - Body(options). - Do(). - Error() -} - -// DeleteCollection deletes a collection of objects. -func (c *virtualNodes) DeleteCollection(options *v1.DeleteOptions, listOptions v1.ListOptions) error { - var timeout time.Duration - if listOptions.TimeoutSeconds != nil { - timeout = time.Duration(*listOptions.TimeoutSeconds) * time.Second - } - return c.client.Delete(). - Namespace(c.ns). - Resource("virtualnodes"). - VersionedParams(&listOptions, scheme.ParameterCodec). - Timeout(timeout). - Body(options). - Do(). - Error() -} - -// Patch applies the patch and returns the patched virtualNode. -func (c *virtualNodes) Patch(name string, pt types.PatchType, data []byte, subresources ...string) (result *v1beta1.VirtualNode, err error) { - result = &v1beta1.VirtualNode{} - err = c.client.Patch(pt). - Namespace(c.ns). - Resource("virtualnodes"). - SubResource(subresources...). - Name(name). - Body(data). - Do(). - Into(result) - return -} diff --git a/pkg/client/clientset/versioned/typed/appmesh/v1beta1/virtualservice.go b/pkg/client/clientset/versioned/typed/appmesh/v1beta1/virtualservice.go deleted file mode 100644 index 1ce926c9..00000000 --- a/pkg/client/clientset/versioned/typed/appmesh/v1beta1/virtualservice.go +++ /dev/null @@ -1,191 +0,0 @@ -/* -Copyright The Flagger Authors. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -// Code generated by client-gen. DO NOT EDIT. - -package v1beta1 - -import ( - "time" - - v1beta1 "github.com/weaveworks/flagger/pkg/apis/appmesh/v1beta1" - scheme "github.com/weaveworks/flagger/pkg/client/clientset/versioned/scheme" - v1 "k8s.io/apimachinery/pkg/apis/meta/v1" - types "k8s.io/apimachinery/pkg/types" - watch "k8s.io/apimachinery/pkg/watch" - rest "k8s.io/client-go/rest" -) - -// VirtualServicesGetter has a method to return a VirtualServiceInterface. -// A group's client should implement this interface. -type VirtualServicesGetter interface { - VirtualServices(namespace string) VirtualServiceInterface -} - -// VirtualServiceInterface has methods to work with VirtualService resources. -type VirtualServiceInterface interface { - Create(*v1beta1.VirtualService) (*v1beta1.VirtualService, error) - Update(*v1beta1.VirtualService) (*v1beta1.VirtualService, error) - UpdateStatus(*v1beta1.VirtualService) (*v1beta1.VirtualService, error) - Delete(name string, options *v1.DeleteOptions) error - DeleteCollection(options *v1.DeleteOptions, listOptions v1.ListOptions) error - Get(name string, options v1.GetOptions) (*v1beta1.VirtualService, error) - List(opts v1.ListOptions) (*v1beta1.VirtualServiceList, error) - Watch(opts v1.ListOptions) (watch.Interface, error) - Patch(name string, pt types.PatchType, data []byte, subresources ...string) (result *v1beta1.VirtualService, err error) - VirtualServiceExpansion -} - -// virtualServices implements VirtualServiceInterface -type virtualServices struct { - client rest.Interface - ns string -} - -// newVirtualServices returns a VirtualServices -func newVirtualServices(c *AppmeshV1beta1Client, namespace string) *virtualServices { - return &virtualServices{ - client: c.RESTClient(), - ns: namespace, - } -} - -// Get takes name of the virtualService, and returns the corresponding virtualService object, and an error if there is any. -func (c *virtualServices) Get(name string, options v1.GetOptions) (result *v1beta1.VirtualService, err error) { - result = &v1beta1.VirtualService{} - err = c.client.Get(). - Namespace(c.ns). - Resource("virtualservices"). - Name(name). - VersionedParams(&options, scheme.ParameterCodec). - Do(). - Into(result) - return -} - -// List takes label and field selectors, and returns the list of VirtualServices that match those selectors. -func (c *virtualServices) List(opts v1.ListOptions) (result *v1beta1.VirtualServiceList, err error) { - var timeout time.Duration - if opts.TimeoutSeconds != nil { - timeout = time.Duration(*opts.TimeoutSeconds) * time.Second - } - result = &v1beta1.VirtualServiceList{} - err = c.client.Get(). - Namespace(c.ns). - Resource("virtualservices"). - VersionedParams(&opts, scheme.ParameterCodec). - Timeout(timeout). - Do(). - Into(result) - return -} - -// Watch returns a watch.Interface that watches the requested virtualServices. -func (c *virtualServices) Watch(opts v1.ListOptions) (watch.Interface, error) { - var timeout time.Duration - if opts.TimeoutSeconds != nil { - timeout = time.Duration(*opts.TimeoutSeconds) * time.Second - } - opts.Watch = true - return c.client.Get(). - Namespace(c.ns). - Resource("virtualservices"). - VersionedParams(&opts, scheme.ParameterCodec). - Timeout(timeout). - Watch() -} - -// Create takes the representation of a virtualService and creates it. Returns the server's representation of the virtualService, and an error, if there is any. -func (c *virtualServices) Create(virtualService *v1beta1.VirtualService) (result *v1beta1.VirtualService, err error) { - result = &v1beta1.VirtualService{} - err = c.client.Post(). - Namespace(c.ns). - Resource("virtualservices"). - Body(virtualService). - Do(). - Into(result) - return -} - -// Update takes the representation of a virtualService and updates it. Returns the server's representation of the virtualService, and an error, if there is any. -func (c *virtualServices) Update(virtualService *v1beta1.VirtualService) (result *v1beta1.VirtualService, err error) { - result = &v1beta1.VirtualService{} - err = c.client.Put(). - Namespace(c.ns). - Resource("virtualservices"). - Name(virtualService.Name). - Body(virtualService). - Do(). - Into(result) - return -} - -// UpdateStatus was generated because the type contains a Status member. -// Add a +genclient:noStatus comment above the type to avoid generating UpdateStatus(). - -func (c *virtualServices) UpdateStatus(virtualService *v1beta1.VirtualService) (result *v1beta1.VirtualService, err error) { - result = &v1beta1.VirtualService{} - err = c.client.Put(). - Namespace(c.ns). - Resource("virtualservices"). - Name(virtualService.Name). - SubResource("status"). - Body(virtualService). - Do(). - Into(result) - return -} - -// Delete takes name of the virtualService and deletes it. Returns an error if one occurs. -func (c *virtualServices) Delete(name string, options *v1.DeleteOptions) error { - return c.client.Delete(). - Namespace(c.ns). - Resource("virtualservices"). - Name(name). - Body(options). - Do(). - Error() -} - -// DeleteCollection deletes a collection of objects. -func (c *virtualServices) DeleteCollection(options *v1.DeleteOptions, listOptions v1.ListOptions) error { - var timeout time.Duration - if listOptions.TimeoutSeconds != nil { - timeout = time.Duration(*listOptions.TimeoutSeconds) * time.Second - } - return c.client.Delete(). - Namespace(c.ns). - Resource("virtualservices"). - VersionedParams(&listOptions, scheme.ParameterCodec). - Timeout(timeout). - Body(options). - Do(). - Error() -} - -// Patch applies the patch and returns the patched virtualService. -func (c *virtualServices) Patch(name string, pt types.PatchType, data []byte, subresources ...string) (result *v1beta1.VirtualService, err error) { - result = &v1beta1.VirtualService{} - err = c.client.Patch(pt). - Namespace(c.ns). - Resource("virtualservices"). - SubResource(subresources...). - Name(name). - Body(data). - Do(). - Into(result) - return -} diff --git a/pkg/client/clientset/versioned/typed/flagger/v1alpha3/canary.go b/pkg/client/clientset/versioned/typed/flagger/v1alpha3/canary.go deleted file mode 100644 index 047a0d14..00000000 --- a/pkg/client/clientset/versioned/typed/flagger/v1alpha3/canary.go +++ /dev/null @@ -1,191 +0,0 @@ -/* -Copyright The Flagger Authors. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -// Code generated by client-gen. DO NOT EDIT. - -package v1alpha3 - -import ( - "time" - - v1alpha3 "github.com/weaveworks/flagger/pkg/apis/flagger/v1alpha3" - scheme "github.com/weaveworks/flagger/pkg/client/clientset/versioned/scheme" - v1 "k8s.io/apimachinery/pkg/apis/meta/v1" - types "k8s.io/apimachinery/pkg/types" - watch "k8s.io/apimachinery/pkg/watch" - rest "k8s.io/client-go/rest" -) - -// CanariesGetter has a method to return a CanaryInterface. -// A group's client should implement this interface. -type CanariesGetter interface { - Canaries(namespace string) CanaryInterface -} - -// CanaryInterface has methods to work with Canary resources. -type CanaryInterface interface { - Create(*v1alpha3.Canary) (*v1alpha3.Canary, error) - Update(*v1alpha3.Canary) (*v1alpha3.Canary, error) - UpdateStatus(*v1alpha3.Canary) (*v1alpha3.Canary, error) - Delete(name string, options *v1.DeleteOptions) error - DeleteCollection(options *v1.DeleteOptions, listOptions v1.ListOptions) error - Get(name string, options v1.GetOptions) (*v1alpha3.Canary, error) - List(opts v1.ListOptions) (*v1alpha3.CanaryList, error) - Watch(opts v1.ListOptions) (watch.Interface, error) - Patch(name string, pt types.PatchType, data []byte, subresources ...string) (result *v1alpha3.Canary, err error) - CanaryExpansion -} - -// canaries implements CanaryInterface -type canaries struct { - client rest.Interface - ns string -} - -// newCanaries returns a Canaries -func newCanaries(c *FlaggerV1alpha3Client, namespace string) *canaries { - return &canaries{ - client: c.RESTClient(), - ns: namespace, - } -} - -// Get takes name of the canary, and returns the corresponding canary object, and an error if there is any. -func (c *canaries) Get(name string, options v1.GetOptions) (result *v1alpha3.Canary, err error) { - result = &v1alpha3.Canary{} - err = c.client.Get(). - Namespace(c.ns). - Resource("canaries"). - Name(name). - VersionedParams(&options, scheme.ParameterCodec). - Do(). - Into(result) - return -} - -// List takes label and field selectors, and returns the list of Canaries that match those selectors. -func (c *canaries) List(opts v1.ListOptions) (result *v1alpha3.CanaryList, err error) { - var timeout time.Duration - if opts.TimeoutSeconds != nil { - timeout = time.Duration(*opts.TimeoutSeconds) * time.Second - } - result = &v1alpha3.CanaryList{} - err = c.client.Get(). - Namespace(c.ns). - Resource("canaries"). - VersionedParams(&opts, scheme.ParameterCodec). - Timeout(timeout). - Do(). - Into(result) - return -} - -// Watch returns a watch.Interface that watches the requested canaries. -func (c *canaries) Watch(opts v1.ListOptions) (watch.Interface, error) { - var timeout time.Duration - if opts.TimeoutSeconds != nil { - timeout = time.Duration(*opts.TimeoutSeconds) * time.Second - } - opts.Watch = true - return c.client.Get(). - Namespace(c.ns). - Resource("canaries"). - VersionedParams(&opts, scheme.ParameterCodec). - Timeout(timeout). - Watch() -} - -// Create takes the representation of a canary and creates it. Returns the server's representation of the canary, and an error, if there is any. -func (c *canaries) Create(canary *v1alpha3.Canary) (result *v1alpha3.Canary, err error) { - result = &v1alpha3.Canary{} - err = c.client.Post(). - Namespace(c.ns). - Resource("canaries"). - Body(canary). - Do(). - Into(result) - return -} - -// Update takes the representation of a canary and updates it. Returns the server's representation of the canary, and an error, if there is any. -func (c *canaries) Update(canary *v1alpha3.Canary) (result *v1alpha3.Canary, err error) { - result = &v1alpha3.Canary{} - err = c.client.Put(). - Namespace(c.ns). - Resource("canaries"). - Name(canary.Name). - Body(canary). - Do(). - Into(result) - return -} - -// UpdateStatus was generated because the type contains a Status member. -// Add a +genclient:noStatus comment above the type to avoid generating UpdateStatus(). - -func (c *canaries) UpdateStatus(canary *v1alpha3.Canary) (result *v1alpha3.Canary, err error) { - result = &v1alpha3.Canary{} - err = c.client.Put(). - Namespace(c.ns). - Resource("canaries"). - Name(canary.Name). - SubResource("status"). - Body(canary). - Do(). - Into(result) - return -} - -// Delete takes name of the canary and deletes it. Returns an error if one occurs. -func (c *canaries) Delete(name string, options *v1.DeleteOptions) error { - return c.client.Delete(). - Namespace(c.ns). - Resource("canaries"). - Name(name). - Body(options). - Do(). - Error() -} - -// DeleteCollection deletes a collection of objects. -func (c *canaries) DeleteCollection(options *v1.DeleteOptions, listOptions v1.ListOptions) error { - var timeout time.Duration - if listOptions.TimeoutSeconds != nil { - timeout = time.Duration(*listOptions.TimeoutSeconds) * time.Second - } - return c.client.Delete(). - Namespace(c.ns). - Resource("canaries"). - VersionedParams(&listOptions, scheme.ParameterCodec). - Timeout(timeout). - Body(options). - Do(). - Error() -} - -// Patch applies the patch and returns the patched canary. -func (c *canaries) Patch(name string, pt types.PatchType, data []byte, subresources ...string) (result *v1alpha3.Canary, err error) { - result = &v1alpha3.Canary{} - err = c.client.Patch(pt). - Namespace(c.ns). - Resource("canaries"). - SubResource(subresources...). - Name(name). - Body(data). - Do(). - Into(result) - return -} diff --git a/pkg/client/clientset/versioned/typed/flagger/v1alpha3/doc.go b/pkg/client/clientset/versioned/typed/flagger/v1alpha3/doc.go deleted file mode 100644 index d49e3fd3..00000000 --- a/pkg/client/clientset/versioned/typed/flagger/v1alpha3/doc.go +++ /dev/null @@ -1,20 +0,0 @@ -/* -Copyright The Flagger Authors. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -// Code generated by client-gen. DO NOT EDIT. - -// This package has the automatically generated typed clients. -package v1alpha3 diff --git a/pkg/client/clientset/versioned/typed/flagger/v1alpha3/fake/doc.go b/pkg/client/clientset/versioned/typed/flagger/v1alpha3/fake/doc.go deleted file mode 100644 index 7a3b19cb..00000000 --- a/pkg/client/clientset/versioned/typed/flagger/v1alpha3/fake/doc.go +++ /dev/null @@ -1,20 +0,0 @@ -/* -Copyright The Flagger Authors. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -// Code generated by client-gen. DO NOT EDIT. - -// Package fake has the automatically generated clients. -package fake diff --git a/pkg/client/clientset/versioned/typed/flagger/v1alpha3/fake/fake_canary.go b/pkg/client/clientset/versioned/typed/flagger/v1alpha3/fake/fake_canary.go deleted file mode 100644 index 33f461f7..00000000 --- a/pkg/client/clientset/versioned/typed/flagger/v1alpha3/fake/fake_canary.go +++ /dev/null @@ -1,140 +0,0 @@ -/* -Copyright The Flagger Authors. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -// Code generated by client-gen. DO NOT EDIT. - -package fake - -import ( - v1alpha3 "github.com/weaveworks/flagger/pkg/apis/flagger/v1alpha3" - v1 "k8s.io/apimachinery/pkg/apis/meta/v1" - labels "k8s.io/apimachinery/pkg/labels" - schema "k8s.io/apimachinery/pkg/runtime/schema" - types "k8s.io/apimachinery/pkg/types" - watch "k8s.io/apimachinery/pkg/watch" - testing "k8s.io/client-go/testing" -) - -// FakeCanaries implements CanaryInterface -type FakeCanaries struct { - Fake *FakeFlaggerV1alpha3 - ns string -} - -var canariesResource = schema.GroupVersionResource{Group: "flagger.app", Version: "v1alpha3", Resource: "canaries"} - -var canariesKind = schema.GroupVersionKind{Group: "flagger.app", Version: "v1alpha3", Kind: "Canary"} - -// Get takes name of the canary, and returns the corresponding canary object, and an error if there is any. -func (c *FakeCanaries) Get(name string, options v1.GetOptions) (result *v1alpha3.Canary, err error) { - obj, err := c.Fake. - Invokes(testing.NewGetAction(canariesResource, c.ns, name), &v1alpha3.Canary{}) - - if obj == nil { - return nil, err - } - return obj.(*v1alpha3.Canary), err -} - -// List takes label and field selectors, and returns the list of Canaries that match those selectors. -func (c *FakeCanaries) List(opts v1.ListOptions) (result *v1alpha3.CanaryList, err error) { - obj, err := c.Fake. - Invokes(testing.NewListAction(canariesResource, canariesKind, c.ns, opts), &v1alpha3.CanaryList{}) - - if obj == nil { - return nil, err - } - - label, _, _ := testing.ExtractFromListOptions(opts) - if label == nil { - label = labels.Everything() - } - list := &v1alpha3.CanaryList{ListMeta: obj.(*v1alpha3.CanaryList).ListMeta} - for _, item := range obj.(*v1alpha3.CanaryList).Items { - if label.Matches(labels.Set(item.Labels)) { - list.Items = append(list.Items, item) - } - } - return list, err -} - -// Watch returns a watch.Interface that watches the requested canaries. -func (c *FakeCanaries) Watch(opts v1.ListOptions) (watch.Interface, error) { - return c.Fake. - InvokesWatch(testing.NewWatchAction(canariesResource, c.ns, opts)) - -} - -// Create takes the representation of a canary and creates it. Returns the server's representation of the canary, and an error, if there is any. -func (c *FakeCanaries) Create(canary *v1alpha3.Canary) (result *v1alpha3.Canary, err error) { - obj, err := c.Fake. - Invokes(testing.NewCreateAction(canariesResource, c.ns, canary), &v1alpha3.Canary{}) - - if obj == nil { - return nil, err - } - return obj.(*v1alpha3.Canary), err -} - -// Update takes the representation of a canary and updates it. Returns the server's representation of the canary, and an error, if there is any. -func (c *FakeCanaries) Update(canary *v1alpha3.Canary) (result *v1alpha3.Canary, err error) { - obj, err := c.Fake. - Invokes(testing.NewUpdateAction(canariesResource, c.ns, canary), &v1alpha3.Canary{}) - - if obj == nil { - return nil, err - } - return obj.(*v1alpha3.Canary), err -} - -// UpdateStatus was generated because the type contains a Status member. -// Add a +genclient:noStatus comment above the type to avoid generating UpdateStatus(). -func (c *FakeCanaries) UpdateStatus(canary *v1alpha3.Canary) (*v1alpha3.Canary, error) { - obj, err := c.Fake. - Invokes(testing.NewUpdateSubresourceAction(canariesResource, "status", c.ns, canary), &v1alpha3.Canary{}) - - if obj == nil { - return nil, err - } - return obj.(*v1alpha3.Canary), err -} - -// Delete takes name of the canary and deletes it. Returns an error if one occurs. -func (c *FakeCanaries) Delete(name string, options *v1.DeleteOptions) error { - _, err := c.Fake. - Invokes(testing.NewDeleteAction(canariesResource, c.ns, name), &v1alpha3.Canary{}) - - return err -} - -// DeleteCollection deletes a collection of objects. -func (c *FakeCanaries) DeleteCollection(options *v1.DeleteOptions, listOptions v1.ListOptions) error { - action := testing.NewDeleteCollectionAction(canariesResource, c.ns, listOptions) - - _, err := c.Fake.Invokes(action, &v1alpha3.CanaryList{}) - return err -} - -// Patch applies the patch and returns the patched canary. -func (c *FakeCanaries) Patch(name string, pt types.PatchType, data []byte, subresources ...string) (result *v1alpha3.Canary, err error) { - obj, err := c.Fake. - Invokes(testing.NewPatchSubresourceAction(canariesResource, c.ns, name, pt, data, subresources...), &v1alpha3.Canary{}) - - if obj == nil { - return nil, err - } - return obj.(*v1alpha3.Canary), err -} diff --git a/pkg/client/clientset/versioned/typed/flagger/v1alpha3/fake/fake_flagger_client.go b/pkg/client/clientset/versioned/typed/flagger/v1alpha3/fake/fake_flagger_client.go deleted file mode 100644 index d26224ea..00000000 --- a/pkg/client/clientset/versioned/typed/flagger/v1alpha3/fake/fake_flagger_client.go +++ /dev/null @@ -1,40 +0,0 @@ -/* -Copyright The Flagger Authors. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -// Code generated by client-gen. DO NOT EDIT. - -package fake - -import ( - v1alpha3 "github.com/weaveworks/flagger/pkg/client/clientset/versioned/typed/flagger/v1alpha3" - rest "k8s.io/client-go/rest" - testing "k8s.io/client-go/testing" -) - -type FakeFlaggerV1alpha3 struct { - *testing.Fake -} - -func (c *FakeFlaggerV1alpha3) Canaries(namespace string) v1alpha3.CanaryInterface { - return &FakeCanaries{c, namespace} -} - -// RESTClient returns a RESTClient that is used to communicate -// with API server by this client implementation. -func (c *FakeFlaggerV1alpha3) RESTClient() rest.Interface { - var ret *rest.RESTClient - return ret -} diff --git a/pkg/client/clientset/versioned/typed/flagger/v1alpha3/flagger_client.go b/pkg/client/clientset/versioned/typed/flagger/v1alpha3/flagger_client.go deleted file mode 100644 index 75765dac..00000000 --- a/pkg/client/clientset/versioned/typed/flagger/v1alpha3/flagger_client.go +++ /dev/null @@ -1,89 +0,0 @@ -/* -Copyright The Flagger Authors. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -// Code generated by client-gen. DO NOT EDIT. - -package v1alpha3 - -import ( - v1alpha3 "github.com/weaveworks/flagger/pkg/apis/flagger/v1alpha3" - "github.com/weaveworks/flagger/pkg/client/clientset/versioned/scheme" - rest "k8s.io/client-go/rest" -) - -type FlaggerV1alpha3Interface interface { - RESTClient() rest.Interface - CanariesGetter -} - -// FlaggerV1alpha3Client is used to interact with features provided by the flagger.app group. -type FlaggerV1alpha3Client struct { - restClient rest.Interface -} - -func (c *FlaggerV1alpha3Client) Canaries(namespace string) CanaryInterface { - return newCanaries(c, namespace) -} - -// NewForConfig creates a new FlaggerV1alpha3Client for the given config. -func NewForConfig(c *rest.Config) (*FlaggerV1alpha3Client, error) { - config := *c - if err := setConfigDefaults(&config); err != nil { - return nil, err - } - client, err := rest.RESTClientFor(&config) - if err != nil { - return nil, err - } - return &FlaggerV1alpha3Client{client}, nil -} - -// NewForConfigOrDie creates a new FlaggerV1alpha3Client for the given config and -// panics if there is an error in the config. -func NewForConfigOrDie(c *rest.Config) *FlaggerV1alpha3Client { - client, err := NewForConfig(c) - if err != nil { - panic(err) - } - return client -} - -// New creates a new FlaggerV1alpha3Client for the given RESTClient. -func New(c rest.Interface) *FlaggerV1alpha3Client { - return &FlaggerV1alpha3Client{c} -} - -func setConfigDefaults(config *rest.Config) error { - gv := v1alpha3.SchemeGroupVersion - config.GroupVersion = &gv - config.APIPath = "/apis" - config.NegotiatedSerializer = scheme.Codecs.WithoutConversion() - - if config.UserAgent == "" { - config.UserAgent = rest.DefaultKubernetesUserAgent() - } - - return nil -} - -// RESTClient returns a RESTClient that is used to communicate -// with API server by this client implementation. -func (c *FlaggerV1alpha3Client) RESTClient() rest.Interface { - if c == nil { - return nil - } - return c.restClient -} diff --git a/pkg/client/clientset/versioned/typed/flagger/v1alpha3/generated_expansion.go b/pkg/client/clientset/versioned/typed/flagger/v1alpha3/generated_expansion.go deleted file mode 100644 index b6e80265..00000000 --- a/pkg/client/clientset/versioned/typed/flagger/v1alpha3/generated_expansion.go +++ /dev/null @@ -1,21 +0,0 @@ -/* -Copyright The Flagger Authors. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -// Code generated by client-gen. DO NOT EDIT. - -package v1alpha3 - -type CanaryExpansion interface{} diff --git a/pkg/client/clientset/versioned/typed/istio/v1alpha3/destinationrule.go b/pkg/client/clientset/versioned/typed/istio/v1alpha3/destinationrule.go deleted file mode 100644 index 5328b3ce..00000000 --- a/pkg/client/clientset/versioned/typed/istio/v1alpha3/destinationrule.go +++ /dev/null @@ -1,174 +0,0 @@ -/* -Copyright The Flagger Authors. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -// Code generated by client-gen. DO NOT EDIT. - -package v1alpha3 - -import ( - "time" - - v1alpha3 "github.com/weaveworks/flagger/pkg/apis/istio/v1alpha3" - scheme "github.com/weaveworks/flagger/pkg/client/clientset/versioned/scheme" - v1 "k8s.io/apimachinery/pkg/apis/meta/v1" - types "k8s.io/apimachinery/pkg/types" - watch "k8s.io/apimachinery/pkg/watch" - rest "k8s.io/client-go/rest" -) - -// DestinationRulesGetter has a method to return a DestinationRuleInterface. -// A group's client should implement this interface. -type DestinationRulesGetter interface { - DestinationRules(namespace string) DestinationRuleInterface -} - -// DestinationRuleInterface has methods to work with DestinationRule resources. -type DestinationRuleInterface interface { - Create(*v1alpha3.DestinationRule) (*v1alpha3.DestinationRule, error) - Update(*v1alpha3.DestinationRule) (*v1alpha3.DestinationRule, error) - Delete(name string, options *v1.DeleteOptions) error - DeleteCollection(options *v1.DeleteOptions, listOptions v1.ListOptions) error - Get(name string, options v1.GetOptions) (*v1alpha3.DestinationRule, error) - List(opts v1.ListOptions) (*v1alpha3.DestinationRuleList, error) - Watch(opts v1.ListOptions) (watch.Interface, error) - Patch(name string, pt types.PatchType, data []byte, subresources ...string) (result *v1alpha3.DestinationRule, err error) - DestinationRuleExpansion -} - -// destinationRules implements DestinationRuleInterface -type destinationRules struct { - client rest.Interface - ns string -} - -// newDestinationRules returns a DestinationRules -func newDestinationRules(c *NetworkingV1alpha3Client, namespace string) *destinationRules { - return &destinationRules{ - client: c.RESTClient(), - ns: namespace, - } -} - -// Get takes name of the destinationRule, and returns the corresponding destinationRule object, and an error if there is any. -func (c *destinationRules) Get(name string, options v1.GetOptions) (result *v1alpha3.DestinationRule, err error) { - result = &v1alpha3.DestinationRule{} - err = c.client.Get(). - Namespace(c.ns). - Resource("destinationrules"). - Name(name). - VersionedParams(&options, scheme.ParameterCodec). - Do(). - Into(result) - return -} - -// List takes label and field selectors, and returns the list of DestinationRules that match those selectors. -func (c *destinationRules) List(opts v1.ListOptions) (result *v1alpha3.DestinationRuleList, err error) { - var timeout time.Duration - if opts.TimeoutSeconds != nil { - timeout = time.Duration(*opts.TimeoutSeconds) * time.Second - } - result = &v1alpha3.DestinationRuleList{} - err = c.client.Get(). - Namespace(c.ns). - Resource("destinationrules"). - VersionedParams(&opts, scheme.ParameterCodec). - Timeout(timeout). - Do(). - Into(result) - return -} - -// Watch returns a watch.Interface that watches the requested destinationRules. -func (c *destinationRules) Watch(opts v1.ListOptions) (watch.Interface, error) { - var timeout time.Duration - if opts.TimeoutSeconds != nil { - timeout = time.Duration(*opts.TimeoutSeconds) * time.Second - } - opts.Watch = true - return c.client.Get(). - Namespace(c.ns). - Resource("destinationrules"). - VersionedParams(&opts, scheme.ParameterCodec). - Timeout(timeout). - Watch() -} - -// Create takes the representation of a destinationRule and creates it. Returns the server's representation of the destinationRule, and an error, if there is any. -func (c *destinationRules) Create(destinationRule *v1alpha3.DestinationRule) (result *v1alpha3.DestinationRule, err error) { - result = &v1alpha3.DestinationRule{} - err = c.client.Post(). - Namespace(c.ns). - Resource("destinationrules"). - Body(destinationRule). - Do(). - Into(result) - return -} - -// Update takes the representation of a destinationRule and updates it. Returns the server's representation of the destinationRule, and an error, if there is any. -func (c *destinationRules) Update(destinationRule *v1alpha3.DestinationRule) (result *v1alpha3.DestinationRule, err error) { - result = &v1alpha3.DestinationRule{} - err = c.client.Put(). - Namespace(c.ns). - Resource("destinationrules"). - Name(destinationRule.Name). - Body(destinationRule). - Do(). - Into(result) - return -} - -// Delete takes name of the destinationRule and deletes it. Returns an error if one occurs. -func (c *destinationRules) Delete(name string, options *v1.DeleteOptions) error { - return c.client.Delete(). - Namespace(c.ns). - Resource("destinationrules"). - Name(name). - Body(options). - Do(). - Error() -} - -// DeleteCollection deletes a collection of objects. -func (c *destinationRules) DeleteCollection(options *v1.DeleteOptions, listOptions v1.ListOptions) error { - var timeout time.Duration - if listOptions.TimeoutSeconds != nil { - timeout = time.Duration(*listOptions.TimeoutSeconds) * time.Second - } - return c.client.Delete(). - Namespace(c.ns). - Resource("destinationrules"). - VersionedParams(&listOptions, scheme.ParameterCodec). - Timeout(timeout). - Body(options). - Do(). - Error() -} - -// Patch applies the patch and returns the patched destinationRule. -func (c *destinationRules) Patch(name string, pt types.PatchType, data []byte, subresources ...string) (result *v1alpha3.DestinationRule, err error) { - result = &v1alpha3.DestinationRule{} - err = c.client.Patch(pt). - Namespace(c.ns). - Resource("destinationrules"). - SubResource(subresources...). - Name(name). - Body(data). - Do(). - Into(result) - return -} diff --git a/pkg/client/clientset/versioned/typed/istio/v1alpha3/doc.go b/pkg/client/clientset/versioned/typed/istio/v1alpha3/doc.go deleted file mode 100644 index d49e3fd3..00000000 --- a/pkg/client/clientset/versioned/typed/istio/v1alpha3/doc.go +++ /dev/null @@ -1,20 +0,0 @@ -/* -Copyright The Flagger Authors. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -// Code generated by client-gen. DO NOT EDIT. - -// This package has the automatically generated typed clients. -package v1alpha3 diff --git a/pkg/client/clientset/versioned/typed/istio/v1alpha3/fake/doc.go b/pkg/client/clientset/versioned/typed/istio/v1alpha3/fake/doc.go deleted file mode 100644 index 7a3b19cb..00000000 --- a/pkg/client/clientset/versioned/typed/istio/v1alpha3/fake/doc.go +++ /dev/null @@ -1,20 +0,0 @@ -/* -Copyright The Flagger Authors. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -// Code generated by client-gen. DO NOT EDIT. - -// Package fake has the automatically generated clients. -package fake diff --git a/pkg/client/clientset/versioned/typed/istio/v1alpha3/fake/fake_destinationrule.go b/pkg/client/clientset/versioned/typed/istio/v1alpha3/fake/fake_destinationrule.go deleted file mode 100644 index 12197a07..00000000 --- a/pkg/client/clientset/versioned/typed/istio/v1alpha3/fake/fake_destinationrule.go +++ /dev/null @@ -1,128 +0,0 @@ -/* -Copyright The Flagger Authors. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -// Code generated by client-gen. DO NOT EDIT. - -package fake - -import ( - v1alpha3 "github.com/weaveworks/flagger/pkg/apis/istio/v1alpha3" - v1 "k8s.io/apimachinery/pkg/apis/meta/v1" - labels "k8s.io/apimachinery/pkg/labels" - schema "k8s.io/apimachinery/pkg/runtime/schema" - types "k8s.io/apimachinery/pkg/types" - watch "k8s.io/apimachinery/pkg/watch" - testing "k8s.io/client-go/testing" -) - -// FakeDestinationRules implements DestinationRuleInterface -type FakeDestinationRules struct { - Fake *FakeNetworkingV1alpha3 - ns string -} - -var destinationrulesResource = schema.GroupVersionResource{Group: "networking.istio.io", Version: "v1alpha3", Resource: "destinationrules"} - -var destinationrulesKind = schema.GroupVersionKind{Group: "networking.istio.io", Version: "v1alpha3", Kind: "DestinationRule"} - -// Get takes name of the destinationRule, and returns the corresponding destinationRule object, and an error if there is any. -func (c *FakeDestinationRules) Get(name string, options v1.GetOptions) (result *v1alpha3.DestinationRule, err error) { - obj, err := c.Fake. - Invokes(testing.NewGetAction(destinationrulesResource, c.ns, name), &v1alpha3.DestinationRule{}) - - if obj == nil { - return nil, err - } - return obj.(*v1alpha3.DestinationRule), err -} - -// List takes label and field selectors, and returns the list of DestinationRules that match those selectors. -func (c *FakeDestinationRules) List(opts v1.ListOptions) (result *v1alpha3.DestinationRuleList, err error) { - obj, err := c.Fake. - Invokes(testing.NewListAction(destinationrulesResource, destinationrulesKind, c.ns, opts), &v1alpha3.DestinationRuleList{}) - - if obj == nil { - return nil, err - } - - label, _, _ := testing.ExtractFromListOptions(opts) - if label == nil { - label = labels.Everything() - } - list := &v1alpha3.DestinationRuleList{ListMeta: obj.(*v1alpha3.DestinationRuleList).ListMeta} - for _, item := range obj.(*v1alpha3.DestinationRuleList).Items { - if label.Matches(labels.Set(item.Labels)) { - list.Items = append(list.Items, item) - } - } - return list, err -} - -// Watch returns a watch.Interface that watches the requested destinationRules. -func (c *FakeDestinationRules) Watch(opts v1.ListOptions) (watch.Interface, error) { - return c.Fake. - InvokesWatch(testing.NewWatchAction(destinationrulesResource, c.ns, opts)) - -} - -// Create takes the representation of a destinationRule and creates it. Returns the server's representation of the destinationRule, and an error, if there is any. -func (c *FakeDestinationRules) Create(destinationRule *v1alpha3.DestinationRule) (result *v1alpha3.DestinationRule, err error) { - obj, err := c.Fake. - Invokes(testing.NewCreateAction(destinationrulesResource, c.ns, destinationRule), &v1alpha3.DestinationRule{}) - - if obj == nil { - return nil, err - } - return obj.(*v1alpha3.DestinationRule), err -} - -// Update takes the representation of a destinationRule and updates it. Returns the server's representation of the destinationRule, and an error, if there is any. -func (c *FakeDestinationRules) Update(destinationRule *v1alpha3.DestinationRule) (result *v1alpha3.DestinationRule, err error) { - obj, err := c.Fake. - Invokes(testing.NewUpdateAction(destinationrulesResource, c.ns, destinationRule), &v1alpha3.DestinationRule{}) - - if obj == nil { - return nil, err - } - return obj.(*v1alpha3.DestinationRule), err -} - -// Delete takes name of the destinationRule and deletes it. Returns an error if one occurs. -func (c *FakeDestinationRules) Delete(name string, options *v1.DeleteOptions) error { - _, err := c.Fake. - Invokes(testing.NewDeleteAction(destinationrulesResource, c.ns, name), &v1alpha3.DestinationRule{}) - - return err -} - -// DeleteCollection deletes a collection of objects. -func (c *FakeDestinationRules) DeleteCollection(options *v1.DeleteOptions, listOptions v1.ListOptions) error { - action := testing.NewDeleteCollectionAction(destinationrulesResource, c.ns, listOptions) - - _, err := c.Fake.Invokes(action, &v1alpha3.DestinationRuleList{}) - return err -} - -// Patch applies the patch and returns the patched destinationRule. -func (c *FakeDestinationRules) Patch(name string, pt types.PatchType, data []byte, subresources ...string) (result *v1alpha3.DestinationRule, err error) { - obj, err := c.Fake. - Invokes(testing.NewPatchSubresourceAction(destinationrulesResource, c.ns, name, pt, data, subresources...), &v1alpha3.DestinationRule{}) - - if obj == nil { - return nil, err - } - return obj.(*v1alpha3.DestinationRule), err -} diff --git a/pkg/client/clientset/versioned/typed/istio/v1alpha3/fake/fake_istio_client.go b/pkg/client/clientset/versioned/typed/istio/v1alpha3/fake/fake_istio_client.go deleted file mode 100644 index ab698310..00000000 --- a/pkg/client/clientset/versioned/typed/istio/v1alpha3/fake/fake_istio_client.go +++ /dev/null @@ -1,44 +0,0 @@ -/* -Copyright The Flagger Authors. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -// Code generated by client-gen. DO NOT EDIT. - -package fake - -import ( - v1alpha3 "github.com/weaveworks/flagger/pkg/client/clientset/versioned/typed/istio/v1alpha3" - rest "k8s.io/client-go/rest" - testing "k8s.io/client-go/testing" -) - -type FakeNetworkingV1alpha3 struct { - *testing.Fake -} - -func (c *FakeNetworkingV1alpha3) DestinationRules(namespace string) v1alpha3.DestinationRuleInterface { - return &FakeDestinationRules{c, namespace} -} - -func (c *FakeNetworkingV1alpha3) VirtualServices(namespace string) v1alpha3.VirtualServiceInterface { - return &FakeVirtualServices{c, namespace} -} - -// RESTClient returns a RESTClient that is used to communicate -// with API server by this client implementation. -func (c *FakeNetworkingV1alpha3) RESTClient() rest.Interface { - var ret *rest.RESTClient - return ret -} diff --git a/pkg/client/clientset/versioned/typed/istio/v1alpha3/fake/fake_virtualservice.go b/pkg/client/clientset/versioned/typed/istio/v1alpha3/fake/fake_virtualservice.go deleted file mode 100644 index c49ae2f5..00000000 --- a/pkg/client/clientset/versioned/typed/istio/v1alpha3/fake/fake_virtualservice.go +++ /dev/null @@ -1,128 +0,0 @@ -/* -Copyright The Flagger Authors. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -// Code generated by client-gen. DO NOT EDIT. - -package fake - -import ( - v1alpha3 "github.com/weaveworks/flagger/pkg/apis/istio/v1alpha3" - v1 "k8s.io/apimachinery/pkg/apis/meta/v1" - labels "k8s.io/apimachinery/pkg/labels" - schema "k8s.io/apimachinery/pkg/runtime/schema" - types "k8s.io/apimachinery/pkg/types" - watch "k8s.io/apimachinery/pkg/watch" - testing "k8s.io/client-go/testing" -) - -// FakeVirtualServices implements VirtualServiceInterface -type FakeVirtualServices struct { - Fake *FakeNetworkingV1alpha3 - ns string -} - -var virtualservicesResource = schema.GroupVersionResource{Group: "networking.istio.io", Version: "v1alpha3", Resource: "virtualservices"} - -var virtualservicesKind = schema.GroupVersionKind{Group: "networking.istio.io", Version: "v1alpha3", Kind: "VirtualService"} - -// Get takes name of the virtualService, and returns the corresponding virtualService object, and an error if there is any. -func (c *FakeVirtualServices) Get(name string, options v1.GetOptions) (result *v1alpha3.VirtualService, err error) { - obj, err := c.Fake. - Invokes(testing.NewGetAction(virtualservicesResource, c.ns, name), &v1alpha3.VirtualService{}) - - if obj == nil { - return nil, err - } - return obj.(*v1alpha3.VirtualService), err -} - -// List takes label and field selectors, and returns the list of VirtualServices that match those selectors. -func (c *FakeVirtualServices) List(opts v1.ListOptions) (result *v1alpha3.VirtualServiceList, err error) { - obj, err := c.Fake. - Invokes(testing.NewListAction(virtualservicesResource, virtualservicesKind, c.ns, opts), &v1alpha3.VirtualServiceList{}) - - if obj == nil { - return nil, err - } - - label, _, _ := testing.ExtractFromListOptions(opts) - if label == nil { - label = labels.Everything() - } - list := &v1alpha3.VirtualServiceList{ListMeta: obj.(*v1alpha3.VirtualServiceList).ListMeta} - for _, item := range obj.(*v1alpha3.VirtualServiceList).Items { - if label.Matches(labels.Set(item.Labels)) { - list.Items = append(list.Items, item) - } - } - return list, err -} - -// Watch returns a watch.Interface that watches the requested virtualServices. -func (c *FakeVirtualServices) Watch(opts v1.ListOptions) (watch.Interface, error) { - return c.Fake. - InvokesWatch(testing.NewWatchAction(virtualservicesResource, c.ns, opts)) - -} - -// Create takes the representation of a virtualService and creates it. Returns the server's representation of the virtualService, and an error, if there is any. -func (c *FakeVirtualServices) Create(virtualService *v1alpha3.VirtualService) (result *v1alpha3.VirtualService, err error) { - obj, err := c.Fake. - Invokes(testing.NewCreateAction(virtualservicesResource, c.ns, virtualService), &v1alpha3.VirtualService{}) - - if obj == nil { - return nil, err - } - return obj.(*v1alpha3.VirtualService), err -} - -// Update takes the representation of a virtualService and updates it. Returns the server's representation of the virtualService, and an error, if there is any. -func (c *FakeVirtualServices) Update(virtualService *v1alpha3.VirtualService) (result *v1alpha3.VirtualService, err error) { - obj, err := c.Fake. - Invokes(testing.NewUpdateAction(virtualservicesResource, c.ns, virtualService), &v1alpha3.VirtualService{}) - - if obj == nil { - return nil, err - } - return obj.(*v1alpha3.VirtualService), err -} - -// Delete takes name of the virtualService and deletes it. Returns an error if one occurs. -func (c *FakeVirtualServices) Delete(name string, options *v1.DeleteOptions) error { - _, err := c.Fake. - Invokes(testing.NewDeleteAction(virtualservicesResource, c.ns, name), &v1alpha3.VirtualService{}) - - return err -} - -// DeleteCollection deletes a collection of objects. -func (c *FakeVirtualServices) DeleteCollection(options *v1.DeleteOptions, listOptions v1.ListOptions) error { - action := testing.NewDeleteCollectionAction(virtualservicesResource, c.ns, listOptions) - - _, err := c.Fake.Invokes(action, &v1alpha3.VirtualServiceList{}) - return err -} - -// Patch applies the patch and returns the patched virtualService. -func (c *FakeVirtualServices) Patch(name string, pt types.PatchType, data []byte, subresources ...string) (result *v1alpha3.VirtualService, err error) { - obj, err := c.Fake. - Invokes(testing.NewPatchSubresourceAction(virtualservicesResource, c.ns, name, pt, data, subresources...), &v1alpha3.VirtualService{}) - - if obj == nil { - return nil, err - } - return obj.(*v1alpha3.VirtualService), err -} diff --git a/pkg/client/clientset/versioned/typed/istio/v1alpha3/generated_expansion.go b/pkg/client/clientset/versioned/typed/istio/v1alpha3/generated_expansion.go deleted file mode 100644 index da0d96c9..00000000 --- a/pkg/client/clientset/versioned/typed/istio/v1alpha3/generated_expansion.go +++ /dev/null @@ -1,23 +0,0 @@ -/* -Copyright The Flagger Authors. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -// Code generated by client-gen. DO NOT EDIT. - -package v1alpha3 - -type DestinationRuleExpansion interface{} - -type VirtualServiceExpansion interface{} diff --git a/pkg/client/clientset/versioned/typed/istio/v1alpha3/istio_client.go b/pkg/client/clientset/versioned/typed/istio/v1alpha3/istio_client.go deleted file mode 100644 index f5ad3f78..00000000 --- a/pkg/client/clientset/versioned/typed/istio/v1alpha3/istio_client.go +++ /dev/null @@ -1,94 +0,0 @@ -/* -Copyright The Flagger Authors. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -// Code generated by client-gen. DO NOT EDIT. - -package v1alpha3 - -import ( - v1alpha3 "github.com/weaveworks/flagger/pkg/apis/istio/v1alpha3" - "github.com/weaveworks/flagger/pkg/client/clientset/versioned/scheme" - rest "k8s.io/client-go/rest" -) - -type NetworkingV1alpha3Interface interface { - RESTClient() rest.Interface - DestinationRulesGetter - VirtualServicesGetter -} - -// NetworkingV1alpha3Client is used to interact with features provided by the networking.istio.io group. -type NetworkingV1alpha3Client struct { - restClient rest.Interface -} - -func (c *NetworkingV1alpha3Client) DestinationRules(namespace string) DestinationRuleInterface { - return newDestinationRules(c, namespace) -} - -func (c *NetworkingV1alpha3Client) VirtualServices(namespace string) VirtualServiceInterface { - return newVirtualServices(c, namespace) -} - -// NewForConfig creates a new NetworkingV1alpha3Client for the given config. -func NewForConfig(c *rest.Config) (*NetworkingV1alpha3Client, error) { - config := *c - if err := setConfigDefaults(&config); err != nil { - return nil, err - } - client, err := rest.RESTClientFor(&config) - if err != nil { - return nil, err - } - return &NetworkingV1alpha3Client{client}, nil -} - -// NewForConfigOrDie creates a new NetworkingV1alpha3Client for the given config and -// panics if there is an error in the config. -func NewForConfigOrDie(c *rest.Config) *NetworkingV1alpha3Client { - client, err := NewForConfig(c) - if err != nil { - panic(err) - } - return client -} - -// New creates a new NetworkingV1alpha3Client for the given RESTClient. -func New(c rest.Interface) *NetworkingV1alpha3Client { - return &NetworkingV1alpha3Client{c} -} - -func setConfigDefaults(config *rest.Config) error { - gv := v1alpha3.SchemeGroupVersion - config.GroupVersion = &gv - config.APIPath = "/apis" - config.NegotiatedSerializer = scheme.Codecs.WithoutConversion() - - if config.UserAgent == "" { - config.UserAgent = rest.DefaultKubernetesUserAgent() - } - - return nil -} - -// RESTClient returns a RESTClient that is used to communicate -// with API server by this client implementation. -func (c *NetworkingV1alpha3Client) RESTClient() rest.Interface { - if c == nil { - return nil - } - return c.restClient -} diff --git a/pkg/client/clientset/versioned/typed/istio/v1alpha3/virtualservice.go b/pkg/client/clientset/versioned/typed/istio/v1alpha3/virtualservice.go deleted file mode 100644 index 82ff4462..00000000 --- a/pkg/client/clientset/versioned/typed/istio/v1alpha3/virtualservice.go +++ /dev/null @@ -1,174 +0,0 @@ -/* -Copyright The Flagger Authors. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -// Code generated by client-gen. DO NOT EDIT. - -package v1alpha3 - -import ( - "time" - - v1alpha3 "github.com/weaveworks/flagger/pkg/apis/istio/v1alpha3" - scheme "github.com/weaveworks/flagger/pkg/client/clientset/versioned/scheme" - v1 "k8s.io/apimachinery/pkg/apis/meta/v1" - types "k8s.io/apimachinery/pkg/types" - watch "k8s.io/apimachinery/pkg/watch" - rest "k8s.io/client-go/rest" -) - -// VirtualServicesGetter has a method to return a VirtualServiceInterface. -// A group's client should implement this interface. -type VirtualServicesGetter interface { - VirtualServices(namespace string) VirtualServiceInterface -} - -// VirtualServiceInterface has methods to work with VirtualService resources. -type VirtualServiceInterface interface { - Create(*v1alpha3.VirtualService) (*v1alpha3.VirtualService, error) - Update(*v1alpha3.VirtualService) (*v1alpha3.VirtualService, error) - Delete(name string, options *v1.DeleteOptions) error - DeleteCollection(options *v1.DeleteOptions, listOptions v1.ListOptions) error - Get(name string, options v1.GetOptions) (*v1alpha3.VirtualService, error) - List(opts v1.ListOptions) (*v1alpha3.VirtualServiceList, error) - Watch(opts v1.ListOptions) (watch.Interface, error) - Patch(name string, pt types.PatchType, data []byte, subresources ...string) (result *v1alpha3.VirtualService, err error) - VirtualServiceExpansion -} - -// virtualServices implements VirtualServiceInterface -type virtualServices struct { - client rest.Interface - ns string -} - -// newVirtualServices returns a VirtualServices -func newVirtualServices(c *NetworkingV1alpha3Client, namespace string) *virtualServices { - return &virtualServices{ - client: c.RESTClient(), - ns: namespace, - } -} - -// Get takes name of the virtualService, and returns the corresponding virtualService object, and an error if there is any. -func (c *virtualServices) Get(name string, options v1.GetOptions) (result *v1alpha3.VirtualService, err error) { - result = &v1alpha3.VirtualService{} - err = c.client.Get(). - Namespace(c.ns). - Resource("virtualservices"). - Name(name). - VersionedParams(&options, scheme.ParameterCodec). - Do(). - Into(result) - return -} - -// List takes label and field selectors, and returns the list of VirtualServices that match those selectors. -func (c *virtualServices) List(opts v1.ListOptions) (result *v1alpha3.VirtualServiceList, err error) { - var timeout time.Duration - if opts.TimeoutSeconds != nil { - timeout = time.Duration(*opts.TimeoutSeconds) * time.Second - } - result = &v1alpha3.VirtualServiceList{} - err = c.client.Get(). - Namespace(c.ns). - Resource("virtualservices"). - VersionedParams(&opts, scheme.ParameterCodec). - Timeout(timeout). - Do(). - Into(result) - return -} - -// Watch returns a watch.Interface that watches the requested virtualServices. -func (c *virtualServices) Watch(opts v1.ListOptions) (watch.Interface, error) { - var timeout time.Duration - if opts.TimeoutSeconds != nil { - timeout = time.Duration(*opts.TimeoutSeconds) * time.Second - } - opts.Watch = true - return c.client.Get(). - Namespace(c.ns). - Resource("virtualservices"). - VersionedParams(&opts, scheme.ParameterCodec). - Timeout(timeout). - Watch() -} - -// Create takes the representation of a virtualService and creates it. Returns the server's representation of the virtualService, and an error, if there is any. -func (c *virtualServices) Create(virtualService *v1alpha3.VirtualService) (result *v1alpha3.VirtualService, err error) { - result = &v1alpha3.VirtualService{} - err = c.client.Post(). - Namespace(c.ns). - Resource("virtualservices"). - Body(virtualService). - Do(). - Into(result) - return -} - -// Update takes the representation of a virtualService and updates it. Returns the server's representation of the virtualService, and an error, if there is any. -func (c *virtualServices) Update(virtualService *v1alpha3.VirtualService) (result *v1alpha3.VirtualService, err error) { - result = &v1alpha3.VirtualService{} - err = c.client.Put(). - Namespace(c.ns). - Resource("virtualservices"). - Name(virtualService.Name). - Body(virtualService). - Do(). - Into(result) - return -} - -// Delete takes name of the virtualService and deletes it. Returns an error if one occurs. -func (c *virtualServices) Delete(name string, options *v1.DeleteOptions) error { - return c.client.Delete(). - Namespace(c.ns). - Resource("virtualservices"). - Name(name). - Body(options). - Do(). - Error() -} - -// DeleteCollection deletes a collection of objects. -func (c *virtualServices) DeleteCollection(options *v1.DeleteOptions, listOptions v1.ListOptions) error { - var timeout time.Duration - if listOptions.TimeoutSeconds != nil { - timeout = time.Duration(*listOptions.TimeoutSeconds) * time.Second - } - return c.client.Delete(). - Namespace(c.ns). - Resource("virtualservices"). - VersionedParams(&listOptions, scheme.ParameterCodec). - Timeout(timeout). - Body(options). - Do(). - Error() -} - -// Patch applies the patch and returns the patched virtualService. -func (c *virtualServices) Patch(name string, pt types.PatchType, data []byte, subresources ...string) (result *v1alpha3.VirtualService, err error) { - result = &v1alpha3.VirtualService{} - err = c.client.Patch(pt). - Namespace(c.ns). - Resource("virtualservices"). - SubResource(subresources...). - Name(name). - Body(data). - Do(). - Into(result) - return -} diff --git a/pkg/client/clientset/versioned/typed/smi/v1alpha1/doc.go b/pkg/client/clientset/versioned/typed/smi/v1alpha1/doc.go deleted file mode 100644 index 20b3d7fd..00000000 --- a/pkg/client/clientset/versioned/typed/smi/v1alpha1/doc.go +++ /dev/null @@ -1,20 +0,0 @@ -/* -Copyright The Flagger Authors. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -// Code generated by client-gen. DO NOT EDIT. - -// This package has the automatically generated typed clients. -package v1alpha1 diff --git a/pkg/client/clientset/versioned/typed/smi/v1alpha1/fake/doc.go b/pkg/client/clientset/versioned/typed/smi/v1alpha1/fake/doc.go deleted file mode 100644 index 7a3b19cb..00000000 --- a/pkg/client/clientset/versioned/typed/smi/v1alpha1/fake/doc.go +++ /dev/null @@ -1,20 +0,0 @@ -/* -Copyright The Flagger Authors. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -// Code generated by client-gen. DO NOT EDIT. - -// Package fake has the automatically generated clients. -package fake diff --git a/pkg/client/clientset/versioned/typed/smi/v1alpha1/fake/fake_smi_client.go b/pkg/client/clientset/versioned/typed/smi/v1alpha1/fake/fake_smi_client.go deleted file mode 100644 index e3cf89ed..00000000 --- a/pkg/client/clientset/versioned/typed/smi/v1alpha1/fake/fake_smi_client.go +++ /dev/null @@ -1,40 +0,0 @@ -/* -Copyright The Flagger Authors. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -// Code generated by client-gen. DO NOT EDIT. - -package fake - -import ( - v1alpha1 "github.com/weaveworks/flagger/pkg/client/clientset/versioned/typed/smi/v1alpha1" - rest "k8s.io/client-go/rest" - testing "k8s.io/client-go/testing" -) - -type FakeSplitV1alpha1 struct { - *testing.Fake -} - -func (c *FakeSplitV1alpha1) TrafficSplits(namespace string) v1alpha1.TrafficSplitInterface { - return &FakeTrafficSplits{c, namespace} -} - -// RESTClient returns a RESTClient that is used to communicate -// with API server by this client implementation. -func (c *FakeSplitV1alpha1) RESTClient() rest.Interface { - var ret *rest.RESTClient - return ret -} diff --git a/pkg/client/clientset/versioned/typed/smi/v1alpha1/fake/fake_trafficsplit.go b/pkg/client/clientset/versioned/typed/smi/v1alpha1/fake/fake_trafficsplit.go deleted file mode 100644 index ac6b67cc..00000000 --- a/pkg/client/clientset/versioned/typed/smi/v1alpha1/fake/fake_trafficsplit.go +++ /dev/null @@ -1,128 +0,0 @@ -/* -Copyright The Flagger Authors. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -// Code generated by client-gen. DO NOT EDIT. - -package fake - -import ( - v1alpha1 "github.com/weaveworks/flagger/pkg/apis/smi/v1alpha1" - v1 "k8s.io/apimachinery/pkg/apis/meta/v1" - labels "k8s.io/apimachinery/pkg/labels" - schema "k8s.io/apimachinery/pkg/runtime/schema" - types "k8s.io/apimachinery/pkg/types" - watch "k8s.io/apimachinery/pkg/watch" - testing "k8s.io/client-go/testing" -) - -// FakeTrafficSplits implements TrafficSplitInterface -type FakeTrafficSplits struct { - Fake *FakeSplitV1alpha1 - ns string -} - -var trafficsplitsResource = schema.GroupVersionResource{Group: "split.smi-spec.io", Version: "v1alpha1", Resource: "trafficsplits"} - -var trafficsplitsKind = schema.GroupVersionKind{Group: "split.smi-spec.io", Version: "v1alpha1", Kind: "TrafficSplit"} - -// Get takes name of the trafficSplit, and returns the corresponding trafficSplit object, and an error if there is any. -func (c *FakeTrafficSplits) Get(name string, options v1.GetOptions) (result *v1alpha1.TrafficSplit, err error) { - obj, err := c.Fake. - Invokes(testing.NewGetAction(trafficsplitsResource, c.ns, name), &v1alpha1.TrafficSplit{}) - - if obj == nil { - return nil, err - } - return obj.(*v1alpha1.TrafficSplit), err -} - -// List takes label and field selectors, and returns the list of TrafficSplits that match those selectors. -func (c *FakeTrafficSplits) List(opts v1.ListOptions) (result *v1alpha1.TrafficSplitList, err error) { - obj, err := c.Fake. - Invokes(testing.NewListAction(trafficsplitsResource, trafficsplitsKind, c.ns, opts), &v1alpha1.TrafficSplitList{}) - - if obj == nil { - return nil, err - } - - label, _, _ := testing.ExtractFromListOptions(opts) - if label == nil { - label = labels.Everything() - } - list := &v1alpha1.TrafficSplitList{ListMeta: obj.(*v1alpha1.TrafficSplitList).ListMeta} - for _, item := range obj.(*v1alpha1.TrafficSplitList).Items { - if label.Matches(labels.Set(item.Labels)) { - list.Items = append(list.Items, item) - } - } - return list, err -} - -// Watch returns a watch.Interface that watches the requested trafficSplits. -func (c *FakeTrafficSplits) Watch(opts v1.ListOptions) (watch.Interface, error) { - return c.Fake. - InvokesWatch(testing.NewWatchAction(trafficsplitsResource, c.ns, opts)) - -} - -// Create takes the representation of a trafficSplit and creates it. Returns the server's representation of the trafficSplit, and an error, if there is any. -func (c *FakeTrafficSplits) Create(trafficSplit *v1alpha1.TrafficSplit) (result *v1alpha1.TrafficSplit, err error) { - obj, err := c.Fake. - Invokes(testing.NewCreateAction(trafficsplitsResource, c.ns, trafficSplit), &v1alpha1.TrafficSplit{}) - - if obj == nil { - return nil, err - } - return obj.(*v1alpha1.TrafficSplit), err -} - -// Update takes the representation of a trafficSplit and updates it. Returns the server's representation of the trafficSplit, and an error, if there is any. -func (c *FakeTrafficSplits) Update(trafficSplit *v1alpha1.TrafficSplit) (result *v1alpha1.TrafficSplit, err error) { - obj, err := c.Fake. - Invokes(testing.NewUpdateAction(trafficsplitsResource, c.ns, trafficSplit), &v1alpha1.TrafficSplit{}) - - if obj == nil { - return nil, err - } - return obj.(*v1alpha1.TrafficSplit), err -} - -// Delete takes name of the trafficSplit and deletes it. Returns an error if one occurs. -func (c *FakeTrafficSplits) Delete(name string, options *v1.DeleteOptions) error { - _, err := c.Fake. - Invokes(testing.NewDeleteAction(trafficsplitsResource, c.ns, name), &v1alpha1.TrafficSplit{}) - - return err -} - -// DeleteCollection deletes a collection of objects. -func (c *FakeTrafficSplits) DeleteCollection(options *v1.DeleteOptions, listOptions v1.ListOptions) error { - action := testing.NewDeleteCollectionAction(trafficsplitsResource, c.ns, listOptions) - - _, err := c.Fake.Invokes(action, &v1alpha1.TrafficSplitList{}) - return err -} - -// Patch applies the patch and returns the patched trafficSplit. -func (c *FakeTrafficSplits) Patch(name string, pt types.PatchType, data []byte, subresources ...string) (result *v1alpha1.TrafficSplit, err error) { - obj, err := c.Fake. - Invokes(testing.NewPatchSubresourceAction(trafficsplitsResource, c.ns, name, pt, data, subresources...), &v1alpha1.TrafficSplit{}) - - if obj == nil { - return nil, err - } - return obj.(*v1alpha1.TrafficSplit), err -} diff --git a/pkg/client/clientset/versioned/typed/smi/v1alpha1/generated_expansion.go b/pkg/client/clientset/versioned/typed/smi/v1alpha1/generated_expansion.go deleted file mode 100644 index 4cc5c42a..00000000 --- a/pkg/client/clientset/versioned/typed/smi/v1alpha1/generated_expansion.go +++ /dev/null @@ -1,21 +0,0 @@ -/* -Copyright The Flagger Authors. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -// Code generated by client-gen. DO NOT EDIT. - -package v1alpha1 - -type TrafficSplitExpansion interface{} diff --git a/pkg/client/clientset/versioned/typed/smi/v1alpha1/smi_client.go b/pkg/client/clientset/versioned/typed/smi/v1alpha1/smi_client.go deleted file mode 100644 index 9e027f58..00000000 --- a/pkg/client/clientset/versioned/typed/smi/v1alpha1/smi_client.go +++ /dev/null @@ -1,89 +0,0 @@ -/* -Copyright The Flagger Authors. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -// Code generated by client-gen. DO NOT EDIT. - -package v1alpha1 - -import ( - v1alpha1 "github.com/weaveworks/flagger/pkg/apis/smi/v1alpha1" - "github.com/weaveworks/flagger/pkg/client/clientset/versioned/scheme" - rest "k8s.io/client-go/rest" -) - -type SplitV1alpha1Interface interface { - RESTClient() rest.Interface - TrafficSplitsGetter -} - -// SplitV1alpha1Client is used to interact with features provided by the split.smi-spec.io group. -type SplitV1alpha1Client struct { - restClient rest.Interface -} - -func (c *SplitV1alpha1Client) TrafficSplits(namespace string) TrafficSplitInterface { - return newTrafficSplits(c, namespace) -} - -// NewForConfig creates a new SplitV1alpha1Client for the given config. -func NewForConfig(c *rest.Config) (*SplitV1alpha1Client, error) { - config := *c - if err := setConfigDefaults(&config); err != nil { - return nil, err - } - client, err := rest.RESTClientFor(&config) - if err != nil { - return nil, err - } - return &SplitV1alpha1Client{client}, nil -} - -// NewForConfigOrDie creates a new SplitV1alpha1Client for the given config and -// panics if there is an error in the config. -func NewForConfigOrDie(c *rest.Config) *SplitV1alpha1Client { - client, err := NewForConfig(c) - if err != nil { - panic(err) - } - return client -} - -// New creates a new SplitV1alpha1Client for the given RESTClient. -func New(c rest.Interface) *SplitV1alpha1Client { - return &SplitV1alpha1Client{c} -} - -func setConfigDefaults(config *rest.Config) error { - gv := v1alpha1.SchemeGroupVersion - config.GroupVersion = &gv - config.APIPath = "/apis" - config.NegotiatedSerializer = scheme.Codecs.WithoutConversion() - - if config.UserAgent == "" { - config.UserAgent = rest.DefaultKubernetesUserAgent() - } - - return nil -} - -// RESTClient returns a RESTClient that is used to communicate -// with API server by this client implementation. -func (c *SplitV1alpha1Client) RESTClient() rest.Interface { - if c == nil { - return nil - } - return c.restClient -} diff --git a/pkg/client/clientset/versioned/typed/smi/v1alpha1/trafficsplit.go b/pkg/client/clientset/versioned/typed/smi/v1alpha1/trafficsplit.go deleted file mode 100644 index 18f7bdc6..00000000 --- a/pkg/client/clientset/versioned/typed/smi/v1alpha1/trafficsplit.go +++ /dev/null @@ -1,174 +0,0 @@ -/* -Copyright The Flagger Authors. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -// Code generated by client-gen. DO NOT EDIT. - -package v1alpha1 - -import ( - "time" - - v1alpha1 "github.com/weaveworks/flagger/pkg/apis/smi/v1alpha1" - scheme "github.com/weaveworks/flagger/pkg/client/clientset/versioned/scheme" - v1 "k8s.io/apimachinery/pkg/apis/meta/v1" - types "k8s.io/apimachinery/pkg/types" - watch "k8s.io/apimachinery/pkg/watch" - rest "k8s.io/client-go/rest" -) - -// TrafficSplitsGetter has a method to return a TrafficSplitInterface. -// A group's client should implement this interface. -type TrafficSplitsGetter interface { - TrafficSplits(namespace string) TrafficSplitInterface -} - -// TrafficSplitInterface has methods to work with TrafficSplit resources. -type TrafficSplitInterface interface { - Create(*v1alpha1.TrafficSplit) (*v1alpha1.TrafficSplit, error) - Update(*v1alpha1.TrafficSplit) (*v1alpha1.TrafficSplit, error) - Delete(name string, options *v1.DeleteOptions) error - DeleteCollection(options *v1.DeleteOptions, listOptions v1.ListOptions) error - Get(name string, options v1.GetOptions) (*v1alpha1.TrafficSplit, error) - List(opts v1.ListOptions) (*v1alpha1.TrafficSplitList, error) - Watch(opts v1.ListOptions) (watch.Interface, error) - Patch(name string, pt types.PatchType, data []byte, subresources ...string) (result *v1alpha1.TrafficSplit, err error) - TrafficSplitExpansion -} - -// trafficSplits implements TrafficSplitInterface -type trafficSplits struct { - client rest.Interface - ns string -} - -// newTrafficSplits returns a TrafficSplits -func newTrafficSplits(c *SplitV1alpha1Client, namespace string) *trafficSplits { - return &trafficSplits{ - client: c.RESTClient(), - ns: namespace, - } -} - -// Get takes name of the trafficSplit, and returns the corresponding trafficSplit object, and an error if there is any. -func (c *trafficSplits) Get(name string, options v1.GetOptions) (result *v1alpha1.TrafficSplit, err error) { - result = &v1alpha1.TrafficSplit{} - err = c.client.Get(). - Namespace(c.ns). - Resource("trafficsplits"). - Name(name). - VersionedParams(&options, scheme.ParameterCodec). - Do(). - Into(result) - return -} - -// List takes label and field selectors, and returns the list of TrafficSplits that match those selectors. -func (c *trafficSplits) List(opts v1.ListOptions) (result *v1alpha1.TrafficSplitList, err error) { - var timeout time.Duration - if opts.TimeoutSeconds != nil { - timeout = time.Duration(*opts.TimeoutSeconds) * time.Second - } - result = &v1alpha1.TrafficSplitList{} - err = c.client.Get(). - Namespace(c.ns). - Resource("trafficsplits"). - VersionedParams(&opts, scheme.ParameterCodec). - Timeout(timeout). - Do(). - Into(result) - return -} - -// Watch returns a watch.Interface that watches the requested trafficSplits. -func (c *trafficSplits) Watch(opts v1.ListOptions) (watch.Interface, error) { - var timeout time.Duration - if opts.TimeoutSeconds != nil { - timeout = time.Duration(*opts.TimeoutSeconds) * time.Second - } - opts.Watch = true - return c.client.Get(). - Namespace(c.ns). - Resource("trafficsplits"). - VersionedParams(&opts, scheme.ParameterCodec). - Timeout(timeout). - Watch() -} - -// Create takes the representation of a trafficSplit and creates it. Returns the server's representation of the trafficSplit, and an error, if there is any. -func (c *trafficSplits) Create(trafficSplit *v1alpha1.TrafficSplit) (result *v1alpha1.TrafficSplit, err error) { - result = &v1alpha1.TrafficSplit{} - err = c.client.Post(). - Namespace(c.ns). - Resource("trafficsplits"). - Body(trafficSplit). - Do(). - Into(result) - return -} - -// Update takes the representation of a trafficSplit and updates it. Returns the server's representation of the trafficSplit, and an error, if there is any. -func (c *trafficSplits) Update(trafficSplit *v1alpha1.TrafficSplit) (result *v1alpha1.TrafficSplit, err error) { - result = &v1alpha1.TrafficSplit{} - err = c.client.Put(). - Namespace(c.ns). - Resource("trafficsplits"). - Name(trafficSplit.Name). - Body(trafficSplit). - Do(). - Into(result) - return -} - -// Delete takes name of the trafficSplit and deletes it. Returns an error if one occurs. -func (c *trafficSplits) Delete(name string, options *v1.DeleteOptions) error { - return c.client.Delete(). - Namespace(c.ns). - Resource("trafficsplits"). - Name(name). - Body(options). - Do(). - Error() -} - -// DeleteCollection deletes a collection of objects. -func (c *trafficSplits) DeleteCollection(options *v1.DeleteOptions, listOptions v1.ListOptions) error { - var timeout time.Duration - if listOptions.TimeoutSeconds != nil { - timeout = time.Duration(*listOptions.TimeoutSeconds) * time.Second - } - return c.client.Delete(). - Namespace(c.ns). - Resource("trafficsplits"). - VersionedParams(&listOptions, scheme.ParameterCodec). - Timeout(timeout). - Body(options). - Do(). - Error() -} - -// Patch applies the patch and returns the patched trafficSplit. -func (c *trafficSplits) Patch(name string, pt types.PatchType, data []byte, subresources ...string) (result *v1alpha1.TrafficSplit, err error) { - result = &v1alpha1.TrafficSplit{} - err = c.client.Patch(pt). - Namespace(c.ns). - Resource("trafficsplits"). - SubResource(subresources...). - Name(name). - Body(data). - Do(). - Into(result) - return -} diff --git a/pkg/client/informers/externalversions/appmesh/interface.go b/pkg/client/informers/externalversions/appmesh/interface.go deleted file mode 100644 index 376f4361..00000000 --- a/pkg/client/informers/externalversions/appmesh/interface.go +++ /dev/null @@ -1,46 +0,0 @@ -/* -Copyright The Flagger Authors. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -// Code generated by informer-gen. DO NOT EDIT. - -package appmesh - -import ( - v1beta1 "github.com/weaveworks/flagger/pkg/client/informers/externalversions/appmesh/v1beta1" - internalinterfaces "github.com/weaveworks/flagger/pkg/client/informers/externalversions/internalinterfaces" -) - -// Interface provides access to each of this group's versions. -type Interface interface { - // V1beta1 provides access to shared informers for resources in V1beta1. - V1beta1() v1beta1.Interface -} - -type group struct { - factory internalinterfaces.SharedInformerFactory - namespace string - tweakListOptions internalinterfaces.TweakListOptionsFunc -} - -// New returns a new Interface. -func New(f internalinterfaces.SharedInformerFactory, namespace string, tweakListOptions internalinterfaces.TweakListOptionsFunc) Interface { - return &group{factory: f, namespace: namespace, tweakListOptions: tweakListOptions} -} - -// V1beta1 returns a new v1beta1.Interface. -func (g *group) V1beta1() v1beta1.Interface { - return v1beta1.New(g.factory, g.namespace, g.tweakListOptions) -} diff --git a/pkg/client/informers/externalversions/appmesh/v1beta1/interface.go b/pkg/client/informers/externalversions/appmesh/v1beta1/interface.go deleted file mode 100644 index f26966e5..00000000 --- a/pkg/client/informers/externalversions/appmesh/v1beta1/interface.go +++ /dev/null @@ -1,59 +0,0 @@ -/* -Copyright The Flagger Authors. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -// Code generated by informer-gen. DO NOT EDIT. - -package v1beta1 - -import ( - internalinterfaces "github.com/weaveworks/flagger/pkg/client/informers/externalversions/internalinterfaces" -) - -// Interface provides access to all the informers in this group version. -type Interface interface { - // Meshes returns a MeshInformer. - Meshes() MeshInformer - // VirtualNodes returns a VirtualNodeInformer. - VirtualNodes() VirtualNodeInformer - // VirtualServices returns a VirtualServiceInformer. - VirtualServices() VirtualServiceInformer -} - -type version struct { - factory internalinterfaces.SharedInformerFactory - namespace string - tweakListOptions internalinterfaces.TweakListOptionsFunc -} - -// New returns a new Interface. -func New(f internalinterfaces.SharedInformerFactory, namespace string, tweakListOptions internalinterfaces.TweakListOptionsFunc) Interface { - return &version{factory: f, namespace: namespace, tweakListOptions: tweakListOptions} -} - -// Meshes returns a MeshInformer. -func (v *version) Meshes() MeshInformer { - return &meshInformer{factory: v.factory, tweakListOptions: v.tweakListOptions} -} - -// VirtualNodes returns a VirtualNodeInformer. -func (v *version) VirtualNodes() VirtualNodeInformer { - return &virtualNodeInformer{factory: v.factory, namespace: v.namespace, tweakListOptions: v.tweakListOptions} -} - -// VirtualServices returns a VirtualServiceInformer. -func (v *version) VirtualServices() VirtualServiceInformer { - return &virtualServiceInformer{factory: v.factory, namespace: v.namespace, tweakListOptions: v.tweakListOptions} -} diff --git a/pkg/client/informers/externalversions/appmesh/v1beta1/mesh.go b/pkg/client/informers/externalversions/appmesh/v1beta1/mesh.go deleted file mode 100644 index ccb9c711..00000000 --- a/pkg/client/informers/externalversions/appmesh/v1beta1/mesh.go +++ /dev/null @@ -1,88 +0,0 @@ -/* -Copyright The Flagger Authors. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -// Code generated by informer-gen. DO NOT EDIT. - -package v1beta1 - -import ( - time "time" - - appmeshv1beta1 "github.com/weaveworks/flagger/pkg/apis/appmesh/v1beta1" - versioned "github.com/weaveworks/flagger/pkg/client/clientset/versioned" - internalinterfaces "github.com/weaveworks/flagger/pkg/client/informers/externalversions/internalinterfaces" - v1beta1 "github.com/weaveworks/flagger/pkg/client/listers/appmesh/v1beta1" - v1 "k8s.io/apimachinery/pkg/apis/meta/v1" - runtime "k8s.io/apimachinery/pkg/runtime" - watch "k8s.io/apimachinery/pkg/watch" - cache "k8s.io/client-go/tools/cache" -) - -// MeshInformer provides access to a shared informer and lister for -// Meshes. -type MeshInformer interface { - Informer() cache.SharedIndexInformer - Lister() v1beta1.MeshLister -} - -type meshInformer struct { - factory internalinterfaces.SharedInformerFactory - tweakListOptions internalinterfaces.TweakListOptionsFunc -} - -// NewMeshInformer constructs a new informer for Mesh type. -// Always prefer using an informer factory to get a shared informer instead of getting an independent -// one. This reduces memory footprint and number of connections to the server. -func NewMeshInformer(client versioned.Interface, resyncPeriod time.Duration, indexers cache.Indexers) cache.SharedIndexInformer { - return NewFilteredMeshInformer(client, resyncPeriod, indexers, nil) -} - -// NewFilteredMeshInformer constructs a new informer for Mesh type. -// Always prefer using an informer factory to get a shared informer instead of getting an independent -// one. This reduces memory footprint and number of connections to the server. -func NewFilteredMeshInformer(client versioned.Interface, resyncPeriod time.Duration, indexers cache.Indexers, tweakListOptions internalinterfaces.TweakListOptionsFunc) cache.SharedIndexInformer { - return cache.NewSharedIndexInformer( - &cache.ListWatch{ - ListFunc: func(options v1.ListOptions) (runtime.Object, error) { - if tweakListOptions != nil { - tweakListOptions(&options) - } - return client.AppmeshV1beta1().Meshes().List(options) - }, - WatchFunc: func(options v1.ListOptions) (watch.Interface, error) { - if tweakListOptions != nil { - tweakListOptions(&options) - } - return client.AppmeshV1beta1().Meshes().Watch(options) - }, - }, - &appmeshv1beta1.Mesh{}, - resyncPeriod, - indexers, - ) -} - -func (f *meshInformer) defaultInformer(client versioned.Interface, resyncPeriod time.Duration) cache.SharedIndexInformer { - return NewFilteredMeshInformer(client, resyncPeriod, cache.Indexers{cache.NamespaceIndex: cache.MetaNamespaceIndexFunc}, f.tweakListOptions) -} - -func (f *meshInformer) Informer() cache.SharedIndexInformer { - return f.factory.InformerFor(&appmeshv1beta1.Mesh{}, f.defaultInformer) -} - -func (f *meshInformer) Lister() v1beta1.MeshLister { - return v1beta1.NewMeshLister(f.Informer().GetIndexer()) -} diff --git a/pkg/client/informers/externalversions/appmesh/v1beta1/virtualnode.go b/pkg/client/informers/externalversions/appmesh/v1beta1/virtualnode.go deleted file mode 100644 index bd87c5db..00000000 --- a/pkg/client/informers/externalversions/appmesh/v1beta1/virtualnode.go +++ /dev/null @@ -1,89 +0,0 @@ -/* -Copyright The Flagger Authors. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -// Code generated by informer-gen. DO NOT EDIT. - -package v1beta1 - -import ( - time "time" - - appmeshv1beta1 "github.com/weaveworks/flagger/pkg/apis/appmesh/v1beta1" - versioned "github.com/weaveworks/flagger/pkg/client/clientset/versioned" - internalinterfaces "github.com/weaveworks/flagger/pkg/client/informers/externalversions/internalinterfaces" - v1beta1 "github.com/weaveworks/flagger/pkg/client/listers/appmesh/v1beta1" - v1 "k8s.io/apimachinery/pkg/apis/meta/v1" - runtime "k8s.io/apimachinery/pkg/runtime" - watch "k8s.io/apimachinery/pkg/watch" - cache "k8s.io/client-go/tools/cache" -) - -// VirtualNodeInformer provides access to a shared informer and lister for -// VirtualNodes. -type VirtualNodeInformer interface { - Informer() cache.SharedIndexInformer - Lister() v1beta1.VirtualNodeLister -} - -type virtualNodeInformer struct { - factory internalinterfaces.SharedInformerFactory - tweakListOptions internalinterfaces.TweakListOptionsFunc - namespace string -} - -// NewVirtualNodeInformer constructs a new informer for VirtualNode type. -// Always prefer using an informer factory to get a shared informer instead of getting an independent -// one. This reduces memory footprint and number of connections to the server. -func NewVirtualNodeInformer(client versioned.Interface, namespace string, resyncPeriod time.Duration, indexers cache.Indexers) cache.SharedIndexInformer { - return NewFilteredVirtualNodeInformer(client, namespace, resyncPeriod, indexers, nil) -} - -// NewFilteredVirtualNodeInformer constructs a new informer for VirtualNode type. -// Always prefer using an informer factory to get a shared informer instead of getting an independent -// one. This reduces memory footprint and number of connections to the server. -func NewFilteredVirtualNodeInformer(client versioned.Interface, namespace string, resyncPeriod time.Duration, indexers cache.Indexers, tweakListOptions internalinterfaces.TweakListOptionsFunc) cache.SharedIndexInformer { - return cache.NewSharedIndexInformer( - &cache.ListWatch{ - ListFunc: func(options v1.ListOptions) (runtime.Object, error) { - if tweakListOptions != nil { - tweakListOptions(&options) - } - return client.AppmeshV1beta1().VirtualNodes(namespace).List(options) - }, - WatchFunc: func(options v1.ListOptions) (watch.Interface, error) { - if tweakListOptions != nil { - tweakListOptions(&options) - } - return client.AppmeshV1beta1().VirtualNodes(namespace).Watch(options) - }, - }, - &appmeshv1beta1.VirtualNode{}, - resyncPeriod, - indexers, - ) -} - -func (f *virtualNodeInformer) defaultInformer(client versioned.Interface, resyncPeriod time.Duration) cache.SharedIndexInformer { - return NewFilteredVirtualNodeInformer(client, f.namespace, resyncPeriod, cache.Indexers{cache.NamespaceIndex: cache.MetaNamespaceIndexFunc}, f.tweakListOptions) -} - -func (f *virtualNodeInformer) Informer() cache.SharedIndexInformer { - return f.factory.InformerFor(&appmeshv1beta1.VirtualNode{}, f.defaultInformer) -} - -func (f *virtualNodeInformer) Lister() v1beta1.VirtualNodeLister { - return v1beta1.NewVirtualNodeLister(f.Informer().GetIndexer()) -} diff --git a/pkg/client/informers/externalversions/appmesh/v1beta1/virtualservice.go b/pkg/client/informers/externalversions/appmesh/v1beta1/virtualservice.go deleted file mode 100644 index fe8174f3..00000000 --- a/pkg/client/informers/externalversions/appmesh/v1beta1/virtualservice.go +++ /dev/null @@ -1,89 +0,0 @@ -/* -Copyright The Flagger Authors. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -// Code generated by informer-gen. DO NOT EDIT. - -package v1beta1 - -import ( - time "time" - - appmeshv1beta1 "github.com/weaveworks/flagger/pkg/apis/appmesh/v1beta1" - versioned "github.com/weaveworks/flagger/pkg/client/clientset/versioned" - internalinterfaces "github.com/weaveworks/flagger/pkg/client/informers/externalversions/internalinterfaces" - v1beta1 "github.com/weaveworks/flagger/pkg/client/listers/appmesh/v1beta1" - v1 "k8s.io/apimachinery/pkg/apis/meta/v1" - runtime "k8s.io/apimachinery/pkg/runtime" - watch "k8s.io/apimachinery/pkg/watch" - cache "k8s.io/client-go/tools/cache" -) - -// VirtualServiceInformer provides access to a shared informer and lister for -// VirtualServices. -type VirtualServiceInformer interface { - Informer() cache.SharedIndexInformer - Lister() v1beta1.VirtualServiceLister -} - -type virtualServiceInformer struct { - factory internalinterfaces.SharedInformerFactory - tweakListOptions internalinterfaces.TweakListOptionsFunc - namespace string -} - -// NewVirtualServiceInformer constructs a new informer for VirtualService type. -// Always prefer using an informer factory to get a shared informer instead of getting an independent -// one. This reduces memory footprint and number of connections to the server. -func NewVirtualServiceInformer(client versioned.Interface, namespace string, resyncPeriod time.Duration, indexers cache.Indexers) cache.SharedIndexInformer { - return NewFilteredVirtualServiceInformer(client, namespace, resyncPeriod, indexers, nil) -} - -// NewFilteredVirtualServiceInformer constructs a new informer for VirtualService type. -// Always prefer using an informer factory to get a shared informer instead of getting an independent -// one. This reduces memory footprint and number of connections to the server. -func NewFilteredVirtualServiceInformer(client versioned.Interface, namespace string, resyncPeriod time.Duration, indexers cache.Indexers, tweakListOptions internalinterfaces.TweakListOptionsFunc) cache.SharedIndexInformer { - return cache.NewSharedIndexInformer( - &cache.ListWatch{ - ListFunc: func(options v1.ListOptions) (runtime.Object, error) { - if tweakListOptions != nil { - tweakListOptions(&options) - } - return client.AppmeshV1beta1().VirtualServices(namespace).List(options) - }, - WatchFunc: func(options v1.ListOptions) (watch.Interface, error) { - if tweakListOptions != nil { - tweakListOptions(&options) - } - return client.AppmeshV1beta1().VirtualServices(namespace).Watch(options) - }, - }, - &appmeshv1beta1.VirtualService{}, - resyncPeriod, - indexers, - ) -} - -func (f *virtualServiceInformer) defaultInformer(client versioned.Interface, resyncPeriod time.Duration) cache.SharedIndexInformer { - return NewFilteredVirtualServiceInformer(client, f.namespace, resyncPeriod, cache.Indexers{cache.NamespaceIndex: cache.MetaNamespaceIndexFunc}, f.tweakListOptions) -} - -func (f *virtualServiceInformer) Informer() cache.SharedIndexInformer { - return f.factory.InformerFor(&appmeshv1beta1.VirtualService{}, f.defaultInformer) -} - -func (f *virtualServiceInformer) Lister() v1beta1.VirtualServiceLister { - return v1beta1.NewVirtualServiceLister(f.Informer().GetIndexer()) -} diff --git a/pkg/client/informers/externalversions/factory.go b/pkg/client/informers/externalversions/factory.go deleted file mode 100644 index c19c5ca2..00000000 --- a/pkg/client/informers/externalversions/factory.go +++ /dev/null @@ -1,198 +0,0 @@ -/* -Copyright The Flagger Authors. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -// Code generated by informer-gen. DO NOT EDIT. - -package externalversions - -import ( - reflect "reflect" - sync "sync" - time "time" - - versioned "github.com/weaveworks/flagger/pkg/client/clientset/versioned" - appmesh "github.com/weaveworks/flagger/pkg/client/informers/externalversions/appmesh" - flagger "github.com/weaveworks/flagger/pkg/client/informers/externalversions/flagger" - internalinterfaces "github.com/weaveworks/flagger/pkg/client/informers/externalversions/internalinterfaces" - istio "github.com/weaveworks/flagger/pkg/client/informers/externalversions/istio" - smi "github.com/weaveworks/flagger/pkg/client/informers/externalversions/smi" - v1 "k8s.io/apimachinery/pkg/apis/meta/v1" - runtime "k8s.io/apimachinery/pkg/runtime" - schema "k8s.io/apimachinery/pkg/runtime/schema" - cache "k8s.io/client-go/tools/cache" -) - -// SharedInformerOption defines the functional option type for SharedInformerFactory. -type SharedInformerOption func(*sharedInformerFactory) *sharedInformerFactory - -type sharedInformerFactory struct { - client versioned.Interface - namespace string - tweakListOptions internalinterfaces.TweakListOptionsFunc - lock sync.Mutex - defaultResync time.Duration - customResync map[reflect.Type]time.Duration - - informers map[reflect.Type]cache.SharedIndexInformer - // startedInformers is used for tracking which informers have been started. - // This allows Start() to be called multiple times safely. - startedInformers map[reflect.Type]bool -} - -// WithCustomResyncConfig sets a custom resync period for the specified informer types. -func WithCustomResyncConfig(resyncConfig map[v1.Object]time.Duration) SharedInformerOption { - return func(factory *sharedInformerFactory) *sharedInformerFactory { - for k, v := range resyncConfig { - factory.customResync[reflect.TypeOf(k)] = v - } - return factory - } -} - -// WithTweakListOptions sets a custom filter on all listers of the configured SharedInformerFactory. -func WithTweakListOptions(tweakListOptions internalinterfaces.TweakListOptionsFunc) SharedInformerOption { - return func(factory *sharedInformerFactory) *sharedInformerFactory { - factory.tweakListOptions = tweakListOptions - return factory - } -} - -// WithNamespace limits the SharedInformerFactory to the specified namespace. -func WithNamespace(namespace string) SharedInformerOption { - return func(factory *sharedInformerFactory) *sharedInformerFactory { - factory.namespace = namespace - return factory - } -} - -// NewSharedInformerFactory constructs a new instance of sharedInformerFactory for all namespaces. -func NewSharedInformerFactory(client versioned.Interface, defaultResync time.Duration) SharedInformerFactory { - return NewSharedInformerFactoryWithOptions(client, defaultResync) -} - -// NewFilteredSharedInformerFactory constructs a new instance of sharedInformerFactory. -// Listers obtained via this SharedInformerFactory will be subject to the same filters -// as specified here. -// Deprecated: Please use NewSharedInformerFactoryWithOptions instead -func NewFilteredSharedInformerFactory(client versioned.Interface, defaultResync time.Duration, namespace string, tweakListOptions internalinterfaces.TweakListOptionsFunc) SharedInformerFactory { - return NewSharedInformerFactoryWithOptions(client, defaultResync, WithNamespace(namespace), WithTweakListOptions(tweakListOptions)) -} - -// NewSharedInformerFactoryWithOptions constructs a new instance of a SharedInformerFactory with additional options. -func NewSharedInformerFactoryWithOptions(client versioned.Interface, defaultResync time.Duration, options ...SharedInformerOption) SharedInformerFactory { - factory := &sharedInformerFactory{ - client: client, - namespace: v1.NamespaceAll, - defaultResync: defaultResync, - informers: make(map[reflect.Type]cache.SharedIndexInformer), - startedInformers: make(map[reflect.Type]bool), - customResync: make(map[reflect.Type]time.Duration), - } - - // Apply all options - for _, opt := range options { - factory = opt(factory) - } - - return factory -} - -// Start initializes all requested informers. -func (f *sharedInformerFactory) Start(stopCh <-chan struct{}) { - f.lock.Lock() - defer f.lock.Unlock() - - for informerType, informer := range f.informers { - if !f.startedInformers[informerType] { - go informer.Run(stopCh) - f.startedInformers[informerType] = true - } - } -} - -// WaitForCacheSync waits for all started informers' cache were synced. -func (f *sharedInformerFactory) WaitForCacheSync(stopCh <-chan struct{}) map[reflect.Type]bool { - informers := func() map[reflect.Type]cache.SharedIndexInformer { - f.lock.Lock() - defer f.lock.Unlock() - - informers := map[reflect.Type]cache.SharedIndexInformer{} - for informerType, informer := range f.informers { - if f.startedInformers[informerType] { - informers[informerType] = informer - } - } - return informers - }() - - res := map[reflect.Type]bool{} - for informType, informer := range informers { - res[informType] = cache.WaitForCacheSync(stopCh, informer.HasSynced) - } - return res -} - -// InternalInformerFor returns the SharedIndexInformer for obj using an internal -// client. -func (f *sharedInformerFactory) InformerFor(obj runtime.Object, newFunc internalinterfaces.NewInformerFunc) cache.SharedIndexInformer { - f.lock.Lock() - defer f.lock.Unlock() - - informerType := reflect.TypeOf(obj) - informer, exists := f.informers[informerType] - if exists { - return informer - } - - resyncPeriod, exists := f.customResync[informerType] - if !exists { - resyncPeriod = f.defaultResync - } - - informer = newFunc(f.client, resyncPeriod) - f.informers[informerType] = informer - - return informer -} - -// SharedInformerFactory provides shared informers for resources in all known -// API group versions. -type SharedInformerFactory interface { - internalinterfaces.SharedInformerFactory - ForResource(resource schema.GroupVersionResource) (GenericInformer, error) - WaitForCacheSync(stopCh <-chan struct{}) map[reflect.Type]bool - - Appmesh() appmesh.Interface - Flagger() flagger.Interface - Networking() istio.Interface - Split() smi.Interface -} - -func (f *sharedInformerFactory) Appmesh() appmesh.Interface { - return appmesh.New(f, f.namespace, f.tweakListOptions) -} - -func (f *sharedInformerFactory) Flagger() flagger.Interface { - return flagger.New(f, f.namespace, f.tweakListOptions) -} - -func (f *sharedInformerFactory) Networking() istio.Interface { - return istio.New(f, f.namespace, f.tweakListOptions) -} - -func (f *sharedInformerFactory) Split() smi.Interface { - return smi.New(f, f.namespace, f.tweakListOptions) -} diff --git a/pkg/client/informers/externalversions/flagger/interface.go b/pkg/client/informers/externalversions/flagger/interface.go deleted file mode 100644 index 1e03780e..00000000 --- a/pkg/client/informers/externalversions/flagger/interface.go +++ /dev/null @@ -1,46 +0,0 @@ -/* -Copyright The Flagger Authors. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -// Code generated by informer-gen. DO NOT EDIT. - -package flagger - -import ( - v1alpha3 "github.com/weaveworks/flagger/pkg/client/informers/externalversions/flagger/v1alpha3" - internalinterfaces "github.com/weaveworks/flagger/pkg/client/informers/externalversions/internalinterfaces" -) - -// Interface provides access to each of this group's versions. -type Interface interface { - // V1alpha3 provides access to shared informers for resources in V1alpha3. - V1alpha3() v1alpha3.Interface -} - -type group struct { - factory internalinterfaces.SharedInformerFactory - namespace string - tweakListOptions internalinterfaces.TweakListOptionsFunc -} - -// New returns a new Interface. -func New(f internalinterfaces.SharedInformerFactory, namespace string, tweakListOptions internalinterfaces.TweakListOptionsFunc) Interface { - return &group{factory: f, namespace: namespace, tweakListOptions: tweakListOptions} -} - -// V1alpha3 returns a new v1alpha3.Interface. -func (g *group) V1alpha3() v1alpha3.Interface { - return v1alpha3.New(g.factory, g.namespace, g.tweakListOptions) -} diff --git a/pkg/client/informers/externalversions/flagger/v1alpha3/canary.go b/pkg/client/informers/externalversions/flagger/v1alpha3/canary.go deleted file mode 100644 index e5cc80de..00000000 --- a/pkg/client/informers/externalversions/flagger/v1alpha3/canary.go +++ /dev/null @@ -1,89 +0,0 @@ -/* -Copyright The Flagger Authors. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -// Code generated by informer-gen. DO NOT EDIT. - -package v1alpha3 - -import ( - time "time" - - flaggerv1alpha3 "github.com/weaveworks/flagger/pkg/apis/flagger/v1alpha3" - versioned "github.com/weaveworks/flagger/pkg/client/clientset/versioned" - internalinterfaces "github.com/weaveworks/flagger/pkg/client/informers/externalversions/internalinterfaces" - v1alpha3 "github.com/weaveworks/flagger/pkg/client/listers/flagger/v1alpha3" - v1 "k8s.io/apimachinery/pkg/apis/meta/v1" - runtime "k8s.io/apimachinery/pkg/runtime" - watch "k8s.io/apimachinery/pkg/watch" - cache "k8s.io/client-go/tools/cache" -) - -// CanaryInformer provides access to a shared informer and lister for -// Canaries. -type CanaryInformer interface { - Informer() cache.SharedIndexInformer - Lister() v1alpha3.CanaryLister -} - -type canaryInformer struct { - factory internalinterfaces.SharedInformerFactory - tweakListOptions internalinterfaces.TweakListOptionsFunc - namespace string -} - -// NewCanaryInformer constructs a new informer for Canary type. -// Always prefer using an informer factory to get a shared informer instead of getting an independent -// one. This reduces memory footprint and number of connections to the server. -func NewCanaryInformer(client versioned.Interface, namespace string, resyncPeriod time.Duration, indexers cache.Indexers) cache.SharedIndexInformer { - return NewFilteredCanaryInformer(client, namespace, resyncPeriod, indexers, nil) -} - -// NewFilteredCanaryInformer constructs a new informer for Canary type. -// Always prefer using an informer factory to get a shared informer instead of getting an independent -// one. This reduces memory footprint and number of connections to the server. -func NewFilteredCanaryInformer(client versioned.Interface, namespace string, resyncPeriod time.Duration, indexers cache.Indexers, tweakListOptions internalinterfaces.TweakListOptionsFunc) cache.SharedIndexInformer { - return cache.NewSharedIndexInformer( - &cache.ListWatch{ - ListFunc: func(options v1.ListOptions) (runtime.Object, error) { - if tweakListOptions != nil { - tweakListOptions(&options) - } - return client.FlaggerV1alpha3().Canaries(namespace).List(options) - }, - WatchFunc: func(options v1.ListOptions) (watch.Interface, error) { - if tweakListOptions != nil { - tweakListOptions(&options) - } - return client.FlaggerV1alpha3().Canaries(namespace).Watch(options) - }, - }, - &flaggerv1alpha3.Canary{}, - resyncPeriod, - indexers, - ) -} - -func (f *canaryInformer) defaultInformer(client versioned.Interface, resyncPeriod time.Duration) cache.SharedIndexInformer { - return NewFilteredCanaryInformer(client, f.namespace, resyncPeriod, cache.Indexers{cache.NamespaceIndex: cache.MetaNamespaceIndexFunc}, f.tweakListOptions) -} - -func (f *canaryInformer) Informer() cache.SharedIndexInformer { - return f.factory.InformerFor(&flaggerv1alpha3.Canary{}, f.defaultInformer) -} - -func (f *canaryInformer) Lister() v1alpha3.CanaryLister { - return v1alpha3.NewCanaryLister(f.Informer().GetIndexer()) -} diff --git a/pkg/client/informers/externalversions/flagger/v1alpha3/interface.go b/pkg/client/informers/externalversions/flagger/v1alpha3/interface.go deleted file mode 100644 index ff8598b7..00000000 --- a/pkg/client/informers/externalversions/flagger/v1alpha3/interface.go +++ /dev/null @@ -1,45 +0,0 @@ -/* -Copyright The Flagger Authors. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -// Code generated by informer-gen. DO NOT EDIT. - -package v1alpha3 - -import ( - internalinterfaces "github.com/weaveworks/flagger/pkg/client/informers/externalversions/internalinterfaces" -) - -// Interface provides access to all the informers in this group version. -type Interface interface { - // Canaries returns a CanaryInformer. - Canaries() CanaryInformer -} - -type version struct { - factory internalinterfaces.SharedInformerFactory - namespace string - tweakListOptions internalinterfaces.TweakListOptionsFunc -} - -// New returns a new Interface. -func New(f internalinterfaces.SharedInformerFactory, namespace string, tweakListOptions internalinterfaces.TweakListOptionsFunc) Interface { - return &version{factory: f, namespace: namespace, tweakListOptions: tweakListOptions} -} - -// Canaries returns a CanaryInformer. -func (v *version) Canaries() CanaryInformer { - return &canaryInformer{factory: v.factory, namespace: v.namespace, tweakListOptions: v.tweakListOptions} -} diff --git a/pkg/client/informers/externalversions/generic.go b/pkg/client/informers/externalversions/generic.go deleted file mode 100644 index 31be5272..00000000 --- a/pkg/client/informers/externalversions/generic.go +++ /dev/null @@ -1,83 +0,0 @@ -/* -Copyright The Flagger Authors. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -// Code generated by informer-gen. DO NOT EDIT. - -package externalversions - -import ( - "fmt" - - v1beta1 "github.com/weaveworks/flagger/pkg/apis/appmesh/v1beta1" - v1alpha3 "github.com/weaveworks/flagger/pkg/apis/flagger/v1alpha3" - istiov1alpha3 "github.com/weaveworks/flagger/pkg/apis/istio/v1alpha3" - v1alpha1 "github.com/weaveworks/flagger/pkg/apis/smi/v1alpha1" - schema "k8s.io/apimachinery/pkg/runtime/schema" - cache "k8s.io/client-go/tools/cache" -) - -// GenericInformer is type of SharedIndexInformer which will locate and delegate to other -// sharedInformers based on type -type GenericInformer interface { - Informer() cache.SharedIndexInformer - Lister() cache.GenericLister -} - -type genericInformer struct { - informer cache.SharedIndexInformer - resource schema.GroupResource -} - -// Informer returns the SharedIndexInformer. -func (f *genericInformer) Informer() cache.SharedIndexInformer { - return f.informer -} - -// Lister returns the GenericLister. -func (f *genericInformer) Lister() cache.GenericLister { - return cache.NewGenericLister(f.Informer().GetIndexer(), f.resource) -} - -// ForResource gives generic access to a shared informer of the matching type -// TODO extend this to unknown resources with a client pool -func (f *sharedInformerFactory) ForResource(resource schema.GroupVersionResource) (GenericInformer, error) { - switch resource { - // Group=appmesh.k8s.aws, Version=v1beta1 - case v1beta1.SchemeGroupVersion.WithResource("meshes"): - return &genericInformer{resource: resource.GroupResource(), informer: f.Appmesh().V1beta1().Meshes().Informer()}, nil - case v1beta1.SchemeGroupVersion.WithResource("virtualnodes"): - return &genericInformer{resource: resource.GroupResource(), informer: f.Appmesh().V1beta1().VirtualNodes().Informer()}, nil - case v1beta1.SchemeGroupVersion.WithResource("virtualservices"): - return &genericInformer{resource: resource.GroupResource(), informer: f.Appmesh().V1beta1().VirtualServices().Informer()}, nil - - // Group=flagger.app, Version=v1alpha3 - case v1alpha3.SchemeGroupVersion.WithResource("canaries"): - return &genericInformer{resource: resource.GroupResource(), informer: f.Flagger().V1alpha3().Canaries().Informer()}, nil - - // Group=networking.istio.io, Version=v1alpha3 - case istiov1alpha3.SchemeGroupVersion.WithResource("destinationrules"): - return &genericInformer{resource: resource.GroupResource(), informer: f.Networking().V1alpha3().DestinationRules().Informer()}, nil - case istiov1alpha3.SchemeGroupVersion.WithResource("virtualservices"): - return &genericInformer{resource: resource.GroupResource(), informer: f.Networking().V1alpha3().VirtualServices().Informer()}, nil - - // Group=split.smi-spec.io, Version=v1alpha1 - case v1alpha1.SchemeGroupVersion.WithResource("trafficsplits"): - return &genericInformer{resource: resource.GroupResource(), informer: f.Split().V1alpha1().TrafficSplits().Informer()}, nil - - } - - return nil, fmt.Errorf("no informer found for %v", resource) -} diff --git a/pkg/client/informers/externalversions/internalinterfaces/factory_interfaces.go b/pkg/client/informers/externalversions/internalinterfaces/factory_interfaces.go deleted file mode 100644 index 02380091..00000000 --- a/pkg/client/informers/externalversions/internalinterfaces/factory_interfaces.go +++ /dev/null @@ -1,40 +0,0 @@ -/* -Copyright The Flagger Authors. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -// Code generated by informer-gen. DO NOT EDIT. - -package internalinterfaces - -import ( - time "time" - - versioned "github.com/weaveworks/flagger/pkg/client/clientset/versioned" - v1 "k8s.io/apimachinery/pkg/apis/meta/v1" - runtime "k8s.io/apimachinery/pkg/runtime" - cache "k8s.io/client-go/tools/cache" -) - -// NewInformerFunc takes versioned.Interface and time.Duration to return a SharedIndexInformer. -type NewInformerFunc func(versioned.Interface, time.Duration) cache.SharedIndexInformer - -// SharedInformerFactory a small interface to allow for adding an informer without an import cycle -type SharedInformerFactory interface { - Start(stopCh <-chan struct{}) - InformerFor(obj runtime.Object, newFunc NewInformerFunc) cache.SharedIndexInformer -} - -// TweakListOptionsFunc is a function that transforms a v1.ListOptions. -type TweakListOptionsFunc func(*v1.ListOptions) diff --git a/pkg/client/informers/externalversions/istio/interface.go b/pkg/client/informers/externalversions/istio/interface.go deleted file mode 100644 index 4d1d0a9a..00000000 --- a/pkg/client/informers/externalversions/istio/interface.go +++ /dev/null @@ -1,46 +0,0 @@ -/* -Copyright The Flagger Authors. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -// Code generated by informer-gen. DO NOT EDIT. - -package networking - -import ( - internalinterfaces "github.com/weaveworks/flagger/pkg/client/informers/externalversions/internalinterfaces" - v1alpha3 "github.com/weaveworks/flagger/pkg/client/informers/externalversions/istio/v1alpha3" -) - -// Interface provides access to each of this group's versions. -type Interface interface { - // V1alpha3 provides access to shared informers for resources in V1alpha3. - V1alpha3() v1alpha3.Interface -} - -type group struct { - factory internalinterfaces.SharedInformerFactory - namespace string - tweakListOptions internalinterfaces.TweakListOptionsFunc -} - -// New returns a new Interface. -func New(f internalinterfaces.SharedInformerFactory, namespace string, tweakListOptions internalinterfaces.TweakListOptionsFunc) Interface { - return &group{factory: f, namespace: namespace, tweakListOptions: tweakListOptions} -} - -// V1alpha3 returns a new v1alpha3.Interface. -func (g *group) V1alpha3() v1alpha3.Interface { - return v1alpha3.New(g.factory, g.namespace, g.tweakListOptions) -} diff --git a/pkg/client/informers/externalversions/istio/v1alpha3/destinationrule.go b/pkg/client/informers/externalversions/istio/v1alpha3/destinationrule.go deleted file mode 100644 index c723573a..00000000 --- a/pkg/client/informers/externalversions/istio/v1alpha3/destinationrule.go +++ /dev/null @@ -1,89 +0,0 @@ -/* -Copyright The Flagger Authors. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -// Code generated by informer-gen. DO NOT EDIT. - -package v1alpha3 - -import ( - time "time" - - istiov1alpha3 "github.com/weaveworks/flagger/pkg/apis/istio/v1alpha3" - versioned "github.com/weaveworks/flagger/pkg/client/clientset/versioned" - internalinterfaces "github.com/weaveworks/flagger/pkg/client/informers/externalversions/internalinterfaces" - v1alpha3 "github.com/weaveworks/flagger/pkg/client/listers/istio/v1alpha3" - v1 "k8s.io/apimachinery/pkg/apis/meta/v1" - runtime "k8s.io/apimachinery/pkg/runtime" - watch "k8s.io/apimachinery/pkg/watch" - cache "k8s.io/client-go/tools/cache" -) - -// DestinationRuleInformer provides access to a shared informer and lister for -// DestinationRules. -type DestinationRuleInformer interface { - Informer() cache.SharedIndexInformer - Lister() v1alpha3.DestinationRuleLister -} - -type destinationRuleInformer struct { - factory internalinterfaces.SharedInformerFactory - tweakListOptions internalinterfaces.TweakListOptionsFunc - namespace string -} - -// NewDestinationRuleInformer constructs a new informer for DestinationRule type. -// Always prefer using an informer factory to get a shared informer instead of getting an independent -// one. This reduces memory footprint and number of connections to the server. -func NewDestinationRuleInformer(client versioned.Interface, namespace string, resyncPeriod time.Duration, indexers cache.Indexers) cache.SharedIndexInformer { - return NewFilteredDestinationRuleInformer(client, namespace, resyncPeriod, indexers, nil) -} - -// NewFilteredDestinationRuleInformer constructs a new informer for DestinationRule type. -// Always prefer using an informer factory to get a shared informer instead of getting an independent -// one. This reduces memory footprint and number of connections to the server. -func NewFilteredDestinationRuleInformer(client versioned.Interface, namespace string, resyncPeriod time.Duration, indexers cache.Indexers, tweakListOptions internalinterfaces.TweakListOptionsFunc) cache.SharedIndexInformer { - return cache.NewSharedIndexInformer( - &cache.ListWatch{ - ListFunc: func(options v1.ListOptions) (runtime.Object, error) { - if tweakListOptions != nil { - tweakListOptions(&options) - } - return client.NetworkingV1alpha3().DestinationRules(namespace).List(options) - }, - WatchFunc: func(options v1.ListOptions) (watch.Interface, error) { - if tweakListOptions != nil { - tweakListOptions(&options) - } - return client.NetworkingV1alpha3().DestinationRules(namespace).Watch(options) - }, - }, - &istiov1alpha3.DestinationRule{}, - resyncPeriod, - indexers, - ) -} - -func (f *destinationRuleInformer) defaultInformer(client versioned.Interface, resyncPeriod time.Duration) cache.SharedIndexInformer { - return NewFilteredDestinationRuleInformer(client, f.namespace, resyncPeriod, cache.Indexers{cache.NamespaceIndex: cache.MetaNamespaceIndexFunc}, f.tweakListOptions) -} - -func (f *destinationRuleInformer) Informer() cache.SharedIndexInformer { - return f.factory.InformerFor(&istiov1alpha3.DestinationRule{}, f.defaultInformer) -} - -func (f *destinationRuleInformer) Lister() v1alpha3.DestinationRuleLister { - return v1alpha3.NewDestinationRuleLister(f.Informer().GetIndexer()) -} diff --git a/pkg/client/informers/externalversions/istio/v1alpha3/interface.go b/pkg/client/informers/externalversions/istio/v1alpha3/interface.go deleted file mode 100644 index 07a5ff09..00000000 --- a/pkg/client/informers/externalversions/istio/v1alpha3/interface.go +++ /dev/null @@ -1,52 +0,0 @@ -/* -Copyright The Flagger Authors. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -// Code generated by informer-gen. DO NOT EDIT. - -package v1alpha3 - -import ( - internalinterfaces "github.com/weaveworks/flagger/pkg/client/informers/externalversions/internalinterfaces" -) - -// Interface provides access to all the informers in this group version. -type Interface interface { - // DestinationRules returns a DestinationRuleInformer. - DestinationRules() DestinationRuleInformer - // VirtualServices returns a VirtualServiceInformer. - VirtualServices() VirtualServiceInformer -} - -type version struct { - factory internalinterfaces.SharedInformerFactory - namespace string - tweakListOptions internalinterfaces.TweakListOptionsFunc -} - -// New returns a new Interface. -func New(f internalinterfaces.SharedInformerFactory, namespace string, tweakListOptions internalinterfaces.TweakListOptionsFunc) Interface { - return &version{factory: f, namespace: namespace, tweakListOptions: tweakListOptions} -} - -// DestinationRules returns a DestinationRuleInformer. -func (v *version) DestinationRules() DestinationRuleInformer { - return &destinationRuleInformer{factory: v.factory, namespace: v.namespace, tweakListOptions: v.tweakListOptions} -} - -// VirtualServices returns a VirtualServiceInformer. -func (v *version) VirtualServices() VirtualServiceInformer { - return &virtualServiceInformer{factory: v.factory, namespace: v.namespace, tweakListOptions: v.tweakListOptions} -} diff --git a/pkg/client/informers/externalversions/istio/v1alpha3/virtualservice.go b/pkg/client/informers/externalversions/istio/v1alpha3/virtualservice.go deleted file mode 100644 index 6f022b51..00000000 --- a/pkg/client/informers/externalversions/istio/v1alpha3/virtualservice.go +++ /dev/null @@ -1,89 +0,0 @@ -/* -Copyright The Flagger Authors. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -// Code generated by informer-gen. DO NOT EDIT. - -package v1alpha3 - -import ( - time "time" - - istiov1alpha3 "github.com/weaveworks/flagger/pkg/apis/istio/v1alpha3" - versioned "github.com/weaveworks/flagger/pkg/client/clientset/versioned" - internalinterfaces "github.com/weaveworks/flagger/pkg/client/informers/externalversions/internalinterfaces" - v1alpha3 "github.com/weaveworks/flagger/pkg/client/listers/istio/v1alpha3" - v1 "k8s.io/apimachinery/pkg/apis/meta/v1" - runtime "k8s.io/apimachinery/pkg/runtime" - watch "k8s.io/apimachinery/pkg/watch" - cache "k8s.io/client-go/tools/cache" -) - -// VirtualServiceInformer provides access to a shared informer and lister for -// VirtualServices. -type VirtualServiceInformer interface { - Informer() cache.SharedIndexInformer - Lister() v1alpha3.VirtualServiceLister -} - -type virtualServiceInformer struct { - factory internalinterfaces.SharedInformerFactory - tweakListOptions internalinterfaces.TweakListOptionsFunc - namespace string -} - -// NewVirtualServiceInformer constructs a new informer for VirtualService type. -// Always prefer using an informer factory to get a shared informer instead of getting an independent -// one. This reduces memory footprint and number of connections to the server. -func NewVirtualServiceInformer(client versioned.Interface, namespace string, resyncPeriod time.Duration, indexers cache.Indexers) cache.SharedIndexInformer { - return NewFilteredVirtualServiceInformer(client, namespace, resyncPeriod, indexers, nil) -} - -// NewFilteredVirtualServiceInformer constructs a new informer for VirtualService type. -// Always prefer using an informer factory to get a shared informer instead of getting an independent -// one. This reduces memory footprint and number of connections to the server. -func NewFilteredVirtualServiceInformer(client versioned.Interface, namespace string, resyncPeriod time.Duration, indexers cache.Indexers, tweakListOptions internalinterfaces.TweakListOptionsFunc) cache.SharedIndexInformer { - return cache.NewSharedIndexInformer( - &cache.ListWatch{ - ListFunc: func(options v1.ListOptions) (runtime.Object, error) { - if tweakListOptions != nil { - tweakListOptions(&options) - } - return client.NetworkingV1alpha3().VirtualServices(namespace).List(options) - }, - WatchFunc: func(options v1.ListOptions) (watch.Interface, error) { - if tweakListOptions != nil { - tweakListOptions(&options) - } - return client.NetworkingV1alpha3().VirtualServices(namespace).Watch(options) - }, - }, - &istiov1alpha3.VirtualService{}, - resyncPeriod, - indexers, - ) -} - -func (f *virtualServiceInformer) defaultInformer(client versioned.Interface, resyncPeriod time.Duration) cache.SharedIndexInformer { - return NewFilteredVirtualServiceInformer(client, f.namespace, resyncPeriod, cache.Indexers{cache.NamespaceIndex: cache.MetaNamespaceIndexFunc}, f.tweakListOptions) -} - -func (f *virtualServiceInformer) Informer() cache.SharedIndexInformer { - return f.factory.InformerFor(&istiov1alpha3.VirtualService{}, f.defaultInformer) -} - -func (f *virtualServiceInformer) Lister() v1alpha3.VirtualServiceLister { - return v1alpha3.NewVirtualServiceLister(f.Informer().GetIndexer()) -} diff --git a/pkg/client/informers/externalversions/smi/interface.go b/pkg/client/informers/externalversions/smi/interface.go deleted file mode 100644 index cfc8f719..00000000 --- a/pkg/client/informers/externalversions/smi/interface.go +++ /dev/null @@ -1,46 +0,0 @@ -/* -Copyright The Flagger Authors. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -// Code generated by informer-gen. DO NOT EDIT. - -package split - -import ( - internalinterfaces "github.com/weaveworks/flagger/pkg/client/informers/externalversions/internalinterfaces" - v1alpha1 "github.com/weaveworks/flagger/pkg/client/informers/externalversions/smi/v1alpha1" -) - -// Interface provides access to each of this group's versions. -type Interface interface { - // V1alpha1 provides access to shared informers for resources in V1alpha1. - V1alpha1() v1alpha1.Interface -} - -type group struct { - factory internalinterfaces.SharedInformerFactory - namespace string - tweakListOptions internalinterfaces.TweakListOptionsFunc -} - -// New returns a new Interface. -func New(f internalinterfaces.SharedInformerFactory, namespace string, tweakListOptions internalinterfaces.TweakListOptionsFunc) Interface { - return &group{factory: f, namespace: namespace, tweakListOptions: tweakListOptions} -} - -// V1alpha1 returns a new v1alpha1.Interface. -func (g *group) V1alpha1() v1alpha1.Interface { - return v1alpha1.New(g.factory, g.namespace, g.tweakListOptions) -} diff --git a/pkg/client/informers/externalversions/smi/v1alpha1/interface.go b/pkg/client/informers/externalversions/smi/v1alpha1/interface.go deleted file mode 100644 index dabfa548..00000000 --- a/pkg/client/informers/externalversions/smi/v1alpha1/interface.go +++ /dev/null @@ -1,45 +0,0 @@ -/* -Copyright The Flagger Authors. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -// Code generated by informer-gen. DO NOT EDIT. - -package v1alpha1 - -import ( - internalinterfaces "github.com/weaveworks/flagger/pkg/client/informers/externalversions/internalinterfaces" -) - -// Interface provides access to all the informers in this group version. -type Interface interface { - // TrafficSplits returns a TrafficSplitInformer. - TrafficSplits() TrafficSplitInformer -} - -type version struct { - factory internalinterfaces.SharedInformerFactory - namespace string - tweakListOptions internalinterfaces.TweakListOptionsFunc -} - -// New returns a new Interface. -func New(f internalinterfaces.SharedInformerFactory, namespace string, tweakListOptions internalinterfaces.TweakListOptionsFunc) Interface { - return &version{factory: f, namespace: namespace, tweakListOptions: tweakListOptions} -} - -// TrafficSplits returns a TrafficSplitInformer. -func (v *version) TrafficSplits() TrafficSplitInformer { - return &trafficSplitInformer{factory: v.factory, namespace: v.namespace, tweakListOptions: v.tweakListOptions} -} diff --git a/pkg/client/informers/externalversions/smi/v1alpha1/trafficsplit.go b/pkg/client/informers/externalversions/smi/v1alpha1/trafficsplit.go deleted file mode 100644 index feb6b703..00000000 --- a/pkg/client/informers/externalversions/smi/v1alpha1/trafficsplit.go +++ /dev/null @@ -1,89 +0,0 @@ -/* -Copyright The Flagger Authors. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -// Code generated by informer-gen. DO NOT EDIT. - -package v1alpha1 - -import ( - time "time" - - smiv1alpha1 "github.com/weaveworks/flagger/pkg/apis/smi/v1alpha1" - versioned "github.com/weaveworks/flagger/pkg/client/clientset/versioned" - internalinterfaces "github.com/weaveworks/flagger/pkg/client/informers/externalversions/internalinterfaces" - v1alpha1 "github.com/weaveworks/flagger/pkg/client/listers/smi/v1alpha1" - v1 "k8s.io/apimachinery/pkg/apis/meta/v1" - runtime "k8s.io/apimachinery/pkg/runtime" - watch "k8s.io/apimachinery/pkg/watch" - cache "k8s.io/client-go/tools/cache" -) - -// TrafficSplitInformer provides access to a shared informer and lister for -// TrafficSplits. -type TrafficSplitInformer interface { - Informer() cache.SharedIndexInformer - Lister() v1alpha1.TrafficSplitLister -} - -type trafficSplitInformer struct { - factory internalinterfaces.SharedInformerFactory - tweakListOptions internalinterfaces.TweakListOptionsFunc - namespace string -} - -// NewTrafficSplitInformer constructs a new informer for TrafficSplit type. -// Always prefer using an informer factory to get a shared informer instead of getting an independent -// one. This reduces memory footprint and number of connections to the server. -func NewTrafficSplitInformer(client versioned.Interface, namespace string, resyncPeriod time.Duration, indexers cache.Indexers) cache.SharedIndexInformer { - return NewFilteredTrafficSplitInformer(client, namespace, resyncPeriod, indexers, nil) -} - -// NewFilteredTrafficSplitInformer constructs a new informer for TrafficSplit type. -// Always prefer using an informer factory to get a shared informer instead of getting an independent -// one. This reduces memory footprint and number of connections to the server. -func NewFilteredTrafficSplitInformer(client versioned.Interface, namespace string, resyncPeriod time.Duration, indexers cache.Indexers, tweakListOptions internalinterfaces.TweakListOptionsFunc) cache.SharedIndexInformer { - return cache.NewSharedIndexInformer( - &cache.ListWatch{ - ListFunc: func(options v1.ListOptions) (runtime.Object, error) { - if tweakListOptions != nil { - tweakListOptions(&options) - } - return client.SplitV1alpha1().TrafficSplits(namespace).List(options) - }, - WatchFunc: func(options v1.ListOptions) (watch.Interface, error) { - if tweakListOptions != nil { - tweakListOptions(&options) - } - return client.SplitV1alpha1().TrafficSplits(namespace).Watch(options) - }, - }, - &smiv1alpha1.TrafficSplit{}, - resyncPeriod, - indexers, - ) -} - -func (f *trafficSplitInformer) defaultInformer(client versioned.Interface, resyncPeriod time.Duration) cache.SharedIndexInformer { - return NewFilteredTrafficSplitInformer(client, f.namespace, resyncPeriod, cache.Indexers{cache.NamespaceIndex: cache.MetaNamespaceIndexFunc}, f.tweakListOptions) -} - -func (f *trafficSplitInformer) Informer() cache.SharedIndexInformer { - return f.factory.InformerFor(&smiv1alpha1.TrafficSplit{}, f.defaultInformer) -} - -func (f *trafficSplitInformer) Lister() v1alpha1.TrafficSplitLister { - return v1alpha1.NewTrafficSplitLister(f.Informer().GetIndexer()) -} diff --git a/pkg/client/listers/appmesh/v1beta1/expansion_generated.go b/pkg/client/listers/appmesh/v1beta1/expansion_generated.go deleted file mode 100644 index c3100366..00000000 --- a/pkg/client/listers/appmesh/v1beta1/expansion_generated.go +++ /dev/null @@ -1,39 +0,0 @@ -/* -Copyright The Flagger Authors. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -// Code generated by lister-gen. DO NOT EDIT. - -package v1beta1 - -// MeshListerExpansion allows custom methods to be added to -// MeshLister. -type MeshListerExpansion interface{} - -// VirtualNodeListerExpansion allows custom methods to be added to -// VirtualNodeLister. -type VirtualNodeListerExpansion interface{} - -// VirtualNodeNamespaceListerExpansion allows custom methods to be added to -// VirtualNodeNamespaceLister. -type VirtualNodeNamespaceListerExpansion interface{} - -// VirtualServiceListerExpansion allows custom methods to be added to -// VirtualServiceLister. -type VirtualServiceListerExpansion interface{} - -// VirtualServiceNamespaceListerExpansion allows custom methods to be added to -// VirtualServiceNamespaceLister. -type VirtualServiceNamespaceListerExpansion interface{} diff --git a/pkg/client/listers/appmesh/v1beta1/mesh.go b/pkg/client/listers/appmesh/v1beta1/mesh.go deleted file mode 100644 index 3d632b66..00000000 --- a/pkg/client/listers/appmesh/v1beta1/mesh.go +++ /dev/null @@ -1,65 +0,0 @@ -/* -Copyright The Flagger Authors. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -// Code generated by lister-gen. DO NOT EDIT. - -package v1beta1 - -import ( - v1beta1 "github.com/weaveworks/flagger/pkg/apis/appmesh/v1beta1" - "k8s.io/apimachinery/pkg/api/errors" - "k8s.io/apimachinery/pkg/labels" - "k8s.io/client-go/tools/cache" -) - -// MeshLister helps list Meshes. -type MeshLister interface { - // List lists all Meshes in the indexer. - List(selector labels.Selector) (ret []*v1beta1.Mesh, err error) - // Get retrieves the Mesh from the index for a given name. - Get(name string) (*v1beta1.Mesh, error) - MeshListerExpansion -} - -// meshLister implements the MeshLister interface. -type meshLister struct { - indexer cache.Indexer -} - -// NewMeshLister returns a new MeshLister. -func NewMeshLister(indexer cache.Indexer) MeshLister { - return &meshLister{indexer: indexer} -} - -// List lists all Meshes in the indexer. -func (s *meshLister) List(selector labels.Selector) (ret []*v1beta1.Mesh, err error) { - err = cache.ListAll(s.indexer, selector, func(m interface{}) { - ret = append(ret, m.(*v1beta1.Mesh)) - }) - return ret, err -} - -// Get retrieves the Mesh from the index for a given name. -func (s *meshLister) Get(name string) (*v1beta1.Mesh, error) { - obj, exists, err := s.indexer.GetByKey(name) - if err != nil { - return nil, err - } - if !exists { - return nil, errors.NewNotFound(v1beta1.Resource("mesh"), name) - } - return obj.(*v1beta1.Mesh), nil -} diff --git a/pkg/client/listers/appmesh/v1beta1/virtualnode.go b/pkg/client/listers/appmesh/v1beta1/virtualnode.go deleted file mode 100644 index 9ec949f5..00000000 --- a/pkg/client/listers/appmesh/v1beta1/virtualnode.go +++ /dev/null @@ -1,94 +0,0 @@ -/* -Copyright The Flagger Authors. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -// Code generated by lister-gen. DO NOT EDIT. - -package v1beta1 - -import ( - v1beta1 "github.com/weaveworks/flagger/pkg/apis/appmesh/v1beta1" - "k8s.io/apimachinery/pkg/api/errors" - "k8s.io/apimachinery/pkg/labels" - "k8s.io/client-go/tools/cache" -) - -// VirtualNodeLister helps list VirtualNodes. -type VirtualNodeLister interface { - // List lists all VirtualNodes in the indexer. - List(selector labels.Selector) (ret []*v1beta1.VirtualNode, err error) - // VirtualNodes returns an object that can list and get VirtualNodes. - VirtualNodes(namespace string) VirtualNodeNamespaceLister - VirtualNodeListerExpansion -} - -// virtualNodeLister implements the VirtualNodeLister interface. -type virtualNodeLister struct { - indexer cache.Indexer -} - -// NewVirtualNodeLister returns a new VirtualNodeLister. -func NewVirtualNodeLister(indexer cache.Indexer) VirtualNodeLister { - return &virtualNodeLister{indexer: indexer} -} - -// List lists all VirtualNodes in the indexer. -func (s *virtualNodeLister) List(selector labels.Selector) (ret []*v1beta1.VirtualNode, err error) { - err = cache.ListAll(s.indexer, selector, func(m interface{}) { - ret = append(ret, m.(*v1beta1.VirtualNode)) - }) - return ret, err -} - -// VirtualNodes returns an object that can list and get VirtualNodes. -func (s *virtualNodeLister) VirtualNodes(namespace string) VirtualNodeNamespaceLister { - return virtualNodeNamespaceLister{indexer: s.indexer, namespace: namespace} -} - -// VirtualNodeNamespaceLister helps list and get VirtualNodes. -type VirtualNodeNamespaceLister interface { - // List lists all VirtualNodes in the indexer for a given namespace. - List(selector labels.Selector) (ret []*v1beta1.VirtualNode, err error) - // Get retrieves the VirtualNode from the indexer for a given namespace and name. - Get(name string) (*v1beta1.VirtualNode, error) - VirtualNodeNamespaceListerExpansion -} - -// virtualNodeNamespaceLister implements the VirtualNodeNamespaceLister -// interface. -type virtualNodeNamespaceLister struct { - indexer cache.Indexer - namespace string -} - -// List lists all VirtualNodes in the indexer for a given namespace. -func (s virtualNodeNamespaceLister) List(selector labels.Selector) (ret []*v1beta1.VirtualNode, err error) { - err = cache.ListAllByNamespace(s.indexer, s.namespace, selector, func(m interface{}) { - ret = append(ret, m.(*v1beta1.VirtualNode)) - }) - return ret, err -} - -// Get retrieves the VirtualNode from the indexer for a given namespace and name. -func (s virtualNodeNamespaceLister) Get(name string) (*v1beta1.VirtualNode, error) { - obj, exists, err := s.indexer.GetByKey(s.namespace + "/" + name) - if err != nil { - return nil, err - } - if !exists { - return nil, errors.NewNotFound(v1beta1.Resource("virtualnode"), name) - } - return obj.(*v1beta1.VirtualNode), nil -} diff --git a/pkg/client/listers/appmesh/v1beta1/virtualservice.go b/pkg/client/listers/appmesh/v1beta1/virtualservice.go deleted file mode 100644 index cb052d73..00000000 --- a/pkg/client/listers/appmesh/v1beta1/virtualservice.go +++ /dev/null @@ -1,94 +0,0 @@ -/* -Copyright The Flagger Authors. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -// Code generated by lister-gen. DO NOT EDIT. - -package v1beta1 - -import ( - v1beta1 "github.com/weaveworks/flagger/pkg/apis/appmesh/v1beta1" - "k8s.io/apimachinery/pkg/api/errors" - "k8s.io/apimachinery/pkg/labels" - "k8s.io/client-go/tools/cache" -) - -// VirtualServiceLister helps list VirtualServices. -type VirtualServiceLister interface { - // List lists all VirtualServices in the indexer. - List(selector labels.Selector) (ret []*v1beta1.VirtualService, err error) - // VirtualServices returns an object that can list and get VirtualServices. - VirtualServices(namespace string) VirtualServiceNamespaceLister - VirtualServiceListerExpansion -} - -// virtualServiceLister implements the VirtualServiceLister interface. -type virtualServiceLister struct { - indexer cache.Indexer -} - -// NewVirtualServiceLister returns a new VirtualServiceLister. -func NewVirtualServiceLister(indexer cache.Indexer) VirtualServiceLister { - return &virtualServiceLister{indexer: indexer} -} - -// List lists all VirtualServices in the indexer. -func (s *virtualServiceLister) List(selector labels.Selector) (ret []*v1beta1.VirtualService, err error) { - err = cache.ListAll(s.indexer, selector, func(m interface{}) { - ret = append(ret, m.(*v1beta1.VirtualService)) - }) - return ret, err -} - -// VirtualServices returns an object that can list and get VirtualServices. -func (s *virtualServiceLister) VirtualServices(namespace string) VirtualServiceNamespaceLister { - return virtualServiceNamespaceLister{indexer: s.indexer, namespace: namespace} -} - -// VirtualServiceNamespaceLister helps list and get VirtualServices. -type VirtualServiceNamespaceLister interface { - // List lists all VirtualServices in the indexer for a given namespace. - List(selector labels.Selector) (ret []*v1beta1.VirtualService, err error) - // Get retrieves the VirtualService from the indexer for a given namespace and name. - Get(name string) (*v1beta1.VirtualService, error) - VirtualServiceNamespaceListerExpansion -} - -// virtualServiceNamespaceLister implements the VirtualServiceNamespaceLister -// interface. -type virtualServiceNamespaceLister struct { - indexer cache.Indexer - namespace string -} - -// List lists all VirtualServices in the indexer for a given namespace. -func (s virtualServiceNamespaceLister) List(selector labels.Selector) (ret []*v1beta1.VirtualService, err error) { - err = cache.ListAllByNamespace(s.indexer, s.namespace, selector, func(m interface{}) { - ret = append(ret, m.(*v1beta1.VirtualService)) - }) - return ret, err -} - -// Get retrieves the VirtualService from the indexer for a given namespace and name. -func (s virtualServiceNamespaceLister) Get(name string) (*v1beta1.VirtualService, error) { - obj, exists, err := s.indexer.GetByKey(s.namespace + "/" + name) - if err != nil { - return nil, err - } - if !exists { - return nil, errors.NewNotFound(v1beta1.Resource("virtualservice"), name) - } - return obj.(*v1beta1.VirtualService), nil -} diff --git a/pkg/client/listers/flagger/v1alpha3/canary.go b/pkg/client/listers/flagger/v1alpha3/canary.go deleted file mode 100644 index d5a050bf..00000000 --- a/pkg/client/listers/flagger/v1alpha3/canary.go +++ /dev/null @@ -1,94 +0,0 @@ -/* -Copyright The Flagger Authors. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -// Code generated by lister-gen. DO NOT EDIT. - -package v1alpha3 - -import ( - v1alpha3 "github.com/weaveworks/flagger/pkg/apis/flagger/v1alpha3" - "k8s.io/apimachinery/pkg/api/errors" - "k8s.io/apimachinery/pkg/labels" - "k8s.io/client-go/tools/cache" -) - -// CanaryLister helps list Canaries. -type CanaryLister interface { - // List lists all Canaries in the indexer. - List(selector labels.Selector) (ret []*v1alpha3.Canary, err error) - // Canaries returns an object that can list and get Canaries. - Canaries(namespace string) CanaryNamespaceLister - CanaryListerExpansion -} - -// canaryLister implements the CanaryLister interface. -type canaryLister struct { - indexer cache.Indexer -} - -// NewCanaryLister returns a new CanaryLister. -func NewCanaryLister(indexer cache.Indexer) CanaryLister { - return &canaryLister{indexer: indexer} -} - -// List lists all Canaries in the indexer. -func (s *canaryLister) List(selector labels.Selector) (ret []*v1alpha3.Canary, err error) { - err = cache.ListAll(s.indexer, selector, func(m interface{}) { - ret = append(ret, m.(*v1alpha3.Canary)) - }) - return ret, err -} - -// Canaries returns an object that can list and get Canaries. -func (s *canaryLister) Canaries(namespace string) CanaryNamespaceLister { - return canaryNamespaceLister{indexer: s.indexer, namespace: namespace} -} - -// CanaryNamespaceLister helps list and get Canaries. -type CanaryNamespaceLister interface { - // List lists all Canaries in the indexer for a given namespace. - List(selector labels.Selector) (ret []*v1alpha3.Canary, err error) - // Get retrieves the Canary from the indexer for a given namespace and name. - Get(name string) (*v1alpha3.Canary, error) - CanaryNamespaceListerExpansion -} - -// canaryNamespaceLister implements the CanaryNamespaceLister -// interface. -type canaryNamespaceLister struct { - indexer cache.Indexer - namespace string -} - -// List lists all Canaries in the indexer for a given namespace. -func (s canaryNamespaceLister) List(selector labels.Selector) (ret []*v1alpha3.Canary, err error) { - err = cache.ListAllByNamespace(s.indexer, s.namespace, selector, func(m interface{}) { - ret = append(ret, m.(*v1alpha3.Canary)) - }) - return ret, err -} - -// Get retrieves the Canary from the indexer for a given namespace and name. -func (s canaryNamespaceLister) Get(name string) (*v1alpha3.Canary, error) { - obj, exists, err := s.indexer.GetByKey(s.namespace + "/" + name) - if err != nil { - return nil, err - } - if !exists { - return nil, errors.NewNotFound(v1alpha3.Resource("canary"), name) - } - return obj.(*v1alpha3.Canary), nil -} diff --git a/pkg/client/listers/flagger/v1alpha3/expansion_generated.go b/pkg/client/listers/flagger/v1alpha3/expansion_generated.go deleted file mode 100644 index 632464cf..00000000 --- a/pkg/client/listers/flagger/v1alpha3/expansion_generated.go +++ /dev/null @@ -1,27 +0,0 @@ -/* -Copyright The Flagger Authors. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -// Code generated by lister-gen. DO NOT EDIT. - -package v1alpha3 - -// CanaryListerExpansion allows custom methods to be added to -// CanaryLister. -type CanaryListerExpansion interface{} - -// CanaryNamespaceListerExpansion allows custom methods to be added to -// CanaryNamespaceLister. -type CanaryNamespaceListerExpansion interface{} diff --git a/pkg/client/listers/istio/v1alpha3/destinationrule.go b/pkg/client/listers/istio/v1alpha3/destinationrule.go deleted file mode 100644 index 3717bed5..00000000 --- a/pkg/client/listers/istio/v1alpha3/destinationrule.go +++ /dev/null @@ -1,94 +0,0 @@ -/* -Copyright The Flagger Authors. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -// Code generated by lister-gen. DO NOT EDIT. - -package v1alpha3 - -import ( - v1alpha3 "github.com/weaveworks/flagger/pkg/apis/istio/v1alpha3" - "k8s.io/apimachinery/pkg/api/errors" - "k8s.io/apimachinery/pkg/labels" - "k8s.io/client-go/tools/cache" -) - -// DestinationRuleLister helps list DestinationRules. -type DestinationRuleLister interface { - // List lists all DestinationRules in the indexer. - List(selector labels.Selector) (ret []*v1alpha3.DestinationRule, err error) - // DestinationRules returns an object that can list and get DestinationRules. - DestinationRules(namespace string) DestinationRuleNamespaceLister - DestinationRuleListerExpansion -} - -// destinationRuleLister implements the DestinationRuleLister interface. -type destinationRuleLister struct { - indexer cache.Indexer -} - -// NewDestinationRuleLister returns a new DestinationRuleLister. -func NewDestinationRuleLister(indexer cache.Indexer) DestinationRuleLister { - return &destinationRuleLister{indexer: indexer} -} - -// List lists all DestinationRules in the indexer. -func (s *destinationRuleLister) List(selector labels.Selector) (ret []*v1alpha3.DestinationRule, err error) { - err = cache.ListAll(s.indexer, selector, func(m interface{}) { - ret = append(ret, m.(*v1alpha3.DestinationRule)) - }) - return ret, err -} - -// DestinationRules returns an object that can list and get DestinationRules. -func (s *destinationRuleLister) DestinationRules(namespace string) DestinationRuleNamespaceLister { - return destinationRuleNamespaceLister{indexer: s.indexer, namespace: namespace} -} - -// DestinationRuleNamespaceLister helps list and get DestinationRules. -type DestinationRuleNamespaceLister interface { - // List lists all DestinationRules in the indexer for a given namespace. - List(selector labels.Selector) (ret []*v1alpha3.DestinationRule, err error) - // Get retrieves the DestinationRule from the indexer for a given namespace and name. - Get(name string) (*v1alpha3.DestinationRule, error) - DestinationRuleNamespaceListerExpansion -} - -// destinationRuleNamespaceLister implements the DestinationRuleNamespaceLister -// interface. -type destinationRuleNamespaceLister struct { - indexer cache.Indexer - namespace string -} - -// List lists all DestinationRules in the indexer for a given namespace. -func (s destinationRuleNamespaceLister) List(selector labels.Selector) (ret []*v1alpha3.DestinationRule, err error) { - err = cache.ListAllByNamespace(s.indexer, s.namespace, selector, func(m interface{}) { - ret = append(ret, m.(*v1alpha3.DestinationRule)) - }) - return ret, err -} - -// Get retrieves the DestinationRule from the indexer for a given namespace and name. -func (s destinationRuleNamespaceLister) Get(name string) (*v1alpha3.DestinationRule, error) { - obj, exists, err := s.indexer.GetByKey(s.namespace + "/" + name) - if err != nil { - return nil, err - } - if !exists { - return nil, errors.NewNotFound(v1alpha3.Resource("destinationrule"), name) - } - return obj.(*v1alpha3.DestinationRule), nil -} diff --git a/pkg/client/listers/istio/v1alpha3/expansion_generated.go b/pkg/client/listers/istio/v1alpha3/expansion_generated.go deleted file mode 100644 index 6c0921d5..00000000 --- a/pkg/client/listers/istio/v1alpha3/expansion_generated.go +++ /dev/null @@ -1,35 +0,0 @@ -/* -Copyright The Flagger Authors. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -// Code generated by lister-gen. DO NOT EDIT. - -package v1alpha3 - -// DestinationRuleListerExpansion allows custom methods to be added to -// DestinationRuleLister. -type DestinationRuleListerExpansion interface{} - -// DestinationRuleNamespaceListerExpansion allows custom methods to be added to -// DestinationRuleNamespaceLister. -type DestinationRuleNamespaceListerExpansion interface{} - -// VirtualServiceListerExpansion allows custom methods to be added to -// VirtualServiceLister. -type VirtualServiceListerExpansion interface{} - -// VirtualServiceNamespaceListerExpansion allows custom methods to be added to -// VirtualServiceNamespaceLister. -type VirtualServiceNamespaceListerExpansion interface{} diff --git a/pkg/client/listers/istio/v1alpha3/virtualservice.go b/pkg/client/listers/istio/v1alpha3/virtualservice.go deleted file mode 100644 index dbe6d7ab..00000000 --- a/pkg/client/listers/istio/v1alpha3/virtualservice.go +++ /dev/null @@ -1,94 +0,0 @@ -/* -Copyright The Flagger Authors. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -// Code generated by lister-gen. DO NOT EDIT. - -package v1alpha3 - -import ( - v1alpha3 "github.com/weaveworks/flagger/pkg/apis/istio/v1alpha3" - "k8s.io/apimachinery/pkg/api/errors" - "k8s.io/apimachinery/pkg/labels" - "k8s.io/client-go/tools/cache" -) - -// VirtualServiceLister helps list VirtualServices. -type VirtualServiceLister interface { - // List lists all VirtualServices in the indexer. - List(selector labels.Selector) (ret []*v1alpha3.VirtualService, err error) - // VirtualServices returns an object that can list and get VirtualServices. - VirtualServices(namespace string) VirtualServiceNamespaceLister - VirtualServiceListerExpansion -} - -// virtualServiceLister implements the VirtualServiceLister interface. -type virtualServiceLister struct { - indexer cache.Indexer -} - -// NewVirtualServiceLister returns a new VirtualServiceLister. -func NewVirtualServiceLister(indexer cache.Indexer) VirtualServiceLister { - return &virtualServiceLister{indexer: indexer} -} - -// List lists all VirtualServices in the indexer. -func (s *virtualServiceLister) List(selector labels.Selector) (ret []*v1alpha3.VirtualService, err error) { - err = cache.ListAll(s.indexer, selector, func(m interface{}) { - ret = append(ret, m.(*v1alpha3.VirtualService)) - }) - return ret, err -} - -// VirtualServices returns an object that can list and get VirtualServices. -func (s *virtualServiceLister) VirtualServices(namespace string) VirtualServiceNamespaceLister { - return virtualServiceNamespaceLister{indexer: s.indexer, namespace: namespace} -} - -// VirtualServiceNamespaceLister helps list and get VirtualServices. -type VirtualServiceNamespaceLister interface { - // List lists all VirtualServices in the indexer for a given namespace. - List(selector labels.Selector) (ret []*v1alpha3.VirtualService, err error) - // Get retrieves the VirtualService from the indexer for a given namespace and name. - Get(name string) (*v1alpha3.VirtualService, error) - VirtualServiceNamespaceListerExpansion -} - -// virtualServiceNamespaceLister implements the VirtualServiceNamespaceLister -// interface. -type virtualServiceNamespaceLister struct { - indexer cache.Indexer - namespace string -} - -// List lists all VirtualServices in the indexer for a given namespace. -func (s virtualServiceNamespaceLister) List(selector labels.Selector) (ret []*v1alpha3.VirtualService, err error) { - err = cache.ListAllByNamespace(s.indexer, s.namespace, selector, func(m interface{}) { - ret = append(ret, m.(*v1alpha3.VirtualService)) - }) - return ret, err -} - -// Get retrieves the VirtualService from the indexer for a given namespace and name. -func (s virtualServiceNamespaceLister) Get(name string) (*v1alpha3.VirtualService, error) { - obj, exists, err := s.indexer.GetByKey(s.namespace + "/" + name) - if err != nil { - return nil, err - } - if !exists { - return nil, errors.NewNotFound(v1alpha3.Resource("virtualservice"), name) - } - return obj.(*v1alpha3.VirtualService), nil -} diff --git a/pkg/client/listers/smi/v1alpha1/expansion_generated.go b/pkg/client/listers/smi/v1alpha1/expansion_generated.go deleted file mode 100644 index 271ee243..00000000 --- a/pkg/client/listers/smi/v1alpha1/expansion_generated.go +++ /dev/null @@ -1,27 +0,0 @@ -/* -Copyright The Flagger Authors. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -// Code generated by lister-gen. DO NOT EDIT. - -package v1alpha1 - -// TrafficSplitListerExpansion allows custom methods to be added to -// TrafficSplitLister. -type TrafficSplitListerExpansion interface{} - -// TrafficSplitNamespaceListerExpansion allows custom methods to be added to -// TrafficSplitNamespaceLister. -type TrafficSplitNamespaceListerExpansion interface{} diff --git a/pkg/client/listers/smi/v1alpha1/trafficsplit.go b/pkg/client/listers/smi/v1alpha1/trafficsplit.go deleted file mode 100644 index 23c52eba..00000000 --- a/pkg/client/listers/smi/v1alpha1/trafficsplit.go +++ /dev/null @@ -1,94 +0,0 @@ -/* -Copyright The Flagger Authors. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -// Code generated by lister-gen. DO NOT EDIT. - -package v1alpha1 - -import ( - v1alpha1 "github.com/weaveworks/flagger/pkg/apis/smi/v1alpha1" - "k8s.io/apimachinery/pkg/api/errors" - "k8s.io/apimachinery/pkg/labels" - "k8s.io/client-go/tools/cache" -) - -// TrafficSplitLister helps list TrafficSplits. -type TrafficSplitLister interface { - // List lists all TrafficSplits in the indexer. - List(selector labels.Selector) (ret []*v1alpha1.TrafficSplit, err error) - // TrafficSplits returns an object that can list and get TrafficSplits. - TrafficSplits(namespace string) TrafficSplitNamespaceLister - TrafficSplitListerExpansion -} - -// trafficSplitLister implements the TrafficSplitLister interface. -type trafficSplitLister struct { - indexer cache.Indexer -} - -// NewTrafficSplitLister returns a new TrafficSplitLister. -func NewTrafficSplitLister(indexer cache.Indexer) TrafficSplitLister { - return &trafficSplitLister{indexer: indexer} -} - -// List lists all TrafficSplits in the indexer. -func (s *trafficSplitLister) List(selector labels.Selector) (ret []*v1alpha1.TrafficSplit, err error) { - err = cache.ListAll(s.indexer, selector, func(m interface{}) { - ret = append(ret, m.(*v1alpha1.TrafficSplit)) - }) - return ret, err -} - -// TrafficSplits returns an object that can list and get TrafficSplits. -func (s *trafficSplitLister) TrafficSplits(namespace string) TrafficSplitNamespaceLister { - return trafficSplitNamespaceLister{indexer: s.indexer, namespace: namespace} -} - -// TrafficSplitNamespaceLister helps list and get TrafficSplits. -type TrafficSplitNamespaceLister interface { - // List lists all TrafficSplits in the indexer for a given namespace. - List(selector labels.Selector) (ret []*v1alpha1.TrafficSplit, err error) - // Get retrieves the TrafficSplit from the indexer for a given namespace and name. - Get(name string) (*v1alpha1.TrafficSplit, error) - TrafficSplitNamespaceListerExpansion -} - -// trafficSplitNamespaceLister implements the TrafficSplitNamespaceLister -// interface. -type trafficSplitNamespaceLister struct { - indexer cache.Indexer - namespace string -} - -// List lists all TrafficSplits in the indexer for a given namespace. -func (s trafficSplitNamespaceLister) List(selector labels.Selector) (ret []*v1alpha1.TrafficSplit, err error) { - err = cache.ListAllByNamespace(s.indexer, s.namespace, selector, func(m interface{}) { - ret = append(ret, m.(*v1alpha1.TrafficSplit)) - }) - return ret, err -} - -// Get retrieves the TrafficSplit from the indexer for a given namespace and name. -func (s trafficSplitNamespaceLister) Get(name string) (*v1alpha1.TrafficSplit, error) { - obj, exists, err := s.indexer.GetByKey(s.namespace + "/" + name) - if err != nil { - return nil, err - } - if !exists { - return nil, errors.NewNotFound(v1alpha1.Resource("trafficsplit"), name) - } - return obj.(*v1alpha1.TrafficSplit), nil -} diff --git a/pkg/controller/controller.go b/pkg/controller/controller.go deleted file mode 100644 index 3e82f00d..00000000 --- a/pkg/controller/controller.go +++ /dev/null @@ -1,325 +0,0 @@ -package controller - -import ( - "fmt" - "sync" - "time" - - flaggerv1 "github.com/weaveworks/flagger/pkg/apis/flagger/v1alpha3" - "github.com/weaveworks/flagger/pkg/canary" - clientset "github.com/weaveworks/flagger/pkg/client/clientset/versioned" - flaggerscheme "github.com/weaveworks/flagger/pkg/client/clientset/versioned/scheme" - flaggerinformers "github.com/weaveworks/flagger/pkg/client/informers/externalversions/flagger/v1alpha3" - flaggerlisters "github.com/weaveworks/flagger/pkg/client/listers/flagger/v1alpha3" - "github.com/weaveworks/flagger/pkg/metrics" - "github.com/weaveworks/flagger/pkg/notifier" - "github.com/weaveworks/flagger/pkg/router" - - "github.com/google/go-cmp/cmp" - "go.uber.org/zap" - corev1 "k8s.io/api/core/v1" - "k8s.io/apimachinery/pkg/api/errors" - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - utilruntime "k8s.io/apimachinery/pkg/util/runtime" - "k8s.io/apimachinery/pkg/util/wait" - "k8s.io/client-go/kubernetes" - "k8s.io/client-go/kubernetes/scheme" - typedcorev1 "k8s.io/client-go/kubernetes/typed/core/v1" - "k8s.io/client-go/tools/cache" - "k8s.io/client-go/tools/record" - "k8s.io/client-go/util/workqueue" -) - -const controllerAgentName = "flagger" - -// Controller is managing the canary objects and schedules canary deployments -type Controller struct { - kubeClient kubernetes.Interface - istioClient clientset.Interface - flaggerClient clientset.Interface - flaggerLister flaggerlisters.CanaryLister - flaggerSynced cache.InformerSynced - flaggerWindow time.Duration - workqueue workqueue.RateLimitingInterface - eventRecorder record.EventRecorder - logger *zap.SugaredLogger - canaries *sync.Map - jobs map[string]CanaryJob - deployer canary.Deployer - recorder metrics.Recorder - notifier notifier.Interface - routerFactory *router.Factory - observerFactory *metrics.Factory - meshProvider string -} - -func NewController( - kubeClient kubernetes.Interface, - istioClient clientset.Interface, - flaggerClient clientset.Interface, - flaggerInformer flaggerinformers.CanaryInformer, - flaggerWindow time.Duration, - logger *zap.SugaredLogger, - notifier notifier.Interface, - routerFactory *router.Factory, - observerFactory *metrics.Factory, - meshProvider string, - version string, - labels []string, -) *Controller { - logger.Debug("Creating event broadcaster") - flaggerscheme.AddToScheme(scheme.Scheme) - eventBroadcaster := record.NewBroadcaster() - eventBroadcaster.StartLogging(logger.Named("event-broadcaster").Debugf) - eventBroadcaster.StartRecordingToSink(&typedcorev1.EventSinkImpl{ - Interface: kubeClient.CoreV1().Events(""), - }) - eventRecorder := eventBroadcaster.NewRecorder( - scheme.Scheme, corev1.EventSource{Component: controllerAgentName}) - - deployer := canary.Deployer{ - Logger: logger, - KubeClient: kubeClient, - FlaggerClient: flaggerClient, - Labels: labels, - ConfigTracker: canary.ConfigTracker{ - Logger: logger, - KubeClient: kubeClient, - FlaggerClient: flaggerClient, - }, - } - - recorder := metrics.NewRecorder(controllerAgentName, true) - recorder.SetInfo(version, meshProvider) - - ctrl := &Controller{ - kubeClient: kubeClient, - istioClient: istioClient, - flaggerClient: flaggerClient, - flaggerLister: flaggerInformer.Lister(), - flaggerSynced: flaggerInformer.Informer().HasSynced, - workqueue: workqueue.NewNamedRateLimitingQueue(workqueue.DefaultControllerRateLimiter(), controllerAgentName), - eventRecorder: eventRecorder, - logger: logger, - canaries: new(sync.Map), - jobs: map[string]CanaryJob{}, - flaggerWindow: flaggerWindow, - deployer: deployer, - observerFactory: observerFactory, - recorder: recorder, - notifier: notifier, - routerFactory: routerFactory, - meshProvider: meshProvider, - } - - flaggerInformer.Informer().AddEventHandler(cache.ResourceEventHandlerFuncs{ - AddFunc: ctrl.enqueue, - UpdateFunc: func(old, new interface{}) { - oldRoll, ok := checkCustomResourceType(old, logger) - if !ok { - return - } - newRoll, ok := checkCustomResourceType(new, logger) - if !ok { - return - } - - if diff := cmp.Diff(newRoll.Spec, oldRoll.Spec); diff != "" { - ctrl.logger.Debugf("Diff detected %s.%s %s", oldRoll.Name, oldRoll.Namespace, diff) - ctrl.enqueue(new) - } - }, - DeleteFunc: func(old interface{}) { - r, ok := checkCustomResourceType(old, logger) - if ok { - ctrl.logger.Infof("Deleting %s.%s from cache", r.Name, r.Namespace) - ctrl.canaries.Delete(fmt.Sprintf("%s.%s", r.Name, r.Namespace)) - } - }, - }) - - return ctrl -} - -// Run starts the K8s workers and the canary scheduler -func (c *Controller) Run(threadiness int, stopCh <-chan struct{}) error { - defer utilruntime.HandleCrash() - defer c.workqueue.ShutDown() - - c.logger.Info("Starting operator") - - for i := 0; i < threadiness; i++ { - go wait.Until(func() { - for c.processNextWorkItem() { - } - }, time.Second, stopCh) - } - - c.logger.Info("Started operator workers") - - tickChan := time.NewTicker(c.flaggerWindow).C - for { - select { - case <-tickChan: - c.scheduleCanaries() - case <-stopCh: - c.logger.Info("Shutting down operator workers") - return nil - } - } -} - -func (c *Controller) processNextWorkItem() bool { - obj, shutdown := c.workqueue.Get() - - if shutdown { - return false - } - - err := func(obj interface{}) error { - defer c.workqueue.Done(obj) - var key string - var ok bool - if key, ok = obj.(string); !ok { - c.workqueue.Forget(obj) - utilruntime.HandleError(fmt.Errorf("expected string in workqueue but got %#v", obj)) - return nil - } - // Run the syncHandler, passing it the namespace/name string of the - // Foo resource to be synced. - if err := c.syncHandler(key); err != nil { - return fmt.Errorf("error syncing '%s': %s", key, err.Error()) - } - // Finally, if no error occurs we Forget this item so it does not - // get queued again until another change happens. - c.workqueue.Forget(obj) - return nil - }(obj) - - if err != nil { - utilruntime.HandleError(err) - return true - } - - return true -} - -func (c *Controller) syncHandler(key string) error { - namespace, name, err := cache.SplitMetaNamespaceKey(key) - if err != nil { - utilruntime.HandleError(fmt.Errorf("invalid resource key: %s", key)) - return nil - } - cd, err := c.flaggerLister.Canaries(namespace).Get(name) - if errors.IsNotFound(err) { - utilruntime.HandleError(fmt.Errorf("%s in work queue no longer exists", key)) - return nil - } - - // set status condition for new canaries - if cd.Status.Conditions == nil { - if ok, conditions := c.deployer.MakeStatusConditions(cd.Status, flaggerv1.CanaryPhaseInitializing); ok { - cdCopy := cd.DeepCopy() - cdCopy.Status.Conditions = conditions - cdCopy.Status.LastTransitionTime = metav1.Now() - cdCopy.Status.Phase = flaggerv1.CanaryPhaseInitializing - _, err := c.flaggerClient.FlaggerV1alpha3().Canaries(cd.Namespace).UpdateStatus(cdCopy) - if err != nil { - c.logger.Errorf("%s status condition update error: %v", key, err) - return fmt.Errorf("%s status condition update error: %v", key, err) - } - } - } - - c.canaries.Store(fmt.Sprintf("%s.%s", cd.Name, cd.Namespace), cd) - c.logger.Infof("Synced %s", key) - - return nil -} - -func (c *Controller) enqueue(obj interface{}) { - var key string - var err error - if key, err = cache.MetaNamespaceKeyFunc(obj); err != nil { - utilruntime.HandleError(err) - return - } - c.workqueue.AddRateLimited(key) -} - -func checkCustomResourceType(obj interface{}, logger *zap.SugaredLogger) (flaggerv1.Canary, bool) { - var roll *flaggerv1.Canary - var ok bool - if roll, ok = obj.(*flaggerv1.Canary); !ok { - logger.Errorf("Event Watch received an invalid object: %#v", obj) - return flaggerv1.Canary{}, false - } - return *roll, true -} - -func (c *Controller) recordEventInfof(r *flaggerv1.Canary, template string, args ...interface{}) { - c.logger.With("canary", fmt.Sprintf("%s.%s", r.Name, r.Namespace)).Infof(template, args...) - c.eventRecorder.Event(r, corev1.EventTypeNormal, "Synced", fmt.Sprintf(template, args...)) -} - -func (c *Controller) recordEventErrorf(r *flaggerv1.Canary, template string, args ...interface{}) { - c.logger.With("canary", fmt.Sprintf("%s.%s", r.Name, r.Namespace)).Errorf(template, args...) - c.eventRecorder.Event(r, corev1.EventTypeWarning, "Synced", fmt.Sprintf(template, args...)) -} - -func (c *Controller) recordEventWarningf(r *flaggerv1.Canary, template string, args ...interface{}) { - c.logger.With("canary", fmt.Sprintf("%s.%s", r.Name, r.Namespace)).Infof(template, args...) - c.eventRecorder.Event(r, corev1.EventTypeWarning, "Synced", fmt.Sprintf(template, args...)) -} - -func (c *Controller) sendNotification(cd *flaggerv1.Canary, message string, metadata bool, warn bool) { - if c.notifier == nil { - return - } - - var fields []notifier.Field - - if metadata { - fields = append(fields, - notifier.Field{ - Name: "Target", - Value: fmt.Sprintf("%s/%s.%s", cd.Spec.TargetRef.Kind, cd.Spec.TargetRef.Name, cd.Namespace), - }, - notifier.Field{ - Name: "Failed checks threshold", - Value: fmt.Sprintf("%v", cd.Spec.CanaryAnalysis.Threshold), - }, - notifier.Field{ - Name: "Progress deadline", - Value: fmt.Sprintf("%vs", cd.GetProgressDeadlineSeconds()), - }, - ) - - if cd.Spec.CanaryAnalysis.StepWeight > 0 { - fields = append(fields, notifier.Field{ - Name: "Traffic routing", - Value: fmt.Sprintf("Weight step: %v max: %v", - cd.Spec.CanaryAnalysis.StepWeight, - cd.Spec.CanaryAnalysis.MaxWeight), - }) - } else if len(cd.Spec.CanaryAnalysis.Match) > 0 { - fields = append(fields, notifier.Field{ - Name: "Traffic routing", - Value: "A/B Testing", - }) - } else if cd.Spec.CanaryAnalysis.Iterations > 0 { - fields = append(fields, notifier.Field{ - Name: "Traffic routing", - Value: "Blue/Green", - }) - } - } - err := c.notifier.Post(cd.Name, cd.Namespace, message, fields, warn) - if err != nil { - c.logger.Error(err) - } -} - -func int32p(i int32) *int32 { - return &i -} diff --git a/pkg/controller/controller_test.go b/pkg/controller/controller_test.go deleted file mode 100644 index 61af3475..00000000 --- a/pkg/controller/controller_test.go +++ /dev/null @@ -1,584 +0,0 @@ -package controller - -import ( - "sync" - "time" - - "github.com/weaveworks/flagger/pkg/apis/flagger/v1alpha3" - istiov1alpha1 "github.com/weaveworks/flagger/pkg/apis/istio/common/v1alpha1" - istiov1alpha3 "github.com/weaveworks/flagger/pkg/apis/istio/v1alpha3" - "github.com/weaveworks/flagger/pkg/canary" - clientset "github.com/weaveworks/flagger/pkg/client/clientset/versioned" - fakeFlagger "github.com/weaveworks/flagger/pkg/client/clientset/versioned/fake" - informers "github.com/weaveworks/flagger/pkg/client/informers/externalversions" - "github.com/weaveworks/flagger/pkg/logger" - "github.com/weaveworks/flagger/pkg/metrics" - "github.com/weaveworks/flagger/pkg/router" - "go.uber.org/zap" - appsv1 "k8s.io/api/apps/v1" - hpav1 "k8s.io/api/autoscaling/v1" - hpav2 "k8s.io/api/autoscaling/v2beta1" - corev1 "k8s.io/api/core/v1" - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - "k8s.io/client-go/kubernetes" - "k8s.io/client-go/kubernetes/fake" - "k8s.io/client-go/tools/record" - "k8s.io/client-go/util/workqueue" -) - -var ( - alwaysReady = func() bool { return true } - noResyncPeriodFunc = func() time.Duration { return 0 } -) - -type Mocks struct { - canary *v1alpha3.Canary - kubeClient kubernetes.Interface - meshClient clientset.Interface - flaggerClient clientset.Interface - deployer canary.Deployer - ctrl *Controller - logger *zap.SugaredLogger - router router.Interface -} - -func SetupMocks(abtest bool) Mocks { - // init canary - c := newTestCanary() - if abtest { - c = newTestCanaryAB() - } - flaggerClient := fakeFlagger.NewSimpleClientset(c) - - // init kube clientset and register mock objects - kubeClient := fake.NewSimpleClientset( - newTestDeployment(), - newTestHPA(), - NewTestConfigMap(), - NewTestConfigMapEnv(), - NewTestConfigMapVol(), - NewTestSecret(), - NewTestSecretEnv(), - NewTestSecretVol(), - ) - - logger, _ := logger.NewLogger("debug") - - // init controller helpers - deployer := canary.Deployer{ - Logger: logger, - KubeClient: kubeClient, - FlaggerClient: flaggerClient, - Labels: []string{"app", "name"}, - ConfigTracker: canary.ConfigTracker{ - Logger: logger, - KubeClient: kubeClient, - FlaggerClient: flaggerClient, - }, - } - - // init controller - flaggerInformerFactory := informers.NewSharedInformerFactory(flaggerClient, noResyncPeriodFunc()) - flaggerInformer := flaggerInformerFactory.Flagger().V1alpha3().Canaries() - - // init router - rf := router.NewFactory(nil, kubeClient, flaggerClient, logger, flaggerClient) - - // init observer - observerFactory, _ := metrics.NewFactory("fake", "istio", 5*time.Second) - - ctrl := &Controller{ - kubeClient: kubeClient, - istioClient: flaggerClient, - flaggerClient: flaggerClient, - flaggerLister: flaggerInformer.Lister(), - flaggerSynced: flaggerInformer.Informer().HasSynced, - workqueue: workqueue.NewNamedRateLimitingQueue(workqueue.DefaultControllerRateLimiter(), controllerAgentName), - eventRecorder: &record.FakeRecorder{}, - logger: logger, - canaries: new(sync.Map), - flaggerWindow: time.Second, - deployer: deployer, - observerFactory: observerFactory, - recorder: metrics.NewRecorder(controllerAgentName, false), - routerFactory: rf, - } - ctrl.flaggerSynced = alwaysReady - - meshRouter := rf.MeshRouter("istio") - - return Mocks{ - canary: c, - deployer: deployer, - logger: logger, - flaggerClient: flaggerClient, - meshClient: flaggerClient, - kubeClient: kubeClient, - ctrl: ctrl, - router: meshRouter, - } -} - -func NewTestConfigMap() *corev1.ConfigMap { - return &corev1.ConfigMap{ - TypeMeta: metav1.TypeMeta{APIVersion: corev1.SchemeGroupVersion.String()}, - ObjectMeta: metav1.ObjectMeta{ - Namespace: "default", - Name: "podinfo-config-env", - }, - Data: map[string]string{ - "color": "red", - }, - } -} - -func NewTestConfigMapV2() *corev1.ConfigMap { - return &corev1.ConfigMap{ - TypeMeta: metav1.TypeMeta{APIVersion: corev1.SchemeGroupVersion.String()}, - ObjectMeta: metav1.ObjectMeta{ - Namespace: "default", - Name: "podinfo-config-env", - }, - Data: map[string]string{ - "color": "blue", - "output": "console", - }, - } -} - -func NewTestConfigMapEnv() *corev1.ConfigMap { - return &corev1.ConfigMap{ - TypeMeta: metav1.TypeMeta{APIVersion: corev1.SchemeGroupVersion.String()}, - ObjectMeta: metav1.ObjectMeta{ - Namespace: "default", - Name: "podinfo-config-all-env", - }, - Data: map[string]string{ - "color": "red", - }, - } -} - -func NewTestConfigMapVol() *corev1.ConfigMap { - return &corev1.ConfigMap{ - TypeMeta: metav1.TypeMeta{APIVersion: corev1.SchemeGroupVersion.String()}, - ObjectMeta: metav1.ObjectMeta{ - Namespace: "default", - Name: "podinfo-config-vol", - }, - Data: map[string]string{ - "color": "red", - }, - } -} - -func NewTestSecret() *corev1.Secret { - return &corev1.Secret{ - TypeMeta: metav1.TypeMeta{APIVersion: corev1.SchemeGroupVersion.String()}, - ObjectMeta: metav1.ObjectMeta{ - Namespace: "default", - Name: "podinfo-secret-env", - }, - Type: corev1.SecretTypeOpaque, - Data: map[string][]byte{ - "apiKey": []byte("test"), - }, - } -} - -func NewTestSecretV2() *corev1.Secret { - return &corev1.Secret{ - TypeMeta: metav1.TypeMeta{APIVersion: corev1.SchemeGroupVersion.String()}, - ObjectMeta: metav1.ObjectMeta{ - Namespace: "default", - Name: "podinfo-secret-env", - }, - Type: corev1.SecretTypeOpaque, - Data: map[string][]byte{ - "apiKey": []byte("test2"), - }, - } -} - -func NewTestSecretEnv() *corev1.Secret { - return &corev1.Secret{ - TypeMeta: metav1.TypeMeta{APIVersion: corev1.SchemeGroupVersion.String()}, - ObjectMeta: metav1.ObjectMeta{ - Namespace: "default", - Name: "podinfo-secret-all-env", - }, - Type: corev1.SecretTypeOpaque, - Data: map[string][]byte{ - "apiKey": []byte("test"), - }, - } -} - -func NewTestSecretVol() *corev1.Secret { - return &corev1.Secret{ - TypeMeta: metav1.TypeMeta{APIVersion: corev1.SchemeGroupVersion.String()}, - ObjectMeta: metav1.ObjectMeta{ - Namespace: "default", - Name: "podinfo-secret-vol", - }, - Type: corev1.SecretTypeOpaque, - Data: map[string][]byte{ - "apiKey": []byte("test"), - }, - } -} - -func newTestCanary() *v1alpha3.Canary { - cd := &v1alpha3.Canary{ - TypeMeta: metav1.TypeMeta{APIVersion: v1alpha3.SchemeGroupVersion.String()}, - ObjectMeta: metav1.ObjectMeta{ - Namespace: "default", - Name: "podinfo", - }, - Spec: v1alpha3.CanarySpec{ - TargetRef: hpav1.CrossVersionObjectReference{ - Name: "podinfo", - APIVersion: "apps/v1", - Kind: "Deployment", - }, - AutoscalerRef: &hpav1.CrossVersionObjectReference{ - Name: "podinfo", - APIVersion: "autoscaling/v2beta1", - Kind: "HorizontalPodAutoscaler", - }, Service: v1alpha3.CanaryService{ - Port: 9898, - }, CanaryAnalysis: v1alpha3.CanaryAnalysis{ - Threshold: 10, - StepWeight: 10, - MaxWeight: 50, - Metrics: []v1alpha3.CanaryMetric{ - { - Name: "istio_requests_total", - Threshold: 99, - Interval: "1m", - }, - { - Name: "istio_request_duration_seconds_bucket", - Threshold: 500, - Interval: "1m", - }, - }, - }, - }, - } - return cd -} - -func newTestCanaryAB() *v1alpha3.Canary { - cd := &v1alpha3.Canary{ - TypeMeta: metav1.TypeMeta{APIVersion: v1alpha3.SchemeGroupVersion.String()}, - ObjectMeta: metav1.ObjectMeta{ - Namespace: "default", - Name: "podinfo", - }, - Spec: v1alpha3.CanarySpec{ - TargetRef: hpav1.CrossVersionObjectReference{ - Name: "podinfo", - APIVersion: "apps/v1", - Kind: "Deployment", - }, - AutoscalerRef: &hpav1.CrossVersionObjectReference{ - Name: "podinfo", - APIVersion: "autoscaling/v2beta1", - Kind: "HorizontalPodAutoscaler", - }, Service: v1alpha3.CanaryService{ - Port: 9898, - }, CanaryAnalysis: v1alpha3.CanaryAnalysis{ - Threshold: 10, - Iterations: 10, - Match: []istiov1alpha3.HTTPMatchRequest{ - { - Headers: map[string]istiov1alpha1.StringMatch{ - "x-user-type": { - Exact: "test", - }, - }, - }, - }, - Metrics: []v1alpha3.CanaryMetric{ - { - Name: "istio_requests_total", - Threshold: 99, - Interval: "1m", - }, - { - Name: "istio_request_duration_seconds_bucket", - Threshold: 500, - Interval: "1m", - }, - }, - }, - }, - } - return cd -} - -func newTestDeployment() *appsv1.Deployment { - d := &appsv1.Deployment{ - TypeMeta: metav1.TypeMeta{APIVersion: appsv1.SchemeGroupVersion.String()}, - ObjectMeta: metav1.ObjectMeta{ - Namespace: "default", - Name: "podinfo", - }, - Spec: appsv1.DeploymentSpec{ - Selector: &metav1.LabelSelector{ - MatchLabels: map[string]string{ - "app": "podinfo", - }, - }, - Template: corev1.PodTemplateSpec{ - ObjectMeta: metav1.ObjectMeta{ - Labels: map[string]string{ - "app": "podinfo", - }, - }, - Spec: corev1.PodSpec{ - Containers: []corev1.Container{ - { - Name: "podinfo", - Image: "quay.io/stefanprodan/podinfo:1.2.0", - Command: []string{ - "./podinfo", - "--port=9898", - }, - Args: nil, - WorkingDir: "", - Ports: []corev1.ContainerPort{ - { - Name: "http", - ContainerPort: 9898, - Protocol: corev1.ProtocolTCP, - }, - { - Name: "http-metrics", - ContainerPort: 8080, - Protocol: corev1.ProtocolTCP, - }, - { - ContainerPort: 8888, - }, - }, - Env: []corev1.EnvVar{ - { - Name: "PODINFO_UI_COLOR", - ValueFrom: &corev1.EnvVarSource{ - ConfigMapKeyRef: &corev1.ConfigMapKeySelector{ - LocalObjectReference: corev1.LocalObjectReference{ - Name: "podinfo-config-env", - }, - Key: "color", - }, - }, - }, - { - Name: "API_KEY", - ValueFrom: &corev1.EnvVarSource{ - SecretKeyRef: &corev1.SecretKeySelector{ - LocalObjectReference: corev1.LocalObjectReference{ - Name: "podinfo-secret-env", - }, - Key: "apiKey", - }, - }, - }, - }, - EnvFrom: []corev1.EnvFromSource{ - { - ConfigMapRef: &corev1.ConfigMapEnvSource{ - LocalObjectReference: corev1.LocalObjectReference{ - Name: "podinfo-config-all-env", - }, - }, - }, - { - SecretRef: &corev1.SecretEnvSource{ - LocalObjectReference: corev1.LocalObjectReference{ - Name: "podinfo-secret-all-env", - }, - }, - }, - }, - VolumeMounts: []corev1.VolumeMount{ - { - Name: "config", - MountPath: "/etc/podinfo/config", - ReadOnly: true, - }, - { - Name: "secret", - MountPath: "/etc/podinfo/secret", - ReadOnly: true, - }, - }, - }, - }, - Volumes: []corev1.Volume{ - { - Name: "config", - VolumeSource: corev1.VolumeSource{ - ConfigMap: &corev1.ConfigMapVolumeSource{ - LocalObjectReference: corev1.LocalObjectReference{ - Name: "podinfo-config-vol", - }, - }, - }, - }, - { - Name: "secret", - VolumeSource: corev1.VolumeSource{ - Secret: &corev1.SecretVolumeSource{ - SecretName: "podinfo-secret-vol", - }, - }, - }, - }, - }, - }, - }, - } - - return d -} - -func newTestDeploymentV2() *appsv1.Deployment { - d := &appsv1.Deployment{ - TypeMeta: metav1.TypeMeta{APIVersion: appsv1.SchemeGroupVersion.String()}, - ObjectMeta: metav1.ObjectMeta{ - Namespace: "default", - Name: "podinfo", - }, - Spec: appsv1.DeploymentSpec{ - Selector: &metav1.LabelSelector{ - MatchLabels: map[string]string{ - "app": "podinfo", - }, - }, - Template: corev1.PodTemplateSpec{ - ObjectMeta: metav1.ObjectMeta{ - Labels: map[string]string{ - "app": "podinfo", - }, - }, - Spec: corev1.PodSpec{ - Containers: []corev1.Container{ - { - Name: "podinfo", - Image: "quay.io/stefanprodan/podinfo:1.2.1", - Ports: []corev1.ContainerPort{ - { - Name: "http", - ContainerPort: 9898, - Protocol: corev1.ProtocolTCP, - }, - }, - Command: []string{ - "./podinfo", - "--port=9898", - }, - Env: []corev1.EnvVar{ - { - Name: "PODINFO_UI_COLOR", - ValueFrom: &corev1.EnvVarSource{ - ConfigMapKeyRef: &corev1.ConfigMapKeySelector{ - LocalObjectReference: corev1.LocalObjectReference{ - Name: "podinfo-config-env", - }, - Key: "color", - }, - }, - }, - { - Name: "API_KEY", - ValueFrom: &corev1.EnvVarSource{ - SecretKeyRef: &corev1.SecretKeySelector{ - LocalObjectReference: corev1.LocalObjectReference{ - Name: "podinfo-secret-env", - }, - Key: "apiKey", - }, - }, - }, - }, - EnvFrom: []corev1.EnvFromSource{ - { - ConfigMapRef: &corev1.ConfigMapEnvSource{ - LocalObjectReference: corev1.LocalObjectReference{ - Name: "podinfo-config-all-env", - }, - }, - }, - }, - VolumeMounts: []corev1.VolumeMount{ - { - Name: "config", - MountPath: "/etc/podinfo/config", - ReadOnly: true, - }, - { - Name: "secret", - MountPath: "/etc/podinfo/secret", - ReadOnly: true, - }, - }, - }, - }, - Volumes: []corev1.Volume{ - { - Name: "config", - VolumeSource: corev1.VolumeSource{ - ConfigMap: &corev1.ConfigMapVolumeSource{ - LocalObjectReference: corev1.LocalObjectReference{ - Name: "podinfo-config-vol", - }, - }, - }, - }, - { - Name: "secret", - VolumeSource: corev1.VolumeSource{ - Secret: &corev1.SecretVolumeSource{ - SecretName: "podinfo-secret-vol", - }, - }, - }, - }, - }, - }, - }, - } - - return d -} - -func newTestHPA() *hpav2.HorizontalPodAutoscaler { - h := &hpav2.HorizontalPodAutoscaler{ - TypeMeta: metav1.TypeMeta{APIVersion: hpav2.SchemeGroupVersion.String()}, - ObjectMeta: metav1.ObjectMeta{ - Namespace: "default", - Name: "podinfo", - }, - Spec: hpav2.HorizontalPodAutoscalerSpec{ - ScaleTargetRef: hpav2.CrossVersionObjectReference{ - Name: "podinfo", - APIVersion: "apps/v1", - Kind: "Deployment", - }, - Metrics: []hpav2.MetricSpec{ - { - Type: "Resource", - Resource: &hpav2.ResourceMetricSource{ - Name: "cpu", - TargetAverageUtilization: int32p(99), - }, - }, - }, - }, - } - - return h -} diff --git a/pkg/controller/job.go b/pkg/controller/job.go deleted file mode 100644 index edd1c846..00000000 --- a/pkg/controller/job.go +++ /dev/null @@ -1,40 +0,0 @@ -package controller - -import "time" - -// CanaryJob holds the reference to a canary deployment schedule -type CanaryJob struct { - Name string - Namespace string - SkipTests bool - function func(name string, namespace string, skipTests bool) - done chan bool - ticker *time.Ticker - analysisInterval time.Duration -} - -// Start runs the canary analysis on a schedule -func (j CanaryJob) Start() { - go func() { - // run the infra bootstrap on job creation - j.function(j.Name, j.Namespace, j.SkipTests) - for { - select { - case <-j.ticker.C: - j.function(j.Name, j.Namespace, j.SkipTests) - case <-j.done: - return - } - } - }() -} - -// Stop closes the job channel and stops the ticker -func (j CanaryJob) Stop() { - close(j.done) - j.ticker.Stop() -} - -func (j CanaryJob) GetCanaryAnalysisInterval() time.Duration { - return j.analysisInterval -} diff --git a/pkg/controller/scheduler.go b/pkg/controller/scheduler.go deleted file mode 100644 index 6d7a35c7..00000000 --- a/pkg/controller/scheduler.go +++ /dev/null @@ -1,694 +0,0 @@ -package controller - -import ( - "fmt" - "strings" - "time" - - "github.com/weaveworks/flagger/pkg/router" - - flaggerv1 "github.com/weaveworks/flagger/pkg/apis/flagger/v1alpha3" - v1 "k8s.io/apimachinery/pkg/apis/meta/v1" -) - -// scheduleCanaries synchronises the canary map with the jobs map, -// for new canaries new jobs are created and started -// for the removed canaries the jobs are stopped and deleted -func (c *Controller) scheduleCanaries() { - current := make(map[string]string) - stats := make(map[string]int) - - c.canaries.Range(func(key interface{}, value interface{}) bool { - canary := value.(*flaggerv1.Canary) - - // format: . - name := key.(string) - current[name] = fmt.Sprintf("%s.%s", canary.Spec.TargetRef.Name, canary.Namespace) - - job, exists := c.jobs[name] - // schedule new job for existing job with different analysis interval or non-existing job - if (exists && job.GetCanaryAnalysisInterval() != canary.GetAnalysisInterval()) || !exists { - if exists { - job.Stop() - } - - newJob := CanaryJob{ - Name: canary.Name, - Namespace: canary.Namespace, - function: c.advanceCanary, - done: make(chan bool), - ticker: time.NewTicker(canary.GetAnalysisInterval()), - analysisInterval: canary.GetAnalysisInterval(), - } - - c.jobs[name] = newJob - newJob.Start() - } - - // compute canaries per namespace total - t, ok := stats[canary.Namespace] - if !ok { - stats[canary.Namespace] = 1 - } else { - stats[canary.Namespace] = t + 1 - } - return true - }) - - // cleanup deleted jobs - for job := range c.jobs { - if _, exists := current[job]; !exists { - c.jobs[job].Stop() - delete(c.jobs, job) - } - } - - // check if multiple canaries have the same target - for canaryName, targetName := range current { - for name, target := range current { - if name != canaryName && target == targetName { - c.logger.With("canary", canaryName).Errorf("Bad things will happen! Found more than one canary with the same target %s", targetName) - } - } - } - - // set total canaries per namespace metric - for k, v := range stats { - c.recorder.SetTotal(k, v) - } -} - -func (c *Controller) advanceCanary(name string, namespace string, skipLivenessChecks bool) { - begin := time.Now() - // check if the canary exists - cd, err := c.flaggerClient.FlaggerV1alpha3().Canaries(namespace).Get(name, v1.GetOptions{}) - if err != nil { - c.logger.With("canary", fmt.Sprintf("%s.%s", name, namespace)). - Errorf("Canary %s.%s not found", name, namespace) - return - } - - primaryName := fmt.Sprintf("%s-primary", cd.Spec.TargetRef.Name) - - // override the global provider if one is specified in the canary spec - provider := c.meshProvider - if cd.Spec.Provider != "" { - provider = cd.Spec.Provider - } - - // create primary deployment and hpa if needed - // skip primary check for Istio since the deployment will become ready after the ClusterIP are created - skipPrimaryCheck := false - if skipLivenessChecks || strings.Contains(provider, "istio") { - skipPrimaryCheck = true - } - label, ports, err := c.deployer.Initialize(cd, skipPrimaryCheck) - if err != nil { - c.recordEventWarningf(cd, "%v", err) - return - } - - // init routers - meshRouter := c.routerFactory.MeshRouter(provider) - - // create or update ClusterIP services - if err := c.routerFactory.KubernetesRouter(label, ports).Reconcile(cd); err != nil { - c.recordEventWarningf(cd, "%v", err) - return - } - - // create or update virtual service - if err := meshRouter.Reconcile(cd); err != nil { - c.recordEventWarningf(cd, "%v", err) - return - } - - shouldAdvance, err := c.shouldAdvance(cd) - if err != nil { - c.recordEventWarningf(cd, "%v", err) - return - } - - if !shouldAdvance { - c.recorder.SetStatus(cd, cd.Status.Phase) - return - } - - // check gates - if isApproved := c.runConfirmRolloutHooks(cd); !isApproved { - return - } - - // set max weight default value to 100% - maxWeight := 100 - if cd.Spec.CanaryAnalysis.MaxWeight > 0 { - maxWeight = cd.Spec.CanaryAnalysis.MaxWeight - } - - // check primary deployment status - if !skipLivenessChecks { - if _, err := c.deployer.IsPrimaryReady(cd); err != nil { - c.recordEventWarningf(cd, "%v", err) - return - } - } - - // check if virtual service exists - // and if it contains weighted destination routes to the primary and canary services - primaryWeight, canaryWeight, err := meshRouter.GetRoutes(cd) - if err != nil { - c.recordEventWarningf(cd, "%v", err) - return - } - - c.recorder.SetWeight(cd, primaryWeight, canaryWeight) - - // check if canary analysis should start (canary revision has changes) or continue - if ok := c.checkCanaryStatus(cd, shouldAdvance); !ok { - return - } - - // check if canary revision changed during analysis - if restart := c.hasCanaryRevisionChanged(cd); restart { - c.recordEventInfof(cd, "New revision detected! Restarting analysis for %s.%s", - cd.Spec.TargetRef.Name, cd.Namespace) - - // route all traffic back to primary - primaryWeight = 100 - canaryWeight = 0 - if err := meshRouter.SetRoutes(cd, primaryWeight, canaryWeight); err != nil { - c.recordEventWarningf(cd, "%v", err) - return - } - - // reset status - status := flaggerv1.CanaryStatus{ - Phase: flaggerv1.CanaryPhaseProgressing, - CanaryWeight: 0, - FailedChecks: 0, - Iterations: 0, - } - if err := c.deployer.SyncStatus(cd, status); err != nil { - c.recordEventWarningf(cd, "%v", err) - return - } - return - } - - defer func() { - c.recorder.SetDuration(cd, time.Since(begin)) - }() - - // check canary deployment status - var retriable = true - if !skipLivenessChecks { - retriable, err = c.deployer.IsCanaryReady(cd) - if err != nil && retriable { - c.recordEventWarningf(cd, "%v", err) - return - } - } - - // check if analysis should be skipped - if skip := c.shouldSkipAnalysis(cd, meshRouter, primaryWeight, canaryWeight); skip { - return - } - - // scale canary to zero if analysis has succeeded - if cd.Status.Phase == flaggerv1.CanaryPhaseFinalising { - if err := c.deployer.Scale(cd, 0); err != nil { - c.recordEventWarningf(cd, "%v", err) - return - } - - // set status to succeeded - if err := c.deployer.SetStatusPhase(cd, flaggerv1.CanaryPhaseSucceeded); err != nil { - c.recordEventWarningf(cd, "%v", err) - return - } - c.recorder.SetStatus(cd, flaggerv1.CanaryPhaseSucceeded) - c.runPostRolloutHooks(cd, flaggerv1.CanaryPhaseSucceeded) - c.recordEventInfof(cd, "Promotion completed! Scaling down %s.%s", cd.Spec.TargetRef.Name, cd.Namespace) - c.sendNotification(cd, "Canary analysis completed successfully, promotion finished.", - false, false) - return - } - - // check if the number of failed checks reached the threshold - if cd.Status.Phase == flaggerv1.CanaryPhaseProgressing && - (!retriable || cd.Status.FailedChecks >= cd.Spec.CanaryAnalysis.Threshold) { - - if cd.Status.FailedChecks >= cd.Spec.CanaryAnalysis.Threshold { - c.recordEventWarningf(cd, "Rolling back %s.%s failed checks threshold reached %v", - cd.Name, cd.Namespace, cd.Status.FailedChecks) - c.sendNotification(cd, fmt.Sprintf("Failed checks threshold reached %v", cd.Status.FailedChecks), - false, true) - } - - if !retriable { - c.recordEventWarningf(cd, "Rolling back %s.%s progress deadline exceeded %v", - cd.Name, cd.Namespace, err) - c.sendNotification(cd, fmt.Sprintf("Progress deadline exceeded %v", err), - false, true) - } - - // route all traffic back to primary - primaryWeight = 100 - canaryWeight = 0 - if err := meshRouter.SetRoutes(cd, primaryWeight, canaryWeight); err != nil { - c.recordEventWarningf(cd, "%v", err) - return - } - - c.recorder.SetWeight(cd, primaryWeight, canaryWeight) - c.recordEventWarningf(cd, "Canary failed! Scaling down %s.%s", - cd.Name, cd.Namespace) - - // shutdown canary - if err := c.deployer.Scale(cd, 0); err != nil { - c.recordEventWarningf(cd, "%v", err) - return - } - - // mark canary as failed - if err := c.deployer.SyncStatus(cd, flaggerv1.CanaryStatus{Phase: flaggerv1.CanaryPhaseFailed, CanaryWeight: 0}); err != nil { - c.logger.With("canary", fmt.Sprintf("%s.%s", cd.Name, cd.Namespace)).Errorf("%v", err) - return - } - - c.recorder.SetStatus(cd, flaggerv1.CanaryPhaseFailed) - c.runPostRolloutHooks(cd, flaggerv1.CanaryPhaseFailed) - return - } - - // check if the canary success rate is above the threshold - // skip check if no traffic is routed to canary - if canaryWeight == 0 { - c.recordEventInfof(cd, "Starting canary analysis for %s.%s", cd.Spec.TargetRef.Name, cd.Namespace) - - // run pre-rollout web hooks - if ok := c.runPreRolloutHooks(cd); !ok { - if err := c.deployer.SetStatusFailedChecks(cd, cd.Status.FailedChecks+1); err != nil { - c.recordEventWarningf(cd, "%v", err) - return - } - return - } - } else { - if ok := c.analyseCanary(cd); !ok { - if err := c.deployer.SetStatusFailedChecks(cd, cd.Status.FailedChecks+1); err != nil { - c.recordEventWarningf(cd, "%v", err) - return - } - return - } - } - - // canary fix routing: A/B testing - if len(cd.Spec.CanaryAnalysis.Match) > 0 || cd.Spec.CanaryAnalysis.Iterations > 0 { - // route traffic to canary and increment iterations - if cd.Spec.CanaryAnalysis.Iterations > cd.Status.Iterations { - if err := meshRouter.SetRoutes(cd, 0, 100); err != nil { - c.recordEventWarningf(cd, "%v", err) - return - } - c.recorder.SetWeight(cd, 0, 100) - - if err := c.deployer.SetStatusIterations(cd, cd.Status.Iterations+1); err != nil { - c.recordEventWarningf(cd, "%v", err) - return - } - c.recordEventInfof(cd, "Advance %s.%s canary iteration %v/%v", - cd.Name, cd.Namespace, cd.Status.Iterations+1, cd.Spec.CanaryAnalysis.Iterations) - return - } - - // promote canary - max iterations reached - if cd.Spec.CanaryAnalysis.Iterations == cd.Status.Iterations { - c.recordEventInfof(cd, "Copying %s.%s template spec to %s.%s", - cd.Spec.TargetRef.Name, cd.Namespace, primaryName, cd.Namespace) - if err := c.deployer.Promote(cd); err != nil { - c.recordEventWarningf(cd, "%v", err) - return - } - // increment iterations - if err := c.deployer.SetStatusIterations(cd, cd.Status.Iterations+1); err != nil { - c.recordEventWarningf(cd, "%v", err) - return - } - return - } - - // route all traffic to primary - if cd.Spec.CanaryAnalysis.Iterations < cd.Status.Iterations { - primaryWeight = 100 - canaryWeight = 0 - if err := meshRouter.SetRoutes(cd, primaryWeight, canaryWeight); err != nil { - c.recordEventWarningf(cd, "%v", err) - return - } - c.recorder.SetWeight(cd, primaryWeight, canaryWeight) - - // update status phase - if err := c.deployer.SetStatusPhase(cd, flaggerv1.CanaryPhaseFinalising); err != nil { - c.recordEventWarningf(cd, "%v", err) - return - } - - c.recordEventInfof(cd, "Routing all traffic to primary") - return - } - - return - } - - // canary incremental traffic weight - if canaryWeight < maxWeight { - primaryWeight -= cd.Spec.CanaryAnalysis.StepWeight - if primaryWeight < 0 { - primaryWeight = 0 - } - canaryWeight += cd.Spec.CanaryAnalysis.StepWeight - if primaryWeight > 100 { - primaryWeight = 100 - } - - if err := meshRouter.SetRoutes(cd, primaryWeight, canaryWeight); err != nil { - c.recordEventWarningf(cd, "%v", err) - return - } - - // update weight status - if err := c.deployer.SetStatusWeight(cd, canaryWeight); err != nil { - c.recordEventWarningf(cd, "%v", err) - return - } - - c.recorder.SetWeight(cd, primaryWeight, canaryWeight) - c.recordEventInfof(cd, "Advance %s.%s canary weight %v", cd.Name, cd.Namespace, canaryWeight) - - // promote canary - if canaryWeight >= maxWeight { - c.recordEventInfof(cd, "Copying %s.%s template spec to %s.%s", - cd.Spec.TargetRef.Name, cd.Namespace, primaryName, cd.Namespace) - if err := c.deployer.Promote(cd); err != nil { - c.recordEventWarningf(cd, "%v", err) - return - } - } - } else { - // route all traffic to primary - primaryWeight = 100 - canaryWeight = 0 - if err := meshRouter.SetRoutes(cd, primaryWeight, canaryWeight); err != nil { - c.recordEventWarningf(cd, "%v", err) - return - } - c.recorder.SetWeight(cd, primaryWeight, canaryWeight) - - // update status phase - if err := c.deployer.SetStatusPhase(cd, flaggerv1.CanaryPhaseFinalising); err != nil { - c.recordEventWarningf(cd, "%v", err) - return - } - - c.recordEventInfof(cd, "Routing all traffic to primary") - return - } -} - -func (c *Controller) shouldSkipAnalysis(cd *flaggerv1.Canary, meshRouter router.Interface, primaryWeight int, canaryWeight int) bool { - if !cd.Spec.SkipAnalysis { - return false - } - - // route all traffic to primary - primaryWeight = 100 - canaryWeight = 0 - if err := meshRouter.SetRoutes(cd, primaryWeight, canaryWeight); err != nil { - c.recordEventWarningf(cd, "%v", err) - return false - } - c.recorder.SetWeight(cd, primaryWeight, canaryWeight) - - // copy spec and configs from canary to primary - c.recordEventInfof(cd, "Copying %s.%s template spec to %s-primary.%s", - cd.Spec.TargetRef.Name, cd.Namespace, cd.Spec.TargetRef.Name, cd.Namespace) - if err := c.deployer.Promote(cd); err != nil { - c.recordEventWarningf(cd, "%v", err) - return false - } - - // shutdown canary - if err := c.deployer.Scale(cd, 0); err != nil { - c.recordEventWarningf(cd, "%v", err) - return false - } - - // update status phase - if err := c.deployer.SetStatusPhase(cd, flaggerv1.CanaryPhaseSucceeded); err != nil { - c.recordEventWarningf(cd, "%v", err) - return false - } - - // notify - c.recorder.SetStatus(cd, flaggerv1.CanaryPhaseSucceeded) - c.recordEventInfof(cd, "Promotion completed! Canary analysis was skipped for %s.%s", - cd.Spec.TargetRef.Name, cd.Namespace) - c.sendNotification(cd, "Canary analysis was skipped, promotion finished.", - false, false) - - return true -} - -func (c *Controller) shouldAdvance(cd *flaggerv1.Canary) (bool, error) { - if cd.Status.LastAppliedSpec == "" || - cd.Status.Phase == flaggerv1.CanaryPhaseInitializing || - cd.Status.Phase == flaggerv1.CanaryPhaseProgressing || - cd.Status.Phase == flaggerv1.CanaryPhaseWaiting || - cd.Status.Phase == flaggerv1.CanaryPhaseFinalising { - return true, nil - } - - newDep, err := c.deployer.HasDeploymentChanged(cd) - if err != nil { - return false, err - } - if newDep { - return newDep, nil - } - - newCfg, err := c.deployer.ConfigTracker.HasConfigChanged(cd) - if err != nil { - return false, err - } - - return newCfg, nil - -} - -func (c *Controller) checkCanaryStatus(cd *flaggerv1.Canary, shouldAdvance bool) bool { - c.recorder.SetStatus(cd, cd.Status.Phase) - if cd.Status.Phase == flaggerv1.CanaryPhaseProgressing || - cd.Status.Phase == flaggerv1.CanaryPhaseFinalising { - return true - } - - if cd.Status.Phase == "" || cd.Status.Phase == flaggerv1.CanaryPhaseInitializing { - if err := c.deployer.SyncStatus(cd, flaggerv1.CanaryStatus{Phase: flaggerv1.CanaryPhaseInitialized}); err != nil { - c.logger.With("canary", fmt.Sprintf("%s.%s", cd.Name, cd.Namespace)).Errorf("%v", err) - return false - } - c.recorder.SetStatus(cd, flaggerv1.CanaryPhaseInitialized) - c.recordEventInfof(cd, "Initialization done! %s.%s", cd.Name, cd.Namespace) - c.sendNotification(cd, "New deployment detected, initialization completed.", - true, false) - return false - } - - if shouldAdvance { - c.recordEventInfof(cd, "New revision detected! Scaling up %s.%s", cd.Spec.TargetRef.Name, cd.Namespace) - c.sendNotification(cd, "New revision detected, starting canary analysis.", - true, false) - if err := c.deployer.Scale(cd, 1); err != nil { - c.recordEventErrorf(cd, "%v", err) - return false - } - if err := c.deployer.SyncStatus(cd, flaggerv1.CanaryStatus{Phase: flaggerv1.CanaryPhaseProgressing}); err != nil { - c.logger.With("canary", fmt.Sprintf("%s.%s", cd.Name, cd.Namespace)).Errorf("%v", err) - return false - } - c.recorder.SetStatus(cd, flaggerv1.CanaryPhaseProgressing) - return false - } - return false -} - -func (c *Controller) hasCanaryRevisionChanged(cd *flaggerv1.Canary) bool { - if cd.Status.Phase == flaggerv1.CanaryPhaseProgressing { - if diff, _ := c.deployer.HasDeploymentChanged(cd); diff { - return true - } - if diff, _ := c.deployer.ConfigTracker.HasConfigChanged(cd); diff { - return true - } - } - return false -} - -func (c *Controller) runConfirmRolloutHooks(canary *flaggerv1.Canary) bool { - for _, webhook := range canary.Spec.CanaryAnalysis.Webhooks { - if webhook.Type == flaggerv1.ConfirmRolloutHook { - err := CallWebhook(canary.Name, canary.Namespace, flaggerv1.CanaryPhaseProgressing, webhook) - if err != nil { - if canary.Status.Phase != flaggerv1.CanaryPhaseWaiting { - if err := c.deployer.SetStatusPhase(canary, flaggerv1.CanaryPhaseWaiting); err != nil { - c.logger.With("canary", fmt.Sprintf("%s.%s", canary.Name, canary.Namespace)).Errorf("%v", err) - } - c.recordEventWarningf(canary, "Halt %s.%s advancement waiting for approval %s", - canary.Name, canary.Namespace, webhook.Name) - c.sendNotification(canary, "Canary is waiting for approval.", false, false) - } - return false - } else { - if canary.Status.Phase == flaggerv1.CanaryPhaseWaiting { - if err := c.deployer.SetStatusPhase(canary, flaggerv1.CanaryPhaseProgressing); err != nil { - c.logger.With("canary", fmt.Sprintf("%s.%s", canary.Name, canary.Namespace)).Errorf("%v", err) - return false - } - c.recordEventInfof(canary, "Confirm-rollout check %s passed", webhook.Name) - return false - } - } - } - } - return true -} - -func (c *Controller) runPreRolloutHooks(canary *flaggerv1.Canary) bool { - for _, webhook := range canary.Spec.CanaryAnalysis.Webhooks { - if webhook.Type == flaggerv1.PreRolloutHook { - err := CallWebhook(canary.Name, canary.Namespace, flaggerv1.CanaryPhaseProgressing, webhook) - if err != nil { - c.recordEventWarningf(canary, "Halt %s.%s advancement pre-rollout check %s failed %v", - canary.Name, canary.Namespace, webhook.Name, err) - return false - } else { - c.recordEventInfof(canary, "Pre-rollout check %s passed", webhook.Name) - } - } - } - return true -} - -func (c *Controller) runPostRolloutHooks(canary *flaggerv1.Canary, phase flaggerv1.CanaryPhase) bool { - for _, webhook := range canary.Spec.CanaryAnalysis.Webhooks { - if webhook.Type == flaggerv1.PostRolloutHook { - err := CallWebhook(canary.Name, canary.Namespace, phase, webhook) - if err != nil { - c.recordEventWarningf(canary, "Post-rollout hook %s failed %v", webhook.Name, err) - return false - } else { - c.recordEventInfof(canary, "Post-rollout check %s passed", webhook.Name) - } - } - } - return true -} - -func (c *Controller) analyseCanary(r *flaggerv1.Canary) bool { - // run external checks - for _, webhook := range r.Spec.CanaryAnalysis.Webhooks { - if webhook.Type == "" || webhook.Type == flaggerv1.RolloutHook { - err := CallWebhook(r.Name, r.Namespace, flaggerv1.CanaryPhaseProgressing, webhook) - if err != nil { - c.recordEventWarningf(r, "Halt %s.%s advancement external check %s failed %v", - r.Name, r.Namespace, webhook.Name, err) - return false - } - } - } - - // override the global provider if one is specified in the canary spec - metricsProvider := c.meshProvider - if r.Spec.Provider != "" { - metricsProvider = r.Spec.Provider - - // set the metrics server to Linkerd Prometheus when Linkerd is the default mesh provider - if strings.Contains(c.meshProvider, "linkerd") { - metricsProvider = "linkerd" - } - } - - // create observer based on the mesh provider - observer := c.observerFactory.Observer(metricsProvider) - - // run metrics checks - for _, metric := range r.Spec.CanaryAnalysis.Metrics { - if metric.Interval == "" { - metric.Interval = r.GetMetricInterval() - } - - if metric.Name == "request-success-rate" { - val, err := observer.GetRequestSuccessRate(r.Spec.TargetRef.Name, r.Namespace, metric.Interval) - if err != nil { - if strings.Contains(err.Error(), "no values found") { - c.recordEventWarningf(r, "Halt advancement no values found for metric %s probably %s.%s is not receiving traffic", - metric.Name, r.Spec.TargetRef.Name, r.Namespace) - } else { - c.recordEventErrorf(r, "Metrics server %s query failed: %v", c.observerFactory.Client.GetMetricsServer(), err) - } - return false - } - if float64(metric.Threshold) > val { - c.recordEventWarningf(r, "Halt %s.%s advancement success rate %.2f%% < %v%%", - r.Name, r.Namespace, val, metric.Threshold) - return false - } - - //c.recordEventInfof(r, "Check %s passed %.2f%% > %v%%", metric.Name, val, metric.Threshold) - } - - if metric.Name == "request-duration" { - val, err := observer.GetRequestDuration(r.Spec.TargetRef.Name, r.Namespace, metric.Interval) - if err != nil { - if strings.Contains(err.Error(), "no values found") { - c.recordEventWarningf(r, "Halt advancement no values found for metric %s probably %s.%s is not receiving traffic", - metric.Name, r.Spec.TargetRef.Name, r.Namespace) - } else { - c.recordEventErrorf(r, "Metrics server %s query failed: %v", c.observerFactory.Client.GetMetricsServer(), err) - } - return false - } - t := time.Duration(metric.Threshold) * time.Millisecond - if val > t { - c.recordEventWarningf(r, "Halt %s.%s advancement request duration %v > %v", - r.Name, r.Namespace, val, t) - return false - } - - //c.recordEventInfof(r, "Check %s passed %v < %v", metric.Name, val, metric.Threshold) - } - - // custom checks - if metric.Query != "" { - val, err := c.observerFactory.Client.RunQuery(metric.Query) - if err != nil { - if strings.Contains(err.Error(), "no values found") { - c.recordEventWarningf(r, "Halt advancement no values found for metric %s probably %s.%s is not receiving traffic", - metric.Name, r.Spec.TargetRef.Name, r.Namespace) - } else { - c.recordEventErrorf(r, "Metrics server %s query failed for %s: %v", c.observerFactory.Client.GetMetricsServer(), metric.Name, err) - } - return false - } - if val > float64(metric.Threshold) { - c.recordEventWarningf(r, "Halt %s.%s advancement %s %.2f > %v", - r.Name, r.Namespace, metric.Name, val, metric.Threshold) - return false - } - } - } - - return true -} diff --git a/pkg/controller/scheduler_test.go b/pkg/controller/scheduler_test.go deleted file mode 100644 index c2838cc9..00000000 --- a/pkg/controller/scheduler_test.go +++ /dev/null @@ -1,398 +0,0 @@ -package controller - -import ( - "fmt" - "github.com/weaveworks/flagger/pkg/apis/flagger/v1alpha3" - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - "testing" -) - -func TestScheduler_Init(t *testing.T) { - mocks := SetupMocks(false) - mocks.ctrl.advanceCanary("podinfo", "default", true) - - _, err := mocks.kubeClient.AppsV1().Deployments("default").Get("podinfo-primary", metav1.GetOptions{}) - if err != nil { - t.Fatal(err.Error()) - } -} - -func TestScheduler_NewRevision(t *testing.T) { - mocks := SetupMocks(false) - mocks.ctrl.advanceCanary("podinfo", "default", true) - - // update - dep2 := newTestDeploymentV2() - _, err := mocks.kubeClient.AppsV1().Deployments("default").Update(dep2) - if err != nil { - t.Fatal(err.Error()) - } - - // detect changes - mocks.ctrl.advanceCanary("podinfo", "default", true) - - c, err := mocks.kubeClient.AppsV1().Deployments("default").Get("podinfo", metav1.GetOptions{}) - if err != nil { - t.Fatal(err.Error()) - } - - if *c.Spec.Replicas != 1 { - t.Errorf("Got canary replicas %v wanted %v", *c.Spec.Replicas, 1) - } -} - -func TestScheduler_Rollback(t *testing.T) { - mocks := SetupMocks(false) - // init - mocks.ctrl.advanceCanary("podinfo", "default", true) - - // update failed checks to max - err := mocks.deployer.SyncStatus(mocks.canary, v1alpha3.CanaryStatus{Phase: v1alpha3.CanaryPhaseProgressing, FailedChecks: 11}) - if err != nil { - t.Fatal(err.Error()) - } - - // detect changes - mocks.ctrl.advanceCanary("podinfo", "default", true) - - c, err := mocks.flaggerClient.FlaggerV1alpha3().Canaries("default").Get("podinfo", metav1.GetOptions{}) - if err != nil { - t.Fatal(err.Error()) - } - - if c.Status.Phase != v1alpha3.CanaryPhaseFailed { - t.Errorf("Got canary state %v wanted %v", c.Status.Phase, v1alpha3.CanaryPhaseFailed) - } -} - -func TestScheduler_SkipAnalysis(t *testing.T) { - mocks := SetupMocks(false) - // init - mocks.ctrl.advanceCanary("podinfo", "default", true) - - // enable skip - cd, err := mocks.flaggerClient.FlaggerV1alpha3().Canaries("default").Get("podinfo", metav1.GetOptions{}) - if err != nil { - t.Fatal(err.Error()) - } - cd.Spec.SkipAnalysis = true - _, err = mocks.flaggerClient.FlaggerV1alpha3().Canaries("default").Update(cd) - if err != nil { - t.Fatal(err.Error()) - } - - // update - dep2 := newTestDeploymentV2() - _, err = mocks.kubeClient.AppsV1().Deployments("default").Update(dep2) - if err != nil { - t.Fatal(err.Error()) - } - - // detect changes - mocks.ctrl.advanceCanary("podinfo", "default", true) - // advance - mocks.ctrl.advanceCanary("podinfo", "default", true) - - c, err := mocks.flaggerClient.FlaggerV1alpha3().Canaries("default").Get("podinfo", metav1.GetOptions{}) - if err != nil { - t.Fatal(err.Error()) - } - if !c.Spec.SkipAnalysis { - t.Errorf("Got skip analysis %v wanted %v", c.Spec.SkipAnalysis, true) - } - - if c.Status.Phase != v1alpha3.CanaryPhaseSucceeded { - t.Errorf("Got canary state %v wanted %v", c.Status.Phase, v1alpha3.CanaryPhaseSucceeded) - } -} - -func TestScheduler_NewRevisionReset(t *testing.T) { - mocks := SetupMocks(false) - // init - mocks.ctrl.advanceCanary("podinfo", "default", true) - - // first update - dep2 := newTestDeploymentV2() - _, err := mocks.kubeClient.AppsV1().Deployments("default").Update(dep2) - if err != nil { - t.Fatal(err.Error()) - } - - // detect changes - mocks.ctrl.advanceCanary("podinfo", "default", true) - // advance - mocks.ctrl.advanceCanary("podinfo", "default", true) - - primaryWeight, canaryWeight, err := mocks.router.GetRoutes(mocks.canary) - if err != nil { - t.Fatal(err.Error()) - } - - if primaryWeight != 90 { - t.Errorf("Got primary route %v wanted %v", primaryWeight, 90) - } - - if canaryWeight != 10 { - t.Errorf("Got canary route %v wanted %v", canaryWeight, 10) - } - - // second update - dep2.Spec.Template.Spec.ServiceAccountName = "test" - _, err = mocks.kubeClient.AppsV1().Deployments("default").Update(dep2) - if err != nil { - t.Fatal(err.Error()) - } - - // detect changes - mocks.ctrl.advanceCanary("podinfo", "default", true) - - primaryWeight, canaryWeight, err = mocks.router.GetRoutes(mocks.canary) - if err != nil { - t.Fatal(err.Error()) - } - - if primaryWeight != 100 { - t.Errorf("Got primary route %v wanted %v", primaryWeight, 100) - } - - if canaryWeight != 0 { - t.Errorf("Got canary route %v wanted %v", canaryWeight, 0) - } -} - -func TestScheduler_Promotion(t *testing.T) { - mocks := SetupMocks(false) - // init - mocks.ctrl.advanceCanary("podinfo", "default", true) - - // update - dep2 := newTestDeploymentV2() - _, err := mocks.kubeClient.AppsV1().Deployments("default").Update(dep2) - if err != nil { - t.Fatal(err.Error()) - } - - // detect pod spec changes - mocks.ctrl.advanceCanary("podinfo", "default", true) - - config2 := NewTestConfigMapV2() - _, err = mocks.kubeClient.CoreV1().ConfigMaps("default").Update(config2) - if err != nil { - t.Fatal(err.Error()) - } - - secret2 := NewTestSecretV2() - _, err = mocks.kubeClient.CoreV1().Secrets("default").Update(secret2) - if err != nil { - t.Fatal(err.Error()) - } - - // detect configs changes - mocks.ctrl.advanceCanary("podinfo", "default", true) - - primaryWeight, canaryWeight, err := mocks.router.GetRoutes(mocks.canary) - if err != nil { - t.Fatal(err.Error()) - } - - primaryWeight = 60 - canaryWeight = 40 - err = mocks.router.SetRoutes(mocks.canary, primaryWeight, canaryWeight) - if err != nil { - t.Fatal(err.Error()) - } - - // advance - mocks.ctrl.advanceCanary("podinfo", "default", true) - - // promote - mocks.ctrl.advanceCanary("podinfo", "default", true) - - primaryWeight, canaryWeight, err = mocks.router.GetRoutes(mocks.canary) - if err != nil { - t.Fatal(err.Error()) - } - - if primaryWeight != 100 { - t.Errorf("Got primary route %v wanted %v", primaryWeight, 100) - } - - if canaryWeight != 0 { - t.Errorf("Got canary route %v wanted %v", canaryWeight, 0) - } - - primaryDep, err := mocks.kubeClient.AppsV1().Deployments("default").Get("podinfo-primary", metav1.GetOptions{}) - if err != nil { - t.Fatal(err.Error()) - } - - primaryImage := primaryDep.Spec.Template.Spec.Containers[0].Image - canaryImage := dep2.Spec.Template.Spec.Containers[0].Image - if primaryImage != canaryImage { - t.Errorf("Got primary image %v wanted %v", primaryImage, canaryImage) - } - - configPrimary, err := mocks.kubeClient.CoreV1().ConfigMaps("default").Get("podinfo-config-env-primary", metav1.GetOptions{}) - if err != nil { - t.Fatal(err.Error()) - } - - if configPrimary.Data["color"] != config2.Data["color"] { - t.Errorf("Got primary ConfigMap color %s wanted %s", configPrimary.Data["color"], config2.Data["color"]) - } - - secretPrimary, err := mocks.kubeClient.CoreV1().Secrets("default").Get("podinfo-secret-env-primary", metav1.GetOptions{}) - if err != nil { - t.Fatal(err.Error()) - } - - if string(secretPrimary.Data["apiKey"]) != string(secret2.Data["apiKey"]) { - t.Errorf("Got primary secret %s wanted %s", secretPrimary.Data["apiKey"], secret2.Data["apiKey"]) - } - - // check finalising status - c, err := mocks.flaggerClient.FlaggerV1alpha3().Canaries("default").Get("podinfo", metav1.GetOptions{}) - if err != nil { - t.Fatal(err.Error()) - } - - // scale canary to zero - mocks.ctrl.advanceCanary("podinfo", "default", true) - - c, err = mocks.flaggerClient.FlaggerV1alpha3().Canaries("default").Get("podinfo", metav1.GetOptions{}) - if err != nil { - t.Fatal(err.Error()) - } - - if c.Status.Phase != v1alpha3.CanaryPhaseSucceeded { - t.Errorf("Got canary state %v wanted %v", c.Status.Phase, v1alpha3.CanaryPhaseSucceeded) - } -} - -func TestScheduler_ABTesting(t *testing.T) { - mocks := SetupMocks(true) - // init - mocks.ctrl.advanceCanary("podinfo", "default", true) - - // update - dep2 := newTestDeploymentV2() - _, err := mocks.kubeClient.AppsV1().Deployments("default").Update(dep2) - if err != nil { - t.Fatal(err.Error()) - } - - // detect pod spec changes - mocks.ctrl.advanceCanary("podinfo", "default", true) - - // advance - mocks.ctrl.advanceCanary("podinfo", "default", true) - - // check if traffic is routed to canary - primaryWeight, canaryWeight, err := mocks.router.GetRoutes(mocks.canary) - if err != nil { - t.Fatal(err.Error()) - } - - if primaryWeight != 0 { - t.Errorf("Got primary route %v wanted %v", primaryWeight, 0) - } - - if canaryWeight != 100 { - t.Errorf("Got canary route %v wanted %v", canaryWeight, 100) - } - - cd, err := mocks.flaggerClient.FlaggerV1alpha3().Canaries("default").Get("podinfo", metav1.GetOptions{}) - if err != nil { - t.Fatal(err.Error()) - } - - // set max iterations - if err := mocks.deployer.SetStatusIterations(cd, 10); err != nil { - t.Fatal(err.Error()) - } - - // advance - mocks.ctrl.advanceCanary("podinfo", "default", true) - - // finalising - mocks.ctrl.advanceCanary("podinfo", "default", true) - - // check finalising status - c, err := mocks.flaggerClient.FlaggerV1alpha3().Canaries("default").Get("podinfo", metav1.GetOptions{}) - if err != nil { - t.Fatal(err.Error()) - } - - if c.Status.Phase != v1alpha3.CanaryPhaseFinalising { - t.Errorf("Got canary state %v wanted %v", c.Status.Phase, v1alpha3.CanaryPhaseFinalising) - } - - // check if the container image tag was updated - primaryDep, err := mocks.kubeClient.AppsV1().Deployments("default").Get("podinfo-primary", metav1.GetOptions{}) - if err != nil { - t.Fatal(err.Error()) - } - - primaryImage := primaryDep.Spec.Template.Spec.Containers[0].Image - canaryImage := dep2.Spec.Template.Spec.Containers[0].Image - if primaryImage != canaryImage { - t.Errorf("Got primary image %v wanted %v", primaryImage, canaryImage) - } - - // shutdown canary - mocks.ctrl.advanceCanary("podinfo", "default", true) - - // check rollout status - c, err = mocks.flaggerClient.FlaggerV1alpha3().Canaries("default").Get("podinfo", metav1.GetOptions{}) - if err != nil { - t.Fatal(err.Error()) - } - - if c.Status.Phase != v1alpha3.CanaryPhaseSucceeded { - t.Errorf("Got canary state %v wanted %v", c.Status.Phase, v1alpha3.CanaryPhaseSucceeded) - } -} - -func TestScheduler_PortDiscovery(t *testing.T) { - mocks := SetupMocks(false) - - // enable port discovery - cd, err := mocks.flaggerClient.FlaggerV1alpha3().Canaries("default").Get("podinfo", metav1.GetOptions{}) - if err != nil { - t.Fatal(err.Error()) - } - cd.Spec.Service.PortDiscovery = true - _, err = mocks.flaggerClient.FlaggerV1alpha3().Canaries("default").Update(cd) - if err != nil { - t.Fatal(err.Error()) - } - - mocks.ctrl.advanceCanary("podinfo", "default", true) - - canarySvc, err := mocks.kubeClient.CoreV1().Services("default").Get("podinfo-canary", metav1.GetOptions{}) - if err != nil { - t.Fatal(err.Error()) - } - - if len(canarySvc.Spec.Ports) != 3 { - t.Fatalf("Got svc port count %v wanted %v", len(canarySvc.Spec.Ports), 3) - } - - matchPorts := func(lookup string) bool { - switch lookup { - case - "http 9898", - "http-metrics 8080", - "tcp-podinfo-2 8888": - return true - } - return false - } - - for _, port := range canarySvc.Spec.Ports { - if !matchPorts(fmt.Sprintf("%s %v", port.Name, port.Port)) { - t.Fatalf("Got wrong svc port %v", port.Name) - } - - } -} diff --git a/pkg/controller/webhook.go b/pkg/controller/webhook.go deleted file mode 100644 index 550921fc..00000000 --- a/pkg/controller/webhook.go +++ /dev/null @@ -1,74 +0,0 @@ -package controller - -import ( - "bytes" - "context" - "encoding/json" - "errors" - "fmt" - flaggerv1 "github.com/weaveworks/flagger/pkg/apis/flagger/v1alpha3" - "io/ioutil" - "net/http" - "net/url" - "time" -) - -// CallWebhook does a HTTP POST to an external service and -// returns an error if the response status code is non-2xx -func CallWebhook(name string, namespace string, phase flaggerv1.CanaryPhase, w flaggerv1.CanaryWebhook) error { - payload := flaggerv1.CanaryWebhookPayload{ - Name: name, - Namespace: namespace, - Phase: phase, - } - - if w.Metadata != nil { - payload.Metadata = *w.Metadata - } - - payloadBin, err := json.Marshal(payload) - if err != nil { - return err - } - - hook, err := url.Parse(w.URL) - if err != nil { - return err - } - - req, err := http.NewRequest("POST", hook.String(), bytes.NewBuffer(payloadBin)) - if err != nil { - return err - } - - req.Header.Set("Content-Type", "application/json") - - if len(w.Timeout) < 2 { - w.Timeout = "10s" - } - - timeout, err := time.ParseDuration(w.Timeout) - if err != nil { - return err - } - - ctx, cancel := context.WithTimeout(req.Context(), timeout) - defer cancel() - - r, err := http.DefaultClient.Do(req.WithContext(ctx)) - if err != nil { - return err - } - defer r.Body.Close() - - b, err := ioutil.ReadAll(r.Body) - if err != nil { - return fmt.Errorf("error reading body: %s", err.Error()) - } - - if r.StatusCode > 202 { - return errors.New(string(b)) - } - - return nil -} diff --git a/pkg/controller/webhook_test.go b/pkg/controller/webhook_test.go deleted file mode 100644 index 669a95c9..00000000 --- a/pkg/controller/webhook_test.go +++ /dev/null @@ -1,42 +0,0 @@ -package controller - -import ( - flaggerv1 "github.com/weaveworks/flagger/pkg/apis/flagger/v1alpha3" - "net/http" - "net/http/httptest" - "testing" -) - -func TestCallWebhook(t *testing.T) { - ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { - w.WriteHeader(http.StatusAccepted) - })) - defer ts.Close() - hook := flaggerv1.CanaryWebhook{ - Name: "validation", - URL: ts.URL, - Timeout: "10s", - Metadata: &map[string]string{"key1": "val1"}, - } - - err := CallWebhook("podinfo", "default", flaggerv1.CanaryPhaseProgressing, hook) - if err != nil { - t.Fatal(err.Error()) - } -} - -func TestCallWebhook_StatusCode(t *testing.T) { - ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { - w.WriteHeader(http.StatusInternalServerError) - })) - defer ts.Close() - hook := flaggerv1.CanaryWebhook{ - Name: "validation", - URL: ts.URL, - } - - err := CallWebhook("podinfo", "default", flaggerv1.CanaryPhaseProgressing, hook) - if err == nil { - t.Errorf("Got no error wanted %v", http.StatusInternalServerError) - } -} diff --git a/pkg/loadtester/bash.go b/pkg/loadtester/bash.go deleted file mode 100644 index 229bda4f..00000000 --- a/pkg/loadtester/bash.go +++ /dev/null @@ -1,39 +0,0 @@ -package loadtester - -import ( - "context" - "fmt" - "os/exec" -) - -const TaskTypeBash = "bash" - -type BashTask struct { - TaskBase - command string - logCmdOutput bool -} - -func (task *BashTask) Hash() string { - return hash(task.canary + task.command) -} - -func (task *BashTask) Run(ctx context.Context) (bool, error) { - cmd := exec.CommandContext(ctx, "bash", "-c", task.command) - out, err := cmd.CombinedOutput() - - if err != nil { - task.logger.With("canary", task.canary).Errorf("command failed %s %v %s", task.command, err, out) - return false, fmt.Errorf(" %v %v", err, out) - } else { - if task.logCmdOutput { - fmt.Printf("%s\n", out) - } - task.logger.With("canary", task.canary).Infof("command finished %s", task.command) - } - return true, nil -} - -func (task *BashTask) String() string { - return task.command -} diff --git a/pkg/loadtester/gate.go b/pkg/loadtester/gate.go deleted file mode 100644 index fdacdd7e..00000000 --- a/pkg/loadtester/gate.go +++ /dev/null @@ -1,31 +0,0 @@ -package loadtester - -import "sync" - -type GateStorage struct { - backend string - data *sync.Map -} - -func NewGateStorage(backend string) *GateStorage { - return &GateStorage{ - backend: backend, - data: new(sync.Map), - } -} - -func (gs *GateStorage) open(key string) { - gs.data.Store(key, true) -} - -func (gs *GateStorage) close(key string) { - gs.data.Store(key, false) -} - -func (gs *GateStorage) isOpen(key string) (locked bool) { - val, ok := gs.data.LoadOrStore(key, false) - if ok { - return val.(bool) - } - return -} diff --git a/pkg/loadtester/helm.go b/pkg/loadtester/helm.go deleted file mode 100644 index 34afde01..00000000 --- a/pkg/loadtester/helm.go +++ /dev/null @@ -1,42 +0,0 @@ -package loadtester - -import ( - "context" - "fmt" - "os/exec" - "strings" -) - -const TaskTypeHelm = "helm" - -type HelmTask struct { - TaskBase - command string - logCmdOutput bool -} - -func (task *HelmTask) Hash() string { - return hash(task.canary + task.command) -} - -func (task *HelmTask) Run(ctx context.Context) (bool, error) { - helmCmd := fmt.Sprintf("helm %s", task.command) - task.logger.With("canary", task.canary).Infof("running command %v", helmCmd) - - cmd := exec.CommandContext(ctx, "helm", strings.Fields(task.command)...) - out, err := cmd.CombinedOutput() - if err != nil { - task.logger.With("canary", task.canary).Errorf("command failed %s %v %s", task.command, err, out) - return false, fmt.Errorf(" %v %v", err, out) - } else { - if task.logCmdOutput { - fmt.Printf("%s\n", out) - } - task.logger.With("canary", task.canary).Infof("command finished %v", helmCmd) - } - return true, nil -} - -func (task *HelmTask) String() string { - return task.command -} diff --git a/pkg/loadtester/runner.go b/pkg/loadtester/runner.go deleted file mode 100644 index 2cba9050..00000000 --- a/pkg/loadtester/runner.go +++ /dev/null @@ -1,82 +0,0 @@ -package loadtester - -import ( - "context" - "go.uber.org/zap" - "sync" - "sync/atomic" - "time" -) - -type TaskRunner struct { - logger *zap.SugaredLogger - timeout time.Duration - todoTasks *sync.Map - runningTasks *sync.Map - totalExecs uint64 - logCmdOutput bool -} - -func NewTaskRunner(logger *zap.SugaredLogger, timeout time.Duration) *TaskRunner { - return &TaskRunner{ - logger: logger, - todoTasks: new(sync.Map), - runningTasks: new(sync.Map), - timeout: timeout, - } -} - -func (tr *TaskRunner) Add(task Task) { - tr.todoTasks.Store(task.Hash(), task) -} - -func (tr *TaskRunner) GetTotalExecs() uint64 { - return atomic.LoadUint64(&tr.totalExecs) -} - -func (tr *TaskRunner) runAll() { - tr.todoTasks.Range(func(key interface{}, value interface{}) bool { - task := value.(Task) - go func(t Task) { - // remove task from the to do list - tr.todoTasks.Delete(t.Hash()) - - // check if task is already running, if not run the task's command - if _, exists := tr.runningTasks.Load(t.Hash()); !exists { - // save the task in the running list - tr.runningTasks.Store(t.Hash(), t) - - // create timeout context - ctx, cancel := context.WithTimeout(context.Background(), tr.timeout) - defer cancel() - - // increment the total exec counter - atomic.AddUint64(&tr.totalExecs, 1) - - tr.logger.With("canary", t.Canary()).Infof("task starting %s", t) - - // run task with the timeout context - t.Run(ctx) - - // remove task from the running list - tr.runningTasks.Delete(t.Hash()) - } else { - tr.logger.With("canary", t.Canary()).Infof("command skipped %s is already running", t) - } - }(task) - return true - }) -} - -func (tr *TaskRunner) Start(interval time.Duration, stopCh <-chan struct{}) { - tickChan := time.NewTicker(interval).C - for { - select { - case <-tickChan: - tr.runAll() - case <-stopCh: - tr.logger.Info("shutting down the task runner") - return - } - } -} diff --git a/pkg/loadtester/runner_test.go b/pkg/loadtester/runner_test.go deleted file mode 100644 index 1c7ab9c7..00000000 --- a/pkg/loadtester/runner_test.go +++ /dev/null @@ -1,47 +0,0 @@ -package loadtester - -import ( - "github.com/weaveworks/flagger/pkg/logger" - "testing" - "time" -) - -func TestTaskRunner_Start(t *testing.T) { - stop := make(chan struct{}) - logger, _ := logger.NewLogger("debug") - tr := NewTaskRunner(logger, time.Hour) - - go tr.Start(10*time.Millisecond, stop) - - taskFactory, _ := GetTaskFactory(TaskTypeShell) - task1, _ := taskFactory(map[string]string{"type": "cmd", "cmd": "sleep 0.6"}, "podinfo.default", logger) - task2, _ := taskFactory(map[string]string{"cmd": "sleep 0.7", "logCmdOutput": "true"}, "podinfo.default", logger) - - tr.Add(task1) - tr.Add(task2) - - time.Sleep(100 * time.Millisecond) - - tr.Add(task1) - tr.Add(task2) - - time.Sleep(100 * time.Millisecond) - - tr.Add(task1) - tr.Add(task2) - - if tr.GetTotalExecs() != 2 { - t.Errorf("Got total executed commands %v wanted %v", tr.GetTotalExecs(), 2) - } - - time.Sleep(time.Second) - - tr.Add(task1) - tr.Add(task2) - - time.Sleep(time.Second) - - if tr.GetTotalExecs() != 4 { - t.Errorf("Got total executed commands %v wanted %v", tr.GetTotalExecs(), 4) - } -} diff --git a/pkg/loadtester/server.go b/pkg/loadtester/server.go deleted file mode 100644 index 3ddc7903..00000000 --- a/pkg/loadtester/server.go +++ /dev/null @@ -1,235 +0,0 @@ -package loadtester - -import ( - "context" - "encoding/json" - "fmt" - "io/ioutil" - "net/http" - "time" - - "github.com/prometheus/client_golang/prometheus/promhttp" - flaggerv1 "github.com/weaveworks/flagger/pkg/apis/flagger/v1alpha3" - "go.uber.org/zap" -) - -// ListenAndServe starts a web server and waits for SIGTERM -func ListenAndServe(port string, timeout time.Duration, logger *zap.SugaredLogger, taskRunner *TaskRunner, gate *GateStorage, stopCh <-chan struct{}) { - mux := http.DefaultServeMux - mux.Handle("/metrics", promhttp.Handler()) - mux.HandleFunc("/healthz", func(w http.ResponseWriter, r *http.Request) { - w.WriteHeader(http.StatusOK) - w.Write([]byte("OK")) - }) - mux.HandleFunc("/gate/approve", func(w http.ResponseWriter, r *http.Request) { - w.WriteHeader(http.StatusOK) - w.Write([]byte("OK")) - }) - mux.HandleFunc("/gate/halt", func(w http.ResponseWriter, r *http.Request) { - w.WriteHeader(http.StatusForbidden) - w.Write([]byte("Forbidden")) - }) - mux.HandleFunc("/gate/check", func(w http.ResponseWriter, r *http.Request) { - body, err := ioutil.ReadAll(r.Body) - if err != nil { - logger.Error("reading the request body failed", zap.Error(err)) - w.WriteHeader(http.StatusBadRequest) - return - } - defer r.Body.Close() - - canary := &flaggerv1.CanaryWebhookPayload{} - err = json.Unmarshal(body, canary) - if err != nil { - logger.Error("decoding the request body failed", zap.Error(err)) - w.WriteHeader(http.StatusBadRequest) - return - } - - canaryName := fmt.Sprintf("%s.%s", canary.Name, canary.Namespace) - approved := gate.isOpen(canaryName) - if approved { - w.WriteHeader(http.StatusOK) - w.Write([]byte("Approved")) - } else { - w.WriteHeader(http.StatusForbidden) - w.Write([]byte("Forbidden")) - } - - logger.Infof("%s gate check: approved %v", canaryName, approved) - }) - - mux.HandleFunc("/gate/open", func(w http.ResponseWriter, r *http.Request) { - body, err := ioutil.ReadAll(r.Body) - if err != nil { - logger.Error("reading the request body failed", zap.Error(err)) - w.WriteHeader(http.StatusBadRequest) - return - } - defer r.Body.Close() - - canary := &flaggerv1.CanaryWebhookPayload{} - err = json.Unmarshal(body, canary) - if err != nil { - logger.Error("decoding the request body failed", zap.Error(err)) - w.WriteHeader(http.StatusBadRequest) - return - } - - canaryName := fmt.Sprintf("%s.%s", canary.Name, canary.Namespace) - gate.open(canaryName) - - w.WriteHeader(http.StatusAccepted) - - logger.Infof("%s gate opened", canaryName) - }) - - mux.HandleFunc("/gate/close", func(w http.ResponseWriter, r *http.Request) { - body, err := ioutil.ReadAll(r.Body) - if err != nil { - logger.Error("reading the request body failed", zap.Error(err)) - w.WriteHeader(http.StatusBadRequest) - return - } - defer r.Body.Close() - - canary := &flaggerv1.CanaryWebhookPayload{} - err = json.Unmarshal(body, canary) - if err != nil { - logger.Error("decoding the request body failed", zap.Error(err)) - w.WriteHeader(http.StatusBadRequest) - return - } - - canaryName := fmt.Sprintf("%s.%s", canary.Name, canary.Namespace) - gate.close(canaryName) - - w.WriteHeader(http.StatusAccepted) - - logger.Infof("%s gate closed", canaryName) - }) - - mux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) { - body, err := ioutil.ReadAll(r.Body) - if err != nil { - logger.Error("reading the request body failed", zap.Error(err)) - w.WriteHeader(http.StatusBadRequest) - return - } - defer r.Body.Close() - - payload := &flaggerv1.CanaryWebhookPayload{} - err = json.Unmarshal(body, payload) - if err != nil { - logger.Error("decoding the request body failed", zap.Error(err)) - w.WriteHeader(http.StatusBadRequest) - return - } - - if len(payload.Metadata) > 0 { - metadata := payload.Metadata - var typ, ok = metadata["type"] - if !ok { - typ = TaskTypeShell - } - - // run bats command (blocking task) - if typ == TaskTypeBash { - logger.With("canary", payload.Name).Infof("bats command %s", payload.Metadata["cmd"]) - - bats := BashTask{ - command: payload.Metadata["cmd"], - logCmdOutput: true, - TaskBase: TaskBase{ - canary: fmt.Sprintf("%s.%s", payload.Name, payload.Namespace), - logger: logger, - }, - } - - ctx, cancel := context.WithTimeout(context.Background(), taskRunner.timeout) - defer cancel() - - ok, err := bats.Run(ctx) - if !ok { - w.WriteHeader(http.StatusInternalServerError) - w.Write([]byte(err.Error())) - return - } - - w.WriteHeader(http.StatusOK) - return - } - - // run helm command (blocking task) - if typ == TaskTypeHelm { - helm := HelmTask{ - command: payload.Metadata["cmd"], - logCmdOutput: true, - TaskBase: TaskBase{ - canary: fmt.Sprintf("%s.%s", payload.Name, payload.Namespace), - logger: logger, - }, - } - - ctx, cancel := context.WithTimeout(context.Background(), taskRunner.timeout) - defer cancel() - - ok, err := helm.Run(ctx) - if !ok { - w.WriteHeader(http.StatusInternalServerError) - w.Write([]byte(err.Error())) - return - } - - w.WriteHeader(http.StatusOK) - return - } - - taskFactory, ok := GetTaskFactory(typ) - if !ok { - w.WriteHeader(http.StatusBadRequest) - w.Write([]byte(fmt.Sprintf("unknown task type %s", typ))) - return - } - canary := fmt.Sprintf("%s.%s", payload.Name, payload.Namespace) - task, err := taskFactory(metadata, canary, logger) - if err != nil { - w.WriteHeader(http.StatusBadRequest) - w.Write([]byte(err.Error())) - return - } - taskRunner.Add(task) - } else { - w.WriteHeader(http.StatusBadRequest) - w.Write([]byte("metadata not found in payload")) - return - } - - w.WriteHeader(http.StatusAccepted) - }) - srv := &http.Server{ - Addr: ":" + port, - Handler: mux, - ReadTimeout: 5 * time.Second, - WriteTimeout: 1 * time.Minute, - IdleTimeout: 15 * time.Second, - } - - // run server in background - go func() { - if err := srv.ListenAndServe(); err != http.ErrServerClosed { - logger.Fatalf("HTTP server crashed %v", err) - } - }() - - // wait for SIGTERM or SIGINT - <-stopCh - ctx, cancel := context.WithTimeout(context.Background(), timeout) - defer cancel() - - if err := srv.Shutdown(ctx); err != nil { - logger.Errorf("HTTP server graceful shutdown failed %v", err) - } else { - logger.Info("HTTP server stopped") - } -} diff --git a/pkg/loadtester/task.go b/pkg/loadtester/task.go deleted file mode 100644 index 395b34a9..00000000 --- a/pkg/loadtester/task.go +++ /dev/null @@ -1,41 +0,0 @@ -package loadtester - -import ( - "context" - "encoding/hex" - "go.uber.org/zap" - "hash/fnv" - "sync" -) - -// Modeling a loadtester task -type Task interface { - Hash() string - Run(ctx context.Context) bool - String() string - Canary() string -} - -type TaskBase struct { - canary string - logger *zap.SugaredLogger -} - -func (task *TaskBase) Canary() string { - return task.canary -} - -func hash(str string) string { - fnvHash := fnv.New32() - fnvBytes := fnvHash.Sum([]byte(str)) - return hex.EncodeToString(fnvBytes[:]) -} - -var taskFactories = new(sync.Map) - -type TaskFactory = func(metadata map[string]string, canary string, logger *zap.SugaredLogger) (Task, error) - -func GetTaskFactory(typ string) (TaskFactory, bool) { - factory, ok := taskFactories.Load(typ) - return factory.(TaskFactory), ok -} diff --git a/pkg/loadtester/task_ngrinder.go b/pkg/loadtester/task_ngrinder.go deleted file mode 100644 index 4a4dca15..00000000 --- a/pkg/loadtester/task_ngrinder.go +++ /dev/null @@ -1,158 +0,0 @@ -package loadtester - -import ( - "context" - "encoding/base64" - "encoding/json" - "errors" - "fmt" - "go.uber.org/zap" - "io/ioutil" - "net/http" - "net/url" - "strconv" - "time" -) - -const TaskTypeNGrinder = "ngrinder" - -func init() { - taskFactories.Store(TaskTypeNGrinder, func(metadata map[string]string, canary string, logger *zap.SugaredLogger) (Task, error) { - server := metadata["server"] - clone := metadata["clone"] - username := metadata["username"] - passwd := metadata["passwd"] - pollInterval := metadata["pollInterval"] - if server == "" || clone == "" || username == "" || passwd == "" { - return nil, errors.New("server, clone, username and passwd are required metadata") - } - baseUrl, err := url.Parse(server) - if err != nil { - return nil, errors.New(fmt.Sprintf("invalid url: %s", server)) - } - cloneId, err := strconv.Atoi(clone) - if err != nil { - return nil, errors.New("metadata clone must be integer") - } - - passwdDecoded, err := base64.StdEncoding.DecodeString(passwd) - if err != nil { - return nil, errors.New("metadata auth provided is invalid, base64 encoded username:password required") - } - interval, err := time.ParseDuration(pollInterval) - if err != nil { - interval = 1 - } - - return &NGrinderTask{ - TaskBase{canary, logger}, - baseUrl, cloneId, username, string(passwdDecoded), -1, interval, - }, nil - }) -} - -type NGrinderTask struct { - TaskBase - // base url of ngrinder server, e.g. http://ngrinder:8080 - baseUrl *url.URL - // template test to clone from - cloneId int - // http basic auth - username string - passwd string - // current ngrinder test id - testId int - // task status polling interval - pollInterval time.Duration -} - -func (task *NGrinderTask) Hash() string { - return hash(task.canary + string(task.cloneId)) -} - -// nGrinder REST endpoints -func (task *NGrinderTask) CloneAndStartEndpoint() *url.URL { - path, _ := url.Parse(fmt.Sprintf("perftest/api/%d/clone_and_start", task.cloneId)) - return task.baseUrl.ResolveReference(path) -} -func (task *NGrinderTask) StatusEndpoint() *url.URL { - path, _ := url.Parse(fmt.Sprintf("perftest/api/%d/status", task.testId)) - return task.baseUrl.ResolveReference(path) -} -func (task *NGrinderTask) StopEndpoint() *url.URL { - path, _ := url.Parse(fmt.Sprintf("perftest/api/%d?action=stop", task.testId)) - return task.baseUrl.ResolveReference(path) -} - -// initiate a clone_and_start request and get new test id from response -func (task *NGrinderTask) Run(ctx context.Context) bool { - url := task.CloneAndStartEndpoint().String() - result, err := task.request("POST", url, ctx) - if err != nil { - task.logger.With("canary", task.canary).Errorf("failed to clone and start ngrinder test %s: %s", url, err.Error()) - return false - } - id := result["id"] - task.testId = int(id.(float64)) - return task.PollStatus(ctx) -} - -func (task *NGrinderTask) String() string { - return task.canary + task.CloneAndStartEndpoint().String() -} - -// polling execution status of the new test and check if finished -func (task *NGrinderTask) PollStatus(ctx context.Context) bool { - // wait until ngrinder test finished/canceled or timedout - tickChan := time.NewTicker(time.Second * task.pollInterval).C - for { - select { - case <-tickChan: - result, err := task.request("GET", task.StatusEndpoint().String(), ctx) - if err == nil { - statusArray, ok := result["status"].([]interface{}) - if ok && len(statusArray) > 0 { - status := statusArray[0].(map[string]interface{}) - statusId := status["status_id"] - task.logger.Debugf("status of ngrinder task %d is %s", task.testId, statusId) - if statusId == "FINISHED" { - return true - } else if statusId == "STOP_BY_ERROR" || statusId == "CANCELED" || statusId == "UNKNOWN" { - return false - } - } - } - case <-ctx.Done(): - task.logger.Warnf("context timedout, top ngrinder task %d forcibly", task.testId) - task.request("PUT", task.StopEndpoint().String(), nil) - return false - } - } -} - -// send request, handle error, and eavl response json -func (task *NGrinderTask) request(method, url string, ctx context.Context) (map[string]interface{}, error) { - task.logger.Debugf("send %s request to %s", method, url) - req, _ := http.NewRequest(method, url, nil) - req.SetBasicAuth(task.username, task.passwd) - if ctx != nil { - req = req.WithContext(ctx) - } - resp, err := http.DefaultClient.Do(req) - if resp != nil { - defer resp.Body.Close() - } - if err != nil { - task.logger.Errorf("bad request: %s", err.Error()) - return nil, err - } - respBytes, err := ioutil.ReadAll(resp.Body) - res := make(map[string]interface{}) - err = json.Unmarshal(respBytes, &res) - if err != nil { - task.logger.Errorf("bad response, %s ,json expected:\n %s", err.Error(), string(respBytes)) - } else if success, ok := res["success"]; ok && success == false { - err = errors.New(res["message"].(string)) - } - return res, err -} diff --git a/pkg/loadtester/task_ngrinder_test.go b/pkg/loadtester/task_ngrinder_test.go deleted file mode 100644 index 699aea72..00000000 --- a/pkg/loadtester/task_ngrinder_test.go +++ /dev/null @@ -1,46 +0,0 @@ -package loadtester - -import ( - "context" - "fmt" - "github.com/weaveworks/flagger/pkg/logger" - "gopkg.in/h2non/gock.v1" - "testing" - "time" -) - -func TestTaskNGrinder(t *testing.T) { - server := "http://ngrinder:8080" - cloneId := "960" - logger, _ := logger.NewLoggerWithEncoding("debug", "console") - canary := "podinfo.default" - taskFactory, ok := GetTaskFactory(TaskTypeNGrinder) - if !ok { - t.Errorf("Failed to get ngrinder task factory") - } - - defer gock.Off() - gock.New(server).Post(fmt.Sprintf("perftest/api/%s/clone_and_start", cloneId)). - Reply(200).BodyString(`{"status": "READY","id": 961}`) - gock.New(server).Get("perftest/api/961/status").Reply(200). - BodyString(`{"status": [{"status_id": "FINISHED"}]}`) - gock.New(server).Put("perftest/api/961").MatchParam("action", "stop").Reply(200). - BodyString(`{"success": true}`) - - t.Run("NormalRequest", func(t *testing.T) { - task, err := taskFactory(map[string]string{ - "server": server, - "clone": cloneId, - "username": "admin", - "passwd": "YWRtaW4=", - "pollInterval": "1s", - }, canary, logger) - if err != nil { - t.Fatalf("Failed to create ngrinder task: %s", err.Error()) - return - } - ctx, _ := context.WithTimeout(context.Background(), time.Second*3) - task.Run(ctx) - <-ctx.Done() - }) -} diff --git a/pkg/loadtester/task_shell.go b/pkg/loadtester/task_shell.go deleted file mode 100644 index 4f79b505..00000000 --- a/pkg/loadtester/task_shell.go +++ /dev/null @@ -1,52 +0,0 @@ -package loadtester - -import ( - "context" - "errors" - "fmt" - "go.uber.org/zap" - "os/exec" - "strconv" -) - -const TaskTypeShell = "cmd" - -func init() { - taskFactories.Store(TaskTypeShell, func(metadata map[string]string, canary string, logger *zap.SugaredLogger) (Task, error) { - cmd, ok := metadata["cmd"] - if !ok { - return nil, errors.New("cmd not found in metadata") - } - logCmdOutput, _ := strconv.ParseBool(metadata["logCmdOutput"]) - return &CmdTask{TaskBase{canary, logger}, cmd, logCmdOutput}, nil - }) -} - -type CmdTask struct { - TaskBase - command string - logCmdOutput bool -} - -func (task *CmdTask) Hash() string { - return hash(task.canary + task.command) -} - -func (task *CmdTask) Run(ctx context.Context) bool { - cmd := exec.CommandContext(ctx, "sh", "-c", task.command) - out, err := cmd.CombinedOutput() - - if err != nil { - task.logger.With("canary", task.canary).Errorf("command failed %s %v %s", task.command, err, out) - } else { - if task.logCmdOutput { - fmt.Printf("%s\n", out) - } - task.logger.With("canary", task.canary).Infof("command finished %s", task.command) - } - return err == nil -} - -func (task *CmdTask) String() string { - return task.command -} diff --git a/pkg/logger/logger.go b/pkg/logger/logger.go deleted file mode 100644 index 87ba8d76..00000000 --- a/pkg/logger/logger.go +++ /dev/null @@ -1,63 +0,0 @@ -package logger - -import ( - "go.uber.org/zap" - "go.uber.org/zap/zapcore" -) - -// NewLogger returns a zap sugared logger configured with json format and caller id -func NewLogger(logLevel string) (*zap.SugaredLogger, error) { - return NewLoggerWithEncoding(logLevel, "json") -} - -// NewLoggerWithEncoding returns a zap sugared logger configured with provided format, e.g. console or json, and caller id -func NewLoggerWithEncoding(logLevel, zapEncoding string) (*zap.SugaredLogger, error) { - level := zap.NewAtomicLevelAt(zapcore.InfoLevel) - switch logLevel { - case "debug": - level = zap.NewAtomicLevelAt(zapcore.DebugLevel) - case "info": - level = zap.NewAtomicLevelAt(zapcore.InfoLevel) - case "warn": - level = zap.NewAtomicLevelAt(zapcore.WarnLevel) - case "error": - level = zap.NewAtomicLevelAt(zapcore.ErrorLevel) - case "fatal": - level = zap.NewAtomicLevelAt(zapcore.FatalLevel) - case "panic": - level = zap.NewAtomicLevelAt(zapcore.PanicLevel) - } - - zapEncoderConfig := zapcore.EncoderConfig{ - TimeKey: "ts", - LevelKey: "level", - NameKey: "logger", - CallerKey: "caller", - MessageKey: "msg", - StacktraceKey: "stacktrace", - LineEnding: zapcore.DefaultLineEnding, - EncodeLevel: zapcore.LowercaseLevelEncoder, - EncodeTime: zapcore.ISO8601TimeEncoder, - EncodeDuration: zapcore.SecondsDurationEncoder, - EncodeCaller: zapcore.ShortCallerEncoder, - } - - zapConfig := zap.Config{ - Level: level, - Development: false, - Sampling: &zap.SamplingConfig{ - Initial: 100, - Thereafter: 100, - }, - Encoding: zapEncoding, - EncoderConfig: zapEncoderConfig, - OutputPaths: []string{"stderr"}, - ErrorOutputPaths: []string{"stderr"}, - } - - logger, err := zapConfig.Build() - if err != nil { - return nil, err - } - return logger.Sugar(), nil -} diff --git a/pkg/metrics/client.go b/pkg/metrics/client.go deleted file mode 100644 index 97647ccc..00000000 --- a/pkg/metrics/client.go +++ /dev/null @@ -1,187 +0,0 @@ -package metrics - -import ( - "bufio" - "bytes" - "context" - "encoding/json" - "fmt" - "io/ioutil" - "net/http" - "net/url" - "path" - "strconv" - "strings" - "text/template" - "time" -) - -// PrometheusClient is executing promql queries -type PrometheusClient struct { - timeout time.Duration - url url.URL -} - -type prometheusResponse struct { - Data struct { - Result []struct { - Metric struct { - Name string `json:"name"` - } - Value []interface{} `json:"value"` - } - } -} - -// NewPrometheusClient creates a Prometheus client for the provided URL address -func NewPrometheusClient(address string, timeout time.Duration) (*PrometheusClient, error) { - promURL, err := url.Parse(address) - if err != nil { - return nil, err - } - - return &PrometheusClient{timeout: timeout, url: *promURL}, nil -} - -// RenderQuery renders the promql query using the provided text template -func (p *PrometheusClient) RenderQuery(name string, namespace string, interval string, tmpl string) (string, error) { - meta := struct { - Name string - Namespace string - Interval string - }{ - name, - namespace, - interval, - } - - t, err := template.New("tmpl").Parse(tmpl) - if err != nil { - return "", err - } - var data bytes.Buffer - b := bufio.NewWriter(&data) - - if err := t.Execute(b, meta); err != nil { - return "", err - } - - err = b.Flush() - if err != nil { - return "", err - } - - return data.String(), nil -} - -// RunQuery executes the promql and converts the result to float64 -func (p *PrometheusClient) RunQuery(query string) (float64, error) { - if p.url.Host == "fake" { - return 100, nil - } - - query = url.QueryEscape(p.TrimQuery(query)) - u, err := url.Parse(fmt.Sprintf("./api/v1/query?query=%s", query)) - if err != nil { - return 0, err - } - u.Path = path.Join(p.url.Path, u.Path) - - u = p.url.ResolveReference(u) - - req, err := http.NewRequest("GET", u.String(), nil) - if err != nil { - return 0, err - } - - ctx, cancel := context.WithTimeout(req.Context(), p.timeout) - defer cancel() - - r, err := http.DefaultClient.Do(req.WithContext(ctx)) - if err != nil { - return 0, err - } - defer r.Body.Close() - - b, err := ioutil.ReadAll(r.Body) - if err != nil { - return 0, fmt.Errorf("error reading body: %s", err.Error()) - } - - if 400 <= r.StatusCode { - return 0, fmt.Errorf("error response: %s", string(b)) - } - - var result prometheusResponse - err = json.Unmarshal(b, &result) - if err != nil { - return 0, fmt.Errorf("error unmarshaling result: %s, '%s'", err.Error(), string(b)) - } - - var value *float64 - for _, v := range result.Data.Result { - metricValue := v.Value[1] - switch metricValue.(type) { - case string: - f, err := strconv.ParseFloat(metricValue.(string), 64) - if err != nil { - return 0, err - } - value = &f - } - } - if value == nil { - return 0, fmt.Errorf("no values found") - } - - return *value, nil -} - -// TrimQuery takes a promql query and removes spaces, tabs and new lines -func (p *PrometheusClient) TrimQuery(query string) string { - query = strings.Replace(query, "\n", "", -1) - query = strings.Replace(query, "\t", "", -1) - query = strings.Replace(query, " ", "", -1) - - return query -} - -// IsOnline call Prometheus status endpoint and returns an error if the API is unreachable -func (p *PrometheusClient) IsOnline() (bool, error) { - u, err := url.Parse("./api/v1/status/flags") - if err != nil { - return false, err - } - u.Path = path.Join(p.url.Path, u.Path) - - u = p.url.ResolveReference(u) - - req, err := http.NewRequest("GET", u.String(), nil) - if err != nil { - return false, err - } - - ctx, cancel := context.WithTimeout(req.Context(), p.timeout) - defer cancel() - - r, err := http.DefaultClient.Do(req.WithContext(ctx)) - if err != nil { - return false, err - } - defer r.Body.Close() - - b, err := ioutil.ReadAll(r.Body) - if err != nil { - return false, fmt.Errorf("error reading body: %s", err.Error()) - } - - if 400 <= r.StatusCode { - return false, fmt.Errorf("error response: %s", string(b)) - } - - return true, nil -} - -func (p *PrometheusClient) GetMetricsServer() string { - return p.url.String() -} diff --git a/pkg/metrics/client_test.go b/pkg/metrics/client_test.go deleted file mode 100644 index 9fdbe85a..00000000 --- a/pkg/metrics/client_test.go +++ /dev/null @@ -1,85 +0,0 @@ -package metrics - -import ( - "net/http" - "net/http/httptest" - "testing" - "time" -) - -func TestPrometheusClient_RunQuery(t *testing.T) { - ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { - json := `{"status":"success","data":{"resultType":"vector","result":[{"metric":{},"value":[1545905245.458,"100"]}]}}` - w.Write([]byte(json)) - })) - defer ts.Close() - - client, err := NewPrometheusClient(ts.URL, time.Second) - if err != nil { - t.Fatal(err) - } - - query := ` - histogram_quantile(0.99, - sum( - rate( - http_request_duration_seconds_bucket{ - kubernetes_namespace="test", - kubernetes_pod_name=~"podinfo-[0-9a-zA-Z]+(-[0-9a-zA-Z]+)" - }[1m] - ) - ) by (le) - )` - - val, err := client.RunQuery(query) - if err != nil { - t.Fatal(err.Error()) - } - - if val != 100 { - t.Errorf("Got %v wanted %v", val, 100) - } -} - -func TestPrometheusClient_IsOnline(t *testing.T) { - ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { - json := `{"status":"success","data":{"config.file":"/etc/prometheus/prometheus.yml"}}` - w.Write([]byte(json)) - })) - defer ts.Close() - - client, err := NewPrometheusClient(ts.URL, time.Second) - if err != nil { - t.Fatal(err) - } - - ok, err := client.IsOnline() - if err != nil { - t.Fatal(err.Error()) - } - - if !ok { - t.Errorf("Got %v wanted %v", ok, true) - } -} - -func TestPrometheusClient_IsOffline(t *testing.T) { - ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { - w.WriteHeader(http.StatusBadGateway) - })) - defer ts.Close() - - client, err := NewPrometheusClient(ts.URL, time.Second) - if err != nil { - t.Fatal(err) - } - - ok, err := client.IsOnline() - if err == nil { - t.Errorf("Got no error wanted %v", http.StatusBadGateway) - } - - if ok { - t.Errorf("Got %v wanted %v", ok, false) - } -} diff --git a/pkg/metrics/envoy.go b/pkg/metrics/envoy.go deleted file mode 100644 index ce88b540..00000000 --- a/pkg/metrics/envoy.go +++ /dev/null @@ -1,73 +0,0 @@ -package metrics - -import ( - "time" -) - -var envoyQueries = map[string]string{ - "request-success-rate": ` - sum( - rate( - envoy_cluster_upstream_rq{ - kubernetes_namespace="{{ .Namespace }}", - kubernetes_pod_name=~"{{ .Name }}-[0-9a-zA-Z]+(-[0-9a-zA-Z]+)", - envoy_response_code!~"5.*" - }[{{ .Interval }}] - ) - ) - / - sum( - rate( - envoy_cluster_upstream_rq{ - kubernetes_namespace="{{ .Namespace }}", - kubernetes_pod_name=~"{{ .Name }}-[0-9a-zA-Z]+(-[0-9a-zA-Z]+)" - }[{{ .Interval }}] - ) - ) - * 100`, - "request-duration": ` - histogram_quantile( - 0.99, - sum( - rate( - envoy_cluster_upstream_rq_time_bucket{ - kubernetes_namespace="{{ .Namespace }}", - kubernetes_pod_name=~"{{ .Name }}-[0-9a-zA-Z]+(-[0-9a-zA-Z]+)" - }[{{ .Interval }}] - ) - ) by (le) - )`, -} - -type EnvoyObserver struct { - client *PrometheusClient -} - -func (ob *EnvoyObserver) GetRequestSuccessRate(name string, namespace string, interval string) (float64, error) { - query, err := ob.client.RenderQuery(name, namespace, interval, envoyQueries["request-success-rate"]) - if err != nil { - return 0, err - } - - value, err := ob.client.RunQuery(query) - if err != nil { - return 0, err - } - - return value, nil -} - -func (ob *EnvoyObserver) GetRequestDuration(name string, namespace string, interval string) (time.Duration, error) { - query, err := ob.client.RenderQuery(name, namespace, interval, envoyQueries["request-duration"]) - if err != nil { - return 0, err - } - - value, err := ob.client.RunQuery(query) - if err != nil { - return 0, err - } - - ms := time.Duration(int64(value)) * time.Millisecond - return ms, nil -} diff --git a/pkg/metrics/envoy_test.go b/pkg/metrics/envoy_test.go deleted file mode 100644 index 85f27905..00000000 --- a/pkg/metrics/envoy_test.go +++ /dev/null @@ -1,74 +0,0 @@ -package metrics - -import ( - "net/http" - "net/http/httptest" - "testing" - "time" -) - -func TestEnvoyObserver_GetRequestSuccessRate(t *testing.T) { - expected := `sum(rate(envoy_cluster_upstream_rq{kubernetes_namespace="default",kubernetes_pod_name=~"podinfo-[0-9a-zA-Z]+(-[0-9a-zA-Z]+)",envoy_response_code!~"5.*"}[1m]))/sum(rate(envoy_cluster_upstream_rq{kubernetes_namespace="default",kubernetes_pod_name=~"podinfo-[0-9a-zA-Z]+(-[0-9a-zA-Z]+)"}[1m]))*100` - - ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { - promql := r.URL.Query()["query"][0] - if promql != expected { - t.Errorf("\nGot %s \nWanted %s", promql, expected) - } - - json := `{"status":"success","data":{"resultType":"vector","result":[{"metric":{},"value":[1,"100"]}]}}` - w.Write([]byte(json)) - })) - defer ts.Close() - - client, err := NewPrometheusClient(ts.URL, time.Second) - if err != nil { - t.Fatal(err) - } - - observer := &EnvoyObserver{ - client: client, - } - - val, err := observer.GetRequestSuccessRate("podinfo", "default", "1m") - if err != nil { - t.Fatal(err.Error()) - } - - if val != 100 { - t.Errorf("Got %v wanted %v", val, 100) - } -} - -func TestEnvoyObserver_GetRequestDuration(t *testing.T) { - expected := `histogram_quantile(0.99,sum(rate(envoy_cluster_upstream_rq_time_bucket{kubernetes_namespace="default",kubernetes_pod_name=~"podinfo-[0-9a-zA-Z]+(-[0-9a-zA-Z]+)"}[1m]))by(le))` - - ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { - promql := r.URL.Query()["query"][0] - if promql != expected { - t.Errorf("\nGot %s \nWanted %s", promql, expected) - } - - json := `{"status":"success","data":{"resultType":"vector","result":[{"metric":{},"value":[1,"100"]}]}}` - w.Write([]byte(json)) - })) - defer ts.Close() - - client, err := NewPrometheusClient(ts.URL, time.Second) - if err != nil { - t.Fatal(err) - } - - observer := &EnvoyObserver{ - client: client, - } - - val, err := observer.GetRequestDuration("podinfo", "default", "1m") - if err != nil { - t.Fatal(err.Error()) - } - - if val != 100*time.Millisecond { - t.Errorf("Got %v wanted %v", val, 100*time.Millisecond) - } -} diff --git a/pkg/metrics/factory.go b/pkg/metrics/factory.go deleted file mode 100644 index e2b69b8d..00000000 --- a/pkg/metrics/factory.go +++ /dev/null @@ -1,60 +0,0 @@ -package metrics - -import ( - "strings" - "time" -) - -type Factory struct { - MeshProvider string - Client *PrometheusClient -} - -func NewFactory(metricsServer string, meshProvider string, timeout time.Duration) (*Factory, error) { - client, err := NewPrometheusClient(metricsServer, timeout) - if err != nil { - return nil, err - } - - return &Factory{ - MeshProvider: meshProvider, - Client: client, - }, nil -} - -func (factory Factory) Observer(provider string) Interface { - switch { - case provider == "none": - return &HttpObserver{ - client: factory.Client, - } - case provider == "kubernetes": - return &HttpObserver{ - client: factory.Client, - } - case provider == "appmesh": - return &EnvoyObserver{ - client: factory.Client, - } - case provider == "nginx": - return &NginxObserver{ - client: factory.Client, - } - case strings.HasPrefix(provider, "gloo"): - return &GlooObserver{ - client: factory.Client, - } - case provider == "smi:linkerd": - return &LinkerdObserver{ - client: factory.Client, - } - case provider == "linkerd": - return &LinkerdObserver{ - client: factory.Client, - } - default: - return &IstioObserver{ - client: factory.Client, - } - } -} diff --git a/pkg/metrics/gloo.go b/pkg/metrics/gloo.go deleted file mode 100644 index 67afd661..00000000 --- a/pkg/metrics/gloo.go +++ /dev/null @@ -1,72 +0,0 @@ -package metrics - -import ( - "time" -) - -//envoy_cluster_name="test-podinfo-primary-9898_gloo-system" - -var glooQueries = map[string]string{ - "request-success-rate": ` - sum( - rate( - envoy_cluster_upstream_rq{ - envoy_cluster_name=~"{{ .Namespace }}-{{ .Name }}-canary-[0-9a-zA-Z-]+_[0-9a-zA-Z-]+", - envoy_response_code!~"5.*" - }[{{ .Interval }}] - ) - ) - / - sum( - rate( - envoy_cluster_upstream_rq{ - envoy_cluster_name=~"{{ .Namespace }}-{{ .Name }}-canary-[0-9a-zA-Z-]+_[0-9a-zA-Z-]+", - }[{{ .Interval }}] - ) - ) - * 100`, - "request-duration": ` - histogram_quantile( - 0.99, - sum( - rate( - envoy_cluster_upstream_rq_time_bucket{ - envoy_cluster_name=~"{{ .Namespace }}-{{ .Name }}-canary-[0-9a-zA-Z-]+_[0-9a-zA-Z-]+", - }[{{ .Interval }}] - ) - ) by (le) - )`, -} - -type GlooObserver struct { - client *PrometheusClient -} - -func (ob *GlooObserver) GetRequestSuccessRate(name string, namespace string, interval string) (float64, error) { - query, err := ob.client.RenderQuery(name, namespace, interval, glooQueries["request-success-rate"]) - if err != nil { - return 0, err - } - - value, err := ob.client.RunQuery(query) - if err != nil { - return 0, err - } - - return value, nil -} - -func (ob *GlooObserver) GetRequestDuration(name string, namespace string, interval string) (time.Duration, error) { - query, err := ob.client.RenderQuery(name, namespace, interval, glooQueries["request-duration"]) - if err != nil { - return 0, err - } - - value, err := ob.client.RunQuery(query) - if err != nil { - return 0, err - } - - ms := time.Duration(int64(value)) * time.Millisecond - return ms, nil -} diff --git a/pkg/metrics/gloo_test.go b/pkg/metrics/gloo_test.go deleted file mode 100644 index fd6ddeba..00000000 --- a/pkg/metrics/gloo_test.go +++ /dev/null @@ -1,74 +0,0 @@ -package metrics - -import ( - "net/http" - "net/http/httptest" - "testing" - "time" -) - -func TestGlooObserver_GetRequestSuccessRate(t *testing.T) { - expected := `sum(rate(envoy_cluster_upstream_rq{envoy_cluster_name=~"default-podinfo-canary-[0-9a-zA-Z-]+_[0-9a-zA-Z-]+",envoy_response_code!~"5.*"}[1m]))/sum(rate(envoy_cluster_upstream_rq{envoy_cluster_name=~"default-podinfo-canary-[0-9a-zA-Z-]+_[0-9a-zA-Z-]+",}[1m]))*100` - - ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { - promql := r.URL.Query()["query"][0] - if promql != expected { - t.Errorf("\nGot %s \nWanted %s", promql, expected) - } - - json := `{"status":"success","data":{"resultType":"vector","result":[{"metric":{},"value":[1,"100"]}]}}` - w.Write([]byte(json)) - })) - defer ts.Close() - - client, err := NewPrometheusClient(ts.URL, time.Second) - if err != nil { - t.Fatal(err) - } - - observer := &GlooObserver{ - client: client, - } - - val, err := observer.GetRequestSuccessRate("podinfo", "default", "1m") - if err != nil { - t.Fatal(err.Error()) - } - - if val != 100 { - t.Errorf("Got %v wanted %v", val, 100) - } -} - -func TestGlooObserver_GetRequestDuration(t *testing.T) { - expected := `histogram_quantile(0.99,sum(rate(envoy_cluster_upstream_rq_time_bucket{envoy_cluster_name=~"default-podinfo-canary-[0-9a-zA-Z-]+_[0-9a-zA-Z-]+",}[1m]))by(le))` - - ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { - promql := r.URL.Query()["query"][0] - if promql != expected { - t.Errorf("\nGot %s \nWanted %s", promql, expected) - } - - json := `{"status":"success","data":{"resultType":"vector","result":[{"metric":{},"value":[1,"100"]}]}}` - w.Write([]byte(json)) - })) - defer ts.Close() - - client, err := NewPrometheusClient(ts.URL, time.Second) - if err != nil { - t.Fatal(err) - } - - observer := &GlooObserver{ - client: client, - } - - val, err := observer.GetRequestDuration("podinfo", "default", "1m") - if err != nil { - t.Fatal(err.Error()) - } - - if val != 100*time.Millisecond { - t.Errorf("Got %v wanted %v", val, 100*time.Millisecond) - } -} diff --git a/pkg/metrics/http.go b/pkg/metrics/http.go deleted file mode 100644 index 9c169e44..00000000 --- a/pkg/metrics/http.go +++ /dev/null @@ -1,71 +0,0 @@ -package metrics - -import "time" - -var httpQueries = map[string]string{ - "request-success-rate": ` - sum( - rate( - http_request_duration_seconds_count{ - kubernetes_namespace="{{ .Namespace }}", - kubernetes_pod_name=~"{{ .Name }}-[0-9a-zA-Z]+(-[0-9a-zA-Z]+)", - status!~"5.*" - }[{{ .Interval }}] - ) - ) - / - sum( - rate( - http_request_duration_seconds_count{ - kubernetes_namespace="{{ .Namespace }}", - kubernetes_pod_name=~"{{ .Name }}-[0-9a-zA-Z]+(-[0-9a-zA-Z]+)" - }[{{ .Interval }}] - ) - ) - * 100`, - "request-duration": ` - histogram_quantile( - 0.99, - sum( - rate( - http_request_duration_seconds_bucket{ - kubernetes_namespace="{{ .Namespace }}", - kubernetes_pod_name=~"{{ .Name }}-[0-9a-zA-Z]+(-[0-9a-zA-Z]+)" - }[{{ .Interval }}] - ) - ) by (le) - )`, -} - -type HttpObserver struct { - client *PrometheusClient -} - -func (ob *HttpObserver) GetRequestSuccessRate(name string, namespace string, interval string) (float64, error) { - query, err := ob.client.RenderQuery(name, namespace, interval, httpQueries["request-success-rate"]) - if err != nil { - return 0, err - } - - value, err := ob.client.RunQuery(query) - if err != nil { - return 0, err - } - - return value, nil -} - -func (ob *HttpObserver) GetRequestDuration(name string, namespace string, interval string) (time.Duration, error) { - query, err := ob.client.RenderQuery(name, namespace, interval, httpQueries["request-duration"]) - if err != nil { - return 0, err - } - - value, err := ob.client.RunQuery(query) - if err != nil { - return 0, err - } - - ms := time.Duration(int64(value*1000)) * time.Millisecond - return ms, nil -} diff --git a/pkg/metrics/http_test.go b/pkg/metrics/http_test.go deleted file mode 100644 index a0d31cd7..00000000 --- a/pkg/metrics/http_test.go +++ /dev/null @@ -1,74 +0,0 @@ -package metrics - -import ( - "net/http" - "net/http/httptest" - "testing" - "time" -) - -func TestHttpObserver_GetRequestSuccessRate(t *testing.T) { - expected := `sum(rate(http_request_duration_seconds_count{kubernetes_namespace="default",kubernetes_pod_name=~"podinfo-[0-9a-zA-Z]+(-[0-9a-zA-Z]+)",status!~"5.*"}[1m]))/sum(rate(http_request_duration_seconds_count{kubernetes_namespace="default",kubernetes_pod_name=~"podinfo-[0-9a-zA-Z]+(-[0-9a-zA-Z]+)"}[1m]))*100` - - ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { - promql := r.URL.Query()["query"][0] - if promql != expected { - t.Errorf("\nGot %s \nWanted %s", promql, expected) - } - - json := `{"status":"success","data":{"resultType":"vector","result":[{"metric":{},"value":[1,"100"]}]}}` - w.Write([]byte(json)) - })) - defer ts.Close() - - client, err := NewPrometheusClient(ts.URL, time.Second) - if err != nil { - t.Fatal(err) - } - - observer := &HttpObserver{ - client: client, - } - - val, err := observer.GetRequestSuccessRate("podinfo", "default", "1m") - if err != nil { - t.Fatal(err.Error()) - } - - if val != 100 { - t.Errorf("Got %v wanted %v", val, 100) - } -} - -func TestHttpObserver_GetRequestDuration(t *testing.T) { - expected := `histogram_quantile(0.99,sum(rate(http_request_duration_seconds_bucket{kubernetes_namespace="default",kubernetes_pod_name=~"podinfo-[0-9a-zA-Z]+(-[0-9a-zA-Z]+)"}[1m]))by(le))` - - ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { - promql := r.URL.Query()["query"][0] - if promql != expected { - t.Errorf("\nGot %s \nWanted %s", promql, expected) - } - - json := `{"status":"success","data":{"resultType":"vector","result":[{"metric":{},"value":[1,"0.100"]}]}}` - w.Write([]byte(json)) - })) - defer ts.Close() - - client, err := NewPrometheusClient(ts.URL, time.Second) - if err != nil { - t.Fatal(err) - } - - observer := &HttpObserver{ - client: client, - } - - val, err := observer.GetRequestDuration("podinfo", "default", "1m") - if err != nil { - t.Fatal(err.Error()) - } - - if val != 100*time.Millisecond { - t.Errorf("Got %v wanted %v", val, 100*time.Millisecond) - } -} diff --git a/pkg/metrics/istio.go b/pkg/metrics/istio.go deleted file mode 100644 index f6894d05..00000000 --- a/pkg/metrics/istio.go +++ /dev/null @@ -1,76 +0,0 @@ -package metrics - -import ( - "time" -) - -var istioQueries = map[string]string{ - "request-success-rate": ` - sum( - rate( - istio_requests_total{ - reporter="destination", - destination_workload_namespace="{{ .Namespace }}", - destination_workload=~"{{ .Name }}", - response_code!~"5.*" - }[{{ .Interval }}] - ) - ) - / - sum( - rate( - istio_requests_total{ - reporter="destination", - destination_workload_namespace="{{ .Namespace }}", - destination_workload=~"{{ .Name }}" - }[{{ .Interval }}] - ) - ) - * 100`, - "request-duration": ` - histogram_quantile( - 0.99, - sum( - rate( - istio_request_duration_seconds_bucket{ - reporter="destination", - destination_workload_namespace="{{ .Namespace }}", - destination_workload=~"{{ .Name }}" - }[{{ .Interval }}] - ) - ) by (le) - )`, -} - -type IstioObserver struct { - client *PrometheusClient -} - -func (ob *IstioObserver) GetRequestSuccessRate(name string, namespace string, interval string) (float64, error) { - query, err := ob.client.RenderQuery(name, namespace, interval, istioQueries["request-success-rate"]) - if err != nil { - return 0, err - } - - value, err := ob.client.RunQuery(query) - if err != nil { - return 0, err - } - - return value, nil -} - -func (ob *IstioObserver) GetRequestDuration(name string, namespace string, interval string) (time.Duration, error) { - query, err := ob.client.RenderQuery(name, namespace, interval, istioQueries["request-duration"]) - if err != nil { - return 0, err - } - - value, err := ob.client.RunQuery(query) - if err != nil { - return 0, err - } - - ms := time.Duration(int64(value*1000)) * time.Millisecond - return ms, nil -} diff --git a/pkg/metrics/istio_test.go b/pkg/metrics/istio_test.go deleted file mode 100644 index 2eb7f5d2..00000000 --- a/pkg/metrics/istio_test.go +++ /dev/null @@ -1,74 +0,0 @@ -package metrics - -import ( - "net/http" - "net/http/httptest" - "testing" - "time" -) - -func TestIstioObserver_GetRequestSuccessRate(t *testing.T) { - expected := `sum(rate(istio_requests_total{reporter="destination",destination_workload_namespace="default",destination_workload=~"podinfo",response_code!~"5.*"}[1m]))/sum(rate(istio_requests_total{reporter="destination",destination_workload_namespace="default",destination_workload=~"podinfo"}[1m]))*100` - - ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { - promql := r.URL.Query()["query"][0] - if promql != expected { - t.Errorf("\nGot %s \nWanted %s", promql, expected) - } - - json := `{"status":"success","data":{"resultType":"vector","result":[{"metric":{},"value":[1,"100"]}]}}` - w.Write([]byte(json)) - })) - defer ts.Close() - - client, err := NewPrometheusClient(ts.URL, time.Second) - if err != nil { - t.Fatal(err) - } - - observer := &IstioObserver{ - client: client, - } - - val, err := observer.GetRequestSuccessRate("podinfo", "default", "1m") - if err != nil { - t.Fatal(err.Error()) - } - - if val != 100 { - t.Errorf("Got %v wanted %v", val, 100) - } -} - -func TestIstioObserver_GetRequestDuration(t *testing.T) { - expected := `histogram_quantile(0.99,sum(rate(istio_request_duration_seconds_bucket{reporter="destination",destination_workload_namespace="default",destination_workload=~"podinfo"}[1m]))by(le))` - - ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { - promql := r.URL.Query()["query"][0] - if promql != expected { - t.Errorf("\nGot %s \nWanted %s", promql, expected) - } - - json := `{"status":"success","data":{"resultType":"vector","result":[{"metric":{},"value":[1,"0.100"]}]}}` - w.Write([]byte(json)) - })) - defer ts.Close() - - client, err := NewPrometheusClient(ts.URL, time.Second) - if err != nil { - t.Fatal(err) - } - - observer := &IstioObserver{ - client: client, - } - - val, err := observer.GetRequestDuration("podinfo", "default", "1m") - if err != nil { - t.Fatal(err.Error()) - } - - if val != 100*time.Millisecond { - t.Errorf("Got %v wanted %v", val, 100*time.Millisecond) - } -} diff --git a/pkg/metrics/linkerd.go b/pkg/metrics/linkerd.go deleted file mode 100644 index f4d5707a..00000000 --- a/pkg/metrics/linkerd.go +++ /dev/null @@ -1,76 +0,0 @@ -package metrics - -import ( - "time" -) - -var linkerdQueries = map[string]string{ - "request-success-rate": ` - sum( - rate( - response_total{ - namespace="{{ .Namespace }}", - deployment=~"{{ .Name }}", - classification!="failure", - direction="inbound" - }[{{ .Interval }}] - ) - ) - / - sum( - rate( - response_total{ - namespace="{{ .Namespace }}", - deployment=~"{{ .Name }}", - direction="inbound" - }[{{ .Interval }}] - ) - ) - * 100`, - "request-duration": ` - histogram_quantile( - 0.99, - sum( - rate( - response_latency_ms_bucket{ - namespace="{{ .Namespace }}", - deployment=~"{{ .Name }}", - direction="inbound" - }[{{ .Interval }}] - ) - ) by (le) - )`, -} - -type LinkerdObserver struct { - client *PrometheusClient -} - -func (ob *LinkerdObserver) GetRequestSuccessRate(name string, namespace string, interval string) (float64, error) { - query, err := ob.client.RenderQuery(name, namespace, interval, linkerdQueries["request-success-rate"]) - if err != nil { - return 0, err - } - - value, err := ob.client.RunQuery(query) - if err != nil { - return 0, err - } - - return value, nil -} - -func (ob *LinkerdObserver) GetRequestDuration(name string, namespace string, interval string) (time.Duration, error) { - query, err := ob.client.RenderQuery(name, namespace, interval, linkerdQueries["request-duration"]) - if err != nil { - return 0, err - } - - value, err := ob.client.RunQuery(query) - if err != nil { - return 0, err - } - - ms := time.Duration(int64(value)) * time.Millisecond - return ms, nil -} diff --git a/pkg/metrics/linkerd_test.go b/pkg/metrics/linkerd_test.go deleted file mode 100644 index 82b62dd5..00000000 --- a/pkg/metrics/linkerd_test.go +++ /dev/null @@ -1,74 +0,0 @@ -package metrics - -import ( - "net/http" - "net/http/httptest" - "testing" - "time" -) - -func TestLinkerdObserver_GetRequestSuccessRate(t *testing.T) { - expected := `sum(rate(response_total{namespace="default",deployment=~"podinfo",classification!="failure",direction="inbound"}[1m]))/sum(rate(response_total{namespace="default",deployment=~"podinfo",direction="inbound"}[1m]))*100` - - ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { - promql := r.URL.Query()["query"][0] - if promql != expected { - t.Errorf("\nGot %s \nWanted %s", promql, expected) - } - - json := `{"status":"success","data":{"resultType":"vector","result":[{"metric":{},"value":[1,"100"]}]}}` - w.Write([]byte(json)) - })) - defer ts.Close() - - client, err := NewPrometheusClient(ts.URL, time.Second) - if err != nil { - t.Fatal(err) - } - - observer := &LinkerdObserver{ - client: client, - } - - val, err := observer.GetRequestSuccessRate("podinfo", "default", "1m") - if err != nil { - t.Fatal(err.Error()) - } - - if val != 100 { - t.Errorf("Got %v wanted %v", val, 100) - } -} - -func TestLinkerdObserver_GetRequestDuration(t *testing.T) { - expected := `histogram_quantile(0.99,sum(rate(response_latency_ms_bucket{namespace="default",deployment=~"podinfo",direction="inbound"}[1m]))by(le))` - - ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { - promql := r.URL.Query()["query"][0] - if promql != expected { - t.Errorf("\nGot %s \nWanted %s", promql, expected) - } - - json := `{"status":"success","data":{"resultType":"vector","result":[{"metric":{},"value":[1,"100"]}]}}` - w.Write([]byte(json)) - })) - defer ts.Close() - - client, err := NewPrometheusClient(ts.URL, time.Second) - if err != nil { - t.Fatal(err) - } - - observer := &LinkerdObserver{ - client: client, - } - - val, err := observer.GetRequestDuration("podinfo", "default", "1m") - if err != nil { - t.Fatal(err.Error()) - } - - if val != 100*time.Millisecond { - t.Errorf("Got %v wanted %v", val, 100*time.Millisecond) - } -} diff --git a/pkg/metrics/nginx.go b/pkg/metrics/nginx.go deleted file mode 100644 index 593d2306..00000000 --- a/pkg/metrics/nginx.go +++ /dev/null @@ -1,80 +0,0 @@ -package metrics - -import ( - "time" -) - -var nginxQueries = map[string]string{ - "request-success-rate": ` - sum( - rate( - nginx_ingress_controller_requests{ - namespace="{{ .Namespace }}", - ingress="{{ .Name }}", - status!~"5.*" - }[{{ .Interval }}] - ) - ) - / - sum( - rate( - nginx_ingress_controller_requests{ - namespace="{{ .Namespace }}", - ingress="{{ .Name }}" - }[{{ .Interval }}] - ) - ) - * 100`, - "request-duration": ` - sum( - rate( - nginx_ingress_controller_ingress_upstream_latency_seconds_sum{ - namespace="{{ .Namespace }}", - ingress="{{ .Name }}" - }[{{ .Interval }}] - ) - ) - / - sum( - rate( - nginx_ingress_controller_ingress_upstream_latency_seconds_count{ - namespace="{{ .Namespace }}", - ingress="{{ .Name }}" - }[{{ .Interval }}] - ) - ) - * 1000`, -} - -type NginxObserver struct { - client *PrometheusClient -} - -func (ob *NginxObserver) GetRequestSuccessRate(name string, namespace string, interval string) (float64, error) { - query, err := ob.client.RenderQuery(name, namespace, interval, nginxQueries["request-success-rate"]) - if err != nil { - return 0, err - } - - value, err := ob.client.RunQuery(query) - if err != nil { - return 0, err - } - - return value, nil -} - -func (ob *NginxObserver) GetRequestDuration(name string, namespace string, interval string) (time.Duration, error) { - query, err := ob.client.RenderQuery(name, namespace, interval, nginxQueries["request-duration"]) - if err != nil { - return 0, err - } - - value, err := ob.client.RunQuery(query) - if err != nil { - return 0, err - } - - ms := time.Duration(int64(value)) * time.Millisecond - return ms, nil -} diff --git a/pkg/metrics/nginx_test.go b/pkg/metrics/nginx_test.go deleted file mode 100644 index 30b51e31..00000000 --- a/pkg/metrics/nginx_test.go +++ /dev/null @@ -1,74 +0,0 @@ -package metrics - -import ( - "net/http" - "net/http/httptest" - "testing" - "time" -) - -func TestNginxObserver_GetRequestSuccessRate(t *testing.T) { - expected := `sum(rate(nginx_ingress_controller_requests{namespace="nginx",ingress="podinfo",status!~"5.*"}[1m]))/sum(rate(nginx_ingress_controller_requests{namespace="nginx",ingress="podinfo"}[1m]))*100` - - ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { - promql := r.URL.Query()["query"][0] - if promql != expected { - t.Errorf("\nGot %s \nWanted %s", promql, expected) - } - - json := `{"status":"success","data":{"resultType":"vector","result":[{"metric":{},"value":[1,"100"]}]}}` - w.Write([]byte(json)) - })) - defer ts.Close() - - client, err := NewPrometheusClient(ts.URL, time.Second) - if err != nil { - t.Fatal(err) - } - - observer := &NginxObserver{ - client: client, - } - - val, err := observer.GetRequestSuccessRate("podinfo", "nginx", "1m") - if err != nil { - t.Fatal(err.Error()) - } - - if val != 100 { - t.Errorf("Got %v wanted %v", val, 100) - } -} - -func TestNginxObserver_GetRequestDuration(t *testing.T) { - expected := `sum(rate(nginx_ingress_controller_ingress_upstream_latency_seconds_sum{namespace="nginx",ingress="podinfo"}[1m]))/sum(rate(nginx_ingress_controller_ingress_upstream_latency_seconds_count{namespace="nginx",ingress="podinfo"}[1m]))*1000` - - ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { - promql := r.URL.Query()["query"][0] - if promql != expected { - t.Errorf("\nGot %s \nWanted %s", promql, expected) - } - - json := `{"status":"success","data":{"resultType":"vector","result":[{"metric":{},"value":[1,"100"]}]}}` - w.Write([]byte(json)) - })) - defer ts.Close() - - client, err := NewPrometheusClient(ts.URL, time.Second) - if err != nil { - t.Fatal(err) - } - - observer := &NginxObserver{ - client: client, - } - - val, err := observer.GetRequestDuration("podinfo", "nginx", "1m") - if err != nil { - t.Fatal(err.Error()) - } - - if val != 100*time.Millisecond { - t.Errorf("Got %v wanted %v", val, 100*time.Millisecond) - } -} diff --git a/pkg/metrics/observer.go b/pkg/metrics/observer.go deleted file mode 100644 index 906285e7..00000000 --- a/pkg/metrics/observer.go +++ /dev/null @@ -1,10 +0,0 @@ -package metrics - -import ( - "time" -) - -type Interface interface { - GetRequestSuccessRate(name string, namespace string, interval string) (float64, error) - GetRequestDuration(name string, namespace string, interval string) (time.Duration, error) -} diff --git a/pkg/metrics/recorder.go b/pkg/metrics/recorder.go deleted file mode 100644 index d798bc65..00000000 --- a/pkg/metrics/recorder.go +++ /dev/null @@ -1,104 +0,0 @@ -package metrics - -import ( - "fmt" - "time" - - "github.com/prometheus/client_golang/prometheus" - flaggerv1 "github.com/weaveworks/flagger/pkg/apis/flagger/v1alpha3" -) - -// Recorder records the canary analysis as Prometheus metrics -type Recorder struct { - info *prometheus.GaugeVec - duration *prometheus.HistogramVec - total *prometheus.GaugeVec - status *prometheus.GaugeVec - weight *prometheus.GaugeVec -} - -// NewRecorder creates a new recorder and registers the Prometheus metrics -func NewRecorder(controller string, register bool) Recorder { - info := prometheus.NewGaugeVec(prometheus.GaugeOpts{ - Subsystem: controller, - Name: "info", - Help: "Flagger version and mesh provider information", - }, []string{"version", "mesh_provider"}) - - duration := prometheus.NewHistogramVec(prometheus.HistogramOpts{ - Subsystem: controller, - Name: "canary_duration_seconds", - Help: "Seconds spent performing canary analysis.", - Buckets: prometheus.DefBuckets, - }, []string{"name", "namespace"}) - - total := prometheus.NewGaugeVec(prometheus.GaugeOpts{ - Subsystem: controller, - Name: "canary_total", - Help: "Total number of canary object", - }, []string{"namespace"}) - - // 0 - running, 1 - successful, 2 - failed - status := prometheus.NewGaugeVec(prometheus.GaugeOpts{ - Subsystem: controller, - Name: "canary_status", - Help: "Last canary analysis result", - }, []string{"name", "namespace"}) - - weight := prometheus.NewGaugeVec(prometheus.GaugeOpts{ - Subsystem: controller, - Name: "canary_weight", - Help: "The virtual service destination weight current value", - }, []string{"workload", "namespace"}) - - if register { - prometheus.MustRegister(info) - prometheus.MustRegister(duration) - prometheus.MustRegister(total) - prometheus.MustRegister(status) - prometheus.MustRegister(weight) - } - - return Recorder{ - info: info, - duration: duration, - total: total, - status: status, - weight: weight, - } -} - -// SetInfo sets the version and mesh provider labels -func (cr *Recorder) SetInfo(version string, meshProvider string) { - cr.info.WithLabelValues(version, meshProvider).Set(1) -} - -// SetDuration sets the time spent in seconds performing canary analysis -func (cr *Recorder) SetDuration(cd *flaggerv1.Canary, duration time.Duration) { - cr.duration.WithLabelValues(cd.Spec.TargetRef.Name, cd.Namespace).Observe(duration.Seconds()) -} - -// SetTotal sets the total number of canaries per namespace -func (cr *Recorder) SetTotal(namespace string, total int) { - cr.total.WithLabelValues(namespace).Set(float64(total)) -} - -// SetStatus sets the last known canary analysis status -func (cr *Recorder) SetStatus(cd *flaggerv1.Canary, phase flaggerv1.CanaryPhase) { - status := 1 - switch phase { - case flaggerv1.CanaryPhaseProgressing: - status = 0 - case flaggerv1.CanaryPhaseFailed: - status = 2 - default: - status = 1 - } - cr.status.WithLabelValues(cd.Spec.TargetRef.Name, cd.Namespace).Set(float64(status)) -} - -// SetWeight sets the weight values for primary and canary destinations -func (cr *Recorder) SetWeight(cd *flaggerv1.Canary, primary int, canary int) { - cr.weight.WithLabelValues(fmt.Sprintf("%s-primary", cd.Spec.TargetRef.Name), cd.Namespace).Set(float64(primary)) - cr.weight.WithLabelValues(cd.Spec.TargetRef.Name, cd.Namespace).Set(float64(canary)) -} diff --git a/pkg/notifier/client.go b/pkg/notifier/client.go deleted file mode 100644 index 72d0b1bd..00000000 --- a/pkg/notifier/client.go +++ /dev/null @@ -1,43 +0,0 @@ -package notifier - -import ( - "bytes" - "context" - "encoding/json" - "fmt" - "io/ioutil" - "net/http" - "time" -) - -func postMessage(address string, payload interface{}) error { - data, err := json.Marshal(payload) - if err != nil { - return fmt.Errorf("marshalling notification payload failed %v", err) - } - - b := bytes.NewBuffer(data) - - req, err := http.NewRequest("POST", address, b) - if err != nil { - return err - } - req.Header.Set("Content-type", "application/json") - - ctx, cancel := context.WithTimeout(req.Context(), 5*time.Second) - defer cancel() - - res, err := http.DefaultClient.Do(req.WithContext(ctx)) - if err != nil { - return fmt.Errorf("sending notification failed %v", err) - } - - defer res.Body.Close() - statusCode := res.StatusCode - if statusCode != 200 { - body, _ := ioutil.ReadAll(res.Body) - return fmt.Errorf("sending notification failed %v", string(body)) - } - - return nil -} diff --git a/pkg/notifier/client_test.go b/pkg/notifier/client_test.go deleted file mode 100644 index 3695f087..00000000 --- a/pkg/notifier/client_test.go +++ /dev/null @@ -1,30 +0,0 @@ -package notifier - -import ( - "encoding/json" - "io/ioutil" - "net/http" - "net/http/httptest" - "testing" -) - -func Test_postMessage(t *testing.T) { - ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { - b, err := ioutil.ReadAll(r.Body) - if err != nil { - t.Fatal(err) - } - var payload = make(map[string]string) - err = json.Unmarshal(b, &payload) - - if payload["status"] != "success" { - t.Fatal("wrong payload") - } - })) - defer ts.Close() - - err := postMessage(ts.URL, map[string]string{"status": "success"}) - if err != nil { - t.Fatal(err) - } -} diff --git a/pkg/notifier/factory.go b/pkg/notifier/factory.go deleted file mode 100644 index d7ef2ec1..00000000 --- a/pkg/notifier/factory.go +++ /dev/null @@ -1,26 +0,0 @@ -package notifier - -type Factory struct { - URL string - Username string - Channel string -} - -func NewFactory(URL string, username string, channel string) *Factory { - return &Factory{ - URL: URL, - Channel: channel, - Username: username, - } -} - -func (f Factory) Notifier(provider string) (Interface, error) { - switch { - case provider == "slack": - return NewSlack(f.URL, f.Username, f.Channel) - case provider == "msteams": - return NewMSTeams(f.URL) - } - - return nil, nil -} diff --git a/pkg/notifier/notifier.go b/pkg/notifier/notifier.go deleted file mode 100644 index c014b030..00000000 --- a/pkg/notifier/notifier.go +++ /dev/null @@ -1,10 +0,0 @@ -package notifier - -type Interface interface { - Post(workload string, namespace string, message string, fields []Field, warn bool) error -} - -type Field struct { - Name string - Value string -} diff --git a/pkg/notifier/slack.go b/pkg/notifier/slack.go deleted file mode 100644 index 1ca4f9d2..00000000 --- a/pkg/notifier/slack.go +++ /dev/null @@ -1,98 +0,0 @@ -package notifier - -import ( - "errors" - "fmt" - "net/url" -) - -// Slack holds the hook URL -type Slack struct { - URL string - Username string - Channel string - IconEmoji string -} - -// SlackPayload holds the channel and attachments -type SlackPayload struct { - Channel string `json:"channel"` - Username string `json:"username"` - IconUrl string `json:"icon_url"` - IconEmoji string `json:"icon_emoji"` - Text string `json:"text,omitempty"` - Attachments []SlackAttachment `json:"attachments,omitempty"` -} - -// SlackAttachment holds the markdown message body -type SlackAttachment struct { - Color string `json:"color"` - AuthorName string `json:"author_name"` - Text string `json:"text"` - MrkdwnIn []string `json:"mrkdwn_in"` - Fields []SlackField `json:"fields"` -} - -type SlackField struct { - Title string `json:"title"` - Value string `json:"value"` - Short bool `json:"short"` -} - -// NewSlack validates the Slack URL and returns a Slack object -func NewSlack(hookURL string, username string, channel string) (*Slack, error) { - _, err := url.ParseRequestURI(hookURL) - if err != nil { - return nil, fmt.Errorf("invalid Slack hook URL %s", hookURL) - } - - if username == "" { - return nil, errors.New("empty Slack username") - } - - if channel == "" { - return nil, errors.New("empty Slack channel") - } - - return &Slack{ - Channel: channel, - URL: hookURL, - Username: username, - IconEmoji: ":rocket:", - }, nil -} - -// Post Slack message -func (s *Slack) Post(workload string, namespace string, message string, fields []Field, warn bool) error { - payload := SlackPayload{ - Channel: s.Channel, - Username: s.Username, - } - - color := "good" - if warn { - color = "danger" - } - - sfields := make([]SlackField, len(fields)) - for _, f := range fields { - sfields = append(sfields, SlackField{f.Name, f.Value, false}) - } - - a := SlackAttachment{ - Color: color, - AuthorName: fmt.Sprintf("%s.%s", workload, namespace), - Text: message, - MrkdwnIn: []string{"text"}, - Fields: sfields, - } - - payload.Attachments = []SlackAttachment{a} - - err := postMessage(s.URL, payload) - if err != nil { - return err - } - - return nil -} diff --git a/pkg/notifier/slack_test.go b/pkg/notifier/slack_test.go deleted file mode 100644 index e0bb976a..00000000 --- a/pkg/notifier/slack_test.go +++ /dev/null @@ -1,35 +0,0 @@ -package notifier - -import ( - "encoding/json" - "io/ioutil" - "net/http" - "net/http/httptest" - "testing" -) - -func TestSlack_Post(t *testing.T) { - ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { - b, err := ioutil.ReadAll(r.Body) - if err != nil { - t.Fatal(err) - } - var payload = SlackPayload{} - err = json.Unmarshal(b, &payload) - - if payload.Attachments[0].AuthorName != "podinfo.test" { - t.Fatal("wrong author name") - } - })) - defer ts.Close() - - slack, err := NewSlack(ts.URL, "test", "test") - if err != nil { - t.Fatal(err) - } - - err = slack.Post("podinfo", "test", "test", nil, true) - if err != nil { - t.Fatal(err) - } -} diff --git a/pkg/notifier/teams.go b/pkg/notifier/teams.go deleted file mode 100644 index e463b908..00000000 --- a/pkg/notifier/teams.go +++ /dev/null @@ -1,77 +0,0 @@ -package notifier - -import ( - "fmt" - "net/url" -) - -// MS Teams holds the incoming webhook URL -type MSTeams struct { - URL string -} - -// MSTeamsPayload holds the message card data -type MSTeamsPayload struct { - Type string `json:"@type"` - Context string `json:"@context"` - ThemeColor string `json:"themeColor"` - Summary string `json:"summary"` - Sections []MSTeamsSection `json:"sections"` -} - -// MSTeamsSection holds the canary analysis result -type MSTeamsSection struct { - ActivityTitle string `json:"activityTitle"` - ActivitySubtitle string `json:"activitySubtitle"` - Facts []MSTeamsField `json:"facts"` -} - -type MSTeamsField struct { - Name string `json:"name"` - Value string `json:"value"` -} - -// NewMSTeams validates the MS Teams URL and returns a MSTeams object -func NewMSTeams(hookURL string) (*MSTeams, error) { - _, err := url.ParseRequestURI(hookURL) - if err != nil { - return nil, fmt.Errorf("invalid MS Teams webhook URL %s", hookURL) - } - - return &MSTeams{ - URL: hookURL, - }, nil -} - -// Post MS Teams message -func (s *MSTeams) Post(workload string, namespace string, message string, fields []Field, warn bool) error { - facts := make([]MSTeamsField, len(fields)) - for _, f := range fields { - facts = append(facts, MSTeamsField{f.Name, f.Value}) - } - - payload := MSTeamsPayload{ - Type: "MessageCard", - Context: "http://schema.org/extensions", - ThemeColor: "0076D7", - Summary: fmt.Sprintf("%s.%s", workload, namespace), - Sections: []MSTeamsSection{ - { - ActivityTitle: message, - ActivitySubtitle: fmt.Sprintf("%s.%s", workload, namespace), - Facts: facts, - }, - }, - } - - if warn { - payload.ThemeColor = "FF0000" - } - - err := postMessage(s.URL, payload) - if err != nil { - return err - } - - return nil -} diff --git a/pkg/notifier/teams_test.go b/pkg/notifier/teams_test.go deleted file mode 100644 index 70799b12..00000000 --- a/pkg/notifier/teams_test.go +++ /dev/null @@ -1,35 +0,0 @@ -package notifier - -import ( - "encoding/json" - "io/ioutil" - "net/http" - "net/http/httptest" - "testing" -) - -func TestTeams_Post(t *testing.T) { - ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { - b, err := ioutil.ReadAll(r.Body) - if err != nil { - t.Fatal(err) - } - var payload = MSTeamsPayload{} - err = json.Unmarshal(b, &payload) - - if payload.Sections[0].ActivitySubtitle != "podinfo.test" { - t.Fatal("wrong activity subtitle") - } - })) - defer ts.Close() - - teams, err := NewMSTeams(ts.URL) - if err != nil { - t.Fatal(err) - } - - err = teams.Post("podinfo", "test", "test", nil, true) - if err != nil { - t.Fatal(err) - } -} diff --git a/pkg/router/appmesh.go b/pkg/router/appmesh.go deleted file mode 100644 index c8b36ae0..00000000 --- a/pkg/router/appmesh.go +++ /dev/null @@ -1,328 +0,0 @@ -package router - -import ( - "fmt" - "github.com/google/go-cmp/cmp" - "github.com/google/go-cmp/cmp/cmpopts" - AppmeshV1beta1 "github.com/weaveworks/flagger/pkg/apis/appmesh/v1beta1" - flaggerv1 "github.com/weaveworks/flagger/pkg/apis/flagger/v1alpha3" - clientset "github.com/weaveworks/flagger/pkg/client/clientset/versioned" - "go.uber.org/zap" - "k8s.io/apimachinery/pkg/api/errors" - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - "k8s.io/apimachinery/pkg/runtime/schema" - "k8s.io/client-go/kubernetes" -) - -// AppMeshRouter is managing AppMesh virtual services -type AppMeshRouter struct { - kubeClient kubernetes.Interface - appmeshClient clientset.Interface - flaggerClient clientset.Interface - logger *zap.SugaredLogger -} - -// Reconcile creates or updates App Mesh virtual nodes and virtual services -func (ar *AppMeshRouter) Reconcile(canary *flaggerv1.Canary) error { - if canary.Spec.Service.MeshName == "" { - return fmt.Errorf("mesh name cannot be empty") - } - - targetName := canary.Spec.TargetRef.Name - targetHost := fmt.Sprintf("%s.%s", targetName, canary.Namespace) - primaryName := fmt.Sprintf("%s-primary", targetName) - primaryHost := fmt.Sprintf("%s.%s", primaryName, canary.Namespace) - canaryName := fmt.Sprintf("%s-canary", targetName) - canaryHost := fmt.Sprintf("%s.%s", canaryName, canary.Namespace) - - // sync virtual node e.g. app-namespace - // DNS app.namespace - err := ar.reconcileVirtualNode(canary, targetName, primaryHost) - if err != nil { - return err - } - - // sync virtual node e.g. app-primary-namespace - // DNS app-primary.namespace - err = ar.reconcileVirtualNode(canary, primaryName, primaryHost) - if err != nil { - return err - } - - // sync virtual node e.g. app-canary-namespace - // DNS app-canary.namespace - err = ar.reconcileVirtualNode(canary, canaryName, canaryHost) - if err != nil { - return err - } - - // sync virtual service e.g. app.namespace - // DNS app.namespace - err = ar.reconcileVirtualService(canary, targetHost) - if err != nil { - return err - } - - return nil -} - -// reconcileVirtualNode creates or updates a virtual node -// the virtual node naming format is name-role-namespace -func (ar *AppMeshRouter) reconcileVirtualNode(canary *flaggerv1.Canary, name string, host string) error { - vnSpec := AppmeshV1beta1.VirtualNodeSpec{ - MeshName: canary.Spec.Service.MeshName, - Listeners: []AppmeshV1beta1.Listener{ - { - PortMapping: AppmeshV1beta1.PortMapping{ - Port: int64(canary.Spec.Service.Port), - Protocol: "http", - }, - }, - }, - ServiceDiscovery: &AppmeshV1beta1.ServiceDiscovery{ - Dns: &AppmeshV1beta1.DnsServiceDiscovery{ - HostName: host, - }, - }, - } - - backends := []AppmeshV1beta1.Backend{} - for _, b := range canary.Spec.Service.Backends { - backend := AppmeshV1beta1.Backend{ - VirtualService: AppmeshV1beta1.VirtualServiceBackend{ - VirtualServiceName: b, - }, - } - backends = append(backends, backend) - } - if len(backends) > 0 { - vnSpec.Backends = backends - } - - virtualnode, err := ar.appmeshClient.AppmeshV1beta1().VirtualNodes(canary.Namespace).Get(name, metav1.GetOptions{}) - - // create virtual node - if errors.IsNotFound(err) { - virtualnode = &AppmeshV1beta1.VirtualNode{ - ObjectMeta: metav1.ObjectMeta{ - Name: name, - Namespace: canary.Namespace, - OwnerReferences: []metav1.OwnerReference{ - *metav1.NewControllerRef(canary, schema.GroupVersionKind{ - Group: flaggerv1.SchemeGroupVersion.Group, - Version: flaggerv1.SchemeGroupVersion.Version, - Kind: flaggerv1.CanaryKind, - }), - }, - }, - Spec: vnSpec, - } - _, err = ar.appmeshClient.AppmeshV1beta1().VirtualNodes(canary.Namespace).Create(virtualnode) - if err != nil { - return fmt.Errorf("VirtualNode %s.%s create error %v", name, canary.Namespace, err) - } - ar.logger.With("canary", fmt.Sprintf("%s.%s", canary.Name, canary.Namespace)). - Infof("VirtualNode %s.%s created", virtualnode.GetName(), canary.Namespace) - return nil - } - - if err != nil { - return fmt.Errorf("VirtualNode %s query error %v", name, err) - } - - // update virtual node - if virtualnode != nil { - if diff := cmp.Diff(vnSpec, virtualnode.Spec); diff != "" { - vnClone := virtualnode.DeepCopy() - vnClone.Spec = vnSpec - _, err = ar.appmeshClient.AppmeshV1beta1().VirtualNodes(canary.Namespace).Update(vnClone) - if err != nil { - return fmt.Errorf("VirtualNode %s update error %v", name, err) - } - ar.logger.With("canary", fmt.Sprintf("%s.%s", canary.Name, canary.Namespace)). - Infof("VirtualNode %s updated", virtualnode.GetName()) - } - } - - return nil -} - -// reconcileVirtualService creates or updates a virtual service -func (ar *AppMeshRouter) reconcileVirtualService(canary *flaggerv1.Canary, name string) error { - targetName := canary.Spec.TargetRef.Name - canaryVirtualNode := fmt.Sprintf("%s-canary", targetName) - primaryVirtualNode := fmt.Sprintf("%s-primary", targetName) - - // App Mesh supports only URI prefix - routePrefix := "/" - if len(canary.Spec.Service.Match) > 0 && - canary.Spec.Service.Match[0].Uri != nil && - canary.Spec.Service.Match[0].Uri.Prefix != "" { - routePrefix = canary.Spec.Service.Match[0].Uri.Prefix - } - - vsSpec := AppmeshV1beta1.VirtualServiceSpec{ - MeshName: canary.Spec.Service.MeshName, - VirtualRouter: &AppmeshV1beta1.VirtualRouter{ - Name: fmt.Sprintf("%s-router", targetName), - Listeners: []AppmeshV1beta1.Listener{ - { - PortMapping: AppmeshV1beta1.PortMapping{ - Port: int64(canary.Spec.Service.Port), - Protocol: "http", - }, - }, - }, - }, - Routes: []AppmeshV1beta1.Route{ - { - Name: fmt.Sprintf("%s-route", targetName), - Http: &AppmeshV1beta1.HttpRoute{ - Match: AppmeshV1beta1.HttpRouteMatch{ - Prefix: routePrefix, - }, - Action: AppmeshV1beta1.HttpRouteAction{ - WeightedTargets: []AppmeshV1beta1.WeightedTarget{ - { - VirtualNodeName: canaryVirtualNode, - Weight: 0, - }, - { - VirtualNodeName: primaryVirtualNode, - Weight: 100, - }, - }, - }, - }, - }, - }, - } - - virtualService, err := ar.appmeshClient.AppmeshV1beta1().VirtualServices(canary.Namespace).Get(name, metav1.GetOptions{}) - - // create virtual service - if errors.IsNotFound(err) { - virtualService = &AppmeshV1beta1.VirtualService{ - ObjectMeta: metav1.ObjectMeta{ - Name: name, - Namespace: canary.Namespace, - OwnerReferences: []metav1.OwnerReference{ - *metav1.NewControllerRef(canary, schema.GroupVersionKind{ - Group: flaggerv1.SchemeGroupVersion.Group, - Version: flaggerv1.SchemeGroupVersion.Version, - Kind: flaggerv1.CanaryKind, - }), - }, - }, - Spec: vsSpec, - } - _, err = ar.appmeshClient.AppmeshV1beta1().VirtualServices(canary.Namespace).Create(virtualService) - if err != nil { - return fmt.Errorf("VirtualService %s create error %v", name, err) - } - ar.logger.With("canary", fmt.Sprintf("%s.%s", canary.Name, canary.Namespace)). - Infof("VirtualService %s created", virtualService.GetName()) - return nil - } - - if err != nil { - return fmt.Errorf("VirtualService %s query error %v", name, err) - } - - // update virtual service but keep the original target weights - if virtualService != nil { - if diff := cmp.Diff(vsSpec, virtualService.Spec, cmpopts.IgnoreTypes(AppmeshV1beta1.WeightedTarget{})); diff != "" { - vsClone := virtualService.DeepCopy() - vsClone.Spec = vsSpec - vsClone.Spec.Routes[0].Http.Action = virtualService.Spec.Routes[0].Http.Action - - _, err = ar.appmeshClient.AppmeshV1beta1().VirtualServices(canary.Namespace).Update(vsClone) - if err != nil { - return fmt.Errorf("VirtualService %s update error %v", name, err) - } - ar.logger.With("canary", fmt.Sprintf("%s.%s", canary.Name, canary.Namespace)). - Infof("VirtualService %s updated", virtualService.GetName()) - } - } - - return nil -} - -// GetRoutes returns the destinations weight for primary and canary -func (ar *AppMeshRouter) GetRoutes(canary *flaggerv1.Canary) ( - primaryWeight int, - canaryWeight int, - err error, -) { - targetName := canary.Spec.TargetRef.Name - vsName := fmt.Sprintf("%s.%s", targetName, canary.Namespace) - vs, err := ar.appmeshClient.AppmeshV1beta1().VirtualServices(canary.Namespace).Get(vsName, metav1.GetOptions{}) - if err != nil { - if errors.IsNotFound(err) { - err = fmt.Errorf("VirtualService %s not found", vsName) - return - } - err = fmt.Errorf("VirtualService %s query error %v", vsName, err) - return - } - - if len(vs.Spec.Routes) < 1 || len(vs.Spec.Routes[0].Http.Action.WeightedTargets) != 2 { - err = fmt.Errorf("VirtualService routes %s not found", vsName) - return - } - - targets := vs.Spec.Routes[0].Http.Action.WeightedTargets - for _, t := range targets { - if t.VirtualNodeName == fmt.Sprintf("%s-canary", targetName) { - canaryWeight = int(t.Weight) - } - if t.VirtualNodeName == fmt.Sprintf("%s-primary", targetName) { - primaryWeight = int(t.Weight) - } - } - - if primaryWeight == 0 && canaryWeight == 0 { - err = fmt.Errorf("VirtualService %s does not contain routes for %s-primary and %s-canary", - vsName, targetName, targetName) - } - - return -} - -// SetRoutes updates the destinations weight for primary and canary -func (ar *AppMeshRouter) SetRoutes( - canary *flaggerv1.Canary, - primaryWeight int, - canaryWeight int, -) error { - targetName := canary.Spec.TargetRef.Name - vsName := fmt.Sprintf("%s.%s", targetName, canary.Namespace) - vs, err := ar.appmeshClient.AppmeshV1beta1().VirtualServices(canary.Namespace).Get(vsName, metav1.GetOptions{}) - if err != nil { - if errors.IsNotFound(err) { - return fmt.Errorf("VirtualService %s not found", vsName) - } - return fmt.Errorf("VirtualService %s query error %v", vsName, err) - } - - vsClone := vs.DeepCopy() - vsClone.Spec.Routes[0].Http.Action = AppmeshV1beta1.HttpRouteAction{ - WeightedTargets: []AppmeshV1beta1.WeightedTarget{ - { - VirtualNodeName: fmt.Sprintf("%s-canary", targetName), - Weight: int64(canaryWeight), - }, - { - VirtualNodeName: fmt.Sprintf("%s-primary", targetName), - Weight: int64(primaryWeight), - }, - }, - } - - _, err = ar.appmeshClient.AppmeshV1beta1().VirtualServices(canary.Namespace).Update(vsClone) - if err != nil { - return fmt.Errorf("VirtualService %s update error %v", vsName, err) - } - - return nil -} diff --git a/pkg/router/appmesh_test.go b/pkg/router/appmesh_test.go deleted file mode 100644 index 0fbed858..00000000 --- a/pkg/router/appmesh_test.go +++ /dev/null @@ -1,164 +0,0 @@ -package router - -import ( - "fmt" - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - "testing" -) - -func TestAppmeshRouter_Reconcile(t *testing.T) { - mocks := setupfakeClients() - router := &AppMeshRouter{ - logger: mocks.logger, - flaggerClient: mocks.flaggerClient, - appmeshClient: mocks.meshClient, - kubeClient: mocks.kubeClient, - } - - err := router.Reconcile(mocks.appmeshCanary) - if err != nil { - t.Fatal(err.Error()) - } - - // check virtual service - vsName := fmt.Sprintf("%s.%s", mocks.appmeshCanary.Spec.TargetRef.Name, mocks.appmeshCanary.Namespace) - vs, err := router.appmeshClient.AppmeshV1beta1().VirtualServices("default").Get(vsName, metav1.GetOptions{}) - if err != nil { - t.Fatal(err.Error()) - } - - meshName := mocks.appmeshCanary.Spec.Service.MeshName - if vs.Spec.MeshName != meshName { - t.Errorf("Got mesh name %v wanted %v", vs.Spec.MeshName, meshName) - } - - targetsCount := len(vs.Spec.Routes[0].Http.Action.WeightedTargets) - if targetsCount != 2 { - t.Errorf("Got routes %v wanted %v", targetsCount, 2) - } - - // check virtual node - vnName := mocks.appmeshCanary.Spec.TargetRef.Name - vn, err := router.appmeshClient.AppmeshV1beta1().VirtualNodes("default").Get(vnName, metav1.GetOptions{}) - if err != nil { - t.Fatal(err.Error()) - } - - primaryDNS := fmt.Sprintf("%s-primary.%s", mocks.appmeshCanary.Spec.TargetRef.Name, mocks.appmeshCanary.Namespace) - vnHostName := vn.Spec.ServiceDiscovery.Dns.HostName - if vnHostName != primaryDNS { - t.Errorf("Got DNS host name %v wanted %v", vnHostName, primaryDNS) - } - - // test backends update - cd, err := mocks.flaggerClient.FlaggerV1alpha3().Canaries("default").Get("appmesh", metav1.GetOptions{}) - if err != nil { - t.Fatal(err.Error()) - } - - cdClone := cd.DeepCopy() - hosts := cdClone.Spec.Service.Backends - hosts = append(hosts, "test.example.com") - cdClone.Spec.Service.Backends = hosts - canary, err := mocks.flaggerClient.FlaggerV1alpha3().Canaries("default").Update(cdClone) - if err != nil { - t.Fatal(err.Error()) - } - - // apply change - err = router.Reconcile(canary) - if err != nil { - t.Fatal(err.Error()) - } - - // verify - vnCanaryName := fmt.Sprintf("%s-canary", mocks.appmeshCanary.Spec.TargetRef.Name) - vnCanary, err := router.appmeshClient.AppmeshV1beta1().VirtualNodes("default").Get(vnCanaryName, metav1.GetOptions{}) - if err != nil { - t.Fatal(err.Error()) - } - - if len(vnCanary.Spec.Backends) != 2 { - t.Errorf("Got backends %v wanted %v", len(vnCanary.Spec.Backends), 2) - } - - // test weight update - vsClone := vs.DeepCopy() - vsClone.Spec.Routes[0].Http.Action.WeightedTargets[0].Weight = 50 - vsClone.Spec.Routes[0].Http.Action.WeightedTargets[1].Weight = 50 - vs, err = mocks.meshClient.AppmeshV1beta1().VirtualServices("default").Update(vsClone) - if err != nil { - t.Fatal(err.Error()) - } - - // apply change - err = router.Reconcile(canary) - if err != nil { - t.Fatal(err.Error()) - } - vs, err = router.appmeshClient.AppmeshV1beta1().VirtualServices("default").Get(vsName, metav1.GetOptions{}) - if err != nil { - t.Fatal(err.Error()) - } - - weight := vs.Spec.Routes[0].Http.Action.WeightedTargets[0].Weight - if weight != 50 { - t.Errorf("Got weight %v wanted %v", weight, 502) - } - - // test URI update - vsClone = vs.DeepCopy() - vsClone.Spec.Routes[0].Http.Match.Prefix = "api" - vs, err = mocks.meshClient.AppmeshV1beta1().VirtualServices("default").Update(vsClone) - if err != nil { - t.Fatal(err.Error()) - } - - // apply change - err = router.Reconcile(canary) - if err != nil { - t.Fatal(err.Error()) - } - vs, err = router.appmeshClient.AppmeshV1beta1().VirtualServices("default").Get(vsName, metav1.GetOptions{}) - if err != nil { - t.Fatal(err.Error()) - } - - prefix := vs.Spec.Routes[0].Http.Match.Prefix - if prefix != "/" { - t.Errorf("Got prefix %v wanted %v", prefix, "/") - } -} - -func TestAppmeshRouter_GetSetRoutes(t *testing.T) { - mocks := setupfakeClients() - router := &AppMeshRouter{ - logger: mocks.logger, - flaggerClient: mocks.flaggerClient, - appmeshClient: mocks.meshClient, - kubeClient: mocks.kubeClient, - } - - err := router.Reconcile(mocks.appmeshCanary) - if err != nil { - t.Fatal(err.Error()) - } - - err = router.SetRoutes(mocks.appmeshCanary, 60, 40) - if err != nil { - t.Fatal(err.Error()) - } - - p, c, err := router.GetRoutes(mocks.appmeshCanary) - if err != nil { - t.Fatal(err.Error()) - } - - if p != 60 { - t.Errorf("Got primary weight %v wanted %v", p, 60) - } - - if c != 40 { - t.Errorf("Got canary weight %v wanted %v", c, 40) - } -} diff --git a/pkg/router/factory.go b/pkg/router/factory.go deleted file mode 100644 index b99f440f..00000000 --- a/pkg/router/factory.go +++ /dev/null @@ -1,101 +0,0 @@ -package router - -import ( - "context" - "strings" - - clientset "github.com/weaveworks/flagger/pkg/client/clientset/versioned" - "go.uber.org/zap" - "k8s.io/client-go/kubernetes" - restclient "k8s.io/client-go/rest" -) - -type Factory struct { - kubeConfig *restclient.Config - kubeClient kubernetes.Interface - meshClient clientset.Interface - flaggerClient clientset.Interface - logger *zap.SugaredLogger -} - -func NewFactory(kubeConfig *restclient.Config, kubeClient kubernetes.Interface, - flaggerClient clientset.Interface, - logger *zap.SugaredLogger, - meshClient clientset.Interface) *Factory { - return &Factory{ - kubeConfig: kubeConfig, - meshClient: meshClient, - kubeClient: kubeClient, - flaggerClient: flaggerClient, - logger: logger, - } -} - -// KubernetesRouter returns a ClusterIP service router -func (factory *Factory) KubernetesRouter(label string, ports *map[string]int32) *KubernetesRouter { - return &KubernetesRouter{ - logger: factory.logger, - flaggerClient: factory.flaggerClient, - kubeClient: factory.kubeClient, - label: label, - ports: ports, - } -} - -// MeshRouter returns a service mesh router -func (factory *Factory) MeshRouter(provider string) Interface { - switch { - case provider == "none": - return &NopRouter{} - case provider == "kubernetes": - return &NopRouter{} - case provider == "nginx": - return &IngressRouter{ - logger: factory.logger, - kubeClient: factory.kubeClient, - } - case provider == "appmesh": - return &AppMeshRouter{ - logger: factory.logger, - flaggerClient: factory.flaggerClient, - kubeClient: factory.kubeClient, - appmeshClient: factory.meshClient, - } - case strings.HasPrefix(provider, "smi:"): - mesh := strings.TrimPrefix(provider, "smi:") - return &SmiRouter{ - logger: factory.logger, - flaggerClient: factory.flaggerClient, - kubeClient: factory.kubeClient, - smiClient: factory.meshClient, - targetMesh: mesh, - } - case provider == "linkerd": - return &SmiRouter{ - logger: factory.logger, - flaggerClient: factory.flaggerClient, - kubeClient: factory.kubeClient, - smiClient: factory.meshClient, - targetMesh: "linkerd", - } - case strings.HasPrefix(provider, "supergloo"): - supergloo, err := NewSuperglooRouter(context.TODO(), provider, factory.flaggerClient, factory.logger, factory.kubeConfig) - if err != nil { - panic("failed creating supergloo client") - } - return supergloo - case strings.HasPrefix(provider, "gloo"): - gloo, err := NewGlooRouter(context.TODO(), provider, factory.flaggerClient, factory.logger, factory.kubeConfig) - if err != nil { - panic("failed creating gloo client") - } - return gloo - default: - return &IstioRouter{ - logger: factory.logger, - flaggerClient: factory.flaggerClient, - kubeClient: factory.kubeClient, - istioClient: factory.meshClient, - } - } -} diff --git a/pkg/router/gloo.go b/pkg/router/gloo.go deleted file mode 100644 index 7cf735c4..00000000 --- a/pkg/router/gloo.go +++ /dev/null @@ -1,187 +0,0 @@ -package router - -import ( - "context" - "fmt" - "strings" - - solokitclients "github.com/solo-io/solo-kit/pkg/api/v1/clients" - "github.com/solo-io/solo-kit/pkg/api/v1/clients/factory" - "github.com/solo-io/solo-kit/pkg/api/v1/clients/kube" - crdv1 "github.com/solo-io/solo-kit/pkg/api/v1/clients/kube/crd/solo.io/v1" - solokitcore "github.com/solo-io/solo-kit/pkg/api/v1/resources/core" - solokiterror "github.com/solo-io/solo-kit/pkg/errors" - - gloov1 "github.com/solo-io/gloo/projects/gloo/pkg/api/v1" - flaggerv1 "github.com/weaveworks/flagger/pkg/apis/flagger/v1alpha3" - clientset "github.com/weaveworks/flagger/pkg/client/clientset/versioned" - "go.uber.org/zap" - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - "k8s.io/apimachinery/pkg/runtime/schema" - "k8s.io/client-go/rest" -) - -// GlooRouter is managing Istio virtual services -type GlooRouter struct { - ugClient gloov1.UpstreamGroupClient - logger *zap.SugaredLogger - upstreamDiscoveryNs string -} - -func NewGlooRouter(ctx context.Context, provider string, flaggerClient clientset.Interface, logger *zap.SugaredLogger, cfg *rest.Config) (*GlooRouter, error) { - // TODO if cfg is nil use memory client instead? - sharedCache := kube.NewKubeCache(ctx) - upstreamGroupClient, err := gloov1.NewUpstreamGroupClient(&factory.KubeResourceClientFactory{ - Crd: gloov1.UpstreamGroupCrd, - Cfg: cfg, - SharedCache: sharedCache, - SkipCrdCreation: true, - }) - if err != nil { - // this should never happen. - return nil, fmt.Errorf("creating UpstreamGroup client %v", err) - } - if err := upstreamGroupClient.Register(); err != nil { - return nil, err - } - upstreamDiscoveryNs := "" - if strings.HasPrefix(provider, "gloo:") { - upstreamDiscoveryNs = strings.TrimPrefix(provider, "gloo:") - } - - return NewGlooRouterWithClient(ctx, upstreamGroupClient, upstreamDiscoveryNs, logger), nil -} - -func NewGlooRouterWithClient(ctx context.Context, routingRuleClient gloov1.UpstreamGroupClient, upstreamDiscoveryNs string, logger *zap.SugaredLogger) *GlooRouter { - - if upstreamDiscoveryNs == "" { - upstreamDiscoveryNs = "gloo-system" - } - return &GlooRouter{ugClient: routingRuleClient, logger: logger, upstreamDiscoveryNs: upstreamDiscoveryNs} -} - -// Reconcile creates or updates the Istio virtual service -func (gr *GlooRouter) Reconcile(canary *flaggerv1.Canary) error { - // do we have routes already? - if _, _, err := gr.GetRoutes(canary); err == nil { - // we have routes, no need to do anything else - return nil - } else if solokiterror.IsNotExist(err) { - return gr.SetRoutes(canary, 100, 0) - } else { - return err - } -} - -// GetRoutes returns the destinations weight for primary and canary -func (gr *GlooRouter) GetRoutes(canary *flaggerv1.Canary) ( - primaryWeight int, - canaryWeight int, - err error, -) { - targetName := canary.Spec.TargetRef.Name - var ug *gloov1.UpstreamGroup - ug, err = gr.ugClient.Read(canary.Namespace, targetName, solokitclients.ReadOpts{}) - if err != nil { - return - } - - dests := ug.GetDestinations() - for _, dest := range dests { - if dest.GetDestination().GetUpstream().Name == upstreamName(canary.Namespace, fmt.Sprintf("%s-primary", targetName), canary.Spec.Service.Port) { - primaryWeight = int(dest.Weight) - } - if dest.GetDestination().GetUpstream().Name == upstreamName(canary.Namespace, fmt.Sprintf("%s-canary", targetName), canary.Spec.Service.Port) { - canaryWeight = int(dest.Weight) - } - } - - if primaryWeight == 0 && canaryWeight == 0 { - err = fmt.Errorf("RoutingRule %s.%s does not contain routes for %s-primary and %s-canary", - targetName, canary.Namespace, targetName, targetName) - } - - return -} - -// SetRoutes updates the destinations weight for primary and canary -func (gr *GlooRouter) SetRoutes( - canary *flaggerv1.Canary, - primaryWeight int, - canaryWeight int, -) error { - targetName := canary.Spec.TargetRef.Name - - if primaryWeight == 0 && canaryWeight == 0 { - return fmt.Errorf("RoutingRule %s.%s update failed: no valid weights", targetName, canary.Namespace) - } - - destinations := []*gloov1.WeightedDestination{} - destinations = append(destinations, &gloov1.WeightedDestination{ - Destination: &gloov1.Destination{ - Upstream: solokitcore.ResourceRef{ - Name: upstreamName(canary.Namespace, fmt.Sprintf("%s-primary", targetName), canary.Spec.Service.Port), - Namespace: gr.upstreamDiscoveryNs, - }, - }, - Weight: uint32(primaryWeight), - }) - - destinations = append(destinations, &gloov1.WeightedDestination{ - Destination: &gloov1.Destination{ - Upstream: solokitcore.ResourceRef{ - Name: upstreamName(canary.Namespace, fmt.Sprintf("%s-canary", targetName), canary.Spec.Service.Port), - Namespace: gr.upstreamDiscoveryNs, - }, - }, - Weight: uint32(canaryWeight), - }) - - upstreamGroup := &gloov1.UpstreamGroup{ - Metadata: solokitcore.Metadata{ - Name: canary.Spec.TargetRef.Name, - Namespace: canary.Namespace, - }, - Destinations: destinations, - } - - return gr.writeUpstreamGroupRuleForCanary(canary, upstreamGroup) -} - -func (gr *GlooRouter) writeUpstreamGroupRuleForCanary(canary *flaggerv1.Canary, ug *gloov1.UpstreamGroup) error { - targetName := canary.Spec.TargetRef.Name - - if oldUg, err := gr.ugClient.Read(ug.Metadata.Namespace, ug.Metadata.Name, solokitclients.ReadOpts{}); err != nil { - if solokiterror.IsNotExist(err) { - gr.logger.With("canary", fmt.Sprintf("%s.%s", canary.Name, canary.Namespace)). - Infof("UpstreamGroup %s created", ug.Metadata.Name) - } else { - return fmt.Errorf("RoutingRule %s.%s read failed: %v", targetName, canary.Namespace, err) - } - } else { - ug.Metadata.ResourceVersion = oldUg.Metadata.ResourceVersion - // if the old and the new one are equal, no need to do anything. - oldUg.Status = solokitcore.Status{} - if oldUg.Equal(ug) { - return nil - } - } - - kubeWriteOpts := &kube.KubeWriteOpts{ - PreWriteCallback: func(r *crdv1.Resource) { - r.ObjectMeta.OwnerReferences = []metav1.OwnerReference{ - *metav1.NewControllerRef(canary, schema.GroupVersionKind{ - Group: flaggerv1.SchemeGroupVersion.Group, - Version: flaggerv1.SchemeGroupVersion.Version, - Kind: flaggerv1.CanaryKind, - }), - } - }, - } - writeOpts := solokitclients.WriteOpts{OverwriteExisting: true, StorageWriteOpts: kubeWriteOpts} - _, err := gr.ugClient.Write(ug, writeOpts) - if err != nil { - return fmt.Errorf("UpstreamGroup %s.%s update failed: %v", targetName, canary.Namespace, err) - } - return nil -} diff --git a/pkg/router/gloo_test.go b/pkg/router/gloo_test.go deleted file mode 100644 index 408be347..00000000 --- a/pkg/router/gloo_test.go +++ /dev/null @@ -1,142 +0,0 @@ -package router - -import ( - "context" - "fmt" - "testing" - - gloov1 "github.com/solo-io/gloo/projects/gloo/pkg/api/v1" - solokitclients "github.com/solo-io/solo-kit/pkg/api/v1/clients" - "github.com/solo-io/solo-kit/pkg/api/v1/clients/factory" - solokitmemory "github.com/solo-io/solo-kit/pkg/api/v1/clients/memory" -) - -func TestGlooRouter_Sync(t *testing.T) { - mocks := setupfakeClients() - - upstreamGroupClient, err := gloov1.NewUpstreamGroupClient(&factory.MemoryResourceClientFactory{ - Cache: solokitmemory.NewInMemoryResourceCache(), - }) - if err != nil { - t.Fatal(err.Error()) - } - if err := upstreamGroupClient.Register(); err != nil { - t.Fatal(err.Error()) - } - router := NewGlooRouterWithClient(context.TODO(), upstreamGroupClient, "gloo-system", mocks.logger) - err = router.Reconcile(mocks.canary) - if err != nil { - t.Fatal(err.Error()) - } - - // test insert - ug, err := upstreamGroupClient.Read("default", "podinfo", solokitclients.ReadOpts{}) - if err != nil { - t.Fatal(err.Error()) - } - dests := ug.GetDestinations() - if len(dests) != 2 { - t.Errorf("Got Destinations %v wanted %v", len(dests), 2) - } - - if dests[0].Weight != 100 { - t.Errorf("Primary weight should is %v wanted 100", dests[0].Weight) - } - if dests[1].Weight != 0 { - t.Errorf("Canary weight should is %v wanted 0", dests[0].Weight) - } - -} - -func TestGlooRouter_SetRoutes(t *testing.T) { - - mocks := setupfakeClients() - - upstreamGroupClient, err := gloov1.NewUpstreamGroupClient(&factory.MemoryResourceClientFactory{ - Cache: solokitmemory.NewInMemoryResourceCache(), - }) - if err != nil { - t.Fatal(err.Error()) - } - if err := upstreamGroupClient.Register(); err != nil { - t.Fatal(err.Error()) - } - router := NewGlooRouterWithClient(context.TODO(), upstreamGroupClient, "gloo-system", mocks.logger) - - err = router.Reconcile(mocks.canary) - if err != nil { - t.Fatal(err.Error()) - } - - p, c, err := router.GetRoutes(mocks.canary) - if err != nil { - t.Fatal(err.Error()) - } - - p = 50 - c = 50 - - err = router.SetRoutes(mocks.canary, p, c) - if err != nil { - t.Fatal(err.Error()) - } - - ug, err := upstreamGroupClient.Read("default", "podinfo", solokitclients.ReadOpts{}) - if err != nil { - t.Fatal(err.Error()) - } - - var pRoute *gloov1.WeightedDestination - var cRoute *gloov1.WeightedDestination - targetName := mocks.canary.Spec.TargetRef.Name - - for _, dest := range ug.GetDestinations() { - if dest.GetDestination().GetUpstream().Name == upstreamName(mocks.canary.Namespace, fmt.Sprintf("%s-primary", targetName), mocks.canary.Spec.Service.Port) { - pRoute = dest - } - if dest.GetDestination().GetUpstream().Name == upstreamName(mocks.canary.Namespace, fmt.Sprintf("%s-canary", targetName), mocks.canary.Spec.Service.Port) { - cRoute = dest - } - } - - if pRoute.Weight != uint32(p) { - t.Errorf("Got primary weight %v wanted %v", pRoute.Weight, p) - } - - if cRoute.Weight != uint32(c) { - t.Errorf("Got canary weight %v wanted %v", cRoute.Weight, c) - } - -} - -func TestGlooRouter_GetRoutes(t *testing.T) { - mocks := setupfakeClients() - - upstreamGroupClient, err := gloov1.NewUpstreamGroupClient(&factory.MemoryResourceClientFactory{ - Cache: solokitmemory.NewInMemoryResourceCache(), - }) - if err != nil { - t.Fatal(err.Error()) - } - if err := upstreamGroupClient.Register(); err != nil { - t.Fatal(err.Error()) - } - router := NewGlooRouterWithClient(context.TODO(), upstreamGroupClient, "gloo-system", mocks.logger) - err = router.Reconcile(mocks.canary) - if err != nil { - t.Fatal(err.Error()) - } - - p, c, err := router.GetRoutes(mocks.canary) - if err != nil { - t.Fatal(err.Error()) - } - - if p != 100 { - t.Errorf("Got primary weight %v wanted %v", p, 100) - } - - if c != 0 { - t.Errorf("Got canary weight %v wanted %v", c, 0) - } -} diff --git a/pkg/router/ingress.go b/pkg/router/ingress.go deleted file mode 100644 index ffc663b1..00000000 --- a/pkg/router/ingress.go +++ /dev/null @@ -1,231 +0,0 @@ -package router - -import ( - "fmt" - "github.com/google/go-cmp/cmp" - flaggerv1 "github.com/weaveworks/flagger/pkg/apis/flagger/v1alpha3" - "go.uber.org/zap" - "k8s.io/api/extensions/v1beta1" - "k8s.io/apimachinery/pkg/api/errors" - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - "k8s.io/apimachinery/pkg/runtime/schema" - "k8s.io/client-go/kubernetes" - "strconv" - "strings" -) - -type IngressRouter struct { - kubeClient kubernetes.Interface - logger *zap.SugaredLogger -} - -func (i *IngressRouter) Reconcile(canary *flaggerv1.Canary) error { - if canary.Spec.IngressRef == nil || canary.Spec.IngressRef.Name == "" { - return fmt.Errorf("ingress selector is empty") - } - - targetName := canary.Spec.TargetRef.Name - canaryName := fmt.Sprintf("%s-canary", targetName) - canaryIngressName := fmt.Sprintf("%s-canary", canary.Spec.IngressRef.Name) - - ingress, err := i.kubeClient.ExtensionsV1beta1().Ingresses(canary.Namespace).Get(canary.Spec.IngressRef.Name, metav1.GetOptions{}) - if err != nil { - return err - } - - ingressClone := ingress.DeepCopy() - - // change backend to -canary - backendExists := false - for k, v := range ingressClone.Spec.Rules { - for x, y := range v.HTTP.Paths { - if y.Backend.ServiceName == targetName { - ingressClone.Spec.Rules[k].HTTP.Paths[x].Backend.ServiceName = canaryName - backendExists = true - break - } - } - } - - if !backendExists { - return fmt.Errorf("backend %s not found in ingress %s", targetName, canary.Spec.IngressRef.Name) - } - - canaryIngress, err := i.kubeClient.ExtensionsV1beta1().Ingresses(canary.Namespace).Get(canaryIngressName, metav1.GetOptions{}) - - if errors.IsNotFound(err) { - ing := &v1beta1.Ingress{ - ObjectMeta: metav1.ObjectMeta{ - Name: canaryIngressName, - Namespace: canary.Namespace, - OwnerReferences: []metav1.OwnerReference{ - *metav1.NewControllerRef(canary, schema.GroupVersionKind{ - Group: flaggerv1.SchemeGroupVersion.Group, - Version: flaggerv1.SchemeGroupVersion.Version, - Kind: flaggerv1.CanaryKind, - }), - }, - Annotations: i.makeAnnotations(ingressClone.Annotations), - Labels: ingressClone.Labels, - }, - Spec: ingressClone.Spec, - } - - _, err := i.kubeClient.ExtensionsV1beta1().Ingresses(canary.Namespace).Create(ing) - if err != nil { - return err - } - - i.logger.With("canary", fmt.Sprintf("%s.%s", canary.Name, canary.Namespace)). - Infof("Ingress %s.%s created", ing.GetName(), canary.Namespace) - return nil - } - - if err != nil { - return fmt.Errorf("ingress %s query error %v", canaryIngressName, err) - } - - if diff := cmp.Diff(ingressClone.Spec, canaryIngress.Spec); diff != "" { - iClone := canaryIngress.DeepCopy() - iClone.Spec = ingressClone.Spec - - _, err := i.kubeClient.ExtensionsV1beta1().Ingresses(canary.Namespace).Update(iClone) - if err != nil { - return fmt.Errorf("ingress %s update error %v", canaryIngressName, err) - } - - i.logger.With("canary", fmt.Sprintf("%s.%s", canary.Name, canary.Namespace)). - Infof("Ingress %s updated", canaryIngressName) - } - - return nil -} - -func (i *IngressRouter) GetRoutes(canary *flaggerv1.Canary) ( - primaryWeight int, - canaryWeight int, - err error, -) { - canaryIngressName := fmt.Sprintf("%s-canary", canary.Spec.IngressRef.Name) - canaryIngress, err := i.kubeClient.ExtensionsV1beta1().Ingresses(canary.Namespace).Get(canaryIngressName, metav1.GetOptions{}) - if err != nil { - return 0, 0, err - } - - // A/B testing - if len(canary.Spec.CanaryAnalysis.Match) > 0 { - for k := range canaryIngress.Annotations { - if k == "nginx.ingress.kubernetes.io/canary-by-cookie" || k == "nginx.ingress.kubernetes.io/canary-by-header" { - return 0, 100, nil - } - } - } - - // Canary - for k, v := range canaryIngress.Annotations { - if k == "nginx.ingress.kubernetes.io/canary-weight" { - val, err := strconv.Atoi(v) - if err != nil { - return 0, 0, err - } - - canaryWeight = val - break - } - } - - primaryWeight = 100 - canaryWeight - return -} - -func (i *IngressRouter) SetRoutes( - canary *flaggerv1.Canary, - primaryWeight int, - canaryWeight int, -) error { - canaryIngressName := fmt.Sprintf("%s-canary", canary.Spec.IngressRef.Name) - canaryIngress, err := i.kubeClient.ExtensionsV1beta1().Ingresses(canary.Namespace).Get(canaryIngressName, metav1.GetOptions{}) - if err != nil { - return err - } - - iClone := canaryIngress.DeepCopy() - - // A/B testing - if len(canary.Spec.CanaryAnalysis.Match) > 0 { - cookie := "" - header := "" - headerValue := "" - for _, m := range canary.Spec.CanaryAnalysis.Match { - for k, v := range m.Headers { - if k == "cookie" { - cookie = v.Exact - } else { - header = k - headerValue = v.Exact - } - } - } - - iClone.Annotations = i.makeHeaderAnnotations(iClone.Annotations, header, headerValue, cookie) - } else { - // canary - iClone.Annotations["nginx.ingress.kubernetes.io/canary-weight"] = fmt.Sprintf("%v", canaryWeight) - } - - // toggle canary - if canaryWeight > 0 { - iClone.Annotations["nginx.ingress.kubernetes.io/canary"] = "true" - } else { - iClone.Annotations = i.makeAnnotations(iClone.Annotations) - } - - _, err = i.kubeClient.ExtensionsV1beta1().Ingresses(canary.Namespace).Update(iClone) - if err != nil { - return fmt.Errorf("ingress %s update error %v", canaryIngressName, err) - } - - return nil -} - -func (i *IngressRouter) makeAnnotations(annotations map[string]string) map[string]string { - res := make(map[string]string) - for k, v := range annotations { - if !strings.Contains(k, "nginx.ingress.kubernetes.io/canary") && - !strings.Contains(k, "kubectl.kubernetes.io/last-applied-configuration") { - res[k] = v - } - } - - res["nginx.ingress.kubernetes.io/canary"] = "false" - res["nginx.ingress.kubernetes.io/canary-weight"] = "0" - - return res -} - -func (i *IngressRouter) makeHeaderAnnotations(annotations map[string]string, - header string, headerValue string, cookie string) map[string]string { - res := make(map[string]string) - for k, v := range annotations { - if !strings.Contains(v, "nginx.ingress.kubernetes.io/canary") { - res[k] = v - } - } - - res["nginx.ingress.kubernetes.io/canary"] = "true" - res["nginx.ingress.kubernetes.io/canary-weight"] = "0" - - if cookie != "" { - res["nginx.ingress.kubernetes.io/canary-by-cookie"] = cookie - } - - if header != "" { - res["nginx.ingress.kubernetes.io/canary-by-header"] = header - } - - if headerValue != "" { - res["nginx.ingress.kubernetes.io/canary-by-header-value"] = headerValue - } - - return res -} diff --git a/pkg/router/ingress_test.go b/pkg/router/ingress_test.go deleted file mode 100644 index 7d2679e6..00000000 --- a/pkg/router/ingress_test.go +++ /dev/null @@ -1,112 +0,0 @@ -package router - -import ( - "fmt" - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - "testing" -) - -func TestIngressRouter_Reconcile(t *testing.T) { - mocks := setupfakeClients() - router := &IngressRouter{ - logger: mocks.logger, - kubeClient: mocks.kubeClient, - } - - err := router.Reconcile(mocks.ingressCanary) - if err != nil { - t.Fatal(err.Error()) - } - - canaryAn := "nginx.ingress.kubernetes.io/canary" - canaryWeightAn := "nginx.ingress.kubernetes.io/canary-weight" - - canaryName := fmt.Sprintf("%s-canary", mocks.ingressCanary.Spec.IngressRef.Name) - inCanary, err := router.kubeClient.ExtensionsV1beta1().Ingresses("default").Get(canaryName, metav1.GetOptions{}) - if err != nil { - t.Fatal(err.Error()) - } - - if _, ok := inCanary.Annotations[canaryAn]; !ok { - t.Errorf("Canary annotation missing") - } - - // test initialisation - if inCanary.Annotations[canaryAn] != "false" { - t.Errorf("Got canary annotation %v wanted false", inCanary.Annotations[canaryAn]) - } - - if inCanary.Annotations[canaryWeightAn] != "0" { - t.Errorf("Got canary weight annotation %v wanted 0", inCanary.Annotations[canaryWeightAn]) - } -} - -func TestIngressRouter_GetSetRoutes(t *testing.T) { - mocks := setupfakeClients() - router := &IngressRouter{ - logger: mocks.logger, - kubeClient: mocks.kubeClient, - } - - err := router.Reconcile(mocks.ingressCanary) - if err != nil { - t.Fatal(err.Error()) - } - - p, c, err := router.GetRoutes(mocks.ingressCanary) - if err != nil { - t.Fatal(err.Error()) - } - - p = 50 - c = 50 - - err = router.SetRoutes(mocks.ingressCanary, p, c) - if err != nil { - t.Fatal(err.Error()) - } - - canaryAn := "nginx.ingress.kubernetes.io/canary" - canaryWeightAn := "nginx.ingress.kubernetes.io/canary-weight" - - canaryName := fmt.Sprintf("%s-canary", mocks.ingressCanary.Spec.IngressRef.Name) - inCanary, err := router.kubeClient.ExtensionsV1beta1().Ingresses("default").Get(canaryName, metav1.GetOptions{}) - if err != nil { - t.Fatal(err.Error()) - } - - if _, ok := inCanary.Annotations[canaryAn]; !ok { - t.Errorf("Canary annotation missing") - } - - // test rollout - if inCanary.Annotations[canaryAn] != "true" { - t.Errorf("Got canary annotation %v wanted true", inCanary.Annotations[canaryAn]) - } - - if inCanary.Annotations[canaryWeightAn] != "50" { - t.Errorf("Got canary weight annotation %v wanted 50", inCanary.Annotations[canaryWeightAn]) - } - - p = 100 - c = 0 - - err = router.SetRoutes(mocks.ingressCanary, p, c) - if err != nil { - t.Fatal(err.Error()) - } - - inCanary, err = router.kubeClient.ExtensionsV1beta1().Ingresses("default").Get(canaryName, metav1.GetOptions{}) - if err != nil { - t.Fatal(err.Error()) - } - - // test promotion - if inCanary.Annotations[canaryAn] != "false" { - t.Errorf("Got canary annotation %v wanted false", inCanary.Annotations[canaryAn]) - } - - if inCanary.Annotations[canaryWeightAn] != "0" { - t.Errorf("Got canary weight annotation %v wanted 0", inCanary.Annotations[canaryWeightAn]) - } -} diff --git a/pkg/router/istio.go b/pkg/router/istio.go deleted file mode 100644 index cb80a819..00000000 --- a/pkg/router/istio.go +++ /dev/null @@ -1,387 +0,0 @@ -package router - -import ( - "fmt" - - "github.com/google/go-cmp/cmp" - "github.com/google/go-cmp/cmp/cmpopts" - flaggerv1 "github.com/weaveworks/flagger/pkg/apis/flagger/v1alpha3" - istiov1alpha3 "github.com/weaveworks/flagger/pkg/apis/istio/v1alpha3" - clientset "github.com/weaveworks/flagger/pkg/client/clientset/versioned" - "go.uber.org/zap" - "k8s.io/apimachinery/pkg/api/errors" - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - "k8s.io/apimachinery/pkg/runtime/schema" - "k8s.io/client-go/kubernetes" -) - -// IstioRouter is managing Istio virtual services -type IstioRouter struct { - kubeClient kubernetes.Interface - istioClient clientset.Interface - flaggerClient clientset.Interface - logger *zap.SugaredLogger -} - -// Reconcile creates or updates the Istio virtual service and destination rules -func (ir *IstioRouter) Reconcile(canary *flaggerv1.Canary) error { - canaryName := fmt.Sprintf("%s-canary", canary.Spec.TargetRef.Name) - primaryName := fmt.Sprintf("%s-primary", canary.Spec.TargetRef.Name) - - err := ir.reconcileDestinationRule(canary, canaryName) - if err != nil { - return err - } - - err = ir.reconcileDestinationRule(canary, primaryName) - if err != nil { - return err - } - - err = ir.reconcileVirtualService(canary) - if err != nil { - return err - } - - return nil -} - -func (ir *IstioRouter) reconcileDestinationRule(canary *flaggerv1.Canary, name string) error { - newSpec := istiov1alpha3.DestinationRuleSpec{ - Host: name, - TrafficPolicy: canary.Spec.Service.TrafficPolicy, - } - - destinationRule, err := ir.istioClient.NetworkingV1alpha3().DestinationRules(canary.Namespace).Get(name, metav1.GetOptions{}) - // insert - if errors.IsNotFound(err) { - destinationRule = &istiov1alpha3.DestinationRule{ - ObjectMeta: metav1.ObjectMeta{ - Name: name, - Namespace: canary.Namespace, - OwnerReferences: []metav1.OwnerReference{ - *metav1.NewControllerRef(canary, schema.GroupVersionKind{ - Group: flaggerv1.SchemeGroupVersion.Group, - Version: flaggerv1.SchemeGroupVersion.Version, - Kind: flaggerv1.CanaryKind, - }), - }, - }, - Spec: newSpec, - } - _, err = ir.istioClient.NetworkingV1alpha3().DestinationRules(canary.Namespace).Create(destinationRule) - if err != nil { - return fmt.Errorf("DestinationRule %s.%s create error %v", name, canary.Namespace, err) - } - ir.logger.With("canary", fmt.Sprintf("%s.%s", canary.Name, canary.Namespace)). - Infof("DestinationRule %s.%s created", destinationRule.GetName(), canary.Namespace) - return nil - } - - if err != nil { - return fmt.Errorf("DestinationRule %s.%s query error %v", name, canary.Namespace, err) - } - - // update - if destinationRule != nil { - if diff := cmp.Diff(newSpec, destinationRule.Spec); diff != "" { - clone := destinationRule.DeepCopy() - clone.Spec = newSpec - _, err = ir.istioClient.NetworkingV1alpha3().DestinationRules(canary.Namespace).Update(clone) - if err != nil { - return fmt.Errorf("DestinationRule %s.%s update error %v", name, canary.Namespace, err) - } - ir.logger.With("canary", fmt.Sprintf("%s.%s", canary.Name, canary.Namespace)). - Infof("DestinationRule %s.%s updated", destinationRule.GetName(), canary.Namespace) - } - } - - return nil -} - -func (ir *IstioRouter) reconcileVirtualService(canary *flaggerv1.Canary) error { - targetName := canary.Spec.TargetRef.Name - primaryName := fmt.Sprintf("%s-primary", targetName) - canaryName := fmt.Sprintf("%s-canary", targetName) - - // set hosts and add the ClusterIP service host if it doesn't exists - hosts := canary.Spec.Service.Hosts - var hasServiceHost bool - for _, h := range hosts { - if h == targetName || h == "*" { - hasServiceHost = true - break - } - } - if !hasServiceHost { - hosts = append(hosts, targetName) - } - - // set gateways and add the mesh gateway if it doesn't exists - gateways := canary.Spec.Service.Gateways - var hasMeshGateway bool - for _, g := range gateways { - if g == "mesh" { - hasMeshGateway = true - break - } - } - - // set default mesh gateway if no gateway is specified - if !hasMeshGateway && len(canary.Spec.Service.Gateways) == 0 { - gateways = append(gateways, "mesh") - } - - // create destinations with primary weight 100% and canary weight 0% - canaryRoute := []istiov1alpha3.DestinationWeight{ - makeDestination(canary, primaryName, 100), - makeDestination(canary, canaryName, 0), - } - - newSpec := istiov1alpha3.VirtualServiceSpec{ - Hosts: hosts, - Gateways: gateways, - Http: []istiov1alpha3.HTTPRoute{ - { - Match: canary.Spec.Service.Match, - Rewrite: canary.Spec.Service.Rewrite, - Timeout: canary.Spec.Service.Timeout, - Retries: canary.Spec.Service.Retries, - CorsPolicy: canary.Spec.Service.CorsPolicy, - AppendHeaders: addHeaders(canary), - Route: canaryRoute, - }, - }, - } - - if len(canary.Spec.CanaryAnalysis.Match) > 0 { - canaryMatch := mergeMatchConditions(canary.Spec.CanaryAnalysis.Match, canary.Spec.Service.Match) - newSpec.Http = []istiov1alpha3.HTTPRoute{ - { - Match: canaryMatch, - Rewrite: canary.Spec.Service.Rewrite, - Timeout: canary.Spec.Service.Timeout, - Retries: canary.Spec.Service.Retries, - CorsPolicy: canary.Spec.Service.CorsPolicy, - AppendHeaders: addHeaders(canary), - Route: canaryRoute, - }, - { - Match: canary.Spec.Service.Match, - Rewrite: canary.Spec.Service.Rewrite, - Timeout: canary.Spec.Service.Timeout, - Retries: canary.Spec.Service.Retries, - CorsPolicy: canary.Spec.Service.CorsPolicy, - AppendHeaders: addHeaders(canary), - Route: []istiov1alpha3.DestinationWeight{ - makeDestination(canary, primaryName, 100), - }, - }, - } - } - - virtualService, err := ir.istioClient.NetworkingV1alpha3().VirtualServices(canary.Namespace).Get(targetName, metav1.GetOptions{}) - // insert - if errors.IsNotFound(err) { - virtualService = &istiov1alpha3.VirtualService{ - ObjectMeta: metav1.ObjectMeta{ - Name: targetName, - Namespace: canary.Namespace, - OwnerReferences: []metav1.OwnerReference{ - *metav1.NewControllerRef(canary, schema.GroupVersionKind{ - Group: flaggerv1.SchemeGroupVersion.Group, - Version: flaggerv1.SchemeGroupVersion.Version, - Kind: flaggerv1.CanaryKind, - }), - }, - }, - Spec: newSpec, - } - _, err = ir.istioClient.NetworkingV1alpha3().VirtualServices(canary.Namespace).Create(virtualService) - if err != nil { - return fmt.Errorf("VirtualService %s.%s create error %v", targetName, canary.Namespace, err) - } - ir.logger.With("canary", fmt.Sprintf("%s.%s", canary.Name, canary.Namespace)). - Infof("VirtualService %s.%s created", virtualService.GetName(), canary.Namespace) - return nil - } - - if err != nil { - return fmt.Errorf("VirtualService %s.%s query error %v", targetName, canary.Namespace, err) - } - - // update service but keep the original destination weights - if virtualService != nil { - if diff := cmp.Diff(newSpec, virtualService.Spec, cmpopts.IgnoreFields(istiov1alpha3.DestinationWeight{}, "Weight")); diff != "" { - vtClone := virtualService.DeepCopy() - vtClone.Spec = newSpec - - _, err = ir.istioClient.NetworkingV1alpha3().VirtualServices(canary.Namespace).Update(vtClone) - if err != nil { - return fmt.Errorf("VirtualService %s.%s update error %v", targetName, canary.Namespace, err) - } - ir.logger.With("canary", fmt.Sprintf("%s.%s", canary.Name, canary.Namespace)). - Infof("VirtualService %s.%s updated", virtualService.GetName(), canary.Namespace) - } - } - - return nil -} - -// GetRoutes returns the destinations weight for primary and canary -func (ir *IstioRouter) GetRoutes(canary *flaggerv1.Canary) ( - primaryWeight int, - canaryWeight int, - err error, -) { - targetName := canary.Spec.TargetRef.Name - vs := &istiov1alpha3.VirtualService{} - vs, err = ir.istioClient.NetworkingV1alpha3().VirtualServices(canary.Namespace).Get(targetName, metav1.GetOptions{}) - if err != nil { - if errors.IsNotFound(err) { - err = fmt.Errorf("VirtualService %s.%s not found", targetName, canary.Namespace) - return - } - err = fmt.Errorf("VirtualService %s.%s query error %v", targetName, canary.Namespace, err) - return - } - - var httpRoute istiov1alpha3.HTTPRoute - for _, http := range vs.Spec.Http { - for _, r := range http.Route { - if r.Destination.Host == fmt.Sprintf("%s-canary", targetName) { - httpRoute = http - break - } - } - } - - for _, route := range httpRoute.Route { - if route.Destination.Host == fmt.Sprintf("%s-primary", targetName) { - primaryWeight = route.Weight - } - if route.Destination.Host == fmt.Sprintf("%s-canary", targetName) { - canaryWeight = route.Weight - } - } - - if primaryWeight == 0 && canaryWeight == 0 { - err = fmt.Errorf("VirtualService %s.%s does not contain routes for %s-primary and %s-canary", - targetName, canary.Namespace, targetName, targetName) - } - - return -} - -// SetRoutes updates the destinations weight for primary and canary -func (ir *IstioRouter) SetRoutes( - canary *flaggerv1.Canary, - primaryWeight int, - canaryWeight int, -) error { - targetName := canary.Spec.TargetRef.Name - primaryName := fmt.Sprintf("%s-primary", targetName) - canaryName := fmt.Sprintf("%s-canary", targetName) - - vs, err := ir.istioClient.NetworkingV1alpha3().VirtualServices(canary.Namespace).Get(targetName, metav1.GetOptions{}) - if err != nil { - if errors.IsNotFound(err) { - return fmt.Errorf("VirtualService %s.%s not found", targetName, canary.Namespace) - - } - return fmt.Errorf("VirtualService %s.%s query error %v", targetName, canary.Namespace, err) - } - - vsCopy := vs.DeepCopy() - - // weighted routing (progressive canary) - vsCopy.Spec.Http = []istiov1alpha3.HTTPRoute{ - { - Match: canary.Spec.Service.Match, - Rewrite: canary.Spec.Service.Rewrite, - Timeout: canary.Spec.Service.Timeout, - Retries: canary.Spec.Service.Retries, - CorsPolicy: canary.Spec.Service.CorsPolicy, - AppendHeaders: addHeaders(canary), - Route: []istiov1alpha3.DestinationWeight{ - makeDestination(canary, primaryName, primaryWeight), - makeDestination(canary, canaryName, canaryWeight), - }, - }, - } - - // fix routing (A/B testing) - if len(canary.Spec.CanaryAnalysis.Match) > 0 { - // merge the common routes with the canary ones - canaryMatch := mergeMatchConditions(canary.Spec.CanaryAnalysis.Match, canary.Spec.Service.Match) - vsCopy.Spec.Http = []istiov1alpha3.HTTPRoute{ - { - Match: canaryMatch, - Rewrite: canary.Spec.Service.Rewrite, - Timeout: canary.Spec.Service.Timeout, - Retries: canary.Spec.Service.Retries, - CorsPolicy: canary.Spec.Service.CorsPolicy, - AppendHeaders: addHeaders(canary), - Route: []istiov1alpha3.DestinationWeight{ - makeDestination(canary, primaryName, primaryWeight), - makeDestination(canary, canaryName, canaryWeight), - }, - }, - { - Match: canary.Spec.Service.Match, - Rewrite: canary.Spec.Service.Rewrite, - Timeout: canary.Spec.Service.Timeout, - Retries: canary.Spec.Service.Retries, - CorsPolicy: canary.Spec.Service.CorsPolicy, - AppendHeaders: addHeaders(canary), - Route: []istiov1alpha3.DestinationWeight{ - makeDestination(canary, primaryName, primaryWeight), - }, - }, - } - } - - vs, err = ir.istioClient.NetworkingV1alpha3().VirtualServices(canary.Namespace).Update(vsCopy) - if err != nil { - return fmt.Errorf("VirtualService %s.%s update failed: %v", targetName, canary.Namespace, err) - - } - return nil -} - -// addHeaders applies headers before forwarding a request to the destination service -// compatible with Istio 1.0.x and 1.1.0 -func addHeaders(canary *flaggerv1.Canary) (headers map[string]string) { - if canary.Spec.Service.Headers != nil && - canary.Spec.Service.Headers.Request != nil && - len(canary.Spec.Service.Headers.Request.Add) > 0 { - headers = canary.Spec.Service.Headers.Request.Add - } - - return -} - -// mergeMatchConditions appends the URI match rules to canary conditions -func mergeMatchConditions(canary, defaults []istiov1alpha3.HTTPMatchRequest) []istiov1alpha3.HTTPMatchRequest { - for i := range canary { - for _, d := range defaults { - if d.Uri != nil { - canary[i].Uri = d.Uri - } - } - } - - return canary -} - -// makeDestination returns a an destination weight for the specified host -func makeDestination(canary *flaggerv1.Canary, host string, weight int) istiov1alpha3.DestinationWeight { - dest := istiov1alpha3.DestinationWeight{ - Destination: istiov1alpha3.Destination{ - Host: host, - }, - Weight: weight, - } - - return dest -} diff --git a/pkg/router/istio_test.go b/pkg/router/istio_test.go deleted file mode 100644 index a013abdb..00000000 --- a/pkg/router/istio_test.go +++ /dev/null @@ -1,311 +0,0 @@ -package router - -import ( - "fmt" - istiov1alpha3 "github.com/weaveworks/flagger/pkg/apis/istio/v1alpha3" - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - "testing" -) - -func TestIstioRouter_Sync(t *testing.T) { - mocks := setupfakeClients() - router := &IstioRouter{ - logger: mocks.logger, - flaggerClient: mocks.flaggerClient, - istioClient: mocks.meshClient, - kubeClient: mocks.kubeClient, - } - - err := router.Reconcile(mocks.canary) - if err != nil { - t.Fatal(err.Error()) - } - - // test insert - _, err = mocks.meshClient.NetworkingV1alpha3().DestinationRules("default").Get("podinfo-canary", metav1.GetOptions{}) - if err != nil { - t.Fatal(err.Error()) - } - - _, err = mocks.meshClient.NetworkingV1alpha3().DestinationRules("default").Get("podinfo-primary", metav1.GetOptions{}) - if err != nil { - t.Fatal(err.Error()) - } - - vs, err := mocks.meshClient.NetworkingV1alpha3().VirtualServices("default").Get("podinfo", metav1.GetOptions{}) - if err != nil { - t.Fatal(err.Error()) - } - - if len(vs.Spec.Http) != 1 { - t.Errorf("Got Istio VS Http %v wanted %v", len(vs.Spec.Http), 1) - } - - if len(vs.Spec.Http[0].Route) != 2 { - t.Errorf("Got Istio VS routes %v wanted %v", len(vs.Spec.Http[0].Route), 2) - } - - // test update - cd, err := mocks.flaggerClient.FlaggerV1alpha3().Canaries("default").Get("podinfo", metav1.GetOptions{}) - if err != nil { - t.Fatal(err.Error()) - } - - cdClone := cd.DeepCopy() - hosts := cdClone.Spec.Service.Hosts - hosts = append(hosts, "test.example.com") - cdClone.Spec.Service.Hosts = hosts - canary, err := mocks.flaggerClient.FlaggerV1alpha3().Canaries("default").Update(cdClone) - if err != nil { - t.Fatal(err.Error()) - } - - // apply change - err = router.Reconcile(canary) - if err != nil { - t.Fatal(err.Error()) - } - - // verify - vs, err = mocks.meshClient.NetworkingV1alpha3().VirtualServices("default").Get("podinfo", metav1.GetOptions{}) - if err != nil { - t.Fatal(err.Error()) - } - if len(vs.Spec.Hosts) != 2 { - t.Errorf("Got Istio VS hosts %v wanted %v", vs.Spec.Hosts, 2) - } - - // test drift - vsClone := vs.DeepCopy() - gateways := vsClone.Spec.Gateways - gateways = append(gateways, "test-gateway.istio-system") - vsClone.Spec.Gateways = gateways - - vsGateways, err := mocks.meshClient.NetworkingV1alpha3().VirtualServices("default").Update(vsClone) - if err != nil { - t.Fatal(err.Error()) - } - if len(vsGateways.Spec.Gateways) != 2 { - t.Errorf("Got Istio VS gateway %v wanted %v", vsGateways.Spec.Gateways, 2) - } - - // undo change - err = router.Reconcile(mocks.canary) - if err != nil { - t.Fatal(err.Error()) - } - - // verify - vs, err = mocks.meshClient.NetworkingV1alpha3().VirtualServices("default").Get("podinfo", metav1.GetOptions{}) - if err != nil { - t.Fatal(err.Error()) - } - if len(vs.Spec.Gateways) != 1 { - t.Errorf("Got Istio VS gateways %v wanted %v", vs.Spec.Gateways, 1) - } -} - -func TestIstioRouter_SetRoutes(t *testing.T) { - mocks := setupfakeClients() - router := &IstioRouter{ - logger: mocks.logger, - flaggerClient: mocks.flaggerClient, - istioClient: mocks.meshClient, - kubeClient: mocks.kubeClient, - } - - err := router.Reconcile(mocks.canary) - if err != nil { - t.Fatal(err.Error()) - } - - p, c, err := router.GetRoutes(mocks.canary) - if err != nil { - t.Fatal(err.Error()) - } - - p = 50 - c = 50 - - err = router.SetRoutes(mocks.canary, p, c) - if err != nil { - t.Fatal(err.Error()) - } - - vs, err := mocks.meshClient.NetworkingV1alpha3().VirtualServices("default").Get("podinfo", metav1.GetOptions{}) - if err != nil { - t.Fatal(err.Error()) - } - - pRoute := istiov1alpha3.DestinationWeight{} - cRoute := istiov1alpha3.DestinationWeight{} - - for _, http := range vs.Spec.Http { - for _, route := range http.Route { - if route.Destination.Host == fmt.Sprintf("%s-primary", mocks.canary.Spec.TargetRef.Name) { - pRoute = route - } - if route.Destination.Host == fmt.Sprintf("%s-canary", mocks.canary.Spec.TargetRef.Name) { - cRoute = route - } - } - } - - if pRoute.Weight != p { - t.Errorf("Got primary weight %v wanted %v", pRoute.Weight, p) - } - - if cRoute.Weight != c { - t.Errorf("Got canary weight %v wanted %v", cRoute.Weight, c) - } -} - -func TestIstioRouter_GetRoutes(t *testing.T) { - mocks := setupfakeClients() - router := &IstioRouter{ - logger: mocks.logger, - flaggerClient: mocks.flaggerClient, - istioClient: mocks.meshClient, - kubeClient: mocks.kubeClient, - } - - err := router.Reconcile(mocks.canary) - if err != nil { - t.Fatal(err.Error()) - } - - p, c, err := router.GetRoutes(mocks.canary) - if err != nil { - t.Fatal(err.Error()) - } - - if p != 100 { - t.Errorf("Got primary weight %v wanted %v", p, 100) - } - - if c != 0 { - t.Errorf("Got canary weight %v wanted %v", c, 0) - } -} - -func TestIstioRouter_HTTPRequestHeaders(t *testing.T) { - mocks := setupfakeClients() - router := &IstioRouter{ - logger: mocks.logger, - flaggerClient: mocks.flaggerClient, - istioClient: mocks.meshClient, - kubeClient: mocks.kubeClient, - } - - err := router.Reconcile(mocks.canary) - if err != nil { - t.Fatal(err.Error()) - } - - vs, err := mocks.meshClient.NetworkingV1alpha3().VirtualServices("default").Get("podinfo", metav1.GetOptions{}) - if err != nil { - t.Fatal(err.Error()) - } - - if len(vs.Spec.Http) != 1 { - t.Fatalf("Got HTTPRoute %v wanted %v", len(vs.Spec.Http), 1) - } - - timeout := vs.Spec.Http[0].AppendHeaders["x-envoy-upstream-rq-timeout-ms"] - if timeout != "15000" { - t.Errorf("Got timeout %v wanted %v", timeout, "15000") - } -} - -func TestIstioRouter_CORS(t *testing.T) { - mocks := setupfakeClients() - router := &IstioRouter{ - logger: mocks.logger, - flaggerClient: mocks.flaggerClient, - istioClient: mocks.meshClient, - kubeClient: mocks.kubeClient, - } - - err := router.Reconcile(mocks.canary) - if err != nil { - t.Fatal(err.Error()) - } - - vs, err := mocks.meshClient.NetworkingV1alpha3().VirtualServices("default").Get("podinfo", metav1.GetOptions{}) - if err != nil { - t.Fatal(err.Error()) - } - - if len(vs.Spec.Http) != 1 { - t.Fatalf("Got HTTPRoute %v wanted %v", len(vs.Spec.Http), 1) - } - - if vs.Spec.Http[0].CorsPolicy == nil { - t.Fatal("Got not CORS policy") - } - - methods := vs.Spec.Http[0].CorsPolicy.AllowMethods - if len(methods) != 2 { - t.Fatalf("Got CORS allow methods %v wanted %v", len(methods), 2) - } -} - -func TestIstioRouter_ABTest(t *testing.T) { - mocks := setupfakeClients() - router := &IstioRouter{ - logger: mocks.logger, - flaggerClient: mocks.flaggerClient, - istioClient: mocks.meshClient, - kubeClient: mocks.kubeClient, - } - - err := router.Reconcile(mocks.abtest) - if err != nil { - t.Fatal(err.Error()) - } - - // test insert - vs, err := mocks.meshClient.NetworkingV1alpha3().VirtualServices("default").Get("abtest", metav1.GetOptions{}) - if err != nil { - t.Fatal(err.Error()) - } - - if len(vs.Spec.Http) != 2 { - t.Errorf("Got Istio VS Http %v wanted %v", len(vs.Spec.Http), 2) - } - - p := 0 - c := 100 - - err = router.SetRoutes(mocks.abtest, p, c) - if err != nil { - t.Fatal(err.Error()) - } - - vs, err = mocks.meshClient.NetworkingV1alpha3().VirtualServices("default").Get("abtest", metav1.GetOptions{}) - if err != nil { - t.Fatal(err.Error()) - } - - pRoute := istiov1alpha3.DestinationWeight{} - cRoute := istiov1alpha3.DestinationWeight{} - - for _, http := range vs.Spec.Http { - for _, route := range http.Route { - if route.Destination.Host == fmt.Sprintf("%s-primary", mocks.abtest.Spec.TargetRef.Name) { - pRoute = route - } - if route.Destination.Host == fmt.Sprintf("%s-canary", mocks.abtest.Spec.TargetRef.Name) { - cRoute = route - } - } - } - - if pRoute.Weight != p { - t.Errorf("Got primary weight %v wanted %v", pRoute.Weight, p) - } - - if cRoute.Weight != c { - t.Errorf("Got canary weight %v wanted %v", cRoute.Weight, c) - } -} diff --git a/pkg/router/kubernetes.go b/pkg/router/kubernetes.go deleted file mode 100644 index 07710fcc..00000000 --- a/pkg/router/kubernetes.go +++ /dev/null @@ -1,148 +0,0 @@ -package router - -import ( - "fmt" - "github.com/google/go-cmp/cmp" - flaggerv1 "github.com/weaveworks/flagger/pkg/apis/flagger/v1alpha3" - clientset "github.com/weaveworks/flagger/pkg/client/clientset/versioned" - "go.uber.org/zap" - corev1 "k8s.io/api/core/v1" - "k8s.io/apimachinery/pkg/api/errors" - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - "k8s.io/apimachinery/pkg/runtime/schema" - "k8s.io/apimachinery/pkg/util/intstr" - "k8s.io/client-go/kubernetes" -) - -// KubernetesRouter is managing ClusterIP services -type KubernetesRouter struct { - kubeClient kubernetes.Interface - flaggerClient clientset.Interface - logger *zap.SugaredLogger - label string - ports *map[string]int32 -} - -// Reconcile creates or updates the primary and canary services -func (c *KubernetesRouter) Reconcile(canary *flaggerv1.Canary) error { - targetName := canary.Spec.TargetRef.Name - primaryName := fmt.Sprintf("%s-primary", targetName) - canaryName := fmt.Sprintf("%s-canary", targetName) - - // main svc - err := c.reconcileService(canary, targetName, primaryName) - if err != nil { - return err - } - - // canary svc - err = c.reconcileService(canary, canaryName, targetName) - if err != nil { - return err - } - - // primary svc - err = c.reconcileService(canary, primaryName, primaryName) - if err != nil { - return err - } - - return nil -} - -func (c *KubernetesRouter) SetRoutes(canary *flaggerv1.Canary, primaryRoute int, canaryRoute int) error { - return nil -} - -func (c *KubernetesRouter) GetRoutes(canary *flaggerv1.Canary) (primaryRoute int, canaryRoute int, err error) { - return 0, 0, nil -} - -func (c *KubernetesRouter) reconcileService(canary *flaggerv1.Canary, name string, target string) error { - portName := canary.Spec.Service.PortName - if portName == "" { - portName = "http" - } - - svcSpec := corev1.ServiceSpec{ - Type: corev1.ServiceTypeClusterIP, - Selector: map[string]string{c.label: target}, - Ports: []corev1.ServicePort{ - { - Name: portName, - Protocol: corev1.ProtocolTCP, - Port: canary.Spec.Service.Port, - TargetPort: intstr.IntOrString{ - Type: intstr.Int, - IntVal: canary.Spec.Service.Port, - }, - }, - }, - } - - if c.ports != nil { - for n, p := range *c.ports { - cp := corev1.ServicePort{ - Name: n, - Protocol: corev1.ProtocolTCP, - Port: p, - TargetPort: intstr.IntOrString{ - Type: intstr.Int, - IntVal: p, - }, - } - - svcSpec.Ports = append(svcSpec.Ports, cp) - } - } - - svc, err := c.kubeClient.CoreV1().Services(canary.Namespace).Get(name, metav1.GetOptions{}) - if errors.IsNotFound(err) { - svc = &corev1.Service{ - ObjectMeta: metav1.ObjectMeta{ - Name: name, - Namespace: canary.Namespace, - OwnerReferences: []metav1.OwnerReference{ - *metav1.NewControllerRef(canary, schema.GroupVersionKind{ - Group: flaggerv1.SchemeGroupVersion.Group, - Version: flaggerv1.SchemeGroupVersion.Version, - Kind: flaggerv1.CanaryKind, - }), - }, - }, - Spec: svcSpec, - } - - _, err = c.kubeClient.CoreV1().Services(canary.Namespace).Create(svc) - if err != nil { - return err - } - - c.logger.With("canary", fmt.Sprintf("%s.%s", canary.Name, canary.Namespace)). - Infof("Service %s.%s created", svc.GetName(), canary.Namespace) - return nil - } - - if err != nil { - return fmt.Errorf("service %s query error %v", name, err) - } - - if svc != nil { - portsDiff := cmp.Diff(svcSpec.Ports, svc.Spec.Ports) - selectorsDiff := cmp.Diff(svcSpec.Selector, svc.Spec.Selector) - - if portsDiff != "" || selectorsDiff != "" { - svcClone := svc.DeepCopy() - svcClone.Spec.Ports = svcSpec.Ports - svcClone.Spec.Selector = svcSpec.Selector - _, err = c.kubeClient.CoreV1().Services(canary.Namespace).Update(svcClone) - if err != nil { - return fmt.Errorf("service %s update error %v", name, err) - } - c.logger.With("canary", fmt.Sprintf("%s.%s", canary.Name, canary.Namespace)). - Infof("Service %s updated", svc.GetName()) - } - } - - return nil -} diff --git a/pkg/router/kubernetes_test.go b/pkg/router/kubernetes_test.go deleted file mode 100644 index 94588e37..00000000 --- a/pkg/router/kubernetes_test.go +++ /dev/null @@ -1,135 +0,0 @@ -package router - -import ( - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - "testing" -) - -func TestServiceRouter_Create(t *testing.T) { - mocks := setupfakeClients() - router := &KubernetesRouter{ - kubeClient: mocks.kubeClient, - flaggerClient: mocks.flaggerClient, - logger: mocks.logger, - } - - err := router.Reconcile(mocks.canary) - if err != nil { - t.Fatal(err.Error()) - } - - canarySvc, err := mocks.kubeClient.CoreV1().Services("default").Get("podinfo-canary", metav1.GetOptions{}) - if err != nil { - t.Fatal(err.Error()) - } - - if canarySvc.Spec.Ports[0].Name != "http" { - t.Errorf("Got svc port name %s wanted %s", canarySvc.Spec.Ports[0].Name, "http") - } - - if canarySvc.Spec.Ports[0].Port != 9898 { - t.Errorf("Got svc port %v wanted %v", canarySvc.Spec.Ports[0].Port, 9898) - } - - primarySvc, err := mocks.kubeClient.CoreV1().Services("default").Get("podinfo-primary", metav1.GetOptions{}) - if err != nil { - t.Fatal(err.Error()) - } - - if primarySvc.Spec.Ports[0].Name != "http" { - t.Errorf("Got primary svc port name %s wanted %s", primarySvc.Spec.Ports[0].Name, "http") - } - - if primarySvc.Spec.Ports[0].Port != 9898 { - t.Errorf("Got primary svc port %v wanted %v", primarySvc.Spec.Ports[0].Port, 9898) - } -} - -func TestServiceRouter_Update(t *testing.T) { - mocks := setupfakeClients() - router := &KubernetesRouter{ - kubeClient: mocks.kubeClient, - flaggerClient: mocks.flaggerClient, - logger: mocks.logger, - } - - err := router.Reconcile(mocks.canary) - if err != nil { - t.Fatal(err.Error()) - } - - canary, err := mocks.flaggerClient.FlaggerV1alpha3().Canaries("default").Get("podinfo", metav1.GetOptions{}) - if err != nil { - t.Fatal(err.Error()) - } - - canaryClone := canary.DeepCopy() - canaryClone.Spec.Service.PortName = "grpc" - - c, err := mocks.flaggerClient.FlaggerV1alpha3().Canaries("default").Update(canaryClone) - if err != nil { - t.Fatal(err.Error()) - } - - // apply changes - err = router.Reconcile(c) - if err != nil { - t.Fatal(err.Error()) - } - - canarySvc, err := mocks.kubeClient.CoreV1().Services("default").Get("podinfo-canary", metav1.GetOptions{}) - if err != nil { - t.Fatal(err.Error()) - } - - if canarySvc.Spec.Ports[0].Name != "grpc" { - t.Errorf("Got svc port name %s wanted %s", canarySvc.Spec.Ports[0].Name, "grpc") - } -} - -func TestServiceRouter_Undo(t *testing.T) { - mocks := setupfakeClients() - router := &KubernetesRouter{ - kubeClient: mocks.kubeClient, - flaggerClient: mocks.flaggerClient, - logger: mocks.logger, - } - - err := router.Reconcile(mocks.canary) - if err != nil { - t.Fatal(err.Error()) - } - - canarySvc, err := mocks.kubeClient.CoreV1().Services("default").Get("podinfo-canary", metav1.GetOptions{}) - if err != nil { - t.Fatal(err.Error()) - } - - svcClone := canarySvc.DeepCopy() - svcClone.Spec.Ports[0].Name = "http2-podinfo" - svcClone.Spec.Ports[0].Port = 8080 - - _, err = mocks.kubeClient.CoreV1().Services("default").Update(svcClone) - if err != nil { - t.Fatal(err.Error()) - } - - // undo changes - err = router.Reconcile(mocks.canary) - if err != nil { - t.Fatal(err.Error()) - } - - canarySvc, err = mocks.kubeClient.CoreV1().Services("default").Get("podinfo-canary", metav1.GetOptions{}) - if err != nil { - t.Fatal(err.Error()) - } - - if canarySvc.Spec.Ports[0].Name != "http" { - t.Errorf("Got svc port name %s wanted %s", canarySvc.Spec.Ports[0].Name, "http") - } - - if canarySvc.Spec.Ports[0].Port != 9898 { - t.Errorf("Got svc port %v wanted %v", canarySvc.Spec.Ports[0].Port, 9898) - } -} diff --git a/pkg/router/nop.go b/pkg/router/nop.go deleted file mode 100644 index 66f1d812..00000000 --- a/pkg/router/nop.go +++ /dev/null @@ -1,24 +0,0 @@ -package router - -import ( - flaggerv1 "github.com/weaveworks/flagger/pkg/apis/flagger/v1alpha3" -) - -// NopRouter no-operation router -type NopRouter struct { -} - -func (*NopRouter) Reconcile(canary *flaggerv1.Canary) error { - return nil -} - -func (*NopRouter) SetRoutes(canary *flaggerv1.Canary, primaryWeight int, canaryWeight int) error { - return nil -} - -func (*NopRouter) GetRoutes(canary *flaggerv1.Canary) (primaryWeight int, canaryWeight int, err error) { - if canary.Status.Iterations > 0 { - return 0, 100, nil - } - return 100, 0, nil -} diff --git a/pkg/router/router.go b/pkg/router/router.go deleted file mode 100644 index 1729b3a2..00000000 --- a/pkg/router/router.go +++ /dev/null @@ -1,9 +0,0 @@ -package router - -import flaggerv1 "github.com/weaveworks/flagger/pkg/apis/flagger/v1alpha3" - -type Interface interface { - Reconcile(canary *flaggerv1.Canary) error - SetRoutes(canary *flaggerv1.Canary, primaryWeight int, canaryWeight int) error - GetRoutes(canary *flaggerv1.Canary) (primaryWeight int, canaryWeight int, err error) -} diff --git a/pkg/router/router_test.go b/pkg/router/router_test.go deleted file mode 100644 index 701cae0c..00000000 --- a/pkg/router/router_test.go +++ /dev/null @@ -1,343 +0,0 @@ -package router - -import ( - "github.com/weaveworks/flagger/pkg/apis/flagger/v1alpha3" - istiov1alpha1 "github.com/weaveworks/flagger/pkg/apis/istio/common/v1alpha1" - istiov1alpha3 "github.com/weaveworks/flagger/pkg/apis/istio/v1alpha3" - clientset "github.com/weaveworks/flagger/pkg/client/clientset/versioned" - fakeFlagger "github.com/weaveworks/flagger/pkg/client/clientset/versioned/fake" - "github.com/weaveworks/flagger/pkg/logger" - "go.uber.org/zap" - appsv1 "k8s.io/api/apps/v1" - hpav1 "k8s.io/api/autoscaling/v1" - corev1 "k8s.io/api/core/v1" - "k8s.io/api/extensions/v1beta1" - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - "k8s.io/apimachinery/pkg/util/intstr" - "k8s.io/client-go/kubernetes" - "k8s.io/client-go/kubernetes/fake" -) - -type fakeClients struct { - canary *v1alpha3.Canary - abtest *v1alpha3.Canary - appmeshCanary *v1alpha3.Canary - ingressCanary *v1alpha3.Canary - kubeClient kubernetes.Interface - meshClient clientset.Interface - flaggerClient clientset.Interface - logger *zap.SugaredLogger -} - -func setupfakeClients() fakeClients { - canary := newMockCanary() - abtest := newMockABTest() - appmeshCanary := newMockCanaryAppMesh() - ingressCanary := newMockCanaryIngress() - flaggerClient := fakeFlagger.NewSimpleClientset(canary, abtest, appmeshCanary, ingressCanary) - - kubeClient := fake.NewSimpleClientset(newMockDeployment(), newMockABTestDeployment(), newMockIngress()) - - meshClient := fakeFlagger.NewSimpleClientset() - logger, _ := logger.NewLogger("debug") - - return fakeClients{ - canary: canary, - abtest: abtest, - appmeshCanary: appmeshCanary, - ingressCanary: ingressCanary, - kubeClient: kubeClient, - meshClient: meshClient, - flaggerClient: flaggerClient, - logger: logger, - } -} - -func newMockCanaryAppMesh() *v1alpha3.Canary { - cd := &v1alpha3.Canary{ - TypeMeta: metav1.TypeMeta{APIVersion: v1alpha3.SchemeGroupVersion.String()}, - ObjectMeta: metav1.ObjectMeta{ - Namespace: "default", - Name: "appmesh", - }, - Spec: v1alpha3.CanarySpec{ - TargetRef: hpav1.CrossVersionObjectReference{ - Name: "podinfo", - APIVersion: "apps/v1", - Kind: "Deployment", - }, - Service: v1alpha3.CanaryService{ - Port: 9898, - MeshName: "global", - Backends: []string{"backend.default"}, - }, CanaryAnalysis: v1alpha3.CanaryAnalysis{ - Threshold: 10, - StepWeight: 10, - MaxWeight: 50, - Metrics: []v1alpha3.CanaryMetric{ - { - Name: "appmesh_requests_total", - Threshold: 99, - Interval: "1m", - }, - }, - }, - }, - } - return cd -} - -func newMockCanary() *v1alpha3.Canary { - cd := &v1alpha3.Canary{ - TypeMeta: metav1.TypeMeta{APIVersion: v1alpha3.SchemeGroupVersion.String()}, - ObjectMeta: metav1.ObjectMeta{ - Namespace: "default", - Name: "podinfo", - }, - Spec: v1alpha3.CanarySpec{ - TargetRef: hpav1.CrossVersionObjectReference{ - Name: "podinfo", - APIVersion: "apps/v1", - Kind: "Deployment", - }, - Service: v1alpha3.CanaryService{ - Port: 9898, - Headers: &istiov1alpha3.Headers{ - Request: &istiov1alpha3.HeaderOperations{ - Add: map[string]string{ - "x-envoy-upstream-rq-timeout-ms": "15000", - }, - }, - }, - CorsPolicy: &istiov1alpha3.CorsPolicy{ - AllowMethods: []string{ - "GET", - "POST", - }, - }, - }, CanaryAnalysis: v1alpha3.CanaryAnalysis{ - Threshold: 10, - StepWeight: 10, - MaxWeight: 50, - Metrics: []v1alpha3.CanaryMetric{ - { - Name: "istio_requests_total", - Threshold: 99, - Interval: "1m", - }, - { - Name: "istio_request_duration_seconds_bucket", - Threshold: 500, - Interval: "1m", - }, - }, - }, - }, - } - return cd -} - -func newMockABTest() *v1alpha3.Canary { - cd := &v1alpha3.Canary{ - TypeMeta: metav1.TypeMeta{APIVersion: v1alpha3.SchemeGroupVersion.String()}, - ObjectMeta: metav1.ObjectMeta{ - Namespace: "default", - Name: "abtest", - }, - Spec: v1alpha3.CanarySpec{ - TargetRef: hpav1.CrossVersionObjectReference{ - Name: "abtest", - APIVersion: "apps/v1", - Kind: "Deployment", - }, - Service: v1alpha3.CanaryService{ - Port: 9898, - }, CanaryAnalysis: v1alpha3.CanaryAnalysis{ - Threshold: 10, - Iterations: 2, - Match: []istiov1alpha3.HTTPMatchRequest{ - { - Headers: map[string]istiov1alpha1.StringMatch{ - "x-user-type": { - Exact: "test", - }, - }, - }, - }, - Metrics: []v1alpha3.CanaryMetric{ - { - Name: "istio_requests_total", - Threshold: 99, - Interval: "1m", - }, - { - Name: "istio_request_duration_seconds_bucket", - Threshold: 500, - Interval: "1m", - }, - }, - }, - }, - } - return cd -} - -func newMockDeployment() *appsv1.Deployment { - d := &appsv1.Deployment{ - TypeMeta: metav1.TypeMeta{APIVersion: appsv1.SchemeGroupVersion.String()}, - ObjectMeta: metav1.ObjectMeta{ - Namespace: "default", - Name: "podinfo", - }, - Spec: appsv1.DeploymentSpec{ - Selector: &metav1.LabelSelector{ - MatchLabels: map[string]string{ - "app": "podinfo", - }, - }, - Template: corev1.PodTemplateSpec{ - ObjectMeta: metav1.ObjectMeta{ - Labels: map[string]string{ - "app": "podinfo", - }, - }, - Spec: corev1.PodSpec{ - Containers: []corev1.Container{ - { - Name: "podinfo", - Image: "quay.io/stefanprodan/podinfo:1.4.0", - Command: []string{ - "./podinfo", - "--port=9898", - }, - Ports: []corev1.ContainerPort{ - { - Name: "http", - ContainerPort: 9898, - Protocol: corev1.ProtocolTCP, - }, - }, - }, - }, - }, - }, - }, - } - - return d -} - -func newMockABTestDeployment() *appsv1.Deployment { - d := &appsv1.Deployment{ - TypeMeta: metav1.TypeMeta{APIVersion: appsv1.SchemeGroupVersion.String()}, - ObjectMeta: metav1.ObjectMeta{ - Namespace: "default", - Name: "abtest", - }, - Spec: appsv1.DeploymentSpec{ - Selector: &metav1.LabelSelector{ - MatchLabels: map[string]string{ - "app": "abtest", - }, - }, - Template: corev1.PodTemplateSpec{ - ObjectMeta: metav1.ObjectMeta{ - Labels: map[string]string{ - "app": "abtest", - }, - }, - Spec: corev1.PodSpec{ - Containers: []corev1.Container{ - { - Name: "podinfo", - Image: "quay.io/stefanprodan/podinfo:1.4.0", - Command: []string{ - "./podinfo", - "--port=9898", - }, - Ports: []corev1.ContainerPort{ - { - Name: "http", - ContainerPort: 9898, - Protocol: corev1.ProtocolTCP, - }, - }, - }, - }, - }, - }, - }, - } - - return d -} - -func newMockCanaryIngress() *v1alpha3.Canary { - cd := &v1alpha3.Canary{ - TypeMeta: metav1.TypeMeta{APIVersion: v1alpha3.SchemeGroupVersion.String()}, - ObjectMeta: metav1.ObjectMeta{ - Namespace: "default", - Name: "nginx", - }, - Spec: v1alpha3.CanarySpec{ - TargetRef: hpav1.CrossVersionObjectReference{ - Name: "podinfo", - APIVersion: "apps/v1", - Kind: "Deployment", - }, - IngressRef: &hpav1.CrossVersionObjectReference{ - Name: "podinfo", - APIVersion: "extensions/v1beta1", - Kind: "Ingress", - }, - Service: v1alpha3.CanaryService{ - Port: 9898, - }, CanaryAnalysis: v1alpha3.CanaryAnalysis{ - Threshold: 10, - StepWeight: 10, - MaxWeight: 50, - Metrics: []v1alpha3.CanaryMetric{ - { - Name: "request-success-rate", - Threshold: 99, - Interval: "1m", - }, - }, - }, - }, - } - return cd -} - -func newMockIngress() *v1beta1.Ingress { - return &v1beta1.Ingress{ - TypeMeta: metav1.TypeMeta{APIVersion: v1beta1.SchemeGroupVersion.String()}, - ObjectMeta: metav1.ObjectMeta{ - Namespace: "default", - Name: "podinfo", - Annotations: map[string]string{ - "kubernetes.io/ingress.class": "nginx", - }, - }, - Spec: v1beta1.IngressSpec{ - Rules: []v1beta1.IngressRule{ - { - Host: "app.example.com", - IngressRuleValue: v1beta1.IngressRuleValue{ - HTTP: &v1beta1.HTTPIngressRuleValue{ - Paths: []v1beta1.HTTPIngressPath{ - { - Path: "/", - Backend: v1beta1.IngressBackend{ - ServiceName: "podinfo", - ServicePort: intstr.FromInt(9898), - }, - }, - }, - }, - }, - }, - }, - }, - } -} diff --git a/pkg/router/smi.go b/pkg/router/smi.go deleted file mode 100644 index 4bdb2fbd..00000000 --- a/pkg/router/smi.go +++ /dev/null @@ -1,190 +0,0 @@ -package router - -import ( - "encoding/json" - "fmt" - - "github.com/google/go-cmp/cmp" - "github.com/google/go-cmp/cmp/cmpopts" - flaggerv1 "github.com/weaveworks/flagger/pkg/apis/flagger/v1alpha3" - smiv1 "github.com/weaveworks/flagger/pkg/apis/smi/v1alpha1" - clientset "github.com/weaveworks/flagger/pkg/client/clientset/versioned" - "go.uber.org/zap" - "k8s.io/apimachinery/pkg/api/errors" - "k8s.io/apimachinery/pkg/api/resource" - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - "k8s.io/apimachinery/pkg/runtime/schema" - "k8s.io/client-go/kubernetes" -) - -type SmiRouter struct { - kubeClient kubernetes.Interface - flaggerClient clientset.Interface - smiClient clientset.Interface - logger *zap.SugaredLogger - targetMesh string -} - -// Reconcile creates or updates the SMI traffic split -func (sr *SmiRouter) Reconcile(canary *flaggerv1.Canary) error { - targetName := canary.Spec.TargetRef.Name - canaryName := fmt.Sprintf("%s-canary", targetName) - primaryName := fmt.Sprintf("%s-primary", targetName) - - var host string - if len(canary.Spec.Service.Hosts) > 0 { - host = canary.Spec.Service.Hosts[0] - } else { - host = targetName - } - - tsSpec := smiv1.TrafficSplitSpec{ - Service: host, - Backends: []smiv1.TrafficSplitBackend{ - { - Service: canaryName, - Weight: resource.NewQuantity(0, resource.DecimalExponent), - }, - { - Service: primaryName, - Weight: resource.NewQuantity(100, resource.DecimalExponent), - }, - }, - } - - ts, err := sr.smiClient.SplitV1alpha1().TrafficSplits(canary.Namespace).Get(targetName, metav1.GetOptions{}) - // create traffic split - if errors.IsNotFound(err) { - t := &smiv1.TrafficSplit{ - ObjectMeta: metav1.ObjectMeta{ - Name: targetName, - Namespace: canary.Namespace, - OwnerReferences: []metav1.OwnerReference{ - *metav1.NewControllerRef(canary, schema.GroupVersionKind{ - Group: flaggerv1.SchemeGroupVersion.Group, - Version: flaggerv1.SchemeGroupVersion.Version, - Kind: flaggerv1.CanaryKind, - }), - }, - Annotations: sr.makeAnnotations(canary.Spec.Service.Gateways), - }, - Spec: tsSpec, - } - - _, err := sr.smiClient.SplitV1alpha1().TrafficSplits(canary.Namespace).Create(t) - if err != nil { - return err - } - - sr.logger.With("canary", fmt.Sprintf("%s.%s", canary.Name, canary.Namespace)). - Infof("TrafficSplit %s.%s created", t.GetName(), canary.Namespace) - return nil - } - - if err != nil { - return fmt.Errorf("traffic split %s query error %v", targetName, err) - } - - // update traffic split - if diff := cmp.Diff(tsSpec, ts.Spec, cmpopts.IgnoreTypes(resource.Quantity{})); diff != "" { - tsClone := ts.DeepCopy() - tsClone.Spec = tsSpec - - _, err := sr.smiClient.SplitV1alpha1().TrafficSplits(canary.Namespace).Update(tsClone) - if err != nil { - return fmt.Errorf("TrafficSplit %s update error %v", targetName, err) - } - - sr.logger.With("canary", fmt.Sprintf("%s.%s", canary.Name, canary.Namespace)). - Infof("TrafficSplit %s.%s updated", targetName, canary.Namespace) - return nil - } - - return nil -} - -// GetRoutes returns the destinations weight for primary and canary -func (sr *SmiRouter) GetRoutes(canary *flaggerv1.Canary) ( - primaryWeight int, - canaryWeight int, - err error, -) { - targetName := canary.Spec.TargetRef.Name - canaryName := fmt.Sprintf("%s-canary", targetName) - primaryName := fmt.Sprintf("%s-primary", targetName) - ts, err := sr.smiClient.SplitV1alpha1().TrafficSplits(canary.Namespace).Get(targetName, metav1.GetOptions{}) - if err != nil { - if errors.IsNotFound(err) { - err = fmt.Errorf("TrafficSplit %s.%s not found", targetName, canary.Namespace) - return - } - err = fmt.Errorf("TrafficSplit %s.%s query error %v", targetName, canary.Namespace, err) - return - } - - for _, r := range ts.Spec.Backends { - w, _ := r.Weight.AsInt64() - if r.Service == primaryName { - primaryWeight = int(w) - } - if r.Service == canaryName { - canaryWeight = int(w) - } - } - - if primaryWeight == 0 && canaryWeight == 0 { - err = fmt.Errorf("TrafficSplit %s.%s does not contain routes for %s and %s", - targetName, canary.Namespace, primaryName, canaryName) - } - - return -} - -// SetRoutes updates the destinations weight for primary and canary -func (sr *SmiRouter) SetRoutes( - canary *flaggerv1.Canary, - primaryWeight int, - canaryWeight int, -) error { - targetName := canary.Spec.TargetRef.Name - canaryName := fmt.Sprintf("%s-canary", targetName) - primaryName := fmt.Sprintf("%s-primary", targetName) - ts, err := sr.smiClient.SplitV1alpha1().TrafficSplits(canary.Namespace).Get(targetName, metav1.GetOptions{}) - if err != nil { - if errors.IsNotFound(err) { - return fmt.Errorf("TrafficSplit %s.%s not found", targetName, canary.Namespace) - - } - return fmt.Errorf("TrafficSplit %s.%s query error %v", targetName, canary.Namespace, err) - } - - backends := []smiv1.TrafficSplitBackend{ - { - Service: canaryName, - Weight: resource.NewQuantity(int64(canaryWeight), resource.DecimalExponent), - }, - { - Service: primaryName, - Weight: resource.NewQuantity(int64(primaryWeight), resource.DecimalExponent), - }, - } - - tsClone := ts.DeepCopy() - tsClone.Spec.Backends = backends - - _, err = sr.smiClient.SplitV1alpha1().TrafficSplits(canary.Namespace).Update(tsClone) - if err != nil { - return fmt.Errorf("TrafficSplit %s update error %v", targetName, err) - } - - return nil -} - -func (sr *SmiRouter) makeAnnotations(gateways []string) map[string]string { - res := make(map[string]string) - if sr.targetMesh == "istio" && len(gateways) > 0 { - g, _ := json.Marshal(gateways) - res["VirtualService.v1alpha3.networking.istio.io/spec.gateways"] = string(g) - } - return res -} diff --git a/pkg/router/supergloo.go b/pkg/router/supergloo.go deleted file mode 100644 index 762e845c..00000000 --- a/pkg/router/supergloo.go +++ /dev/null @@ -1,344 +0,0 @@ -package router - -import ( - "context" - "fmt" - "strings" - "time" - - solokitclients "github.com/solo-io/solo-kit/pkg/api/v1/clients" - "github.com/solo-io/solo-kit/pkg/api/v1/clients/factory" - "github.com/solo-io/solo-kit/pkg/api/v1/clients/kube" - crdv1 "github.com/solo-io/solo-kit/pkg/api/v1/clients/kube/crd/solo.io/v1" - solokitcore "github.com/solo-io/solo-kit/pkg/api/v1/resources/core" - solokiterror "github.com/solo-io/solo-kit/pkg/errors" - - types "github.com/gogo/protobuf/types" - gloov1 "github.com/solo-io/gloo/projects/gloo/pkg/api/v1" - supergloov1alpha3 "github.com/solo-io/supergloo/pkg/api/external/istio/networking/v1alpha3" - supergloov1 "github.com/solo-io/supergloo/pkg/api/v1" - flaggerv1 "github.com/weaveworks/flagger/pkg/apis/flagger/v1alpha3" - istiov1alpha3 "github.com/weaveworks/flagger/pkg/apis/istio/v1alpha3" - clientset "github.com/weaveworks/flagger/pkg/client/clientset/versioned" - "go.uber.org/zap" - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - "k8s.io/apimachinery/pkg/runtime/schema" - "k8s.io/client-go/rest" -) - -// SuperglooRouter is managing Istio virtual services -type SuperglooRouter struct { - rrClient supergloov1.RoutingRuleClient - logger *zap.SugaredLogger - targetMesh solokitcore.ResourceRef -} - -func NewSuperglooRouter(ctx context.Context, provider string, flaggerClient clientset.Interface, logger *zap.SugaredLogger, cfg *rest.Config) (*SuperglooRouter, error) { - // TODO if cfg is nil use memory client instead? - sharedCache := kube.NewKubeCache(ctx) - routingRuleClient, err := supergloov1.NewRoutingRuleClient(&factory.KubeResourceClientFactory{ - Crd: supergloov1.RoutingRuleCrd, - Cfg: cfg, - SharedCache: sharedCache, - SkipCrdCreation: true, - }) - if err != nil { - // this should never happen. - return nil, fmt.Errorf("creating RoutingRule client %v", err) - } - if err := routingRuleClient.Register(); err != nil { - return nil, err - } - - // remove the supergloo: prefix - provider = strings.TrimPrefix(provider, "supergloo:") - // split name.namespace: - parts := strings.Split(provider, ".") - if len(parts) != 2 { - return nil, fmt.Errorf("invalid format for supergloo provider") - } - targetMesh := solokitcore.ResourceRef{ - Namespace: parts[1], - Name: parts[0], - } - return NewSuperglooRouterWithClient(ctx, routingRuleClient, targetMesh, logger), nil -} - -func NewSuperglooRouterWithClient(ctx context.Context, routingRuleClient supergloov1.RoutingRuleClient, targetMesh solokitcore.ResourceRef, logger *zap.SugaredLogger) *SuperglooRouter { - return &SuperglooRouter{rrClient: routingRuleClient, logger: logger, targetMesh: targetMesh} -} - -// Reconcile creates or updates the Istio virtual service -func (sr *SuperglooRouter) Reconcile(canary *flaggerv1.Canary) error { - - if err := sr.setRetries(canary); err != nil { - return err - } - if err := sr.setHeaders(canary); err != nil { - return err - } - if err := sr.setCors(canary); err != nil { - return err - } - - // do we have routes already? - if _, _, err := sr.GetRoutes(canary); err == nil { - // we have routes, no need to do anything else - return nil - } else if solokiterror.IsNotExist(err) { - return sr.SetRoutes(canary, 100, 0) - } else { - return err - } -} - -func (sr *SuperglooRouter) setRetries(canary *flaggerv1.Canary) error { - if canary.Spec.Service.Retries == nil { - return nil - } - retries, err := convertRetries(canary.Spec.Service.Retries) - if err != nil { - return err - } - rule := sr.createRule(canary, "retries", &supergloov1.RoutingRuleSpec{ - RuleType: &supergloov1.RoutingRuleSpec_Retries{ - Retries: retries, - }, - }) - - return sr.writeRuleForCanary(canary, rule) -} -func (sr *SuperglooRouter) setHeaders(canary *flaggerv1.Canary) error { - if canary.Spec.Service.Headers == nil { - return nil - } - headerManipulation, err := convertHeaders(canary.Spec.Service.Headers) - if err != nil { - return err - } - if headerManipulation == nil { - return nil - } - rule := sr.createRule(canary, "headers", &supergloov1.RoutingRuleSpec{ - RuleType: &supergloov1.RoutingRuleSpec_HeaderManipulation{ - HeaderManipulation: headerManipulation, - }, - }) - - return sr.writeRuleForCanary(canary, rule) -} - -func convertHeaders(headers *istiov1alpha3.Headers) (*supergloov1.HeaderManipulation, error) { - var headersMaipulation *supergloov1.HeaderManipulation - - if headers.Request != nil { - headersMaipulation = &supergloov1.HeaderManipulation{} - - headersMaipulation.RemoveRequestHeaders = headers.Request.Remove - headersMaipulation.AppendRequestHeaders = make(map[string]string) - for k, v := range headers.Request.Add { - headersMaipulation.AppendRequestHeaders[k] = v - } - } - if headers.Response != nil { - if headersMaipulation == nil { - headersMaipulation = &supergloov1.HeaderManipulation{} - } - - headersMaipulation.RemoveResponseHeaders = headers.Response.Remove - headersMaipulation.AppendResponseHeaders = make(map[string]string) - for k, v := range headers.Response.Add { - headersMaipulation.AppendResponseHeaders[k] = v - } - } - - return headersMaipulation, nil -} - -func convertRetries(retries *istiov1alpha3.HTTPRetry) (*supergloov1.RetryPolicy, error) { - perTryTimeout, err := time.ParseDuration(retries.PerTryTimeout) - return &supergloov1.RetryPolicy{ - MaxRetries: &supergloov1alpha3.HTTPRetry{ - Attempts: int32(retries.Attempts), - PerTryTimeout: types.DurationProto(perTryTimeout), - RetryOn: retries.RetryOn, - }, - }, err -} - -func (sr *SuperglooRouter) setCors(canary *flaggerv1.Canary) error { - corsPolicy := canary.Spec.Service.CorsPolicy - if corsPolicy == nil { - return nil - } - var maxAgeDuration *types.Duration - if maxAge, err := time.ParseDuration(corsPolicy.MaxAge); err == nil { - maxAgeDuration = types.DurationProto(maxAge) - } - - rule := sr.createRule(canary, "cors", &supergloov1.RoutingRuleSpec{ - RuleType: &supergloov1.RoutingRuleSpec_CorsPolicy{ - CorsPolicy: &supergloov1alpha3.CorsPolicy{ - AllowOrigin: corsPolicy.AllowOrigin, - AllowMethods: corsPolicy.AllowMethods, - AllowHeaders: corsPolicy.AllowHeaders, - ExposeHeaders: corsPolicy.ExposeHeaders, - MaxAge: maxAgeDuration, - AllowCredentials: &types.BoolValue{Value: corsPolicy.AllowCredentials}, - }, - }, - }) - return sr.writeRuleForCanary(canary, rule) -} - -func (sr *SuperglooRouter) createRule(canary *flaggerv1.Canary, namesuffix string, spec *supergloov1.RoutingRuleSpec) *supergloov1.RoutingRule { - if namesuffix != "" { - namesuffix = "-" + namesuffix - } - return &supergloov1.RoutingRule{ - Metadata: solokitcore.Metadata{ - Name: canary.Spec.TargetRef.Name + namesuffix, - Namespace: canary.Namespace, - }, - TargetMesh: &sr.targetMesh, - DestinationSelector: &supergloov1.PodSelector{ - SelectorType: &supergloov1.PodSelector_UpstreamSelector_{ - UpstreamSelector: &supergloov1.PodSelector_UpstreamSelector{ - Upstreams: []solokitcore.ResourceRef{{ - Name: upstreamName(canary.Namespace, fmt.Sprintf("%s", canary.Spec.TargetRef.Name), canary.Spec.Service.Port), - Namespace: sr.targetMesh.Namespace, - }}, - }, - }, - }, - Spec: spec, - } -} - -// GetRoutes returns the destinations weight for primary and canary -func (sr *SuperglooRouter) GetRoutes(canary *flaggerv1.Canary) ( - primaryWeight int, - canaryWeight int, - err error, -) { - targetName := canary.Spec.TargetRef.Name - var rr *supergloov1.RoutingRule - rr, err = sr.rrClient.Read(canary.Namespace, targetName, solokitclients.ReadOpts{}) - if err != nil { - return - } - traffic := rr.GetSpec().GetTrafficShifting() - if traffic == nil { - err = fmt.Errorf("target rule is not for traffic shifting") - return - } - dests := traffic.GetDestinations().GetDestinations() - for _, dest := range dests { - if dest.GetDestination().GetUpstream().Name == upstreamName(canary.Namespace, fmt.Sprintf("%s-primary", targetName), canary.Spec.Service.Port) { - primaryWeight = int(dest.Weight) - } - if dest.GetDestination().GetUpstream().Name == upstreamName(canary.Namespace, fmt.Sprintf("%s-canary", targetName), canary.Spec.Service.Port) { - canaryWeight = int(dest.Weight) - } - } - - if primaryWeight == 0 && canaryWeight == 0 { - err = fmt.Errorf("RoutingRule %s.%s does not contain routes for %s-primary and %s-canary", - targetName, canary.Namespace, targetName, targetName) - } - - return -} - -func upstreamName(serviceNamespace, serviceName string, port int32) string { - return fmt.Sprintf("%s-%s-%d", serviceNamespace, serviceName, port) -} - -// SetRoutes updates the destinations weight for primary and canary -func (sr *SuperglooRouter) SetRoutes( - canary *flaggerv1.Canary, - primaryWeight int, - canaryWeight int, -) error { - // upstream name is - // in gloo-system - // and is the same as - targetName := canary.Spec.TargetRef.Name - - destinations := []*gloov1.WeightedDestination{} - if primaryWeight != 0 { - destinations = append(destinations, &gloov1.WeightedDestination{ - Destination: &gloov1.Destination{ - Upstream: solokitcore.ResourceRef{ - Name: upstreamName(canary.Namespace, fmt.Sprintf("%s-primary", targetName), canary.Spec.Service.Port), - Namespace: sr.targetMesh.Namespace, - }, - }, - Weight: uint32(primaryWeight), - }) - } - - if canaryWeight != 0 { - destinations = append(destinations, &gloov1.WeightedDestination{ - Destination: &gloov1.Destination{ - Upstream: solokitcore.ResourceRef{ - Name: upstreamName(canary.Namespace, fmt.Sprintf("%s-canary", targetName), canary.Spec.Service.Port), - Namespace: sr.targetMesh.Namespace, - }, - }, - Weight: uint32(canaryWeight), - }) - } - - if len(destinations) == 0 { - return fmt.Errorf("RoutingRule %s.%s update failed: no valid weights", targetName, canary.Namespace) - } - - rule := sr.createRule(canary, "", &supergloov1.RoutingRuleSpec{ - RuleType: &supergloov1.RoutingRuleSpec_TrafficShifting{ - TrafficShifting: &supergloov1.TrafficShifting{ - Destinations: &gloov1.MultiDestination{ - Destinations: destinations, - }, - }, - }, - }) - - return sr.writeRuleForCanary(canary, rule) -} - -func (sr *SuperglooRouter) writeRuleForCanary(canary *flaggerv1.Canary, rule *supergloov1.RoutingRule) error { - targetName := canary.Spec.TargetRef.Name - - if oldRr, err := sr.rrClient.Read(rule.Metadata.Namespace, rule.Metadata.Name, solokitclients.ReadOpts{}); err != nil { - // ignore not exist errors.. - if !solokiterror.IsNotExist(err) { - return fmt.Errorf("RoutingRule %s.%s read failed: %v", targetName, canary.Namespace, err) - } - } else { - rule.Metadata.ResourceVersion = oldRr.Metadata.ResourceVersion - // if the old and the new one are equal, no need to do anything. - oldRr.Status = solokitcore.Status{} - if oldRr.Equal(rule) { - return nil - } - } - - kubeWriteOpts := &kube.KubeWriteOpts{ - PreWriteCallback: func(r *crdv1.Resource) { - r.ObjectMeta.OwnerReferences = []metav1.OwnerReference{ - *metav1.NewControllerRef(canary, schema.GroupVersionKind{ - Group: flaggerv1.SchemeGroupVersion.Group, - Version: flaggerv1.SchemeGroupVersion.Version, - Kind: flaggerv1.CanaryKind, - }), - } - }, - } - writeOpts := solokitclients.WriteOpts{OverwriteExisting: true, StorageWriteOpts: kubeWriteOpts} - _, err := sr.rrClient.Write(rule, writeOpts) - if err != nil { - return fmt.Errorf("RoutingRule %s.%s update failed: %v", targetName, canary.Namespace, err) - } - return nil -} diff --git a/pkg/router/supergloo_test.go b/pkg/router/supergloo_test.go deleted file mode 100644 index aa500b63..00000000 --- a/pkg/router/supergloo_test.go +++ /dev/null @@ -1,149 +0,0 @@ -package router - -import ( - "context" - "fmt" - "testing" - - gloov1 "github.com/solo-io/gloo/projects/gloo/pkg/api/v1" - solokitclients "github.com/solo-io/solo-kit/pkg/api/v1/clients" - "github.com/solo-io/solo-kit/pkg/api/v1/clients/factory" - solokitmemory "github.com/solo-io/solo-kit/pkg/api/v1/clients/memory" - solokitcore "github.com/solo-io/solo-kit/pkg/api/v1/resources/core" - supergloov1 "github.com/solo-io/supergloo/pkg/api/v1" -) - -func TestSuperglooRouter_Sync(t *testing.T) { - mocks := setupfakeClients() - - routingRuleClient, err := supergloov1.NewRoutingRuleClient(&factory.MemoryResourceClientFactory{ - Cache: solokitmemory.NewInMemoryResourceCache(), - }) - if err != nil { - t.Fatal(err.Error()) - } - if err := routingRuleClient.Register(); err != nil { - t.Fatal(err.Error()) - } - targetMesh := solokitcore.ResourceRef{ - Namespace: "supergloo-system", - Name: "mesh", - } - router := NewSuperglooRouterWithClient(context.TODO(), routingRuleClient, targetMesh, mocks.logger) - err = router.Reconcile(mocks.canary) - if err != nil { - t.Fatal(err.Error()) - } - - // test insert - rr, err := routingRuleClient.Read("default", "podinfo", solokitclients.ReadOpts{}) - if err != nil { - t.Fatal(err.Error()) - } - dests := rr.Spec.GetTrafficShifting().GetDestinations().GetDestinations() - if len(dests) != 1 { - t.Errorf("Got RoutingRule Destinations %v wanted %v", len(dests), 1) - } - -} - -func TestSuperglooRouter_SetRoutes(t *testing.T) { - - mocks := setupfakeClients() - - routingRuleClient, err := supergloov1.NewRoutingRuleClient(&factory.MemoryResourceClientFactory{ - Cache: solokitmemory.NewInMemoryResourceCache(), - }) - if err != nil { - t.Fatal(err.Error()) - } - if err := routingRuleClient.Register(); err != nil { - t.Fatal(err.Error()) - } - targetMesh := solokitcore.ResourceRef{ - Namespace: "supergloo-system", - Name: "mesh", - } - router := NewSuperglooRouterWithClient(context.TODO(), routingRuleClient, targetMesh, mocks.logger) - - err = router.Reconcile(mocks.canary) - if err != nil { - t.Fatal(err.Error()) - } - - p, c, err := router.GetRoutes(mocks.canary) - if err != nil { - t.Fatal(err.Error()) - } - - p = 50 - c = 50 - - err = router.SetRoutes(mocks.canary, p, c) - if err != nil { - t.Fatal(err.Error()) - } - - rr, err := routingRuleClient.Read("default", "podinfo", solokitclients.ReadOpts{}) - if err != nil { - t.Fatal(err.Error()) - } - - var pRoute *gloov1.WeightedDestination - var cRoute *gloov1.WeightedDestination - targetName := mocks.canary.Spec.TargetRef.Name - - for _, dest := range rr.GetSpec().GetTrafficShifting().GetDestinations().GetDestinations() { - if dest.GetDestination().GetUpstream().Name == upstreamName(mocks.canary.Namespace, fmt.Sprintf("%s-primary", targetName), mocks.canary.Spec.Service.Port) { - pRoute = dest - } - if dest.GetDestination().GetUpstream().Name == upstreamName(mocks.canary.Namespace, fmt.Sprintf("%s-canary", targetName), mocks.canary.Spec.Service.Port) { - cRoute = dest - } - } - - if pRoute.Weight != uint32(p) { - t.Errorf("Got primary weight %v wanted %v", pRoute.Weight, p) - } - - if cRoute.Weight != uint32(c) { - t.Errorf("Got canary weight %v wanted %v", cRoute.Weight, c) - } - -} - -func TestSuperglooRouter_GetRoutes(t *testing.T) { - mocks := setupfakeClients() - - routingRuleClient, err := supergloov1.NewRoutingRuleClient(&factory.MemoryResourceClientFactory{ - Cache: solokitmemory.NewInMemoryResourceCache(), - }) - if err != nil { - t.Fatal(err.Error()) - } - if err := routingRuleClient.Register(); err != nil { - t.Fatal(err.Error()) - } - targetMesh := solokitcore.ResourceRef{ - Namespace: "supergloo-system", - Name: "mesh", - } - router := NewSuperglooRouterWithClient(context.TODO(), routingRuleClient, targetMesh, mocks.logger) - err = router.Reconcile(mocks.canary) - if err != nil { - t.Fatal(err.Error()) - } - - p, c, err := router.GetRoutes(mocks.canary) - if err != nil { - t.Fatal(err.Error()) - } - - if p != 100 { - t.Errorf("Got primary weight %v wanted %v", p, 100) - } - - if c != 0 { - t.Errorf("Got canary weight %v wanted %v", c, 0) - } -} diff --git a/pkg/server/server.go b/pkg/server/server.go deleted file mode 100644 index b4ba1687..00000000 --- a/pkg/server/server.go +++ /dev/null @@ -1,48 +0,0 @@ -package server - -import ( - "context" - "net/http" - "time" - - "github.com/prometheus/client_golang/prometheus/promhttp" - "go.uber.org/zap" -) - -// ListenAndServe starts a web server and waits for SIGTERM -func ListenAndServe(port string, timeout time.Duration, logger *zap.SugaredLogger, stopCh <-chan struct{}) { - mux := http.DefaultServeMux - mux.Handle("/metrics", promhttp.Handler()) - mux.HandleFunc("/healthz", func(w http.ResponseWriter, r *http.Request) { - w.WriteHeader(http.StatusOK) - w.Write([]byte("OK")) - }) - - srv := &http.Server{ - Addr: ":" + port, - Handler: mux, - ReadTimeout: 5 * time.Second, - WriteTimeout: 1 * time.Minute, - IdleTimeout: 15 * time.Second, - } - - logger.Infof("Starting HTTP server on port %s", port) - - // run server in background - go func() { - if err := srv.ListenAndServe(); err != http.ErrServerClosed { - logger.Fatalf("HTTP server crashed %v", err) - } - }() - - // wait for SIGTERM or SIGINT - <-stopCh - ctx, cancel := context.WithTimeout(context.Background(), timeout) - defer cancel() - - if err := srv.Shutdown(ctx); err != nil { - logger.Errorf("HTTP server graceful shutdown failed %v", err) - } else { - logger.Info("HTTP server stopped") - } -} diff --git a/pkg/signals/signal.go b/pkg/signals/signal.go deleted file mode 100644 index 61f44717..00000000 --- a/pkg/signals/signal.go +++ /dev/null @@ -1,40 +0,0 @@ -/* -Copyright 2017 The Kubernetes Authors. -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - http://www.apache.org/licenses/LICENSE-2.0 -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -package signals - -import ( - "os" - "os/signal" -) - -var onlyOneSignalHandler = make(chan struct{}) - -// SetupSignalHandler registered for SIGTERM and SIGINT. A stop channel is returned -// which is closed on one of these signals. If a second signal is caught, the program -// is terminated with exit code 1. -func SetupSignalHandler() (stopCh <-chan struct{}) { - close(onlyOneSignalHandler) // panics when called twice - - stop := make(chan struct{}) - c := make(chan os.Signal, 2) - signal.Notify(c, shutdownSignals...) - go func() { - <-c - close(stop) - <-c - os.Exit(1) // second signal. Exit directly. - }() - - return stop -} diff --git a/pkg/signals/signal_posix.go b/pkg/signals/signal_posix.go deleted file mode 100644 index 81fe1739..00000000 --- a/pkg/signals/signal_posix.go +++ /dev/null @@ -1,23 +0,0 @@ -// +build !windows - -/* -Copyright 2017 The Kubernetes Authors. -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - http://www.apache.org/licenses/LICENSE-2.0 -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -package signals - -import ( - "os" - "syscall" -) - -var shutdownSignals = []os.Signal{os.Interrupt, syscall.SIGTERM} diff --git a/pkg/signals/signal_windows.go b/pkg/signals/signal_windows.go deleted file mode 100644 index 72a5650a..00000000 --- a/pkg/signals/signal_windows.go +++ /dev/null @@ -1,20 +0,0 @@ -/* -Copyright 2017 The Kubernetes Authors. -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - http://www.apache.org/licenses/LICENSE-2.0 -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -package signals - -import ( - "os" -) - -var shutdownSignals = []os.Signal{os.Interrupt} diff --git a/pkg/version/version.go b/pkg/version/version.go deleted file mode 100644 index c52f6568..00000000 --- a/pkg/version/version.go +++ /dev/null @@ -1,4 +0,0 @@ -package version - -var VERSION = "0.18.2" -var REVISION = "unknown" diff --git a/test/README.md b/test/README.md deleted file mode 100644 index 264625b2..00000000 --- a/test/README.md +++ /dev/null @@ -1,38 +0,0 @@ -# Flagger end-to-end testing - -The e2e testing infrastructure is powered by CircleCI and [Kubernetes Kind](https://github.com/kubernetes-sigs/kind). - -### CircleCI e2e Istio workflow - -* install latest stable kubectl [e2e-kind.sh](e2e-kind.sh) -* install Kubernetes Kind [e2e-kind.sh](e2e-kind.sh) -* create local Kubernetes cluster with kind [e2e-kind.sh](e2e-kind.sh) -* install latest stable Helm CLI [e2e-istio.sh](e2e-istio.sh) -* deploy Tiller on the local cluster [e2e-istio.sh](e2e-istio.sh) -* install Istio CRDs with Helm [e2e-istio.sh](e2e-istio.sh) -* install Istio control plane and Prometheus with Helm [e2e-istio.sh](e2e-istio.sh) -* load Flagger image onto the local cluster [e2e-istio.sh](e2e-istio.sh) -* deploy Flagger in the istio-system namespace [e2e-istio.sh](e2e-istio.sh) -* create a test namespace with Istio injection enabled [e2e-tests.sh](e2e-tests.sh) -* deploy the load tester in the test namespace [e2e-tests.sh](e2e-tests.sh) -* deploy a demo workload (podinfo) in the test namespace [e2e-tests.sh](e2e-tests.sh) -* test the canary initialization [e2e-tests.sh](e2e-tests.sh) -* test the canary analysis and promotion using weighted traffic and the load testing webhook [e2e-tests.sh](e2e-tests.sh) -* test the A/B testing analysis and promotion using cookies filters and pre/post rollout webhooks [e2e-tests.sh](e2e-tests.sh) - -### CircleCI e2e NGINX ingress workflow - -* install latest stable kubectl [e2e-kind.sh](e2e-kind.sh) -* install Kubernetes Kind [e2e-kind.sh](e2e-kind.sh) -* create local Kubernetes cluster with kind [e2e-kind.sh](e2e-kind.sh) -* install latest stable Helm CLI [e2e-nginx.sh](e2e-istio.sh) -* deploy Tiller on the local cluster [e2e-nginx.sh](e2e-istio.sh) -* install NGINX ingress with Helm [e2e-nginx.sh](e2e-istio.sh) -* load Flagger image onto the local cluster [e2e-nginx.sh](e2e-nginx.sh) -* install Flagger and Prometheus in the ingress-nginx namespace [e2e-nginx.sh](e2e-nginx.sh) -* create a test namespace [e2e-nginx-tests.sh](e2e-tests.sh) -* deploy the load tester in the test namespace [e2e-nginx-tests.sh](e2e-tests.sh) -* deploy the demo workload (podinfo) and ingress in the test namespace [e2e-nginx-tests.sh](e2e-tests.sh) -* test the canary initialization [e2e-nginx-tests.sh](e2e-tests.sh) -* test the canary analysis and promotion using weighted traffic and the load testing webhook [e2e-nginx-tests.sh](e2e-tests.sh) -* test the A/B testing analysis and promotion using header filters and pre/post rollout webhooks [e2e-nginx-tests.sh](e2e-tests.sh) diff --git a/test/container-build.sh b/test/container-build.sh deleted file mode 100755 index 4ee2ccc8..00000000 --- a/test/container-build.sh +++ /dev/null @@ -1,12 +0,0 @@ -#!/usr/bin/env bash - -set -o errexit - -REPO_ROOT=$(git rev-parse --show-toplevel) - -mkdir -p ${REPO_ROOT}/bin -cp /tmp/bin/flagger ${REPO_ROOT}/bin && chmod +x ${REPO_ROOT}/bin/flagger -cp /tmp/bin/loadtester ${REPO_ROOT}/bin && chmod +x ${REPO_ROOT}/bin/loadtester - -docker build -t test/flagger:latest . -f ${REPO_ROOT}/Dockerfile -docker build -t test/flagger-loadtester:latest . -f ${REPO_ROOT}/Dockerfile.loadtester \ No newline at end of file diff --git a/test/container-push.sh b/test/container-push.sh deleted file mode 100755 index 5960fbac..00000000 --- a/test/container-push.sh +++ /dev/null @@ -1,26 +0,0 @@ -#!/usr/bin/env bash - -set -o errexit - - -push () { - echo $DOCKER_PASS | docker login -u=$DOCKER_USER --password-stdin - - if [[ -z "$CIRCLE_TAG" ]]; then - BRANCH_COMMIT=${CIRCLE_BRANCH}-$(echo ${CIRCLE_SHA1} | head -c7); - docker tag test/flagger:latest weaveworks/flagger:${BRANCH_COMMIT}; - docker push weaveworks/flagger:${BRANCH_COMMIT}; - else - docker tag test/flagger:latest weaveworks/flagger:${CIRCLE_TAG}; - docker tag test/flagger-loadtester:latest weaveworks/flagger-loadtester:${CIRCLE_TAG}; - docker push weaveworks/flagger:${CIRCLE_TAG}; - docker push weaveworks/flagger-loadtester:${CIRCLE_TAG}; - fi -} - -if [[ -z "$DOCKER_PASS" ]]; then - echo "No Docker Hub credentials, skipping image push"; -else - push -fi - diff --git a/test/e2e-gloo-tests.sh b/test/e2e-gloo-tests.sh deleted file mode 100755 index 45c5718e..00000000 --- a/test/e2e-gloo-tests.sh +++ /dev/null @@ -1,126 +0,0 @@ -#!/usr/bin/env bash - -# This script runs e2e tests for Canary initialization, analysis and promotion -# Prerequisites: Kubernetes Kind, Helm and NGINX ingress controller - -set -o errexit - -REPO_ROOT=$(git rev-parse --show-toplevel) -export KUBECONFIG="$(kind get kubeconfig-path --name="kind")" - -echo '>>> Creating test namespace' -kubectl create namespace test - -echo '>>> Installing load tester' -kubectl -n test apply -f ${REPO_ROOT}/artifacts/loadtester/ -kubectl -n test rollout status deployment/flagger-loadtester - -echo '>>> Initialising canary' -kubectl apply -f ${REPO_ROOT}/test/e2e-workload.yaml - -cat <>> Waiting for primary to be ready' -retries=50 -count=0 -ok=false -until ${ok}; do - kubectl -n test get canary/podinfo | grep 'Initialized' && ok=true || ok=false - sleep 5 - count=$(($count + 1)) - if [[ ${count} -eq ${retries} ]]; then - kubectl -n gloo-system logs deployment/flagger - echo "No more retries left" - exit 1 - fi -done - -echo '✔ Canary initialization test passed' - -echo '>>> Triggering canary deployment' -kubectl -n test set image deployment/podinfo podinfod=quay.io/stefanprodan/podinfo:1.4.1 - -echo '>>> Waiting for canary promotion' -retries=50 -count=0 -ok=false -until ${ok}; do - kubectl -n test describe deployment/podinfo-primary | grep '1.4.1' && ok=true || ok=false - sleep 10 - kubectl -n gloo-system logs deployment/flagger --tail 1 - count=$(($count + 1)) - if [[ ${count} -eq ${retries} ]]; then - kubectl -n test describe deployment/podinfo - kubectl -n test describe deployment/podinfo-primary - kubectl -n test logs deployment/flagger-loadtester - kubectl -n gloo-system logs deployment/flagger - kubectl -n gloo-system get all - kubectl -n gloo-system get virtualservice podinfo -oyaml - echo "No more retries left" - exit 1 - fi -done - -echo '✔ Canary promotion test passed' diff --git a/test/e2e-gloo.sh b/test/e2e-gloo.sh deleted file mode 100755 index 6f99dc32..00000000 --- a/test/e2e-gloo.sh +++ /dev/null @@ -1,30 +0,0 @@ -#!/usr/bin/env bash - -set -o errexit - -GLOO_VER="0.18.8" -REPO_ROOT=$(git rev-parse --show-toplevel) -export KUBECONFIG="$(kind get kubeconfig-path --name="kind")" - -echo '>>> Installing Gloo' -helm repo add gloo https://storage.googleapis.com/solo-public-helm -helm upgrade -i gloo gloo/gloo --version ${GLOO_VER} \ ---namespace gloo-system - -kubectl -n gloo-system rollout status deployment/gloo -kubectl -n gloo-system rollout status deployment/gateway-proxy-v2 -kubectl -n gloo-system get all - -echo '>>> Installing Flagger' -kind load docker-image test/flagger:latest - -echo '>>> Installing Flagger' -helm upgrade -i flagger ${REPO_ROOT}/charts/flagger \ ---namespace gloo-system \ ---set prometheus.install=true \ ---set meshProvider=gloo - -kubectl -n gloo-system set image deployment/flagger flagger=test/flagger:latest - -kubectl -n gloo-system rollout status deployment/flagger -kubectl -n gloo-system rollout status deployment/flagger-prometheus \ No newline at end of file diff --git a/test/e2e-ingress.yaml b/test/e2e-ingress.yaml deleted file mode 100644 index c5a6fa62..00000000 --- a/test/e2e-ingress.yaml +++ /dev/null @@ -1,17 +0,0 @@ -apiVersion: extensions/v1beta1 -kind: Ingress -metadata: - name: podinfo - namespace: test - labels: - app: podinfo - annotations: - kubernetes.io/ingress.class: "nginx" -spec: - rules: - - host: app.example.com - http: - paths: - - backend: - serviceName: podinfo - servicePort: 9898 diff --git a/test/e2e-istio-values.yaml b/test/e2e-istio-values.yaml deleted file mode 100644 index b234dedf..00000000 --- a/test/e2e-istio-values.yaml +++ /dev/null @@ -1,60 +0,0 @@ -# -# Minimal Istio Configuration required by Flagger -# - -# pilot configuration -pilot: - enabled: true - sidecar: true - resources: - requests: - cpu: 10m - memory: 128Mi - -gateways: - enabled: false - istio-ingressgateway: - autoscaleMax: 1 - -# sidecar-injector webhook configuration -sidecarInjectorWebhook: - enabled: true - -# galley configuration -galley: - enabled: false - -# mixer configuration -mixer: - policy: - enabled: false - telemetry: - enabled: true - replicaCount: 1 - autoscaleEnabled: false - resources: - requests: - cpu: 10m - memory: 128Mi - -# addon prometheus configuration -prometheus: - enabled: true - scrapeInterval: 5s - -# addon jaeger tracing configuration -tracing: - enabled: false - -# Common settings. -global: - proxy: - # Resources for the sidecar. - resources: - requests: - cpu: 10m - memory: 64Mi - limits: - cpu: 1000m - memory: 256Mi - useMCP: false diff --git a/test/e2e-istio.sh b/test/e2e-istio.sh deleted file mode 100755 index 8eabc72d..00000000 --- a/test/e2e-istio.sh +++ /dev/null @@ -1,32 +0,0 @@ -#!/usr/bin/env bash - -set -o errexit - -ISTIO_VER="1.2.3" -REPO_ROOT=$(git rev-parse --show-toplevel) -export KUBECONFIG="$(kind get kubeconfig-path --name="kind")" - -echo ">>> Installing Istio ${ISTIO_VER}" -helm repo add istio.io https://storage.googleapis.com/istio-release/releases/${ISTIO_VER}/charts - -echo '>>> Installing Istio CRDs' -helm upgrade -i istio-init istio.io/istio-init --wait --namespace istio-system - -echo '>>> Waiting for Istio CRDs to be ready' -kubectl -n istio-system wait --for=condition=complete job/istio-init-crd-10 -kubectl -n istio-system wait --for=condition=complete job/istio-init-crd-11 -kubectl -n istio-system wait --for=condition=complete job/istio-init-crd-12 - -echo '>>> Installing Istio control plane' -helm upgrade -i istio istio.io/istio --wait --namespace istio-system -f ${REPO_ROOT}/test/e2e-istio-values.yaml - -kubectl -n istio-system get all - -echo '>>> Load Flagger image in Kind' -kind load docker-image test/flagger:latest - -echo '>>> Installing Flagger' -kubectl apply -k ${REPO_ROOT}/kustomize/istio - -kubectl -n istio-system set image deployment/flagger flagger=test/flagger:latest -kubectl -n istio-system rollout status deployment/flagger \ No newline at end of file diff --git a/test/e2e-kind.sh b/test/e2e-kind.sh deleted file mode 100755 index 05b13334..00000000 --- a/test/e2e-kind.sh +++ /dev/null @@ -1,34 +0,0 @@ -#!/usr/bin/env bash - -set -o errexit - -REPO_ROOT=$(git rev-parse --show-toplevel) -KIND_VERSION=v0.4.0 - -if [[ "$1" ]]; then - KIND_VERSION=$1 -fi - -echo ">>> Installing kubectl" -curl -LO https://storage.googleapis.com/kubernetes-release/release/$(curl -s https://storage.googleapis.com/kubernetes-release/release/stable.txt)/bin/linux/amd64/kubectl && \ -chmod +x kubectl && \ -sudo mv kubectl /usr/local/bin/ - -echo ">>> Installing kind" -curl -sSLo kind "https://github.com/kubernetes-sigs/kind/releases/download/$KIND_VERSION/kind-linux-amd64" -chmod +x kind -sudo mv kind /usr/local/bin/kind - -echo ">>> Creating kind cluster" -kind create cluster --wait 5m - -export KUBECONFIG="$(kind get kubeconfig-path --name="kind")" -kubectl get pods --all-namespaces - -echo ">>> Installing Helm" -curl https://raw.githubusercontent.com/kubernetes/helm/master/scripts/get | bash - -echo '>>> Installing Tiller' -kubectl --namespace kube-system create sa tiller -kubectl create clusterrolebinding tiller-cluster-rule --clusterrole=cluster-admin --serviceaccount=kube-system:tiller -helm init --service-account tiller --upgrade --wait diff --git a/test/e2e-kubernetes-tests.sh b/test/e2e-kubernetes-tests.sh deleted file mode 100755 index 1af7f2af..00000000 --- a/test/e2e-kubernetes-tests.sh +++ /dev/null @@ -1,108 +0,0 @@ -#!/usr/bin/env bash - -# This script runs e2e tests for Blue/Green initialization, analysis and promotion -# Prerequisites: Kubernetes Kind, Kustomize - -set -o errexit - -REPO_ROOT=$(git rev-parse --show-toplevel) -export KUBECONFIG="$(kind get kubeconfig-path --name="kind")" - -echo '>>> Creating test namespace' -kubectl create namespace test - -echo '>>> Installing the load tester' -kubectl apply -k ${REPO_ROOT}/kustomize/tester -kubectl -n test rollout status deployment/flagger-loadtester - -echo '>>> Initialising canary' -kubectl apply -f ${REPO_ROOT}/test/e2e-workload.yaml - -cat <>> Waiting for primary to be ready' -retries=50 -count=0 -ok=false -until ${ok}; do - kubectl -n test get canary/podinfo | grep 'Initialized' && ok=true || ok=false - sleep 5 - count=$(($count + 1)) - if [[ ${count} -eq ${retries} ]]; then - kubectl -n flagger-system logs deployment/flagger - echo "No more retries left" - exit 1 - fi -done - -echo '✔ Canary initialization test passed' - -echo '>>> Triggering canary deployment' -kubectl -n test set image deployment/podinfo podinfod=quay.io/stefanprodan/podinfo:1.7.0 - -echo '>>> Waiting for canary promotion' -retries=50 -count=0 -ok=false -until ${ok}; do - kubectl -n test describe deployment/podinfo-primary | grep '1.7.0' && ok=true || ok=false - sleep 10 - kubectl -n flagger-system logs deployment/flagger --tail 1 - count=$(($count + 1)) - if [[ ${count} -eq ${retries} ]]; then - kubectl -n test describe deployment/podinfo - kubectl -n test describe deployment/podinfo-primary - kubectl -n flagger-system logs deployment/flagger - echo "No more retries left" - exit 1 - fi -done - -echo '✔ Canary promotion test passed' - -kubectl -n flagger-system logs deployment/flagger diff --git a/test/e2e-kubernetes.sh b/test/e2e-kubernetes.sh deleted file mode 100755 index c85ea1ee..00000000 --- a/test/e2e-kubernetes.sh +++ /dev/null @@ -1,17 +0,0 @@ -#!/usr/bin/env bash - -set -o errexit - -REPO_ROOT=$(git rev-parse --show-toplevel) -export KUBECONFIG="$(kind get kubeconfig-path --name="kind")" - -echo '>>> Loading Flagger image' -kind load docker-image test/flagger:latest - -echo '>>> Installing Flagger' -kubectl apply -k ${REPO_ROOT}/kustomize/kubernetes - -kubectl -n flagger-system set image deployment/flagger flagger=test/flagger:latest - -kubectl -n flagger-system rollout status deployment/flagger -kubectl -n flagger-system rollout status deployment/flagger-prometheus \ No newline at end of file diff --git a/test/e2e-linkerd-tests.sh b/test/e2e-linkerd-tests.sh deleted file mode 100755 index f5e623cb..00000000 --- a/test/e2e-linkerd-tests.sh +++ /dev/null @@ -1,149 +0,0 @@ -#!/usr/bin/env bash - -# This script runs Linkerd e2e tests for Canary initialization, analysis and promotion - -set -o errexit - -REPO_ROOT=$(git rev-parse --show-toplevel) -export KUBECONFIG="$(kind get kubeconfig-path --name="kind")" - -echo '>>> Creating test namespace' -kubectl create namespace test -kubectl annotate namespace test linkerd.io/inject=enabled - -echo '>>> Installing the load tester' -kubectl -n test apply -f ${REPO_ROOT}/artifacts/loadtester/ -kubectl -n test rollout status deployment/flagger-loadtester - -echo '>>> Initialising canary' -kubectl apply -f ${REPO_ROOT}/test/e2e-workload.yaml - -cat <>> Waiting for primary to be ready' -retries=50 -count=0 -ok=false -until ${ok}; do - kubectl -n test get canary/podinfo | grep 'Initialized' && ok=true || ok=false - sleep 5 - count=$(($count + 1)) - if [[ ${count} -eq ${retries} ]]; then - kubectl -n linkerd logs deployment/flagger - echo "No more retries left" - exit 1 - fi -done - -echo '✔ Canary initialization test passed' - -echo '>>> Triggering canary deployment' -kubectl -n test set image deployment/podinfo podinfod=quay.io/stefanprodan/podinfo:1.4.1 - -echo '>>> Waiting for canary promotion' -retries=50 -count=0 -ok=false -until ${ok}; do - kubectl -n test describe deployment/podinfo-primary | grep '1.4.1' && ok=true || ok=false - sleep 10 - kubectl -n linkerd logs deployment/flagger --tail 1 - count=$(($count + 1)) - if [[ ${count} -eq ${retries} ]]; then - kubectl -n linkerd logs deployment/flagger - echo "No more retries left" - exit 1 - fi -done - -echo '✔ Canary promotion test passed' - -cat <>> Triggering canary deployment' -kubectl -n test set image deployment/podinfo podinfod=quay.io/stefanprodan/podinfo:1.4.2 - -echo '>>> Waiting for canary rollback' -retries=50 -count=0 -ok=false -until ${ok}; do - kubectl -n test get canary/podinfo | grep 'Failed' && ok=true || ok=false - sleep 10 - kubectl -n linkerd logs deployment/flagger --tail 1 - count=$(($count + 1)) - if [[ ${count} -eq ${retries} ]]; then - kubectl -n linkerd logs deployment/flagger - echo "No more retries left" - exit 1 - fi -done - -echo '✔ Canary rollback test passed' \ No newline at end of file diff --git a/test/e2e-linkerd.sh b/test/e2e-linkerd.sh deleted file mode 100755 index 15db6ea2..00000000 --- a/test/e2e-linkerd.sh +++ /dev/null @@ -1,31 +0,0 @@ -#!/usr/bin/env bash - -set -o errexit - -LINKERD_VER="stable-2.4.0" -REPO_ROOT=$(git rev-parse --show-toplevel) -export KUBECONFIG="$(kind get kubeconfig-path --name="kind")" - -curl -SsL https://github.com/linkerd/linkerd2/releases/download/${LINKERD_VER}/linkerd2-cli-${LINKERD_VER}-linux > ${REPO_ROOT}/bin/linkerd -chmod +x ${REPO_ROOT}/bin/linkerd - -echo ">>> Installing Linkerd ${LINKERD_VER}" -${REPO_ROOT}/bin/linkerd install | kubectl apply -f - -${REPO_ROOT}/bin/linkerd check - -kubectl -n linkerd rollout status deployment/linkerd-controller -kubectl -n linkerd rollout status deployment/linkerd-proxy-injector - -echo '>>> Load Flagger image in Kind' -kind load docker-image test/flagger:latest - -echo '>>> Installing Flagger' -helm upgrade -i flagger ${REPO_ROOT}/charts/flagger \ ---namespace linkerd \ ---set leaderElection.enabled=true \ ---set leaderElection.replicaCount=2 \ ---set metricsServer=http://linkerd-prometheus:9090 \ ---set meshProvider=smi:linkerd - -kubectl -n linkerd set image deployment/flagger flagger=test/flagger:latest -kubectl -n linkerd rollout status deployment/flagger \ No newline at end of file diff --git a/test/e2e-nginx-tests.sh b/test/e2e-nginx-tests.sh deleted file mode 100755 index dac550f7..00000000 --- a/test/e2e-nginx-tests.sh +++ /dev/null @@ -1,194 +0,0 @@ -#!/usr/bin/env bash - -# This script runs e2e tests for Canary initialization, analysis and promotion -# Prerequisites: Kubernetes Kind, Helm and NGINX ingress controller - -set -o errexit - -REPO_ROOT=$(git rev-parse --show-toplevel) -export KUBECONFIG="$(kind get kubeconfig-path --name="kind")" - -echo '>>> Creating test namespace' -kubectl create namespace test - -echo '>>> Installing load tester' -kubectl -n test apply -f ${REPO_ROOT}/artifacts/loadtester/ -kubectl -n test rollout status deployment/flagger-loadtester - -echo '>>> Initialising canary' -kubectl apply -f ${REPO_ROOT}/test/e2e-workload.yaml -kubectl apply -f ${REPO_ROOT}/test/e2e-ingress.yaml - -cat <>> Waiting for primary to be ready' -retries=50 -count=0 -ok=false -until ${ok}; do - kubectl -n test get canary/podinfo | grep 'Initialized' && ok=true || ok=false - sleep 5 - count=$(($count + 1)) - if [[ ${count} -eq ${retries} ]]; then - kubectl -n ingress-nginx logs deployment/flagger - echo "No more retries left" - exit 1 - fi -done - -echo '✔ Canary initialization test passed' - -echo '>>> Triggering canary deployment' -kubectl -n test set image deployment/podinfo podinfod=quay.io/stefanprodan/podinfo:1.4.1 - -echo '>>> Waiting for canary promotion' -retries=50 -count=0 -ok=false -until ${ok}; do - kubectl -n test describe deployment/podinfo-primary | grep '1.4.1' && ok=true || ok=false - sleep 10 - kubectl -n ingress-nginx logs deployment/flagger --tail 1 - count=$(($count + 1)) - if [[ ${count} -eq ${retries} ]]; then - kubectl -n test describe deployment/podinfo - kubectl -n test describe deployment/podinfo-primary - kubectl -n ingress-nginx logs deployment/flagger - echo "No more retries left" - exit 1 - fi -done - -echo '✔ Canary promotion test passed' - -if [ "$1" = "canary" ]; then - exit 0 -fi - -cat <>> Triggering A/B testing' -kubectl -n test set image deployment/podinfo podinfod=quay.io/stefanprodan/podinfo:1.4.2 - -echo '>>> Waiting for A/B testing promotion' -retries=50 -count=0 -ok=false -until ${ok}; do - kubectl -n test describe deployment/podinfo-primary | grep '1.4.2' && ok=true || ok=false - sleep 10 - kubectl -n ingress-nginx logs deployment/flagger --tail 1 - count=$(($count + 1)) - if [[ ${count} -eq ${retries} ]]; then - kubectl -n test describe deployment/podinfo - kubectl -n test describe deployment/podinfo-primary - kubectl -n ingress-nginx logs deployment/flagger - echo "No more retries left" - exit 1 - fi -done - -echo '✔ A/B testing promotion test passed' - -kubectl -n ingress-nginx logs deployment/flagger - -echo '✔ All tests passed' \ No newline at end of file diff --git a/test/e2e-nginx.sh b/test/e2e-nginx.sh deleted file mode 100755 index 0ee57f26..00000000 --- a/test/e2e-nginx.sh +++ /dev/null @@ -1,35 +0,0 @@ -#!/usr/bin/env bash - -set -o errexit - -REPO_ROOT=$(git rev-parse --show-toplevel) -export KUBECONFIG="$(kind get kubeconfig-path --name="kind")" -NGINX_VERSION=1.12.1 - -echo '>>> Installing NGINX Ingress' -helm upgrade -i nginx-ingress stable/nginx-ingress --version=${NGINX_VERSION} \ ---wait \ ---namespace ingress-nginx \ ---set controller.stats.enabled=true \ ---set controller.metrics.enabled=true \ ---set controller.podAnnotations."prometheus\.io/scrape"=true \ ---set controller.podAnnotations."prometheus\.io/port"=10254 \ ---set controller.service.type=NodePort - -kubectl -n ingress-nginx rollout status deployment/nginx-ingress-controller -kubectl -n ingress-nginx get all - -echo '>>> Loading Flagger image' -kind load docker-image test/flagger:latest - -echo '>>> Installing Flagger' -helm upgrade -i flagger ${REPO_ROOT}/charts/flagger \ ---namespace ingress-nginx \ ---set prometheus.install=true \ ---set meshProvider=nginx - -kubectl -n ingress-nginx set image deployment/flagger flagger=test/flagger:latest - -kubectl -n ingress-nginx rollout status deployment/flagger -kubectl -n ingress-nginx rollout status deployment/flagger-prometheus - diff --git a/test/e2e-smi-istio.sh b/test/e2e-smi-istio.sh deleted file mode 100755 index 604a6ef7..00000000 --- a/test/e2e-smi-istio.sh +++ /dev/null @@ -1,37 +0,0 @@ -#!/usr/bin/env bash - -set -o errexit - -ISTIO_VER="1.1.9" -REPO_ROOT=$(git rev-parse --show-toplevel) -export KUBECONFIG="$(kind get kubeconfig-path --name="kind")" - -echo ">>> Installing Istio ${ISTIO_VER}" -helm repo add istio.io https://storage.googleapis.com/istio-release/releases/${ISTIO_VER}/charts - -echo '>>> Installing Istio CRDs' -helm upgrade -i istio-init istio.io/istio-init --wait --namespace istio-system - -echo '>>> Waiting for Istio CRDs to be ready' -kubectl -n istio-system wait --for=condition=complete job/istio-init-crd-10 -kubectl -n istio-system wait --for=condition=complete job/istio-init-crd-11 - -echo '>>> Installing Istio control plane' -helm upgrade -i istio istio.io/istio --wait --namespace istio-system -f ${REPO_ROOT}/test/e2e-istio-values.yaml - -echo '>>> Installing the SMI Istio adapter' -kubectl apply -f ${REPO_ROOT}/artifacts/smi/istio-adapter.yaml - -kubectl -n istio-system rollout status deployment/smi-adapter-istio - -echo '>>> Load Flagger image in Kind' -kind load docker-image test/flagger:latest - -echo '>>> Installing Flagger' -helm upgrade -i flagger ${REPO_ROOT}/charts/flagger \ ---namespace istio-system \ ---set meshProvider=smi:istio - -kubectl -n istio-system set image deployment/flagger flagger=test/flagger:latest - -kubectl -n istio-system rollout status deployment/flagger diff --git a/test/e2e-supergloo.sh b/test/e2e-supergloo.sh deleted file mode 100755 index 0666729f..00000000 --- a/test/e2e-supergloo.sh +++ /dev/null @@ -1,57 +0,0 @@ -#!/usr/bin/env bash - -set -o errexit - -ISTIO_VER="1.0.6" -SUPERGLOO_VER="v0.3.13" -REPO_ROOT=$(git rev-parse --show-toplevel) -export KUBECONFIG="$(kind get kubeconfig-path --name="kind")" - -echo ">>> Downloading Supergloo CLI" -curl -SsL https://github.com/solo-io/supergloo/releases/download/${SUPERGLOO_VER}/supergloo-cli-linux-amd64 > ${REPO_ROOT}/bin/supergloo-cli -chmod +x ${REPO_ROOT}/bin/supergloo-cli - -echo ">>> Installing Supergloo" -${REPO_ROOT}/bin/supergloo-cli init - -echo ">>> Installing Istio ${ISTIO_VER}" -kubectl create ns istio-system -${REPO_ROOT}/bin/supergloo-cli install istio --name test --version ${ISTIO_VER} \ - --namespace supergloo-system --installation-namespace istio-system \ - --auto-inject=true --mtls=false --prometheus=true - -echo '>>> Waiting for Istio to be ready' -retries=50 -count=0 -ok=false -until ${ok}; do - kubectl -n supergloo-system get mesh test && ok=true || ok=false - sleep 10 - count=$(($count + 1)) - if [[ ${count} -eq ${retries} ]]; then - echo "No more retries left" - exit 1 - fi -done - -kubectl -n istio-system rollout status deployment/istio-pilot -kubectl -n istio-system rollout status deployment/istio-policy -kubectl -n istio-system rollout status deployment/istio-sidecar-injector -kubectl -n istio-system rollout status deployment/istio-telemetry -kubectl -n istio-system rollout status deployment/prometheus - -kubectl -n istio-system get all - -echo '>>> Load Flagger image in Kind' -kind load docker-image test/flagger:latest - -echo '>>> Installing Flagger' -helm upgrade -i flagger ${REPO_ROOT}/charts/flagger \ ---namespace istio-system \ ---set meshProvider=supergloo:test.supergloo-system - -kubectl -n istio-system set image deployment/flagger flagger=test/flagger:latest -kubectl -n istio-system rollout status deployment/flagger - -echo '>>> Adding Flagger Supergloo RBAC' -kubectl create clusterrolebinding flagger-supergloo --clusterrole=mesh-discovery --serviceaccount=istio-system:flagger diff --git a/test/e2e-tests.sh b/test/e2e-tests.sh deleted file mode 100755 index 3d654c15..00000000 --- a/test/e2e-tests.sh +++ /dev/null @@ -1,207 +0,0 @@ -#!/usr/bin/env bash - -# This script runs e2e tests for Canary initialization, analysis and promotion -# Prerequisites: Kubernetes Kind, Helm and Istio - -set -o errexit - -REPO_ROOT=$(git rev-parse --show-toplevel) -export KUBECONFIG="$(kind get kubeconfig-path --name="kind")" - -echo '>>> Creating test namespace' -kubectl create namespace test -kubectl label namespace test istio-injection=enabled - -echo '>>> Installing the load tester' -kubectl apply -k ${REPO_ROOT}/kustomize/tester -kubectl -n test rollout status deployment/flagger-loadtester - -echo '>>> Initialising canary' -kubectl apply -f ${REPO_ROOT}/test/e2e-workload.yaml - -cat <>> Waiting for primary to be ready' -retries=50 -count=0 -ok=false -until ${ok}; do - kubectl -n test get canary/podinfo | grep 'Initialized' && ok=true || ok=false - sleep 5 - count=$(($count + 1)) - if [[ ${count} -eq ${retries} ]]; then - kubectl -n istio-system logs deployment/flagger - echo "No more retries left" - exit 1 - fi -done - -echo '✔ Canary initialization test passed' - -echo '>>> Triggering canary deployment' -kubectl -n test set image deployment/podinfo podinfod=quay.io/stefanprodan/podinfo:1.4.1 - -echo '>>> Waiting for canary promotion' -retries=50 -count=0 -ok=false -until ${ok}; do - kubectl -n test describe deployment/podinfo-primary | grep '1.4.1' && ok=true || ok=false - sleep 10 - kubectl -n istio-system logs deployment/flagger --tail 1 - count=$(($count + 1)) - if [[ ${count} -eq ${retries} ]]; then - kubectl -n test describe deployment/podinfo - kubectl -n test describe deployment/podinfo-primary - kubectl -n istio-system logs deployment/flagger - echo "No more retries left" - exit 1 - fi -done - -echo '✔ Canary promotion test passed' - -if [[ "$1" = "canary" ]]; then - exit 0 -fi - -cat <>> Triggering A/B testing' -kubectl -n test set image deployment/podinfo podinfod=quay.io/stefanprodan/podinfo:1.4.2 - -echo '>>> Waiting for A/B testing promotion' -retries=50 -count=0 -ok=false -until ${ok}; do - kubectl -n test describe deployment/podinfo-primary | grep '1.4.2' && ok=true || ok=false - sleep 10 - kubectl -n istio-system logs deployment/flagger --tail 1 - count=$(($count + 1)) - if [[ ${count} -eq ${retries} ]]; then - kubectl -n test describe deployment/podinfo - kubectl -n test describe deployment/podinfo-primary - kubectl -n istio-system logs deployment/flagger - echo "No more retries left" - exit 1 - fi -done - -echo '✔ A/B testing promotion test passed' - -kubectl -n istio-system logs deployment/flagger - -echo '✔ All tests passed' diff --git a/test/e2e-workload.yaml b/test/e2e-workload.yaml deleted file mode 100644 index ca7c043d..00000000 --- a/test/e2e-workload.yaml +++ /dev/null @@ -1,66 +0,0 @@ -apiVersion: apps/v1 -kind: Deployment -metadata: - name: podinfo - namespace: test - labels: - app: podinfo -spec: - minReadySeconds: 5 - revisionHistoryLimit: 5 - progressDeadlineSeconds: 60 - strategy: - rollingUpdate: - maxUnavailable: 0 - type: RollingUpdate - selector: - matchLabels: - app: podinfo - template: - metadata: - annotations: - prometheus.io/scrape: "true" - prometheus.io/port: "9797" - labels: - app: podinfo - spec: - containers: - - name: podinfod - image: quay.io/stefanprodan/podinfo:1.4.0 - imagePullPolicy: IfNotPresent - ports: - - containerPort: 9898 - name: http - protocol: TCP - - containerPort: 9797 - name: http-prom - protocol: TCP - command: - - ./podinfo - - --port=9898 - - --port-metrics=9797 - - --level=info - - --random-delay=false - - --random-error=false - env: - - name: PODINFO_UI_COLOR - value: blue - livenessProbe: - httpGet: - port: 9898 - path: /healthz - initialDelaySeconds: 5 - timeoutSeconds: 5 - readinessProbe: - httpGet: - port: 9898 - path: /readyz - initialDelaySeconds: 5 - timeoutSeconds: 5 - resources: - limits: - cpu: 1000m - memory: 128Mi - requests: - cpu: 1m - memory: 16Mi diff --git a/test/goreleaser.sh b/test/goreleaser.sh deleted file mode 100755 index 41a30578..00000000 --- a/test/goreleaser.sh +++ /dev/null @@ -1,28 +0,0 @@ -#!/bin/sh -set -e - -TAR_FILE="/tmp/goreleaser.tar.gz" -RELEASES_URL="https://github.com/goreleaser/goreleaser/releases" -test -z "$TMPDIR" && TMPDIR="$(mktemp -d)" - -last_version() { - curl -sL -o /dev/null -w %{url_effective} "$RELEASES_URL/latest" | - rev | - cut -f1 -d'/'| - rev -} - -download() { - test -z "$VERSION" && VERSION="$(last_version)" - test -z "$VERSION" && { - echo "Unable to get goreleaser version." >&2 - exit 1 - } - rm -f "$TAR_FILE" - curl -s -L -o "$TAR_FILE" \ - "$RELEASES_URL/download/$VERSION/goreleaser_$(uname -s)_$(uname -m).tar.gz" -} - -download -tar -xf "$TAR_FILE" -C "$TMPDIR" -"${TMPDIR}/goreleaser" "$@" diff --git a/yarn.lock b/yarn.lock new file mode 100644 index 00000000..feff837d --- /dev/null +++ b/yarn.lock @@ -0,0 +1,7311 @@ +# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY. +# yarn lockfile v1 + + +"@babel/code-frame@^7.0.0", "@babel/code-frame@^7.5.5": + version "7.5.5" + resolved "https://registry.yarnpkg.com/@babel/code-frame/-/code-frame-7.5.5.tgz#bc0782f6d69f7b7d49531219699b988f669a8f9d" + integrity sha512-27d4lZoomVyo51VegxI20xZPuSHusqbQag/ztrBC7wegWoQ1nLREPVSKSW8byhTlzTKyNE4ifaTA6lCp7JjpFw== + dependencies: + "@babel/highlight" "^7.0.0" + +"@babel/core@^7.0.0": + version "7.5.5" + resolved "https://registry.yarnpkg.com/@babel/core/-/core-7.5.5.tgz#17b2686ef0d6bc58f963dddd68ab669755582c30" + integrity sha512-i4qoSr2KTtce0DmkuuQBV4AuQgGPUcPXMr9L5MyYAtk06z068lQ10a4O009fe5OB/DfNV+h+qqT7ddNV8UnRjg== + dependencies: + "@babel/code-frame" "^7.5.5" + "@babel/generator" "^7.5.5" + "@babel/helpers" "^7.5.5" + "@babel/parser" "^7.5.5" + "@babel/template" "^7.4.4" + "@babel/traverse" "^7.5.5" + "@babel/types" "^7.5.5" + convert-source-map "^1.1.0" + debug "^4.1.0" + json5 "^2.1.0" + lodash "^4.17.13" + resolve "^1.3.2" + semver "^5.4.1" + source-map "^0.5.0" + +"@babel/generator@^7.5.5": + version "7.5.5" + resolved "https://registry.yarnpkg.com/@babel/generator/-/generator-7.5.5.tgz#873a7f936a3c89491b43536d12245b626664e3cf" + integrity sha512-ETI/4vyTSxTzGnU2c49XHv2zhExkv9JHLTwDAFz85kmcwuShvYG2H08FwgIguQf4JC75CBnXAUM5PqeF4fj0nQ== + dependencies: + "@babel/types" "^7.5.5" + jsesc "^2.5.1" + lodash "^4.17.13" + source-map "^0.5.0" + trim-right "^1.0.1" + +"@babel/helper-annotate-as-pure@^7.0.0": + version "7.0.0" + resolved "https://registry.yarnpkg.com/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.0.0.tgz#323d39dd0b50e10c7c06ca7d7638e6864d8c5c32" + integrity sha512-3UYcJUj9kvSLbLbUIfQTqzcy5VX7GRZ/CCDrnOaZorFFM01aXp1+GJwuFGV4NDDoAS+mOUyHcO6UD/RfqOks3Q== + dependencies: + "@babel/types" "^7.0.0" + +"@babel/helper-builder-binary-assignment-operator-visitor@^7.1.0": + version "7.1.0" + resolved "https://registry.yarnpkg.com/@babel/helper-builder-binary-assignment-operator-visitor/-/helper-builder-binary-assignment-operator-visitor-7.1.0.tgz#6b69628dfe4087798e0c4ed98e3d4a6b2fbd2f5f" + integrity sha512-qNSR4jrmJ8M1VMM9tibvyRAHXQs2PmaksQF7c1CGJNipfe3D8p+wgNwgso/P2A2r2mdgBWAXljNWR0QRZAMW8w== + dependencies: + "@babel/helper-explode-assignable-expression" "^7.1.0" + "@babel/types" "^7.0.0" + +"@babel/helper-call-delegate@^7.4.4": + version "7.4.4" + resolved "https://registry.yarnpkg.com/@babel/helper-call-delegate/-/helper-call-delegate-7.4.4.tgz#87c1f8ca19ad552a736a7a27b1c1fcf8b1ff1f43" + integrity sha512-l79boDFJ8S1c5hvQvG+rc+wHw6IuH7YldmRKsYtpbawsxURu/paVy57FZMomGK22/JckepaikOkY0MoAmdyOlQ== + dependencies: + "@babel/helper-hoist-variables" "^7.4.4" + "@babel/traverse" "^7.4.4" + "@babel/types" "^7.4.4" + +"@babel/helper-create-class-features-plugin@^7.4.4", "@babel/helper-create-class-features-plugin@^7.5.5": + version "7.5.5" + resolved "https://registry.yarnpkg.com/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.5.5.tgz#401f302c8ddbc0edd36f7c6b2887d8fa1122e5a4" + integrity sha512-ZsxkyYiRA7Bg+ZTRpPvB6AbOFKTFFK4LrvTet8lInm0V468MWCaSYJE+I7v2z2r8KNLtYiV+K5kTCnR7dvyZjg== + dependencies: + "@babel/helper-function-name" "^7.1.0" + "@babel/helper-member-expression-to-functions" "^7.5.5" + "@babel/helper-optimise-call-expression" "^7.0.0" + "@babel/helper-plugin-utils" "^7.0.0" + "@babel/helper-replace-supers" "^7.5.5" + "@babel/helper-split-export-declaration" "^7.4.4" + +"@babel/helper-define-map@^7.5.5": + version "7.5.5" + resolved "https://registry.yarnpkg.com/@babel/helper-define-map/-/helper-define-map-7.5.5.tgz#3dec32c2046f37e09b28c93eb0b103fd2a25d369" + integrity sha512-fTfxx7i0B5NJqvUOBBGREnrqbTxRh7zinBANpZXAVDlsZxYdclDp467G1sQ8VZYMnAURY3RpBUAgOYT9GfzHBg== + dependencies: + "@babel/helper-function-name" "^7.1.0" + "@babel/types" "^7.5.5" + lodash "^4.17.13" + +"@babel/helper-explode-assignable-expression@^7.1.0": + version "7.1.0" + resolved "https://registry.yarnpkg.com/@babel/helper-explode-assignable-expression/-/helper-explode-assignable-expression-7.1.0.tgz#537fa13f6f1674df745b0c00ec8fe4e99681c8f6" + integrity sha512-NRQpfHrJ1msCHtKjbzs9YcMmJZOg6mQMmGRB+hbamEdG5PNpaSm95275VD92DvJKuyl0s2sFiDmMZ+EnnvufqA== + dependencies: + "@babel/traverse" "^7.1.0" + "@babel/types" "^7.0.0" + +"@babel/helper-function-name@^7.1.0": + version "7.1.0" + resolved "https://registry.yarnpkg.com/@babel/helper-function-name/-/helper-function-name-7.1.0.tgz#a0ceb01685f73355d4360c1247f582bfafc8ff53" + integrity sha512-A95XEoCpb3TO+KZzJ4S/5uW5fNe26DjBGqf1o9ucyLyCmi1dXq/B3c8iaWTfBk3VvetUxl16e8tIrd5teOCfGw== + dependencies: + "@babel/helper-get-function-arity" "^7.0.0" + "@babel/template" "^7.1.0" + "@babel/types" "^7.0.0" + +"@babel/helper-get-function-arity@^7.0.0": + version "7.0.0" + resolved "https://registry.yarnpkg.com/@babel/helper-get-function-arity/-/helper-get-function-arity-7.0.0.tgz#83572d4320e2a4657263734113c42868b64e49c3" + integrity sha512-r2DbJeg4svYvt3HOS74U4eWKsUAMRH01Z1ds1zx8KNTPtpTL5JAsdFv8BNyOpVqdFhHkkRDIg5B4AsxmkjAlmQ== + dependencies: + "@babel/types" "^7.0.0" + +"@babel/helper-hoist-variables@^7.4.4": + version "7.4.4" + resolved "https://registry.yarnpkg.com/@babel/helper-hoist-variables/-/helper-hoist-variables-7.4.4.tgz#0298b5f25c8c09c53102d52ac4a98f773eb2850a" + integrity sha512-VYk2/H/BnYbZDDg39hr3t2kKyifAm1W6zHRfhx8jGjIHpQEBv9dry7oQ2f3+J703TLu69nYdxsovl0XYfcnK4w== + dependencies: + "@babel/types" "^7.4.4" + +"@babel/helper-member-expression-to-functions@^7.5.5": + version "7.5.5" + resolved "https://registry.yarnpkg.com/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.5.5.tgz#1fb5b8ec4453a93c439ee9fe3aeea4a84b76b590" + integrity sha512-5qZ3D1uMclSNqYcXqiHoA0meVdv+xUEex9em2fqMnrk/scphGlGgg66zjMrPJESPwrFJ6sbfFQYUSa0Mz7FabA== + dependencies: + "@babel/types" "^7.5.5" + +"@babel/helper-module-imports@^7.0.0": + version "7.0.0" + resolved "https://registry.yarnpkg.com/@babel/helper-module-imports/-/helper-module-imports-7.0.0.tgz#96081b7111e486da4d2cd971ad1a4fe216cc2e3d" + integrity sha512-aP/hlLq01DWNEiDg4Jn23i+CXxW/owM4WpDLFUbpjxe4NS3BhLVZQ5i7E0ZrxuQ/vwekIeciyamgB1UIYxxM6A== + dependencies: + "@babel/types" "^7.0.0" + +"@babel/helper-module-transforms@^7.1.0", "@babel/helper-module-transforms@^7.4.4": + version "7.5.5" + resolved "https://registry.yarnpkg.com/@babel/helper-module-transforms/-/helper-module-transforms-7.5.5.tgz#f84ff8a09038dcbca1fd4355661a500937165b4a" + integrity sha512-jBeCvETKuJqeiaCdyaheF40aXnnU1+wkSiUs/IQg3tB85up1LyL8x77ClY8qJpuRJUcXQo+ZtdNESmZl4j56Pw== + dependencies: + "@babel/helper-module-imports" "^7.0.0" + "@babel/helper-simple-access" "^7.1.0" + "@babel/helper-split-export-declaration" "^7.4.4" + "@babel/template" "^7.4.4" + "@babel/types" "^7.5.5" + lodash "^4.17.13" + +"@babel/helper-optimise-call-expression@^7.0.0": + version "7.0.0" + resolved "https://registry.yarnpkg.com/@babel/helper-optimise-call-expression/-/helper-optimise-call-expression-7.0.0.tgz#a2920c5702b073c15de51106200aa8cad20497d5" + integrity sha512-u8nd9NQePYNQV8iPWu/pLLYBqZBa4ZaY1YWRFMuxrid94wKI1QNt67NEZ7GAe5Kc/0LLScbim05xZFWkAdrj9g== + dependencies: + "@babel/types" "^7.0.0" + +"@babel/helper-plugin-utils@^7.0.0": + version "7.0.0" + resolved "https://registry.yarnpkg.com/@babel/helper-plugin-utils/-/helper-plugin-utils-7.0.0.tgz#bbb3fbee98661c569034237cc03967ba99b4f250" + integrity sha512-CYAOUCARwExnEixLdB6sDm2dIJ/YgEAKDM1MOeMeZu9Ld/bDgVo8aiWrXwcY7OBh+1Ea2uUcVRcxKk0GJvW7QA== + +"@babel/helper-regex@^7.0.0", "@babel/helper-regex@^7.4.4": + version "7.5.5" + resolved "https://registry.yarnpkg.com/@babel/helper-regex/-/helper-regex-7.5.5.tgz#0aa6824f7100a2e0e89c1527c23936c152cab351" + integrity sha512-CkCYQLkfkiugbRDO8eZn6lRuR8kzZoGXCg3149iTk5se7g6qykSpy3+hELSwquhu+TgHn8nkLiBwHvNX8Hofcw== + dependencies: + lodash "^4.17.13" + +"@babel/helper-remap-async-to-generator@^7.1.0": + version "7.1.0" + resolved "https://registry.yarnpkg.com/@babel/helper-remap-async-to-generator/-/helper-remap-async-to-generator-7.1.0.tgz#361d80821b6f38da75bd3f0785ece20a88c5fe7f" + integrity sha512-3fOK0L+Fdlg8S5al8u/hWE6vhufGSn0bN09xm2LXMy//REAF8kDCrYoOBKYmA8m5Nom+sV9LyLCwrFynA8/slg== + dependencies: + "@babel/helper-annotate-as-pure" "^7.0.0" + "@babel/helper-wrap-function" "^7.1.0" + "@babel/template" "^7.1.0" + "@babel/traverse" "^7.1.0" + "@babel/types" "^7.0.0" + +"@babel/helper-replace-supers@^7.5.5": + version "7.5.5" + resolved "https://registry.yarnpkg.com/@babel/helper-replace-supers/-/helper-replace-supers-7.5.5.tgz#f84ce43df031222d2bad068d2626cb5799c34bc2" + integrity sha512-XvRFWrNnlsow2u7jXDuH4jDDctkxbS7gXssrP4q2nUD606ukXHRvydj346wmNg+zAgpFx4MWf4+usfC93bElJg== + dependencies: + "@babel/helper-member-expression-to-functions" "^7.5.5" + "@babel/helper-optimise-call-expression" "^7.0.0" + "@babel/traverse" "^7.5.5" + "@babel/types" "^7.5.5" + +"@babel/helper-simple-access@^7.1.0": + version "7.1.0" + resolved "https://registry.yarnpkg.com/@babel/helper-simple-access/-/helper-simple-access-7.1.0.tgz#65eeb954c8c245beaa4e859da6188f39d71e585c" + integrity sha512-Vk+78hNjRbsiu49zAPALxTb+JUQCz1aolpd8osOF16BGnLtseD21nbHgLPGUwrXEurZgiCOUmvs3ExTu4F5x6w== + dependencies: + "@babel/template" "^7.1.0" + "@babel/types" "^7.0.0" + +"@babel/helper-split-export-declaration@^7.4.4": + version "7.4.4" + resolved "https://registry.yarnpkg.com/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.4.4.tgz#ff94894a340be78f53f06af038b205c49d993677" + integrity sha512-Ro/XkzLf3JFITkW6b+hNxzZ1n5OQ80NvIUdmHspih1XAhtN3vPTuUFT4eQnela+2MaZ5ulH+iyP513KJrxbN7Q== + dependencies: + "@babel/types" "^7.4.4" + +"@babel/helper-wrap-function@^7.1.0": + version "7.2.0" + resolved "https://registry.yarnpkg.com/@babel/helper-wrap-function/-/helper-wrap-function-7.2.0.tgz#c4e0012445769e2815b55296ead43a958549f6fa" + integrity sha512-o9fP1BZLLSrYlxYEYyl2aS+Flun5gtjTIG8iln+XuEzQTs0PLagAGSXUcqruJwD5fM48jzIEggCKpIfWTcR7pQ== + dependencies: + "@babel/helper-function-name" "^7.1.0" + "@babel/template" "^7.1.0" + "@babel/traverse" "^7.1.0" + "@babel/types" "^7.2.0" + +"@babel/helpers@^7.5.5": + version "7.5.5" + resolved "https://registry.yarnpkg.com/@babel/helpers/-/helpers-7.5.5.tgz#63908d2a73942229d1e6685bc2a0e730dde3b75e" + integrity sha512-nRq2BUhxZFnfEn/ciJuhklHvFOqjJUD5wpx+1bxUF2axL9C+v4DE/dmp5sT2dKnpOs4orZWzpAZqlCy8QqE/7g== + dependencies: + "@babel/template" "^7.4.4" + "@babel/traverse" "^7.5.5" + "@babel/types" "^7.5.5" + +"@babel/highlight@^7.0.0": + version "7.5.0" + resolved "https://registry.yarnpkg.com/@babel/highlight/-/highlight-7.5.0.tgz#56d11312bd9248fa619591d02472be6e8cb32540" + integrity sha512-7dV4eu9gBxoM0dAnj/BCFDW9LFU0zvTrkq0ugM7pnHEgguOEeOz1so2ZghEdzviYzQEED0r4EAgpsBChKy1TRQ== + dependencies: + chalk "^2.0.0" + esutils "^2.0.2" + js-tokens "^4.0.0" + +"@babel/parser@^7.4.4", "@babel/parser@^7.5.5": + version "7.5.5" + resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.5.5.tgz#02f077ac8817d3df4a832ef59de67565e71cca4b" + integrity sha512-E5BN68cqR7dhKan1SfqgPGhQ178bkVKpXTPEXnFJBrEt8/DKRZlybmy+IgYLTeN7tp1R5Ccmbm2rBk17sHYU3g== + +"@babel/plugin-proposal-async-generator-functions@^7.2.0": + version "7.2.0" + resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-async-generator-functions/-/plugin-proposal-async-generator-functions-7.2.0.tgz#b289b306669dce4ad20b0252889a15768c9d417e" + integrity sha512-+Dfo/SCQqrwx48ptLVGLdE39YtWRuKc/Y9I5Fy0P1DDBB9lsAHpjcEJQt+4IifuSOSTLBKJObJqMvaO1pIE8LQ== + dependencies: + "@babel/helper-plugin-utils" "^7.0.0" + "@babel/helper-remap-async-to-generator" "^7.1.0" + "@babel/plugin-syntax-async-generators" "^7.2.0" + +"@babel/plugin-proposal-class-properties@^7.0.0": + version "7.5.5" + resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-class-properties/-/plugin-proposal-class-properties-7.5.5.tgz#a974cfae1e37c3110e71f3c6a2e48b8e71958cd4" + integrity sha512-AF79FsnWFxjlaosgdi421vmYG6/jg79bVD0dpD44QdgobzHKuLZ6S3vl8la9qIeSwGi8i1fS0O1mfuDAAdo1/A== + dependencies: + "@babel/helper-create-class-features-plugin" "^7.5.5" + "@babel/helper-plugin-utils" "^7.0.0" + +"@babel/plugin-proposal-decorators@^7.1.0": + version "7.4.4" + resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-decorators/-/plugin-proposal-decorators-7.4.4.tgz#de9b2a1a8ab0196f378e2a82f10b6e2a36f21cc0" + integrity sha512-z7MpQz3XC/iQJWXH9y+MaWcLPNSMY9RQSthrLzak8R8hCj0fuyNk+Dzi9kfNe/JxxlWQ2g7wkABbgWjW36MTcw== + dependencies: + "@babel/helper-create-class-features-plugin" "^7.4.4" + "@babel/helper-plugin-utils" "^7.0.0" + "@babel/plugin-syntax-decorators" "^7.2.0" + +"@babel/plugin-proposal-json-strings@^7.2.0": + version "7.2.0" + resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-json-strings/-/plugin-proposal-json-strings-7.2.0.tgz#568ecc446c6148ae6b267f02551130891e29f317" + integrity sha512-MAFV1CA/YVmYwZG0fBQyXhmj0BHCB5egZHCKWIFVv/XCxAeVGIHfos3SwDck4LvCllENIAg7xMKOG5kH0dzyUg== + dependencies: + "@babel/helper-plugin-utils" "^7.0.0" + "@babel/plugin-syntax-json-strings" "^7.2.0" + +"@babel/plugin-proposal-object-rest-spread@^7.3.4": + version "7.5.5" + resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-object-rest-spread/-/plugin-proposal-object-rest-spread-7.5.5.tgz#61939744f71ba76a3ae46b5eea18a54c16d22e58" + integrity sha512-F2DxJJSQ7f64FyTVl5cw/9MWn6naXGdk3Q3UhDbFEEHv+EilCPoeRD3Zh/Utx1CJz4uyKlQ4uH+bJPbEhMV7Zw== + dependencies: + "@babel/helper-plugin-utils" "^7.0.0" + "@babel/plugin-syntax-object-rest-spread" "^7.2.0" + +"@babel/plugin-proposal-optional-catch-binding@^7.2.0": + version "7.2.0" + resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-optional-catch-binding/-/plugin-proposal-optional-catch-binding-7.2.0.tgz#135d81edb68a081e55e56ec48541ece8065c38f5" + integrity sha512-mgYj3jCcxug6KUcX4OBoOJz3CMrwRfQELPQ5560F70YQUBZB7uac9fqaWamKR1iWUzGiK2t0ygzjTScZnVz75g== + dependencies: + "@babel/helper-plugin-utils" "^7.0.0" + "@babel/plugin-syntax-optional-catch-binding" "^7.2.0" + +"@babel/plugin-proposal-unicode-property-regex@^7.2.0": + version "7.4.4" + resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-unicode-property-regex/-/plugin-proposal-unicode-property-regex-7.4.4.tgz#501ffd9826c0b91da22690720722ac7cb1ca9c78" + integrity sha512-j1NwnOqMG9mFUOH58JTFsA/+ZYzQLUZ/drqWUqxCYLGeu2JFZL8YrNC9hBxKmWtAuOCHPcRpgv7fhap09Fb4kA== + dependencies: + "@babel/helper-plugin-utils" "^7.0.0" + "@babel/helper-regex" "^7.4.4" + regexpu-core "^4.5.4" + +"@babel/plugin-syntax-async-generators@^7.2.0": + version "7.2.0" + resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-async-generators/-/plugin-syntax-async-generators-7.2.0.tgz#69e1f0db34c6f5a0cf7e2b3323bf159a76c8cb7f" + integrity sha512-1ZrIRBv2t0GSlcwVoQ6VgSLpLgiN/FVQUzt9znxo7v2Ov4jJrs8RY8tv0wvDmFN3qIdMKWrmMMW6yZ0G19MfGg== + dependencies: + "@babel/helper-plugin-utils" "^7.0.0" + +"@babel/plugin-syntax-decorators@^7.2.0": + version "7.2.0" + resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-decorators/-/plugin-syntax-decorators-7.2.0.tgz#c50b1b957dcc69e4b1127b65e1c33eef61570c1b" + integrity sha512-38QdqVoXdHUQfTpZo3rQwqQdWtCn5tMv4uV6r2RMfTqNBuv4ZBhz79SfaQWKTVmxHjeFv/DnXVC/+agHCklYWA== + dependencies: + "@babel/helper-plugin-utils" "^7.0.0" + +"@babel/plugin-syntax-dynamic-import@^7.0.0": + version "7.2.0" + resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-dynamic-import/-/plugin-syntax-dynamic-import-7.2.0.tgz#69c159ffaf4998122161ad8ebc5e6d1f55df8612" + integrity sha512-mVxuJ0YroI/h/tbFTPGZR8cv6ai+STMKNBq0f8hFxsxWjl94qqhsb+wXbpNMDPU3cfR1TIsVFzU3nXyZMqyK4w== + dependencies: + "@babel/helper-plugin-utils" "^7.0.0" + +"@babel/plugin-syntax-json-strings@^7.2.0": + version "7.2.0" + resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-json-strings/-/plugin-syntax-json-strings-7.2.0.tgz#72bd13f6ffe1d25938129d2a186b11fd62951470" + integrity sha512-5UGYnMSLRE1dqqZwug+1LISpA403HzlSfsg6P9VXU6TBjcSHeNlw4DxDx7LgpF+iKZoOG/+uzqoRHTdcUpiZNg== + dependencies: + "@babel/helper-plugin-utils" "^7.0.0" + +"@babel/plugin-syntax-jsx@^7.0.0", "@babel/plugin-syntax-jsx@^7.2.0": + version "7.2.0" + resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-jsx/-/plugin-syntax-jsx-7.2.0.tgz#0b85a3b4bc7cdf4cc4b8bf236335b907ca22e7c7" + integrity sha512-VyN4QANJkRW6lDBmENzRszvZf3/4AXaj9YR7GwrWeeN9tEBPuXbmDYVU9bYBN0D70zCWVwUy0HWq2553VCb6Hw== + dependencies: + "@babel/helper-plugin-utils" "^7.0.0" + +"@babel/plugin-syntax-object-rest-spread@^7.2.0": + version "7.2.0" + resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-object-rest-spread/-/plugin-syntax-object-rest-spread-7.2.0.tgz#3b7a3e733510c57e820b9142a6579ac8b0dfad2e" + integrity sha512-t0JKGgqk2We+9may3t0xDdmneaXmyxq0xieYcKHxIsrJO64n1OiMWNUtc5gQK1PA0NpdCRrtZp4z+IUaKugrSA== + dependencies: + "@babel/helper-plugin-utils" "^7.0.0" + +"@babel/plugin-syntax-optional-catch-binding@^7.2.0": + version "7.2.0" + resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-optional-catch-binding/-/plugin-syntax-optional-catch-binding-7.2.0.tgz#a94013d6eda8908dfe6a477e7f9eda85656ecf5c" + integrity sha512-bDe4xKNhb0LI7IvZHiA13kff0KEfaGX/Hv4lMA9+7TEc63hMNvfKo6ZFpXhKuEp+II/q35Gc4NoMeDZyaUbj9w== + dependencies: + "@babel/helper-plugin-utils" "^7.0.0" + +"@babel/plugin-transform-arrow-functions@^7.2.0": + version "7.2.0" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-arrow-functions/-/plugin-transform-arrow-functions-7.2.0.tgz#9aeafbe4d6ffc6563bf8f8372091628f00779550" + integrity sha512-ER77Cax1+8/8jCB9fo4Ud161OZzWN5qawi4GusDuRLcDbDG+bIGYY20zb2dfAFdTRGzrfq2xZPvF0R64EHnimg== + dependencies: + "@babel/helper-plugin-utils" "^7.0.0" + +"@babel/plugin-transform-async-to-generator@^7.3.4": + version "7.5.0" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-async-to-generator/-/plugin-transform-async-to-generator-7.5.0.tgz#89a3848a0166623b5bc481164b5936ab947e887e" + integrity sha512-mqvkzwIGkq0bEF1zLRRiTdjfomZJDV33AH3oQzHVGkI2VzEmXLpKKOBvEVaFZBJdN0XTyH38s9j/Kiqr68dggg== + dependencies: + "@babel/helper-module-imports" "^7.0.0" + "@babel/helper-plugin-utils" "^7.0.0" + "@babel/helper-remap-async-to-generator" "^7.1.0" + +"@babel/plugin-transform-block-scoped-functions@^7.2.0": + version "7.2.0" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-block-scoped-functions/-/plugin-transform-block-scoped-functions-7.2.0.tgz#5d3cc11e8d5ddd752aa64c9148d0db6cb79fd190" + integrity sha512-ntQPR6q1/NKuphly49+QiQiTN0O63uOwjdD6dhIjSWBI5xlrbUFh720TIpzBhpnrLfv2tNH/BXvLIab1+BAI0w== + dependencies: + "@babel/helper-plugin-utils" "^7.0.0" + +"@babel/plugin-transform-block-scoping@^7.3.4": + version "7.5.5" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-block-scoping/-/plugin-transform-block-scoping-7.5.5.tgz#a35f395e5402822f10d2119f6f8e045e3639a2ce" + integrity sha512-82A3CLRRdYubkG85lKwhZB0WZoHxLGsJdux/cOVaJCJpvYFl1LVzAIFyRsa7CvXqW8rBM4Zf3Bfn8PHt5DP0Sg== + dependencies: + "@babel/helper-plugin-utils" "^7.0.0" + lodash "^4.17.13" + +"@babel/plugin-transform-classes@^7.3.4": + version "7.5.5" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-classes/-/plugin-transform-classes-7.5.5.tgz#d094299d9bd680a14a2a0edae38305ad60fb4de9" + integrity sha512-U2htCNK/6e9K7jGyJ++1p5XRU+LJjrwtoiVn9SzRlDT2KubcZ11OOwy3s24TjHxPgxNwonCYP7U2K51uVYCMDg== + dependencies: + "@babel/helper-annotate-as-pure" "^7.0.0" + "@babel/helper-define-map" "^7.5.5" + "@babel/helper-function-name" "^7.1.0" + "@babel/helper-optimise-call-expression" "^7.0.0" + "@babel/helper-plugin-utils" "^7.0.0" + "@babel/helper-replace-supers" "^7.5.5" + "@babel/helper-split-export-declaration" "^7.4.4" + globals "^11.1.0" + +"@babel/plugin-transform-computed-properties@^7.2.0": + version "7.2.0" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-computed-properties/-/plugin-transform-computed-properties-7.2.0.tgz#83a7df6a658865b1c8f641d510c6f3af220216da" + integrity sha512-kP/drqTxY6Xt3NNpKiMomfgkNn4o7+vKxK2DDKcBG9sHj51vHqMBGy8wbDS/J4lMxnqs153/T3+DmCEAkC5cpA== + dependencies: + "@babel/helper-plugin-utils" "^7.0.0" + +"@babel/plugin-transform-destructuring@^7.2.0": + version "7.5.0" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-destructuring/-/plugin-transform-destructuring-7.5.0.tgz#f6c09fdfe3f94516ff074fe877db7bc9ef05855a" + integrity sha512-YbYgbd3TryYYLGyC7ZR+Tq8H/+bCmwoaxHfJHupom5ECstzbRLTch6gOQbhEY9Z4hiCNHEURgq06ykFv9JZ/QQ== + dependencies: + "@babel/helper-plugin-utils" "^7.0.0" + +"@babel/plugin-transform-dotall-regex@^7.2.0": + version "7.4.4" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-dotall-regex/-/plugin-transform-dotall-regex-7.4.4.tgz#361a148bc951444312c69446d76ed1ea8e4450c3" + integrity sha512-P05YEhRc2h53lZDjRPk/OektxCVevFzZs2Gfjd545Wde3k+yFDbXORgl2e0xpbq8mLcKJ7Idss4fAg0zORN/zg== + dependencies: + "@babel/helper-plugin-utils" "^7.0.0" + "@babel/helper-regex" "^7.4.4" + regexpu-core "^4.5.4" + +"@babel/plugin-transform-duplicate-keys@^7.2.0": + version "7.5.0" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-duplicate-keys/-/plugin-transform-duplicate-keys-7.5.0.tgz#c5dbf5106bf84cdf691222c0974c12b1df931853" + integrity sha512-igcziksHizyQPlX9gfSjHkE2wmoCH3evvD2qR5w29/Dk0SMKE/eOI7f1HhBdNhR/zxJDqrgpoDTq5YSLH/XMsQ== + dependencies: + "@babel/helper-plugin-utils" "^7.0.0" + +"@babel/plugin-transform-exponentiation-operator@^7.2.0": + version "7.2.0" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-exponentiation-operator/-/plugin-transform-exponentiation-operator-7.2.0.tgz#a63868289e5b4007f7054d46491af51435766008" + integrity sha512-umh4hR6N7mu4Elq9GG8TOu9M0bakvlsREEC+ialrQN6ABS4oDQ69qJv1VtR3uxlKMCQMCvzk7vr17RHKcjx68A== + dependencies: + "@babel/helper-builder-binary-assignment-operator-visitor" "^7.1.0" + "@babel/helper-plugin-utils" "^7.0.0" + +"@babel/plugin-transform-for-of@^7.2.0": + version "7.4.4" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-for-of/-/plugin-transform-for-of-7.4.4.tgz#0267fc735e24c808ba173866c6c4d1440fc3c556" + integrity sha512-9T/5Dlr14Z9TIEXLXkt8T1DU7F24cbhwhMNUziN3hB1AXoZcdzPcTiKGRn/6iOymDqtTKWnr/BtRKN9JwbKtdQ== + dependencies: + "@babel/helper-plugin-utils" "^7.0.0" + +"@babel/plugin-transform-function-name@^7.2.0": + version "7.4.4" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-function-name/-/plugin-transform-function-name-7.4.4.tgz#e1436116abb0610c2259094848754ac5230922ad" + integrity sha512-iU9pv7U+2jC9ANQkKeNF6DrPy4GBa4NWQtl6dHB4Pb3izX2JOEvDTFarlNsBj/63ZEzNNIAMs3Qw4fNCcSOXJA== + dependencies: + "@babel/helper-function-name" "^7.1.0" + "@babel/helper-plugin-utils" "^7.0.0" + +"@babel/plugin-transform-literals@^7.2.0": + version "7.2.0" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-literals/-/plugin-transform-literals-7.2.0.tgz#690353e81f9267dad4fd8cfd77eafa86aba53ea1" + integrity sha512-2ThDhm4lI4oV7fVQ6pNNK+sx+c/GM5/SaML0w/r4ZB7sAneD/piDJtwdKlNckXeyGK7wlwg2E2w33C/Hh+VFCg== + dependencies: + "@babel/helper-plugin-utils" "^7.0.0" + +"@babel/plugin-transform-modules-amd@^7.2.0": + version "7.5.0" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-modules-amd/-/plugin-transform-modules-amd-7.5.0.tgz#ef00435d46da0a5961aa728a1d2ecff063e4fb91" + integrity sha512-n20UsQMKnWrltocZZm24cRURxQnWIvsABPJlw/fvoy9c6AgHZzoelAIzajDHAQrDpuKFFPPcFGd7ChsYuIUMpg== + dependencies: + "@babel/helper-module-transforms" "^7.1.0" + "@babel/helper-plugin-utils" "^7.0.0" + babel-plugin-dynamic-import-node "^2.3.0" + +"@babel/plugin-transform-modules-commonjs@^7.2.0": + version "7.5.0" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-modules-commonjs/-/plugin-transform-modules-commonjs-7.5.0.tgz#425127e6045231360858eeaa47a71d75eded7a74" + integrity sha512-xmHq0B+ytyrWJvQTc5OWAC4ii6Dhr0s22STOoydokG51JjWhyYo5mRPXoi+ZmtHQhZZwuXNN+GG5jy5UZZJxIQ== + dependencies: + "@babel/helper-module-transforms" "^7.4.4" + "@babel/helper-plugin-utils" "^7.0.0" + "@babel/helper-simple-access" "^7.1.0" + babel-plugin-dynamic-import-node "^2.3.0" + +"@babel/plugin-transform-modules-systemjs@^7.3.4": + version "7.5.0" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-modules-systemjs/-/plugin-transform-modules-systemjs-7.5.0.tgz#e75266a13ef94202db2a0620977756f51d52d249" + integrity sha512-Q2m56tyoQWmuNGxEtUyeEkm6qJYFqs4c+XyXH5RAuYxObRNz9Zgj/1g2GMnjYp2EUyEy7YTrxliGCXzecl/vJg== + dependencies: + "@babel/helper-hoist-variables" "^7.4.4" + "@babel/helper-plugin-utils" "^7.0.0" + babel-plugin-dynamic-import-node "^2.3.0" + +"@babel/plugin-transform-modules-umd@^7.2.0": + version "7.2.0" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-modules-umd/-/plugin-transform-modules-umd-7.2.0.tgz#7678ce75169f0877b8eb2235538c074268dd01ae" + integrity sha512-BV3bw6MyUH1iIsGhXlOK6sXhmSarZjtJ/vMiD9dNmpY8QXFFQTj+6v92pcfy1iqa8DeAfJFwoxcrS/TUZda6sw== + dependencies: + "@babel/helper-module-transforms" "^7.1.0" + "@babel/helper-plugin-utils" "^7.0.0" + +"@babel/plugin-transform-named-capturing-groups-regex@^7.3.0": + version "7.4.5" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-named-capturing-groups-regex/-/plugin-transform-named-capturing-groups-regex-7.4.5.tgz#9d269fd28a370258199b4294736813a60bbdd106" + integrity sha512-z7+2IsWafTBbjNsOxU/Iv5CvTJlr5w4+HGu1HovKYTtgJ362f7kBcQglkfmlspKKZ3bgrbSGvLfNx++ZJgCWsg== + dependencies: + regexp-tree "^0.1.6" + +"@babel/plugin-transform-new-target@^7.0.0": + version "7.4.4" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-new-target/-/plugin-transform-new-target-7.4.4.tgz#18d120438b0cc9ee95a47f2c72bc9768fbed60a5" + integrity sha512-r1z3T2DNGQwwe2vPGZMBNjioT2scgWzK9BCnDEh+46z8EEwXBq24uRzd65I7pjtugzPSj921aM15RpESgzsSuA== + dependencies: + "@babel/helper-plugin-utils" "^7.0.0" + +"@babel/plugin-transform-object-super@^7.2.0": + version "7.5.5" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-object-super/-/plugin-transform-object-super-7.5.5.tgz#c70021df834073c65eb613b8679cc4a381d1a9f9" + integrity sha512-un1zJQAhSosGFBduPgN/YFNvWVpRuHKU7IHBglLoLZsGmruJPOo6pbInneflUdmq7YvSVqhpPs5zdBvLnteltQ== + dependencies: + "@babel/helper-plugin-utils" "^7.0.0" + "@babel/helper-replace-supers" "^7.5.5" + +"@babel/plugin-transform-parameters@^7.2.0": + version "7.4.4" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-parameters/-/plugin-transform-parameters-7.4.4.tgz#7556cf03f318bd2719fe4c922d2d808be5571e16" + integrity sha512-oMh5DUO1V63nZcu/ZVLQFqiihBGo4OpxJxR1otF50GMeCLiRx5nUdtokd+u9SuVJrvvuIh9OosRFPP4pIPnwmw== + dependencies: + "@babel/helper-call-delegate" "^7.4.4" + "@babel/helper-get-function-arity" "^7.0.0" + "@babel/helper-plugin-utils" "^7.0.0" + +"@babel/plugin-transform-regenerator@^7.3.4": + version "7.4.5" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-regenerator/-/plugin-transform-regenerator-7.4.5.tgz#629dc82512c55cee01341fb27bdfcb210354680f" + integrity sha512-gBKRh5qAaCWntnd09S8QC7r3auLCqq5DI6O0DlfoyDjslSBVqBibrMdsqO+Uhmx3+BlOmE/Kw1HFxmGbv0N9dA== + dependencies: + regenerator-transform "^0.14.0" + +"@babel/plugin-transform-runtime@^7.4.0": + version "7.5.5" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-runtime/-/plugin-transform-runtime-7.5.5.tgz#a6331afbfc59189d2135b2e09474457a8e3d28bc" + integrity sha512-6Xmeidsun5rkwnGfMOp6/z9nSzWpHFNVr2Jx7kwoq4mVatQfQx5S56drBgEHF+XQbKOdIaOiMIINvp/kAwMN+w== + dependencies: + "@babel/helper-module-imports" "^7.0.0" + "@babel/helper-plugin-utils" "^7.0.0" + resolve "^1.8.1" + semver "^5.5.1" + +"@babel/plugin-transform-shorthand-properties@^7.2.0": + version "7.2.0" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-shorthand-properties/-/plugin-transform-shorthand-properties-7.2.0.tgz#6333aee2f8d6ee7e28615457298934a3b46198f0" + integrity sha512-QP4eUM83ha9zmYtpbnyjTLAGKQritA5XW/iG9cjtuOI8s1RuL/3V6a3DeSHfKutJQ+ayUfeZJPcnCYEQzaPQqg== + dependencies: + "@babel/helper-plugin-utils" "^7.0.0" + +"@babel/plugin-transform-spread@^7.2.0": + version "7.2.2" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-spread/-/plugin-transform-spread-7.2.2.tgz#3103a9abe22f742b6d406ecd3cd49b774919b406" + integrity sha512-KWfky/58vubwtS0hLqEnrWJjsMGaOeSBn90Ezn5Jeg9Z8KKHmELbP1yGylMlm5N6TPKeY9A2+UaSYLdxahg01w== + dependencies: + "@babel/helper-plugin-utils" "^7.0.0" + +"@babel/plugin-transform-sticky-regex@^7.2.0": + version "7.2.0" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-sticky-regex/-/plugin-transform-sticky-regex-7.2.0.tgz#a1e454b5995560a9c1e0d537dfc15061fd2687e1" + integrity sha512-KKYCoGaRAf+ckH8gEL3JHUaFVyNHKe3ASNsZ+AlktgHevvxGigoIttrEJb8iKN03Q7Eazlv1s6cx2B2cQ3Jabw== + dependencies: + "@babel/helper-plugin-utils" "^7.0.0" + "@babel/helper-regex" "^7.0.0" + +"@babel/plugin-transform-template-literals@^7.2.0": + version "7.4.4" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-template-literals/-/plugin-transform-template-literals-7.4.4.tgz#9d28fea7bbce637fb7612a0750989d8321d4bcb0" + integrity sha512-mQrEC4TWkhLN0z8ygIvEL9ZEToPhG5K7KDW3pzGqOfIGZ28Jb0POUkeWcoz8HnHvhFy6dwAT1j8OzqN8s804+g== + dependencies: + "@babel/helper-annotate-as-pure" "^7.0.0" + "@babel/helper-plugin-utils" "^7.0.0" + +"@babel/plugin-transform-typeof-symbol@^7.2.0": + version "7.2.0" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-typeof-symbol/-/plugin-transform-typeof-symbol-7.2.0.tgz#117d2bcec2fbf64b4b59d1f9819894682d29f2b2" + integrity sha512-2LNhETWYxiYysBtrBTqL8+La0jIoQQnIScUJc74OYvUGRmkskNY4EzLCnjHBzdmb38wqtTaixpo1NctEcvMDZw== + dependencies: + "@babel/helper-plugin-utils" "^7.0.0" + +"@babel/plugin-transform-unicode-regex@^7.2.0": + version "7.4.4" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-unicode-regex/-/plugin-transform-unicode-regex-7.4.4.tgz#ab4634bb4f14d36728bf5978322b35587787970f" + integrity sha512-il+/XdNw01i93+M9J9u4T7/e/Ue/vWfNZE4IRUQjplu2Mqb/AFTDimkw2tdEdSH50wuQXZAbXSql0UphQke+vA== + dependencies: + "@babel/helper-plugin-utils" "^7.0.0" + "@babel/helper-regex" "^7.4.4" + regexpu-core "^4.5.4" + +"@babel/preset-env@^7.0.0 < 7.4.0": + version "7.3.4" + resolved "https://registry.yarnpkg.com/@babel/preset-env/-/preset-env-7.3.4.tgz#887cf38b6d23c82f19b5135298bdb160062e33e1" + integrity sha512-2mwqfYMK8weA0g0uBKOt4FE3iEodiHy9/CW0b+nWXcbL+pGzLx8ESYc+j9IIxr6LTDHWKgPm71i9smo02bw+gA== + dependencies: + "@babel/helper-module-imports" "^7.0.0" + "@babel/helper-plugin-utils" "^7.0.0" + "@babel/plugin-proposal-async-generator-functions" "^7.2.0" + "@babel/plugin-proposal-json-strings" "^7.2.0" + "@babel/plugin-proposal-object-rest-spread" "^7.3.4" + "@babel/plugin-proposal-optional-catch-binding" "^7.2.0" + "@babel/plugin-proposal-unicode-property-regex" "^7.2.0" + "@babel/plugin-syntax-async-generators" "^7.2.0" + "@babel/plugin-syntax-json-strings" "^7.2.0" + "@babel/plugin-syntax-object-rest-spread" "^7.2.0" + "@babel/plugin-syntax-optional-catch-binding" "^7.2.0" + "@babel/plugin-transform-arrow-functions" "^7.2.0" + "@babel/plugin-transform-async-to-generator" "^7.3.4" + "@babel/plugin-transform-block-scoped-functions" "^7.2.0" + "@babel/plugin-transform-block-scoping" "^7.3.4" + "@babel/plugin-transform-classes" "^7.3.4" + "@babel/plugin-transform-computed-properties" "^7.2.0" + "@babel/plugin-transform-destructuring" "^7.2.0" + "@babel/plugin-transform-dotall-regex" "^7.2.0" + "@babel/plugin-transform-duplicate-keys" "^7.2.0" + "@babel/plugin-transform-exponentiation-operator" "^7.2.0" + "@babel/plugin-transform-for-of" "^7.2.0" + "@babel/plugin-transform-function-name" "^7.2.0" + "@babel/plugin-transform-literals" "^7.2.0" + "@babel/plugin-transform-modules-amd" "^7.2.0" + "@babel/plugin-transform-modules-commonjs" "^7.2.0" + "@babel/plugin-transform-modules-systemjs" "^7.3.4" + "@babel/plugin-transform-modules-umd" "^7.2.0" + "@babel/plugin-transform-named-capturing-groups-regex" "^7.3.0" + "@babel/plugin-transform-new-target" "^7.0.0" + "@babel/plugin-transform-object-super" "^7.2.0" + "@babel/plugin-transform-parameters" "^7.2.0" + "@babel/plugin-transform-regenerator" "^7.3.4" + "@babel/plugin-transform-shorthand-properties" "^7.2.0" + "@babel/plugin-transform-spread" "^7.2.0" + "@babel/plugin-transform-sticky-regex" "^7.2.0" + "@babel/plugin-transform-template-literals" "^7.2.0" + "@babel/plugin-transform-typeof-symbol" "^7.2.0" + "@babel/plugin-transform-unicode-regex" "^7.2.0" + browserslist "^4.3.4" + invariant "^2.2.2" + js-levenshtein "^1.1.3" + semver "^5.3.0" + +"@babel/runtime-corejs2@^7.2.0": + version "7.5.5" + resolved "https://registry.yarnpkg.com/@babel/runtime-corejs2/-/runtime-corejs2-7.5.5.tgz#c3214c08ef20341af4187f1c9fbdc357fbec96b2" + integrity sha512-FYATQVR00NSNi7mUfpPDp7E8RYMXDuO8gaix7u/w3GekfUinKgX1AcTxs7SoiEmoEW9mbpjrwqWSW6zCmw5h8A== + dependencies: + core-js "^2.6.5" + regenerator-runtime "^0.13.2" + +"@babel/runtime@^7.0.0": + version "7.5.5" + resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.5.5.tgz#74fba56d35efbeca444091c7850ccd494fd2f132" + integrity sha512-28QvEGyQyNkB0/m2B4FU7IEZGK2NUrcMtT6BZEFALTguLk+AUT6ofsHtPk5QyjAdUkpMJ+/Em+quwz4HOt30AQ== + dependencies: + regenerator-runtime "^0.13.2" + +"@babel/template@^7.1.0", "@babel/template@^7.4.4": + version "7.4.4" + resolved "https://registry.yarnpkg.com/@babel/template/-/template-7.4.4.tgz#f4b88d1225689a08f5bc3a17483545be9e4ed237" + integrity sha512-CiGzLN9KgAvgZsnivND7rkA+AeJ9JB0ciPOD4U59GKbQP2iQl+olF1l76kJOupqidozfZ32ghwBEJDhnk9MEcw== + dependencies: + "@babel/code-frame" "^7.0.0" + "@babel/parser" "^7.4.4" + "@babel/types" "^7.4.4" + +"@babel/traverse@^7.1.0", "@babel/traverse@^7.4.4", "@babel/traverse@^7.5.5": + version "7.5.5" + resolved "https://registry.yarnpkg.com/@babel/traverse/-/traverse-7.5.5.tgz#f664f8f368ed32988cd648da9f72d5ca70f165bb" + integrity sha512-MqB0782whsfffYfSjH4TM+LMjrJnhCNEDMDIjeTpl+ASaUvxcjoiVCo/sM1GhS1pHOXYfWVCYneLjMckuUxDaQ== + dependencies: + "@babel/code-frame" "^7.5.5" + "@babel/generator" "^7.5.5" + "@babel/helper-function-name" "^7.1.0" + "@babel/helper-split-export-declaration" "^7.4.4" + "@babel/parser" "^7.5.5" + "@babel/types" "^7.5.5" + debug "^4.1.0" + globals "^11.1.0" + lodash "^4.17.13" + +"@babel/types@^7.0.0", "@babel/types@^7.2.0", "@babel/types@^7.4.4", "@babel/types@^7.5.5": + version "7.5.5" + resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.5.5.tgz#97b9f728e182785909aa4ab56264f090a028d18a" + integrity sha512-s63F9nJioLqOlW3UkyMd+BYhXt44YuaFm/VV0VwuteqjYwRrObkU7ra9pY4wAJR3oXi8hJrMcrcJdO/HH33vtw== + dependencies: + esutils "^2.0.2" + lodash "^4.17.13" + to-fast-properties "^2.0.0" + +"@mrmlnc/readdir-enhanced@^2.2.1": + version "2.2.1" + resolved "https://registry.yarnpkg.com/@mrmlnc/readdir-enhanced/-/readdir-enhanced-2.2.1.tgz#524af240d1a360527b730475ecfa1344aa540dde" + integrity sha512-bPHp6Ji8b41szTOcaP63VlnbbO5Ny6dwAATtY6JTjh5N2OLrb5Qk/Th5cRkRQhkWCt+EJsYrNB0MiL+Gpn6e3g== + dependencies: + call-me-maybe "^1.0.1" + glob-to-regexp "^0.3.0" + +"@nodelib/fs.stat@^1.1.2": + version "1.1.3" + resolved "https://registry.yarnpkg.com/@nodelib/fs.stat/-/fs.stat-1.1.3.tgz#2b5a3ab3f918cca48a8c754c08168e3f03eba61b" + integrity sha512-shAmDyaQC4H92APFoIaVDHCx5bStIocgvbwQyxPRrbUY20V1EYTbSDchWbuwlMG3V17cprZhA6+78JfB+3DTPw== + +"@types/events@*": + version "3.0.0" + resolved "https://registry.yarnpkg.com/@types/events/-/events-3.0.0.tgz#2862f3f58a9a7f7c3e78d79f130dd4d71c25c2a7" + integrity sha512-EaObqwIvayI5a8dCzhFrjKzVwKLxjoG9T6Ppd5CEo07LRKfQ8Yokw54r5+Wq7FaBQ+yXRvQAYPrHwya1/UFt9g== + +"@types/glob@^7.1.1": + version "7.1.1" + resolved "https://registry.yarnpkg.com/@types/glob/-/glob-7.1.1.tgz#aa59a1c6e3fbc421e07ccd31a944c30eba521575" + integrity sha512-1Bh06cbWJUHMC97acuD6UMG29nMt0Aqz1vF3guLfG+kHHJhy3AyohZFFxYk2f7Q1SQIrNwvncxAE0N/9s70F2w== + dependencies: + "@types/events" "*" + "@types/minimatch" "*" + "@types/node" "*" + +"@types/minimatch@*": + version "3.0.3" + resolved "https://registry.yarnpkg.com/@types/minimatch/-/minimatch-3.0.3.tgz#3dca0e3f33b200fc7d1139c0cd96c1268cadfd9d" + integrity sha512-tHq6qdbT9U1IRSGf14CL0pUlULksvY9OZ+5eEgl1N7t+OA3tGvNpxJCzuKQlsNgCVwbAs670L1vcVQi8j9HjnA== + +"@types/node@*": + version "12.7.2" + resolved "https://registry.yarnpkg.com/@types/node/-/node-12.7.2.tgz#c4e63af5e8823ce9cc3f0b34f7b998c2171f0c44" + integrity sha512-dyYO+f6ihZEtNPDcWNR1fkoTDf3zAK3lAABDze3mz6POyIercH0lEUawUFXlG8xaQZmm1yEBON/4TsYv/laDYg== + +"@types/q@^1.5.1": + version "1.5.2" + resolved "https://registry.yarnpkg.com/@types/q/-/q-1.5.2.tgz#690a1475b84f2a884fd07cd797c00f5f31356ea8" + integrity sha512-ce5d3q03Ex0sy4R14722Rmt6MT07Ua+k4FwDfdcToYJcMKNtRVQvJ6JCAPdAmAnbRb6CsX6aYb9m96NGod9uTw== + +"@vue/babel-helper-vue-jsx-merge-props@^1.0.0": + version "1.0.0" + resolved "https://registry.yarnpkg.com/@vue/babel-helper-vue-jsx-merge-props/-/babel-helper-vue-jsx-merge-props-1.0.0.tgz#048fe579958da408fb7a8b2a3ec050b50a661040" + integrity sha512-6tyf5Cqm4m6v7buITuwS+jHzPlIPxbFzEhXR5JGZpbrvOcp1hiQKckd305/3C7C36wFekNTQSxAtgeM0j0yoUw== + +"@vue/babel-plugin-transform-vue-jsx@^1.0.0": + version "1.0.0" + resolved "https://registry.yarnpkg.com/@vue/babel-plugin-transform-vue-jsx/-/babel-plugin-transform-vue-jsx-1.0.0.tgz#ebcbf39c312c94114c8c4f407ee4f6c97aa45432" + integrity sha512-U+JNwVQSmaLKjO3lzCUC3cNXxprgezV1N+jOdqbP4xWNaqtWUCJnkjTVcgECM18A/AinDKPcUUeoyhU7yxUxXQ== + dependencies: + "@babel/helper-module-imports" "^7.0.0" + "@babel/plugin-syntax-jsx" "^7.2.0" + "@vue/babel-helper-vue-jsx-merge-props" "^1.0.0" + html-tags "^2.0.0" + lodash.kebabcase "^4.1.1" + svg-tags "^1.0.0" + +"@vue/babel-preset-app@^3.1.1": + version "3.10.0" + resolved "https://registry.yarnpkg.com/@vue/babel-preset-app/-/babel-preset-app-3.10.0.tgz#3f89d631dd0f174c8a72e769b55f081c533c4677" + integrity sha512-NzJLI4Qe0SYm9gVHQC9RXyP0YcPjI28TmZ0ds2RJa9NO96LXHLES2U1HqiMDN4+CVjOQFrWUNd7wWeaETRPXbg== + dependencies: + "@babel/helper-module-imports" "^7.0.0" + "@babel/plugin-proposal-class-properties" "^7.0.0" + "@babel/plugin-proposal-decorators" "^7.1.0" + "@babel/plugin-syntax-dynamic-import" "^7.0.0" + "@babel/plugin-syntax-jsx" "^7.0.0" + "@babel/plugin-transform-runtime" "^7.4.0" + "@babel/preset-env" "^7.0.0 < 7.4.0" + "@babel/runtime" "^7.0.0" + "@babel/runtime-corejs2" "^7.2.0" + "@vue/babel-preset-jsx" "^1.0.0" + babel-plugin-dynamic-import-node "^2.2.0" + babel-plugin-module-resolver "3.2.0" + core-js "^2.6.5" + +"@vue/babel-preset-jsx@^1.0.0": + version "1.1.0" + resolved "https://registry.yarnpkg.com/@vue/babel-preset-jsx/-/babel-preset-jsx-1.1.0.tgz#c8001329f5b372297a3111a251eb4f9e956c1266" + integrity sha512-EeZ9gwEmu79B4A6LMLAw5cPCVYIcbKWgJgJafWtLzh1S+SgERUmTkVQ9Vx4k8zYBiCuxHK3XziZ3VJIMau7THA== + dependencies: + "@vue/babel-helper-vue-jsx-merge-props" "^1.0.0" + "@vue/babel-plugin-transform-vue-jsx" "^1.0.0" + "@vue/babel-sugar-functional-vue" "^1.0.0" + "@vue/babel-sugar-inject-h" "^1.0.0" + "@vue/babel-sugar-v-model" "^1.0.0" + "@vue/babel-sugar-v-on" "^1.1.0" + +"@vue/babel-sugar-functional-vue@^1.0.0": + version "1.0.0" + resolved "https://registry.yarnpkg.com/@vue/babel-sugar-functional-vue/-/babel-sugar-functional-vue-1.0.0.tgz#17e2c4ca27b74b244da3b923240ec91d10048cb3" + integrity sha512-XE/jNaaorTuhWayCz+QClk5AB9OV5HzrwbzEC6sIUY0J60A28ONQKeTwxfidW42egOkqNH/UU6eE3KLfmiDj0Q== + dependencies: + "@babel/plugin-syntax-jsx" "^7.2.0" + +"@vue/babel-sugar-inject-h@^1.0.0": + version "1.0.0" + resolved "https://registry.yarnpkg.com/@vue/babel-sugar-inject-h/-/babel-sugar-inject-h-1.0.0.tgz#e5efb6c5b5b7988dc03831af6d133bf7bcde6347" + integrity sha512-NxWU+DqtbZgfGvd25GPoFMj+rvyQ8ZA1pHj8vIeqRij+vx3sXoKkObjA9ulZunvWw5F6uG9xYy4ytpxab/X+Hg== + dependencies: + "@babel/plugin-syntax-jsx" "^7.2.0" + +"@vue/babel-sugar-v-model@^1.0.0": + version "1.0.0" + resolved "https://registry.yarnpkg.com/@vue/babel-sugar-v-model/-/babel-sugar-v-model-1.0.0.tgz#f4da56aa67f65a349bd2c269a95e72e601af4613" + integrity sha512-Pfg2Al0io66P1eO6zUbRIgpyKCU2qTnumiE0lao/wA/uNdb7Dx5Tfd1W6tO5SsByETPnEs8i8+gawRIXX40rFw== + dependencies: + "@babel/plugin-syntax-jsx" "^7.2.0" + "@vue/babel-helper-vue-jsx-merge-props" "^1.0.0" + "@vue/babel-plugin-transform-vue-jsx" "^1.0.0" + camelcase "^5.0.0" + html-tags "^2.0.0" + svg-tags "^1.0.0" + +"@vue/babel-sugar-v-on@^1.1.0": + version "1.1.0" + resolved "https://registry.yarnpkg.com/@vue/babel-sugar-v-on/-/babel-sugar-v-on-1.1.0.tgz#1f2b35eeeabb87eaf8925931f4d34fd8e6404a45" + integrity sha512-8DwAj/RLpmrDP4eZ3erJcKcyuLArLUYagNODTsSQrMdG5zmLJoFFtEjODfYRh/XxM2wXv9Wxe+HAB41FQxxwQA== + dependencies: + "@babel/plugin-syntax-jsx" "^7.2.0" + "@vue/babel-plugin-transform-vue-jsx" "^1.0.0" + camelcase "^5.0.0" + +"@vue/component-compiler-utils@^3.0.0": + version "3.0.0" + resolved "https://registry.yarnpkg.com/@vue/component-compiler-utils/-/component-compiler-utils-3.0.0.tgz#d16fa26b836c06df5baaeb45f3d80afc47e35634" + integrity sha512-am+04/0UX7ektcmvhYmrf84BDVAD8afFOf4asZjN84q8xzxFclbk5x0MtxuKGfp+zjN5WWPJn3fjFAWtDdIGSw== + dependencies: + consolidate "^0.15.1" + hash-sum "^1.0.2" + lru-cache "^4.1.2" + merge-source-map "^1.1.0" + postcss "^7.0.14" + postcss-selector-parser "^5.0.0" + prettier "1.16.3" + source-map "~0.6.1" + vue-template-es2015-compiler "^1.9.0" + +"@vuepress/core@^1.0.3": + version "1.0.3" + resolved "https://registry.yarnpkg.com/@vuepress/core/-/core-1.0.3.tgz#a32aedca770ee763c406a74abab2bc4fa42e9bdc" + integrity sha512-VUzjf2LMxy+DjiDs2QUO0R4zXATn0db7ClDETzc5D+HH08J2YwUO2YNHgIObHMyuihmDozesbYBiDp0kIxAqCw== + dependencies: + "@babel/core" "^7.0.0" + "@vue/babel-preset-app" "^3.1.1" + "@vuepress/markdown" "^1.0.3" + "@vuepress/markdown-loader" "^1.0.3" + "@vuepress/plugin-last-updated" "^1.0.3" + "@vuepress/plugin-register-components" "^1.0.3" + "@vuepress/shared-utils" "^1.0.3" + autoprefixer "^9.5.1" + babel-loader "^8.0.4" + cache-loader "^3.0.0" + chokidar "^2.0.3" + connect-history-api-fallback "^1.5.0" + copy-webpack-plugin "^5.0.2" + cross-spawn "^6.0.5" + css-loader "^2.1.1" + file-loader "^3.0.1" + js-yaml "^3.11.0" + lru-cache "^5.1.1" + mini-css-extract-plugin "0.6.0" + optimize-css-assets-webpack-plugin "^5.0.1" + portfinder "^1.0.13" + postcss-loader "^3.0.0" + postcss-safe-parser "^4.0.1" + toml "^3.0.0" + url-loader "^1.0.1" + vue "^2.5.16" + vue-loader "^15.2.4" + vue-router "^3.0.2" + vue-server-renderer "^2.5.16" + vue-template-compiler "^2.5.16" + vuepress-html-webpack-plugin "^3.2.0" + vuepress-plugin-container "^2.0.0" + webpack "^4.8.1" + webpack-chain "^4.6.0" + webpack-dev-server "^3.5.1" + webpack-merge "^4.1.2" + webpackbar "3.2.0" + +"@vuepress/markdown-loader@^1.0.3": + version "1.0.3" + resolved "https://registry.yarnpkg.com/@vuepress/markdown-loader/-/markdown-loader-1.0.3.tgz#8e63e66d0bb1ec75ee564424bdf8e7ee38077b63" + integrity sha512-2/023ghXi+7XHeHRbcXpUeWAERtSSCopPPdZqFV5/aIhW+Lv1Bl2iV1QfR2jKwlnZO/6g3HYMBq2GJaTNw0QLg== + dependencies: + "@vuepress/markdown" "^1.0.3" + loader-utils "^1.1.0" + lru-cache "^5.1.1" + +"@vuepress/markdown@^1.0.3": + version "1.0.3" + resolved "https://registry.yarnpkg.com/@vuepress/markdown/-/markdown-1.0.3.tgz#633e2b69ee7c00a1aaa8b71584a50e4e1518edc6" + integrity sha512-kauU0EZk5+Ju74MtxiBiQ6HAbcchr8UjbURHSHwJe1k6W0fy0wyQ0ND5EILLhKZl1KhZeHGGDKBW385ruRKfcQ== + dependencies: + "@vuepress/shared-utils" "^1.0.3" + markdown-it "^8.4.1" + markdown-it-anchor "^5.0.2" + markdown-it-chain "^1.3.0" + markdown-it-emoji "^1.4.0" + markdown-it-table-of-contents "^0.4.0" + prismjs "^1.13.0" + +"@vuepress/plugin-active-header-links@^1.0.3": + version "1.0.3" + resolved "https://registry.yarnpkg.com/@vuepress/plugin-active-header-links/-/plugin-active-header-links-1.0.3.tgz#94cae9a4e554fb7989461741748938efaa2fd65e" + integrity sha512-hUxGVfiQs/ywDykklSzMXT4evHe1w/DB5PMtS2LIig3sj5K+gAgNiu6L9SjXFTrYPxp9fWkYmkKPf7guV2QuRw== + dependencies: + lodash.throttle "^4.1.1" + +"@vuepress/plugin-last-updated@^1.0.3": + version "1.0.3" + resolved "https://registry.yarnpkg.com/@vuepress/plugin-last-updated/-/plugin-last-updated-1.0.3.tgz#b9e3cd2d7cc27d0e70e7ea2194c171607bd61493" + integrity sha512-GMD9g8Lw1ASdBiRZgQotkZqOgsGuvX33sDnmRuYjUcO4f6Lo+m8JZsOTStNjcquCvykucbjYqU1LQTyGAMyZWw== + dependencies: + cross-spawn "^6.0.5" + +"@vuepress/plugin-nprogress@^1.0.3": + version "1.0.3" + resolved "https://registry.yarnpkg.com/@vuepress/plugin-nprogress/-/plugin-nprogress-1.0.3.tgz#a0d6913193c933171b4695bf153efb0c29a76421" + integrity sha512-F7+R9EcBV0MT7dn06sUhGpsE7dzkT/eVLBNDqN3hDBedhu1XV8Ch5JYYGXKGFHrRdtDmiwyvEl4W6L6uzCVT4Q== + dependencies: + nprogress "^0.2.0" + +"@vuepress/plugin-register-components@^1.0.3": + version "1.0.3" + resolved "https://registry.yarnpkg.com/@vuepress/plugin-register-components/-/plugin-register-components-1.0.3.tgz#5cbfd0947c904a0f4c1911746fb5586520ed9b9d" + integrity sha512-6vlXEuaEJtV1EIudcVzJciJf0HRAcWRd6ViB9WO87enkqeT+bR32VZENqcN43RyF8vPP+mmZ/2eDUpvM3J6a2w== + dependencies: + "@vuepress/shared-utils" "^1.0.3" + +"@vuepress/plugin-search@^1.0.3": + version "1.0.3" + resolved "https://registry.yarnpkg.com/@vuepress/plugin-search/-/plugin-search-1.0.3.tgz#f8cd98380cc00db067fb243e4fd298c6b8b7179a" + integrity sha512-CD4G6BrKtS6JS9DzPMbwwovanaKMhj/KN6Bv7P5oY5inWTl3lE9KOjzr1YUkoA6wL6f69EfdB5B7cdO2d47n/w== + +"@vuepress/shared-utils@^1.0.3": + version "1.0.3" + resolved "https://registry.yarnpkg.com/@vuepress/shared-utils/-/shared-utils-1.0.3.tgz#224d2fc10fcf26d871eae8dd93b810616731123f" + integrity sha512-E9kh+nk+E0X6GTONXK1OWeY7Yyl/bUkWltmdh89f7hcSn2MxuBmlph4JdtZKrTK2m+9EqzpVR+CYanGjTA/ZQA== + dependencies: + chalk "^2.3.2" + diacritics "^1.3.0" + escape-html "^1.0.3" + fs-extra "^7.0.1" + globby "^9.2.0" + gray-matter "^4.0.1" + hash-sum "^1.0.2" + semver "^6.0.0" + upath "^1.1.0" + +"@vuepress/theme-default@^1.0.3": + version "1.0.3" + resolved "https://registry.yarnpkg.com/@vuepress/theme-default/-/theme-default-1.0.3.tgz#a92025d9be1705ef7b95c53365a8ba3d917b6023" + integrity sha512-rS12CdMQwpSD7RI9XCM1gko13uPKhbVlbaxb7bd6ozjOQm4Iy1qAAyoZredRl1Sx29QvvcXZxLMGzAqx98GMCw== + dependencies: + "@vuepress/plugin-active-header-links" "^1.0.3" + "@vuepress/plugin-nprogress" "^1.0.3" + "@vuepress/plugin-search" "^1.0.3" + docsearch.js "^2.5.2" + stylus "^0.54.5" + stylus-loader "^3.0.2" + vuepress-plugin-container "^2.0.0" + +"@webassemblyjs/ast@1.8.5": + version "1.8.5" + resolved "https://registry.yarnpkg.com/@webassemblyjs/ast/-/ast-1.8.5.tgz#51b1c5fe6576a34953bf4b253df9f0d490d9e359" + integrity sha512-aJMfngIZ65+t71C3y2nBBg5FFG0Okt9m0XEgWZ7Ywgn1oMAT8cNwx00Uv1cQyHtidq0Xn94R4TAywO+LCQ+ZAQ== + dependencies: + "@webassemblyjs/helper-module-context" "1.8.5" + "@webassemblyjs/helper-wasm-bytecode" "1.8.5" + "@webassemblyjs/wast-parser" "1.8.5" + +"@webassemblyjs/floating-point-hex-parser@1.8.5": + version "1.8.5" + resolved "https://registry.yarnpkg.com/@webassemblyjs/floating-point-hex-parser/-/floating-point-hex-parser-1.8.5.tgz#1ba926a2923613edce496fd5b02e8ce8a5f49721" + integrity sha512-9p+79WHru1oqBh9ewP9zW95E3XAo+90oth7S5Re3eQnECGq59ly1Ri5tsIipKGpiStHsUYmY3zMLqtk3gTcOtQ== + +"@webassemblyjs/helper-api-error@1.8.5": + version "1.8.5" + resolved "https://registry.yarnpkg.com/@webassemblyjs/helper-api-error/-/helper-api-error-1.8.5.tgz#c49dad22f645227c5edb610bdb9697f1aab721f7" + integrity sha512-Za/tnzsvnqdaSPOUXHyKJ2XI7PDX64kWtURyGiJJZKVEdFOsdKUCPTNEVFZq3zJ2R0G5wc2PZ5gvdTRFgm81zA== + +"@webassemblyjs/helper-buffer@1.8.5": + version "1.8.5" + resolved "https://registry.yarnpkg.com/@webassemblyjs/helper-buffer/-/helper-buffer-1.8.5.tgz#fea93e429863dd5e4338555f42292385a653f204" + integrity sha512-Ri2R8nOS0U6G49Q86goFIPNgjyl6+oE1abW1pS84BuhP1Qcr5JqMwRFT3Ah3ADDDYGEgGs1iyb1DGX+kAi/c/Q== + +"@webassemblyjs/helper-code-frame@1.8.5": + version "1.8.5" + resolved "https://registry.yarnpkg.com/@webassemblyjs/helper-code-frame/-/helper-code-frame-1.8.5.tgz#9a740ff48e3faa3022b1dff54423df9aa293c25e" + integrity sha512-VQAadSubZIhNpH46IR3yWO4kZZjMxN1opDrzePLdVKAZ+DFjkGD/rf4v1jap744uPVU6yjL/smZbRIIJTOUnKQ== + dependencies: + "@webassemblyjs/wast-printer" "1.8.5" + +"@webassemblyjs/helper-fsm@1.8.5": + version "1.8.5" + resolved "https://registry.yarnpkg.com/@webassemblyjs/helper-fsm/-/helper-fsm-1.8.5.tgz#ba0b7d3b3f7e4733da6059c9332275d860702452" + integrity sha512-kRuX/saORcg8se/ft6Q2UbRpZwP4y7YrWsLXPbbmtepKr22i8Z4O3V5QE9DbZK908dh5Xya4Un57SDIKwB9eow== + +"@webassemblyjs/helper-module-context@1.8.5": + version "1.8.5" + resolved "https://registry.yarnpkg.com/@webassemblyjs/helper-module-context/-/helper-module-context-1.8.5.tgz#def4b9927b0101dc8cbbd8d1edb5b7b9c82eb245" + integrity sha512-/O1B236mN7UNEU4t9X7Pj38i4VoU8CcMHyy3l2cV/kIF4U5KoHXDVqcDuOs1ltkac90IM4vZdHc52t1x8Yfs3g== + dependencies: + "@webassemblyjs/ast" "1.8.5" + mamacro "^0.0.3" + +"@webassemblyjs/helper-wasm-bytecode@1.8.5": + version "1.8.5" + resolved "https://registry.yarnpkg.com/@webassemblyjs/helper-wasm-bytecode/-/helper-wasm-bytecode-1.8.5.tgz#537a750eddf5c1e932f3744206551c91c1b93e61" + integrity sha512-Cu4YMYG3Ddl72CbmpjU/wbP6SACcOPVbHN1dI4VJNJVgFwaKf1ppeFJrwydOG3NDHxVGuCfPlLZNyEdIYlQ6QQ== + +"@webassemblyjs/helper-wasm-section@1.8.5": + version "1.8.5" + resolved "https://registry.yarnpkg.com/@webassemblyjs/helper-wasm-section/-/helper-wasm-section-1.8.5.tgz#74ca6a6bcbe19e50a3b6b462847e69503e6bfcbf" + integrity sha512-VV083zwR+VTrIWWtgIUpqfvVdK4ff38loRmrdDBgBT8ADXYsEZ5mPQ4Nde90N3UYatHdYoDIFb7oHzMncI02tA== + dependencies: + "@webassemblyjs/ast" "1.8.5" + "@webassemblyjs/helper-buffer" "1.8.5" + "@webassemblyjs/helper-wasm-bytecode" "1.8.5" + "@webassemblyjs/wasm-gen" "1.8.5" + +"@webassemblyjs/ieee754@1.8.5": + version "1.8.5" + resolved "https://registry.yarnpkg.com/@webassemblyjs/ieee754/-/ieee754-1.8.5.tgz#712329dbef240f36bf57bd2f7b8fb9bf4154421e" + integrity sha512-aaCvQYrvKbY/n6wKHb/ylAJr27GglahUO89CcGXMItrOBqRarUMxWLJgxm9PJNuKULwN5n1csT9bYoMeZOGF3g== + dependencies: + "@xtuc/ieee754" "^1.2.0" + +"@webassemblyjs/leb128@1.8.5": + version "1.8.5" + resolved "https://registry.yarnpkg.com/@webassemblyjs/leb128/-/leb128-1.8.5.tgz#044edeb34ea679f3e04cd4fd9824d5e35767ae10" + integrity sha512-plYUuUwleLIziknvlP8VpTgO4kqNaH57Y3JnNa6DLpu/sGcP6hbVdfdX5aHAV716pQBKrfuU26BJK29qY37J7A== + dependencies: + "@xtuc/long" "4.2.2" + +"@webassemblyjs/utf8@1.8.5": + version "1.8.5" + resolved "https://registry.yarnpkg.com/@webassemblyjs/utf8/-/utf8-1.8.5.tgz#a8bf3b5d8ffe986c7c1e373ccbdc2a0915f0cedc" + integrity sha512-U7zgftmQriw37tfD934UNInokz6yTmn29inT2cAetAsaU9YeVCveWEwhKL1Mg4yS7q//NGdzy79nlXh3bT8Kjw== + +"@webassemblyjs/wasm-edit@1.8.5": + version "1.8.5" + resolved "https://registry.yarnpkg.com/@webassemblyjs/wasm-edit/-/wasm-edit-1.8.5.tgz#962da12aa5acc1c131c81c4232991c82ce56e01a" + integrity sha512-A41EMy8MWw5yvqj7MQzkDjU29K7UJq1VrX2vWLzfpRHt3ISftOXqrtojn7nlPsZ9Ijhp5NwuODuycSvfAO/26Q== + dependencies: + "@webassemblyjs/ast" "1.8.5" + "@webassemblyjs/helper-buffer" "1.8.5" + "@webassemblyjs/helper-wasm-bytecode" "1.8.5" + "@webassemblyjs/helper-wasm-section" "1.8.5" + "@webassemblyjs/wasm-gen" "1.8.5" + "@webassemblyjs/wasm-opt" "1.8.5" + "@webassemblyjs/wasm-parser" "1.8.5" + "@webassemblyjs/wast-printer" "1.8.5" + +"@webassemblyjs/wasm-gen@1.8.5": + version "1.8.5" + resolved "https://registry.yarnpkg.com/@webassemblyjs/wasm-gen/-/wasm-gen-1.8.5.tgz#54840766c2c1002eb64ed1abe720aded714f98bc" + integrity sha512-BCZBT0LURC0CXDzj5FXSc2FPTsxwp3nWcqXQdOZE4U7h7i8FqtFK5Egia6f9raQLpEKT1VL7zr4r3+QX6zArWg== + dependencies: + "@webassemblyjs/ast" "1.8.5" + "@webassemblyjs/helper-wasm-bytecode" "1.8.5" + "@webassemblyjs/ieee754" "1.8.5" + "@webassemblyjs/leb128" "1.8.5" + "@webassemblyjs/utf8" "1.8.5" + +"@webassemblyjs/wasm-opt@1.8.5": + version "1.8.5" + resolved "https://registry.yarnpkg.com/@webassemblyjs/wasm-opt/-/wasm-opt-1.8.5.tgz#b24d9f6ba50394af1349f510afa8ffcb8a63d264" + integrity sha512-HKo2mO/Uh9A6ojzu7cjslGaHaUU14LdLbGEKqTR7PBKwT6LdPtLLh9fPY33rmr5wcOMrsWDbbdCHq4hQUdd37Q== + dependencies: + "@webassemblyjs/ast" "1.8.5" + "@webassemblyjs/helper-buffer" "1.8.5" + "@webassemblyjs/wasm-gen" "1.8.5" + "@webassemblyjs/wasm-parser" "1.8.5" + +"@webassemblyjs/wasm-parser@1.8.5": + version "1.8.5" + resolved "https://registry.yarnpkg.com/@webassemblyjs/wasm-parser/-/wasm-parser-1.8.5.tgz#21576f0ec88b91427357b8536383668ef7c66b8d" + integrity sha512-pi0SYE9T6tfcMkthwcgCpL0cM9nRYr6/6fjgDtL6q/ZqKHdMWvxitRi5JcZ7RI4SNJJYnYNaWy5UUrHQy998lw== + dependencies: + "@webassemblyjs/ast" "1.8.5" + "@webassemblyjs/helper-api-error" "1.8.5" + "@webassemblyjs/helper-wasm-bytecode" "1.8.5" + "@webassemblyjs/ieee754" "1.8.5" + "@webassemblyjs/leb128" "1.8.5" + "@webassemblyjs/utf8" "1.8.5" + +"@webassemblyjs/wast-parser@1.8.5": + version "1.8.5" + resolved "https://registry.yarnpkg.com/@webassemblyjs/wast-parser/-/wast-parser-1.8.5.tgz#e10eecd542d0e7bd394f6827c49f3df6d4eefb8c" + integrity sha512-daXC1FyKWHF1i11obK086QRlsMsY4+tIOKgBqI1lxAnkp9xe9YMcgOxm9kLe+ttjs5aWV2KKE1TWJCN57/Btsg== + dependencies: + "@webassemblyjs/ast" "1.8.5" + "@webassemblyjs/floating-point-hex-parser" "1.8.5" + "@webassemblyjs/helper-api-error" "1.8.5" + "@webassemblyjs/helper-code-frame" "1.8.5" + "@webassemblyjs/helper-fsm" "1.8.5" + "@xtuc/long" "4.2.2" + +"@webassemblyjs/wast-printer@1.8.5": + version "1.8.5" + resolved "https://registry.yarnpkg.com/@webassemblyjs/wast-printer/-/wast-printer-1.8.5.tgz#114bbc481fd10ca0e23b3560fa812748b0bae5bc" + integrity sha512-w0U0pD4EhlnvRyeJzBqaVSJAo9w/ce7/WPogeXLzGkO6hzhr4GnQIZ4W4uUt5b9ooAaXPtnXlj0gzsXEOUNYMg== + dependencies: + "@webassemblyjs/ast" "1.8.5" + "@webassemblyjs/wast-parser" "1.8.5" + "@xtuc/long" "4.2.2" + +"@xtuc/ieee754@^1.2.0": + version "1.2.0" + resolved "https://registry.yarnpkg.com/@xtuc/ieee754/-/ieee754-1.2.0.tgz#eef014a3145ae477a1cbc00cd1e552336dceb790" + integrity sha512-DX8nKgqcGwsc0eJSqYt5lwP4DH5FlHnmuWWBRy7X0NcaGR0ZtuyeESgMwTYVEtxmsNGY+qit4QYT/MIYTOTPeA== + +"@xtuc/long@4.2.2": + version "4.2.2" + resolved "https://registry.yarnpkg.com/@xtuc/long/-/long-4.2.2.tgz#d291c6a4e97989b5c61d9acf396ae4fe133a718d" + integrity sha512-NuHqBY1PB/D8xU6s/thBgOAiAP7HOYDQ32+BFZILJ8ivkUkAHQnWfn6WhL79Owj1qmUnoN/YPhktdIoucipkAQ== + +abbrev@1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/abbrev/-/abbrev-1.1.1.tgz#f8f2c887ad10bf67f634f005b6987fed3179aac8" + integrity sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q== + +accepts@~1.3.4, accepts@~1.3.5, accepts@~1.3.7: + version "1.3.7" + resolved "https://registry.yarnpkg.com/accepts/-/accepts-1.3.7.tgz#531bc726517a3b2b41f850021c6cc15eaab507cd" + integrity sha512-Il80Qs2WjYlJIBNzNkK6KYqlVMTbZLXgHx2oT0pU/fjRHyEp+PEfEPY0R3WCwAGVOtauxh1hOxNgIf5bv7dQpA== + dependencies: + mime-types "~2.1.24" + negotiator "0.6.2" + +acorn@^6.2.1: + version "6.3.0" + resolved "https://registry.yarnpkg.com/acorn/-/acorn-6.3.0.tgz#0087509119ffa4fc0a0041d1e93a417e68cb856e" + integrity sha512-/czfa8BwS88b9gWQVhc8eknunSA2DoJpJyTQkhheIf5E48u1N0R4q/YxxsAeqRrmK9TQ/uYfgLDfZo91UlANIA== + +agentkeepalive@^2.2.0: + version "2.2.0" + resolved "https://registry.yarnpkg.com/agentkeepalive/-/agentkeepalive-2.2.0.tgz#c5d1bd4b129008f1163f236f86e5faea2026e2ef" + integrity sha1-xdG9SxKQCPEWPyNvhuX66iAm4u8= + +ajv-errors@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/ajv-errors/-/ajv-errors-1.0.1.tgz#f35986aceb91afadec4102fbd85014950cefa64d" + integrity sha512-DCRfO/4nQ+89p/RK43i8Ezd41EqdGIU4ld7nGF8OQ14oc/we5rEntLCUa7+jrn3nn83BosfwZA0wb4pon2o8iQ== + +ajv-keywords@^3.1.0, ajv-keywords@^3.4.1: + version "3.4.1" + resolved "https://registry.yarnpkg.com/ajv-keywords/-/ajv-keywords-3.4.1.tgz#ef916e271c64ac12171fd8384eaae6b2345854da" + integrity sha512-RO1ibKvd27e6FEShVFfPALuHI3WjSVNeK5FIsmme/LYRNxjKuNj+Dt7bucLa6NdSv3JcVTyMlm9kGR84z1XpaQ== + +ajv@^6.1.0, ajv@^6.10.2, ajv@^6.5.5: + version "6.10.2" + resolved "https://registry.yarnpkg.com/ajv/-/ajv-6.10.2.tgz#d3cea04d6b017b2894ad69040fec8b623eb4bd52" + integrity sha512-TXtUUEYHuaTEbLZWIKUr5pmBuhDLy+8KYtPYdcV8qC+pOZL+NKqYwvWSRrVXHn+ZmRRAu8vJTAznH7Oag6RVRw== + dependencies: + fast-deep-equal "^2.0.1" + fast-json-stable-stringify "^2.0.0" + json-schema-traverse "^0.4.1" + uri-js "^4.2.2" + +algoliasearch@^3.24.5: + version "3.33.0" + resolved "https://registry.yarnpkg.com/algoliasearch/-/algoliasearch-3.33.0.tgz#83b541124ebb0db54643009d4e660866b3177cdf" + integrity sha512-9DaVmOd7cvcZeYyV0BWAeJHVWJmgOL2DNUEBY/DTR4MzD1wCWs4Djl7LAlfvkGwGBdRHZCG+l0HA1572w3T8zg== + dependencies: + agentkeepalive "^2.2.0" + debug "^2.6.9" + envify "^4.0.0" + es6-promise "^4.1.0" + events "^1.1.0" + foreach "^2.0.5" + global "^4.3.2" + inherits "^2.0.1" + isarray "^2.0.1" + load-script "^1.0.0" + object-keys "^1.0.11" + querystring-es3 "^0.2.1" + reduce "^1.0.1" + semver "^5.1.0" + tunnel-agent "^0.6.0" + +alphanum-sort@^1.0.0: + version "1.0.2" + resolved "https://registry.yarnpkg.com/alphanum-sort/-/alphanum-sort-1.0.2.tgz#97a1119649b211ad33691d9f9f486a8ec9fbe0a3" + integrity sha1-l6ERlkmyEa0zaR2fn0hqjsn74KM= + +amdefine@>=0.0.4: + version "1.0.1" + resolved "https://registry.yarnpkg.com/amdefine/-/amdefine-1.0.1.tgz#4a5282ac164729e93619bcfd3ad151f817ce91f5" + integrity sha1-SlKCrBZHKek2Gbz9OtFR+BfOkfU= + +ansi-colors@^3.0.0: + version "3.2.4" + resolved "https://registry.yarnpkg.com/ansi-colors/-/ansi-colors-3.2.4.tgz#e3a3da4bfbae6c86a9c285625de124a234026fbf" + integrity sha512-hHUXGagefjN2iRrID63xckIvotOXOojhQKWIPUZ4mNUZ9nLZW+7FMNoE1lOkEhNWYsx/7ysGIuJYCiMAA9FnrA== + +ansi-escapes@^4.1.0: + version "4.2.1" + resolved "https://registry.yarnpkg.com/ansi-escapes/-/ansi-escapes-4.2.1.tgz#4dccdb846c3eee10f6d64dea66273eab90c37228" + integrity sha512-Cg3ymMAdN10wOk/VYfLV7KCQyv7EDirJ64500sU7n9UlmioEtDuU5Gd+hj73hXSU/ex7tHJSssmyftDdkMLO8Q== + dependencies: + type-fest "^0.5.2" + +ansi-html@0.0.7: + version "0.0.7" + resolved "https://registry.yarnpkg.com/ansi-html/-/ansi-html-0.0.7.tgz#813584021962a9e9e6fd039f940d12f56ca7859e" + integrity sha1-gTWEAhliqenm/QOflA0S9WynhZ4= + +ansi-regex@^2.0.0: + version "2.1.1" + resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-2.1.1.tgz#c3b33ab5ee360d86e0e628f0468ae7ef27d654df" + integrity sha1-w7M6te42DYbg5ijwRorn7yfWVN8= + +ansi-regex@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-3.0.0.tgz#ed0317c322064f79466c02966bddb605ab37d998" + integrity sha1-7QMXwyIGT3lGbAKWa922Bas32Zg= + +ansi-regex@^4.1.0: + version "4.1.0" + resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-4.1.0.tgz#8b9f8f08cf1acb843756a839ca8c7e3168c51997" + integrity sha512-1apePfXM1UOSqw0o9IiFAovVz9M5S1Dg+4TrDwfMewQ6p/rmMueb7tWZjQ1rx4Loy1ArBggoqGpfqqdI4rondg== + +ansi-styles@^2.2.1: + version "2.2.1" + resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-2.2.1.tgz#b432dd3358b634cf75e1e4664368240533c1ddbe" + integrity sha1-tDLdM1i2NM914eRmQ2gkBTPB3b4= + +ansi-styles@^3.2.0, ansi-styles@^3.2.1: + version "3.2.1" + resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-3.2.1.tgz#41fbb20243e50b12be0f04b8dedbf07520ce841d" + integrity sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA== + dependencies: + color-convert "^1.9.0" + +anymatch@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/anymatch/-/anymatch-2.0.0.tgz#bcb24b4f37934d9aa7ac17b4adaf89e7c76ef2eb" + integrity sha512-5teOsQWABXHHBFP9y3skS5P3d/WfWXpv3FUpy+LorMrNYaT9pI4oLMQX7jzQ2KklNpGpWHzdCXTDT2Y3XGlZBw== + dependencies: + micromatch "^3.1.4" + normalize-path "^2.1.1" + +aproba@^1.0.3, aproba@^1.1.1: + version "1.2.0" + resolved "https://registry.yarnpkg.com/aproba/-/aproba-1.2.0.tgz#6802e6264efd18c790a1b0d517f0f2627bf2c94a" + integrity sha512-Y9J6ZjXtoYh8RnXVCMOU/ttDmk1aBjunq9vO0ta5x85WDQiQfUF9sIPBITdbiiIVcBo03Hi3jMxigBtsddlXRw== + +are-we-there-yet@~1.1.2: + version "1.1.5" + resolved "https://registry.yarnpkg.com/are-we-there-yet/-/are-we-there-yet-1.1.5.tgz#4b35c2944f062a8bfcda66410760350fe9ddfc21" + integrity sha512-5hYdAkZlcG8tOLujVDTgCT+uPX0VnpAH28gWsLfzpXYm7wP6mp5Q/gYyR7YQ0cKVJcXJnl3j2kpBan13PtQf6w== + dependencies: + delegates "^1.0.0" + readable-stream "^2.0.6" + +argparse@^1.0.7: + version "1.0.10" + resolved "https://registry.yarnpkg.com/argparse/-/argparse-1.0.10.tgz#bcd6791ea5ae09725e17e5ad988134cd40b3d911" + integrity sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg== + dependencies: + sprintf-js "~1.0.2" + +arr-diff@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/arr-diff/-/arr-diff-4.0.0.tgz#d6461074febfec71e7e15235761a329a5dc7c520" + integrity sha1-1kYQdP6/7HHn4VI1dhoyml3HxSA= + +arr-flatten@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/arr-flatten/-/arr-flatten-1.1.0.tgz#36048bbff4e7b47e136644316c99669ea5ae91f1" + integrity sha512-L3hKV5R/p5o81R7O02IGnwpDmkp6E982XhtbuwSe3O4qOtMMMtodicASA1Cny2U+aCXcNpml+m4dPsvsJ3jatg== + +arr-union@^3.1.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/arr-union/-/arr-union-3.1.0.tgz#e39b09aea9def866a8f206e288af63919bae39c4" + integrity sha1-45sJrqne+Gao8gbiiK9jkZuuOcQ= + +array-flatten@1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/array-flatten/-/array-flatten-1.1.1.tgz#9a5f699051b1e7073328f2a008968b64ea2955d2" + integrity sha1-ml9pkFGx5wczKPKgCJaLZOopVdI= + +array-flatten@^2.1.0: + version "2.1.2" + resolved "https://registry.yarnpkg.com/array-flatten/-/array-flatten-2.1.2.tgz#24ef80a28c1a893617e2149b0c6d0d788293b099" + integrity sha512-hNfzcOV8W4NdualtqBFPyVO+54DSJuZGY9qT4pRroB6S9e3iiido2ISIC5h9R2sPJ8H3FHCIiEnsv1lPXO3KtQ== + +array-union@^1.0.1, array-union@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/array-union/-/array-union-1.0.2.tgz#9a34410e4f4e3da23dea375be5be70f24778ec39" + integrity sha1-mjRBDk9OPaI96jdb5b5w8kd47Dk= + dependencies: + array-uniq "^1.0.1" + +array-uniq@^1.0.1: + version "1.0.3" + resolved "https://registry.yarnpkg.com/array-uniq/-/array-uniq-1.0.3.tgz#af6ac877a25cc7f74e058894753858dfdb24fdb6" + integrity sha1-r2rId6Jcx/dOBYiUdThY39sk/bY= + +array-unique@^0.3.2: + version "0.3.2" + resolved "https://registry.yarnpkg.com/array-unique/-/array-unique-0.3.2.tgz#a894b75d4bc4f6cd679ef3244a9fd8f46ae2d428" + integrity sha1-qJS3XUvE9s1nnvMkSp/Y9Gri1Cg= + +asn1.js@^4.0.0: + version "4.10.1" + resolved "https://registry.yarnpkg.com/asn1.js/-/asn1.js-4.10.1.tgz#b9c2bf5805f1e64aadeed6df3a2bfafb5a73f5a0" + integrity sha512-p32cOF5q0Zqs9uBiONKYLm6BClCoBCM5O9JfeUSlnQLBTxYdTK+pW+nXflm8UkKd2UYlEbYz5qEi0JuZR9ckSw== + dependencies: + bn.js "^4.0.0" + inherits "^2.0.1" + minimalistic-assert "^1.0.0" + +asn1@~0.2.3: + version "0.2.4" + resolved "https://registry.yarnpkg.com/asn1/-/asn1-0.2.4.tgz#8d2475dfab553bb33e77b54e59e880bb8ce23136" + integrity sha512-jxwzQpLQjSmWXgwaCZE9Nz+glAG01yF1QnWgbhGwHI5A6FRIEY6IVqtHhIepHqI7/kyEyQEagBC5mBEFlIYvdg== + dependencies: + safer-buffer "~2.1.0" + +assert-plus@1.0.0, assert-plus@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/assert-plus/-/assert-plus-1.0.0.tgz#f12e0f3c5d77b0b1cdd9146942e4e96c1e4dd525" + integrity sha1-8S4PPF13sLHN2RRpQuTpbB5N1SU= + +assert@^1.1.1: + version "1.5.0" + resolved "https://registry.yarnpkg.com/assert/-/assert-1.5.0.tgz#55c109aaf6e0aefdb3dc4b71240c70bf574b18eb" + integrity sha512-EDsgawzwoun2CZkCgtxJbv392v4nbk9XDD06zI+kQYoBM/3RBWLlEyJARDOmhAAosBjWACEkKL6S+lIZtcAubA== + dependencies: + object-assign "^4.1.1" + util "0.10.3" + +assign-symbols@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/assign-symbols/-/assign-symbols-1.0.0.tgz#59667f41fadd4f20ccbc2bb96b8d4f7f78ec0367" + integrity sha1-WWZ/QfrdTyDMvCu5a41Pf3jsA2c= + +async-each@^1.0.1: + version "1.0.3" + resolved "https://registry.yarnpkg.com/async-each/-/async-each-1.0.3.tgz#b727dbf87d7651602f06f4d4ac387f47d91b0cbf" + integrity sha512-z/WhQ5FPySLdvREByI2vZiTWwCnF0moMJ1hK9YQwDTHKh6I7/uSckMetoRGb5UBZPC1z0jlw+n/XCgjeH7y1AQ== + +async-limiter@~1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/async-limiter/-/async-limiter-1.0.1.tgz#dd379e94f0db8310b08291f9d64c3209766617fd" + integrity sha512-csOlWGAcRFJaI6m+F2WKdnMKr4HhdhFVBk0H/QbJFMCr+uO2kwohwXQPxw/9OCxp05r5ghVBFSyioixx3gfkNQ== + +async@^1.5.2: + version "1.5.2" + resolved "https://registry.yarnpkg.com/async/-/async-1.5.2.tgz#ec6a61ae56480c0c3cb241c95618e20892f9672a" + integrity sha1-7GphrlZIDAw8skHJVhjiCJL5Zyo= + +asynckit@^0.4.0: + version "0.4.0" + resolved "https://registry.yarnpkg.com/asynckit/-/asynckit-0.4.0.tgz#c79ed97f7f34cb8f2ba1bc9790bcc366474b4b79" + integrity sha1-x57Zf380y48robyXkLzDZkdLS3k= + +atob@^2.1.1: + version "2.1.2" + resolved "https://registry.yarnpkg.com/atob/-/atob-2.1.2.tgz#6d9517eb9e030d2436666651e86bd9f6f13533c9" + integrity sha512-Wm6ukoaOGJi/73p/cl2GvLjTI5JM1k/O14isD73YML8StrH/7/lRFgmg8nICZgD3bZZvjwCGxtMOD3wWNAu8cg== + +autocomplete.js@0.36.0: + version "0.36.0" + resolved "https://registry.yarnpkg.com/autocomplete.js/-/autocomplete.js-0.36.0.tgz#94fe775fe64b6cd42e622d076dc7fd26bedd837b" + integrity sha512-jEwUXnVMeCHHutUt10i/8ZiRaCb0Wo+ZyKxeGsYwBDtw6EJHqEeDrq4UwZRD8YBSvp3g6klP678il2eeiVXN2Q== + dependencies: + immediate "^3.2.3" + +autoprefixer@^9.5.1: + version "9.6.1" + resolved "https://registry.yarnpkg.com/autoprefixer/-/autoprefixer-9.6.1.tgz#51967a02d2d2300bb01866c1611ec8348d355a47" + integrity sha512-aVo5WxR3VyvyJxcJC3h4FKfwCQvQWb1tSI5VHNibddCVWrcD1NvlxEweg3TSgiPztMnWfjpy2FURKA2kvDE+Tw== + dependencies: + browserslist "^4.6.3" + caniuse-lite "^1.0.30000980" + chalk "^2.4.2" + normalize-range "^0.1.2" + num2fraction "^1.2.2" + postcss "^7.0.17" + postcss-value-parser "^4.0.0" + +aws-sign2@~0.7.0: + version "0.7.0" + resolved "https://registry.yarnpkg.com/aws-sign2/-/aws-sign2-0.7.0.tgz#b46e890934a9591f2d2f6f86d7e6a9f1b3fe76a8" + integrity sha1-tG6JCTSpWR8tL2+G1+ap8bP+dqg= + +aws4@^1.8.0: + version "1.8.0" + resolved "https://registry.yarnpkg.com/aws4/-/aws4-1.8.0.tgz#f0e003d9ca9e7f59c7a508945d7b2ef9a04a542f" + integrity sha512-ReZxvNHIOv88FlT7rxcXIIC0fPt4KZqZbOlivyWtXLt8ESx84zd3kMC6iK5jVeS2qt+g7ftS7ye4fi06X5rtRQ== + +babel-loader@^8.0.4: + version "8.0.6" + resolved "https://registry.yarnpkg.com/babel-loader/-/babel-loader-8.0.6.tgz#e33bdb6f362b03f4bb141a0c21ab87c501b70dfb" + integrity sha512-4BmWKtBOBm13uoUwd08UwjZlaw3O9GWf456R9j+5YykFZ6LUIjIKLc0zEZf+hauxPOJs96C8k6FvYD09vWzhYw== + dependencies: + find-cache-dir "^2.0.0" + loader-utils "^1.0.2" + mkdirp "^0.5.1" + pify "^4.0.1" + +babel-plugin-dynamic-import-node@^2.2.0, babel-plugin-dynamic-import-node@^2.3.0: + version "2.3.0" + resolved "https://registry.yarnpkg.com/babel-plugin-dynamic-import-node/-/babel-plugin-dynamic-import-node-2.3.0.tgz#f00f507bdaa3c3e3ff6e7e5e98d90a7acab96f7f" + integrity sha512-o6qFkpeQEBxcqt0XYlWzAVxNCSCZdUgcR8IRlhD/8DylxjjO4foPcvTW0GGKa/cVt3rvxZ7o5ippJ+/0nvLhlQ== + dependencies: + object.assign "^4.1.0" + +babel-plugin-module-resolver@3.2.0: + version "3.2.0" + resolved "https://registry.yarnpkg.com/babel-plugin-module-resolver/-/babel-plugin-module-resolver-3.2.0.tgz#ddfa5e301e3b9aa12d852a9979f18b37881ff5a7" + integrity sha512-tjR0GvSndzPew/Iayf4uICWZqjBwnlMWjSx6brryfQ81F9rxBVqwDJtFCV8oOs0+vJeefK9TmdZtkIFdFe1UnA== + dependencies: + find-babel-config "^1.1.0" + glob "^7.1.2" + pkg-up "^2.0.0" + reselect "^3.0.1" + resolve "^1.4.0" + +balanced-match@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/balanced-match/-/balanced-match-1.0.0.tgz#89b4d199ab2bee49de164ea02b89ce462d71b767" + integrity sha1-ibTRmasr7kneFk6gK4nORi1xt2c= + +base64-js@^1.0.2: + version "1.3.1" + resolved "https://registry.yarnpkg.com/base64-js/-/base64-js-1.3.1.tgz#58ece8cb75dd07e71ed08c736abc5fac4dbf8df1" + integrity sha512-mLQ4i2QO1ytvGWFWmcngKO//JXAQueZvwEKtjgQFM4jIK0kU+ytMfplL8j+n5mspOfjHwoAg+9yhb7BwAHm36g== + +base@^0.11.1: + version "0.11.2" + resolved "https://registry.yarnpkg.com/base/-/base-0.11.2.tgz#7bde5ced145b6d551a90db87f83c558b4eb48a8f" + integrity sha512-5T6P4xPgpp0YDFvSWwEZ4NoE3aM4QBQXDzmVbraCkFj8zHM+mba8SyqB5DbZWyR7mYHo6Y7BdQo3MoA4m0TeQg== + dependencies: + cache-base "^1.0.1" + class-utils "^0.3.5" + component-emitter "^1.2.1" + define-property "^1.0.0" + isobject "^3.0.1" + mixin-deep "^1.2.0" + pascalcase "^0.1.1" + +batch@0.6.1: + version "0.6.1" + resolved "https://registry.yarnpkg.com/batch/-/batch-0.6.1.tgz#dc34314f4e679318093fc760272525f94bf25c16" + integrity sha1-3DQxT05nkxgJP8dgJyUl+UvyXBY= + +bcrypt-pbkdf@^1.0.0: + version "1.0.2" + resolved "https://registry.yarnpkg.com/bcrypt-pbkdf/-/bcrypt-pbkdf-1.0.2.tgz#a4301d389b6a43f9b67ff3ca11a3f6637e360e9e" + integrity sha1-pDAdOJtqQ/m2f/PKEaP2Y342Dp4= + dependencies: + tweetnacl "^0.14.3" + +big.js@^3.1.3: + version "3.2.0" + resolved "https://registry.yarnpkg.com/big.js/-/big.js-3.2.0.tgz#a5fc298b81b9e0dca2e458824784b65c52ba588e" + integrity sha512-+hN/Zh2D08Mx65pZ/4g5bsmNiZUuChDiQfTUQ7qJr4/kuopCr88xZsAXv6mBoZEsUI4OuGHlX59qE94K2mMW8Q== + +big.js@^5.2.2: + version "5.2.2" + resolved "https://registry.yarnpkg.com/big.js/-/big.js-5.2.2.tgz#65f0af382f578bcdc742bd9c281e9cb2d7768328" + integrity sha512-vyL2OymJxmarO8gxMr0mhChsO9QGwhynfuu4+MHTAW6czfq9humCB7rKpUjDd9YUiDPU4mzpyupFSvOClAwbmQ== + +binary-extensions@^1.0.0: + version "1.13.1" + resolved "https://registry.yarnpkg.com/binary-extensions/-/binary-extensions-1.13.1.tgz#598afe54755b2868a5330d2aff9d4ebb53209b65" + integrity sha512-Un7MIEDdUC5gNpcGDV97op1Ywk748MpHcFTHoYs6qnj1Z3j7I53VG3nwZhKzoBZmbdRNnb6WRdFlwl7tSDuZGw== + +bluebird@^3.1.1, bluebird@^3.5.5: + version "3.5.5" + resolved "https://registry.yarnpkg.com/bluebird/-/bluebird-3.5.5.tgz#a8d0afd73251effbbd5fe384a77d73003c17a71f" + integrity sha512-5am6HnnfN+urzt4yfg7IgTbotDjIT/u8AJpEt0sIU9FtXfVeezXAPKswrG+xKUCOYAINpSdgZVDU6QFh+cuH3w== + +bn.js@^4.0.0, bn.js@^4.1.0, bn.js@^4.1.1, bn.js@^4.4.0: + version "4.11.8" + resolved "https://registry.yarnpkg.com/bn.js/-/bn.js-4.11.8.tgz#2cde09eb5ee341f484746bb0309b3253b1b1442f" + integrity sha512-ItfYfPLkWHUjckQCk8xC+LwxgK8NYcXywGigJgSwOP8Y2iyWT4f2vsZnoOXTTbo+o5yXmIUJ4gn5538SO5S3gA== + +body-parser@1.19.0: + version "1.19.0" + resolved "https://registry.yarnpkg.com/body-parser/-/body-parser-1.19.0.tgz#96b2709e57c9c4e09a6fd66a8fd979844f69f08a" + integrity sha512-dhEPs72UPbDnAQJ9ZKMNTP6ptJaionhP5cBb541nXPlW60Jepo9RV/a4fX4XWW9CuFNK22krhrj1+rgzifNCsw== + dependencies: + bytes "3.1.0" + content-type "~1.0.4" + debug "2.6.9" + depd "~1.1.2" + http-errors "1.7.2" + iconv-lite "0.4.24" + on-finished "~2.3.0" + qs "6.7.0" + raw-body "2.4.0" + type-is "~1.6.17" + +bonjour@^3.5.0: + version "3.5.0" + resolved "https://registry.yarnpkg.com/bonjour/-/bonjour-3.5.0.tgz#8e890a183d8ee9a2393b3844c691a42bcf7bc9f5" + integrity sha1-jokKGD2O6aI5OzhExpGkK897yfU= + dependencies: + array-flatten "^2.1.0" + deep-equal "^1.0.1" + dns-equal "^1.0.0" + dns-txt "^2.0.2" + multicast-dns "^6.0.1" + multicast-dns-service-types "^1.1.0" + +boolbase@^1.0.0, boolbase@~1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/boolbase/-/boolbase-1.0.0.tgz#68dff5fbe60c51eb37725ea9e3ed310dcc1e776e" + integrity sha1-aN/1++YMUes3cl6p4+0xDcwed24= + +brace-expansion@^1.1.7: + version "1.1.11" + resolved "https://registry.yarnpkg.com/brace-expansion/-/brace-expansion-1.1.11.tgz#3c7fcbf529d87226f3d2f52b966ff5271eb441dd" + integrity sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA== + dependencies: + balanced-match "^1.0.0" + concat-map "0.0.1" + +braces@^2.3.1, braces@^2.3.2: + version "2.3.2" + resolved "https://registry.yarnpkg.com/braces/-/braces-2.3.2.tgz#5979fd3f14cd531565e5fa2df1abfff1dfaee729" + integrity sha512-aNdbnj9P8PjdXU4ybaWLK2IF3jc/EoDYbC7AazW6to3TRsfXxscC9UXOB5iDiEQrkyIbWp2SLQda4+QAa7nc3w== + dependencies: + arr-flatten "^1.1.0" + array-unique "^0.3.2" + extend-shallow "^2.0.1" + fill-range "^4.0.0" + isobject "^3.0.1" + repeat-element "^1.1.2" + snapdragon "^0.8.1" + snapdragon-node "^2.0.1" + split-string "^3.0.2" + to-regex "^3.0.1" + +brorand@^1.0.1: + version "1.1.0" + resolved "https://registry.yarnpkg.com/brorand/-/brorand-1.1.0.tgz#12c25efe40a45e3c323eb8675a0a0ce57b22371f" + integrity sha1-EsJe/kCkXjwyPrhnWgoM5XsiNx8= + +browserify-aes@^1.0.0, browserify-aes@^1.0.4: + version "1.2.0" + resolved "https://registry.yarnpkg.com/browserify-aes/-/browserify-aes-1.2.0.tgz#326734642f403dabc3003209853bb70ad428ef48" + integrity sha512-+7CHXqGuspUn/Sl5aO7Ea0xWGAtETPXNSAjHo48JfLdPWcMng33Xe4znFvQweqc/uzk5zSOI3H52CYnjCfb5hA== + dependencies: + buffer-xor "^1.0.3" + cipher-base "^1.0.0" + create-hash "^1.1.0" + evp_bytestokey "^1.0.3" + inherits "^2.0.1" + safe-buffer "^5.0.1" + +browserify-cipher@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/browserify-cipher/-/browserify-cipher-1.0.1.tgz#8d6474c1b870bfdabcd3bcfcc1934a10e94f15f0" + integrity sha512-sPhkz0ARKbf4rRQt2hTpAHqn47X3llLkUGn+xEJzLjwY8LRs2p0v7ljvI5EyoRO/mexrNunNECisZs+gw2zz1w== + dependencies: + browserify-aes "^1.0.4" + browserify-des "^1.0.0" + evp_bytestokey "^1.0.0" + +browserify-des@^1.0.0: + version "1.0.2" + resolved "https://registry.yarnpkg.com/browserify-des/-/browserify-des-1.0.2.tgz#3af4f1f59839403572f1c66204375f7a7f703e9c" + integrity sha512-BioO1xf3hFwz4kc6iBhI3ieDFompMhrMlnDFC4/0/vd5MokpuAc3R+LYbwTA9A5Yc9pq9UYPqffKpW2ObuwX5A== + dependencies: + cipher-base "^1.0.1" + des.js "^1.0.0" + inherits "^2.0.1" + safe-buffer "^5.1.2" + +browserify-rsa@^4.0.0: + version "4.0.1" + resolved "https://registry.yarnpkg.com/browserify-rsa/-/browserify-rsa-4.0.1.tgz#21e0abfaf6f2029cf2fafb133567a701d4135524" + integrity sha1-IeCr+vbyApzy+vsTNWenAdQTVSQ= + dependencies: + bn.js "^4.1.0" + randombytes "^2.0.1" + +browserify-sign@^4.0.0: + version "4.0.4" + resolved "https://registry.yarnpkg.com/browserify-sign/-/browserify-sign-4.0.4.tgz#aa4eb68e5d7b658baa6bf6a57e630cbd7a93d298" + integrity sha1-qk62jl17ZYuqa/alfmMMvXqT0pg= + dependencies: + bn.js "^4.1.1" + browserify-rsa "^4.0.0" + create-hash "^1.1.0" + create-hmac "^1.1.2" + elliptic "^6.0.0" + inherits "^2.0.1" + parse-asn1 "^5.0.0" + +browserify-zlib@^0.2.0: + version "0.2.0" + resolved "https://registry.yarnpkg.com/browserify-zlib/-/browserify-zlib-0.2.0.tgz#2869459d9aa3be245fe8fe2ca1f46e2e7f54d73f" + integrity sha512-Z942RysHXmJrhqk88FmKBVq/v5tqmSkDz7p54G/MGyjMnCFFnC79XWNbg+Vta8W6Wb2qtSZTSxIGkJrRpCFEiA== + dependencies: + pako "~1.0.5" + +browserslist@^4.0.0, browserslist@^4.3.4, browserslist@^4.6.3: + version "4.6.6" + resolved "https://registry.yarnpkg.com/browserslist/-/browserslist-4.6.6.tgz#6e4bf467cde520bc9dbdf3747dafa03531cec453" + integrity sha512-D2Nk3W9JL9Fp/gIcWei8LrERCS+eXu9AM5cfXA8WEZ84lFks+ARnZ0q/R69m2SV3Wjma83QDDPxsNKXUwdIsyA== + dependencies: + caniuse-lite "^1.0.30000984" + electron-to-chromium "^1.3.191" + node-releases "^1.1.25" + +buffer-from@^1.0.0: + version "1.1.1" + resolved "https://registry.yarnpkg.com/buffer-from/-/buffer-from-1.1.1.tgz#32713bc028f75c02fdb710d7c7bcec1f2c6070ef" + integrity sha512-MQcXEUbCKtEo7bhqEs6560Hyd4XaovZlO/k9V3hjVUF/zwW7KBVdSK4gIt/bzwS9MbR5qob+F5jusZsb0YQK2A== + +buffer-indexof@^1.0.0: + version "1.1.1" + resolved "https://registry.yarnpkg.com/buffer-indexof/-/buffer-indexof-1.1.1.tgz#52fabcc6a606d1a00302802648ef68f639da268c" + integrity sha512-4/rOEg86jivtPTeOUUT61jJO1Ya1TrR/OkqCSZDyq84WJh3LuuiphBYJN+fm5xufIk4XAFcEwte/8WzC8If/1g== + +buffer-json@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/buffer-json/-/buffer-json-2.0.0.tgz#f73e13b1e42f196fe2fd67d001c7d7107edd7c23" + integrity sha512-+jjPFVqyfF1esi9fvfUs3NqM0pH1ziZ36VP4hmA/y/Ssfo/5w5xHKfTw9BwQjoJ1w/oVtpLomqwUHKdefGyuHw== + +buffer-xor@^1.0.3: + version "1.0.3" + resolved "https://registry.yarnpkg.com/buffer-xor/-/buffer-xor-1.0.3.tgz#26e61ed1422fb70dd42e6e36729ed51d855fe8d9" + integrity sha1-JuYe0UIvtw3ULm42cp7VHYVf6Nk= + +buffer@^4.3.0: + version "4.9.1" + resolved "https://registry.yarnpkg.com/buffer/-/buffer-4.9.1.tgz#6d1bb601b07a4efced97094132093027c95bc298" + integrity sha1-bRu2AbB6TvztlwlBMgkwJ8lbwpg= + dependencies: + base64-js "^1.0.2" + ieee754 "^1.1.4" + isarray "^1.0.0" + +builtin-status-codes@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/builtin-status-codes/-/builtin-status-codes-3.0.0.tgz#85982878e21b98e1c66425e03d0174788f569ee8" + integrity sha1-hZgoeOIbmOHGZCXgPQF0eI9Wnug= + +bytes@3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/bytes/-/bytes-3.0.0.tgz#d32815404d689699f85a4ea4fa8755dd13a96048" + integrity sha1-0ygVQE1olpn4Wk6k+odV3ROpYEg= + +bytes@3.1.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/bytes/-/bytes-3.1.0.tgz#f6cf7933a360e0588fa9fde85651cdc7f805d1f6" + integrity sha512-zauLjrfCG+xvoyaqLoV8bLVXXNGC4JqlxFCutSDWA6fJrTo2ZuvLYTqZ7aHBLZSMOopbzwv8f+wZcVzfVTI2Dg== + +cac@^6.3.9: + version "6.5.2" + resolved "https://registry.yarnpkg.com/cac/-/cac-6.5.2.tgz#92ef1490b9ffde5f0be7eeadec5ea926f0e78ef6" + integrity sha512-8JdiD9/ZLsG418j/chyZQ3VWuhFELSGlH4EUxzNKgIH8wK8dO0j5Pqu6Pk7B/RP3kX9aasyQhPrrUjYO5e0w7w== + +cacache@^11.3.3: + version "11.3.3" + resolved "https://registry.yarnpkg.com/cacache/-/cacache-11.3.3.tgz#8bd29df8c6a718a6ebd2d010da4d7972ae3bbadc" + integrity sha512-p8WcneCytvzPxhDvYp31PD039vi77I12W+/KfR9S8AZbaiARFBCpsPJS+9uhWfeBfeAtW7o/4vt3MUqLkbY6nA== + dependencies: + bluebird "^3.5.5" + chownr "^1.1.1" + figgy-pudding "^3.5.1" + glob "^7.1.4" + graceful-fs "^4.1.15" + lru-cache "^5.1.1" + mississippi "^3.0.0" + mkdirp "^0.5.1" + move-concurrently "^1.0.1" + promise-inflight "^1.0.1" + rimraf "^2.6.3" + ssri "^6.0.1" + unique-filename "^1.1.1" + y18n "^4.0.0" + +cacache@^12.0.2: + version "12.0.2" + resolved "https://registry.yarnpkg.com/cacache/-/cacache-12.0.2.tgz#8db03205e36089a3df6954c66ce92541441ac46c" + integrity sha512-ifKgxH2CKhJEg6tNdAwziu6Q33EvuG26tYcda6PT3WKisZcYDXsnEdnRv67Po3yCzFfaSoMjGZzJyD2c3DT1dg== + dependencies: + bluebird "^3.5.5" + chownr "^1.1.1" + figgy-pudding "^3.5.1" + glob "^7.1.4" + graceful-fs "^4.1.15" + infer-owner "^1.0.3" + lru-cache "^5.1.1" + mississippi "^3.0.0" + mkdirp "^0.5.1" + move-concurrently "^1.0.1" + promise-inflight "^1.0.1" + rimraf "^2.6.3" + ssri "^6.0.1" + unique-filename "^1.1.1" + y18n "^4.0.0" + +cache-base@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/cache-base/-/cache-base-1.0.1.tgz#0a7f46416831c8b662ee36fe4e7c59d76f666ab2" + integrity sha512-AKcdTnFSWATd5/GCPRxr2ChwIJ85CeyrEyjRHlKxQ56d4XJMGym0uAiKn0xbLOGOl3+yRpOTi484dVCEc5AUzQ== + dependencies: + collection-visit "^1.0.0" + component-emitter "^1.2.1" + get-value "^2.0.6" + has-value "^1.0.0" + isobject "^3.0.1" + set-value "^2.0.0" + to-object-path "^0.3.0" + union-value "^1.0.0" + unset-value "^1.0.0" + +cache-loader@^3.0.0: + version "3.0.1" + resolved "https://registry.yarnpkg.com/cache-loader/-/cache-loader-3.0.1.tgz#cee6cf4b3cdc7c610905b26bad6c2fc439c821af" + integrity sha512-HzJIvGiGqYsFUrMjAJNDbVZoG7qQA+vy9AIoKs7s9DscNfki0I589mf2w6/tW+kkFH3zyiknoWV5Jdynu6b/zw== + dependencies: + buffer-json "^2.0.0" + find-cache-dir "^2.1.0" + loader-utils "^1.2.3" + mkdirp "^0.5.1" + neo-async "^2.6.1" + schema-utils "^1.0.0" + +call-me-maybe@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/call-me-maybe/-/call-me-maybe-1.0.1.tgz#26d208ea89e37b5cbde60250a15f031c16a4d66b" + integrity sha1-JtII6onje1y95gJQoV8DHBak1ms= + +caller-callsite@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/caller-callsite/-/caller-callsite-2.0.0.tgz#847e0fce0a223750a9a027c54b33731ad3154134" + integrity sha1-hH4PzgoiN1CpoCfFSzNzGtMVQTQ= + dependencies: + callsites "^2.0.0" + +caller-path@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/caller-path/-/caller-path-2.0.0.tgz#468f83044e369ab2010fac5f06ceee15bb2cb1f4" + integrity sha1-Ro+DBE42mrIBD6xfBs7uFbsssfQ= + dependencies: + caller-callsite "^2.0.0" + +callsites@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/callsites/-/callsites-2.0.0.tgz#06eb84f00eea413da86affefacbffb36093b3c50" + integrity sha1-BuuE8A7qQT2oav/vrL/7Ngk7PFA= + +camel-case@3.0.x: + version "3.0.0" + resolved "https://registry.yarnpkg.com/camel-case/-/camel-case-3.0.0.tgz#ca3c3688a4e9cf3a4cda777dc4dcbc713249cf73" + integrity sha1-yjw2iKTpzzpM2nd9xNy8cTJJz3M= + dependencies: + no-case "^2.2.0" + upper-case "^1.1.1" + +camelcase@^5.0.0, camelcase@^5.2.0: + version "5.3.1" + resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-5.3.1.tgz#e3c9b31569e106811df242f715725a1f4c494320" + integrity sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg== + +caniuse-api@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/caniuse-api/-/caniuse-api-3.0.0.tgz#5e4d90e2274961d46291997df599e3ed008ee4c0" + integrity sha512-bsTwuIg/BZZK/vreVTYYbSWoe2F+71P7K5QGEX+pT250DZbfU1MQ5prOKpPR+LL6uWKK3KMwMCAS74QB3Um1uw== + dependencies: + browserslist "^4.0.0" + caniuse-lite "^1.0.0" + lodash.memoize "^4.1.2" + lodash.uniq "^4.5.0" + +caniuse-lite@^1.0.0, caniuse-lite@^1.0.30000980, caniuse-lite@^1.0.30000984: + version "1.0.30000989" + resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30000989.tgz#b9193e293ccf7e4426c5245134b8f2a56c0ac4b9" + integrity sha512-vrMcvSuMz16YY6GSVZ0dWDTJP8jqk3iFQ/Aq5iqblPwxSVVZI+zxDyTX0VPqtQsDnfdrBDcsmhgTEOh5R8Lbpw== + +caseless@~0.12.0: + version "0.12.0" + resolved "https://registry.yarnpkg.com/caseless/-/caseless-0.12.0.tgz#1b681c21ff84033c826543090689420d187151dc" + integrity sha1-G2gcIf+EAzyCZUMJBolCDRhxUdw= + +chalk@^1.1.3: + version "1.1.3" + resolved "https://registry.yarnpkg.com/chalk/-/chalk-1.1.3.tgz#a8115c55e4a702fe4d150abd3872822a7e09fc98" + integrity sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg= + dependencies: + ansi-styles "^2.2.1" + escape-string-regexp "^1.0.2" + has-ansi "^2.0.0" + strip-ansi "^3.0.0" + supports-color "^2.0.0" + +chalk@^2.0.0, chalk@^2.3.2, chalk@^2.4.1, chalk@^2.4.2: + version "2.4.2" + resolved "https://registry.yarnpkg.com/chalk/-/chalk-2.4.2.tgz#cd42541677a54333cf541a49108c1432b44c9424" + integrity sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ== + dependencies: + ansi-styles "^3.2.1" + escape-string-regexp "^1.0.5" + supports-color "^5.3.0" + +chokidar@^2.0.2, chokidar@^2.0.3, chokidar@^2.1.6: + version "2.1.6" + resolved "https://registry.yarnpkg.com/chokidar/-/chokidar-2.1.6.tgz#b6cad653a929e244ce8a834244164d241fa954c5" + integrity sha512-V2jUo67OKkc6ySiRpJrjlpJKl9kDuG+Xb8VgsGzb+aEouhgS1D0weyPU4lEzdAcsCAvrih2J2BqyXqHWvVLw5g== + dependencies: + anymatch "^2.0.0" + async-each "^1.0.1" + braces "^2.3.2" + glob-parent "^3.1.0" + inherits "^2.0.3" + is-binary-path "^1.0.0" + is-glob "^4.0.0" + normalize-path "^3.0.0" + path-is-absolute "^1.0.0" + readdirp "^2.2.1" + upath "^1.1.1" + optionalDependencies: + fsevents "^1.2.7" + +chownr@^1.1.1: + version "1.1.2" + resolved "https://registry.yarnpkg.com/chownr/-/chownr-1.1.2.tgz#a18f1e0b269c8a6a5d3c86eb298beb14c3dd7bf6" + integrity sha512-GkfeAQh+QNy3wquu9oIZr6SS5x7wGdSgNQvD10X3r+AZr1Oys22HW8kAmDMvNg2+Dm0TeGaEuO8gFwdBXxwO8A== + +chrome-trace-event@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/chrome-trace-event/-/chrome-trace-event-1.0.2.tgz#234090ee97c7d4ad1a2c4beae27505deffc608a4" + integrity sha512-9e/zx1jw7B4CO+c/RXoCsfg/x1AfUBioy4owYH0bJprEYAx5hRFLRhWBqHAG57D0ZM4H7vxbP7bPe0VwhQRYDQ== + dependencies: + tslib "^1.9.0" + +ci-info@^1.6.0: + version "1.6.0" + resolved "https://registry.yarnpkg.com/ci-info/-/ci-info-1.6.0.tgz#2ca20dbb9ceb32d4524a683303313f0304b1e497" + integrity sha512-vsGdkwSCDpWmP80ncATX7iea5DWQemg1UgCW5J8tqjU3lYw4FBYuj89J0CTVomA7BEfvSZd84GmHko+MxFQU2A== + +cipher-base@^1.0.0, cipher-base@^1.0.1, cipher-base@^1.0.3: + version "1.0.4" + resolved "https://registry.yarnpkg.com/cipher-base/-/cipher-base-1.0.4.tgz#8760e4ecc272f4c363532f926d874aae2c1397de" + integrity sha512-Kkht5ye6ZGmwv40uUDZztayT2ThLQGfnj/T71N/XzeZeo3nf8foyW7zGTsPYkEya3m5f3cAypH+qe7YOrM1U2Q== + dependencies: + inherits "^2.0.1" + safe-buffer "^5.0.1" + +class-utils@^0.3.5: + version "0.3.6" + resolved "https://registry.yarnpkg.com/class-utils/-/class-utils-0.3.6.tgz#f93369ae8b9a7ce02fd41faad0ca83033190c463" + integrity sha512-qOhPa/Fj7s6TY8H8esGu5QNpMMQxz79h+urzrNYN6mn+9BnxlDGf5QZ+XeCDsxSjPqsSR56XOZOJmpeurnLMeg== + dependencies: + arr-union "^3.1.0" + define-property "^0.2.5" + isobject "^3.0.0" + static-extend "^0.1.1" + +clean-css@4.2.x: + version "4.2.1" + resolved "https://registry.yarnpkg.com/clean-css/-/clean-css-4.2.1.tgz#2d411ef76b8569b6d0c84068dabe85b0aa5e5c17" + integrity sha512-4ZxI6dy4lrY6FHzfiy1aEOXgu4LIsW2MhwG0VBKdcoGoH/XLFgaHSdLTGr4O8Be6A8r3MOphEiI8Gc1n0ecf3g== + dependencies: + source-map "~0.6.0" + +clipboard@^2.0.0: + version "2.0.4" + resolved "https://registry.yarnpkg.com/clipboard/-/clipboard-2.0.4.tgz#836dafd66cf0fea5d71ce5d5b0bf6e958009112d" + integrity sha512-Vw26VSLRpJfBofiVaFb/I8PVfdI1OxKcYShe6fm0sP/DtmiWQNCjhM/okTvdCo0G+lMMm1rMYbk4IK4x1X+kgQ== + dependencies: + good-listener "^1.2.2" + select "^1.1.2" + tiny-emitter "^2.0.0" + +cliui@^4.0.0: + version "4.1.0" + resolved "https://registry.yarnpkg.com/cliui/-/cliui-4.1.0.tgz#348422dbe82d800b3022eef4f6ac10bf2e4d1b49" + integrity sha512-4FG+RSG9DL7uEwRUZXZn3SS34DiDPfzP0VOiEwtUWlE+AR2EIg+hSyvrIgUUfhdgR/UkAeW2QHgeP+hWrXs7jQ== + dependencies: + string-width "^2.1.1" + strip-ansi "^4.0.0" + wrap-ansi "^2.0.0" + +coa@^2.0.2: + version "2.0.2" + resolved "https://registry.yarnpkg.com/coa/-/coa-2.0.2.tgz#43f6c21151b4ef2bf57187db0d73de229e3e7ec3" + integrity sha512-q5/jG+YQnSy4nRTV4F7lPepBJZ8qBNJJDBuJdoejDyLXgmL7IEo+Le2JDZudFTFt7mrCqIRaSjws4ygRCTCAXA== + dependencies: + "@types/q" "^1.5.1" + chalk "^2.4.1" + q "^1.1.2" + +code-point-at@^1.0.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/code-point-at/-/code-point-at-1.1.0.tgz#0d070b4d043a5bea33a2f1a40e2edb3d9a4ccf77" + integrity sha1-DQcLTQQ6W+ozovGkDi7bPZpMz3c= + +collection-visit@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/collection-visit/-/collection-visit-1.0.0.tgz#4bc0373c164bc3291b4d368c829cf1a80a59dca0" + integrity sha1-S8A3PBZLwykbTTaMgpzxqApZ3KA= + dependencies: + map-visit "^1.0.0" + object-visit "^1.0.0" + +color-convert@^1.9.0, color-convert@^1.9.1: + version "1.9.3" + resolved "https://registry.yarnpkg.com/color-convert/-/color-convert-1.9.3.tgz#bb71850690e1f136567de629d2d5471deda4c1e8" + integrity sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg== + dependencies: + color-name "1.1.3" + +color-name@1.1.3: + version "1.1.3" + resolved "https://registry.yarnpkg.com/color-name/-/color-name-1.1.3.tgz#a7d0558bd89c42f795dd42328f740831ca53bc25" + integrity sha1-p9BVi9icQveV3UIyj3QIMcpTvCU= + +color-name@^1.0.0: + version "1.1.4" + resolved "https://registry.yarnpkg.com/color-name/-/color-name-1.1.4.tgz#c2a09a87acbde69543de6f63fa3995c826c536a2" + integrity sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA== + +color-string@^1.5.2: + version "1.5.3" + resolved "https://registry.yarnpkg.com/color-string/-/color-string-1.5.3.tgz#c9bbc5f01b58b5492f3d6857459cb6590ce204cc" + integrity sha512-dC2C5qeWoYkxki5UAXapdjqO672AM4vZuPGRQfO8b5HKuKGBbKWpITyDYN7TOFKvRW7kOgAn3746clDBMDJyQw== + dependencies: + color-name "^1.0.0" + simple-swizzle "^0.2.2" + +color@^3.0.0: + version "3.1.2" + resolved "https://registry.yarnpkg.com/color/-/color-3.1.2.tgz#68148e7f85d41ad7649c5fa8c8106f098d229e10" + integrity sha512-vXTJhHebByxZn3lDvDJYw4lR5+uB3vuoHsuYA5AKuxRVn5wzzIfQKGLBmgdVRHKTJYeK5rvJcHnrd0Li49CFpg== + dependencies: + color-convert "^1.9.1" + color-string "^1.5.2" + +combined-stream@^1.0.6, combined-stream@~1.0.6: + version "1.0.8" + resolved "https://registry.yarnpkg.com/combined-stream/-/combined-stream-1.0.8.tgz#c3d45a8b34fd730631a110a8a2520682b31d5a7f" + integrity sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg== + dependencies: + delayed-stream "~1.0.0" + +commander@2.17.x: + version "2.17.1" + resolved "https://registry.yarnpkg.com/commander/-/commander-2.17.1.tgz#bd77ab7de6de94205ceacc72f1716d29f20a77bf" + integrity sha512-wPMUt6FnH2yzG95SA6mzjQOEKUU3aLaDEmzs1ti+1E9h+CsrZghRlqEM/EJ4KscsQVG8uNN4uVreUeT8+drlgg== + +commander@^2.20.0: + version "2.20.0" + resolved "https://registry.yarnpkg.com/commander/-/commander-2.20.0.tgz#d58bb2b5c1ee8f87b0d340027e9e94e222c5a422" + integrity sha512-7j2y+40w61zy6YC2iRNpUe/NwhNyoXrYpHMrSunaMG64nRnaf96zO/KMQR4OyN/UnE5KLyEBnKHd4aG3rskjpQ== + +commander@~2.19.0: + version "2.19.0" + resolved "https://registry.yarnpkg.com/commander/-/commander-2.19.0.tgz#f6198aa84e5b83c46054b94ddedbfed5ee9ff12a" + integrity sha512-6tvAOO+D6OENvRAh524Dh9jcfKTYDQAqvqezbCW82xj5X0pSrcpxtvRKHLG0yBY6SD7PSDrJaj+0AiOcKVd1Xg== + +commondir@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/commondir/-/commondir-1.0.1.tgz#ddd800da0c66127393cca5950ea968a3aaf1253b" + integrity sha1-3dgA2gxmEnOTzKWVDqloo6rxJTs= + +component-emitter@^1.2.1: + version "1.3.0" + resolved "https://registry.yarnpkg.com/component-emitter/-/component-emitter-1.3.0.tgz#16e4070fba8ae29b679f2215853ee181ab2eabc0" + integrity sha512-Rd3se6QB+sO1TwqZjscQrurpEPIfO0/yYnSin6Q/rD3mOutHvUrCAhJub3r90uNb+SESBuE0QYoB90YdfatsRg== + +compressible@~2.0.16: + version "2.0.17" + resolved "https://registry.yarnpkg.com/compressible/-/compressible-2.0.17.tgz#6e8c108a16ad58384a977f3a482ca20bff2f38c1" + integrity sha512-BGHeLCK1GV7j1bSmQQAi26X+GgWcTjLr/0tzSvMCl3LH1w1IJ4PFSPoV5316b30cneTziC+B1a+3OjoSUcQYmw== + dependencies: + mime-db ">= 1.40.0 < 2" + +compression@^1.7.4: + version "1.7.4" + resolved "https://registry.yarnpkg.com/compression/-/compression-1.7.4.tgz#95523eff170ca57c29a0ca41e6fe131f41e5bb8f" + integrity sha512-jaSIDzP9pZVS4ZfQ+TzvtiWhdpFhE2RDHz8QJkpX9SIpLq88VueF5jJw6t+6CUQcAoA6t+x89MLrWAqpfDE8iQ== + dependencies: + accepts "~1.3.5" + bytes "3.0.0" + compressible "~2.0.16" + debug "2.6.9" + on-headers "~1.0.2" + safe-buffer "5.1.2" + vary "~1.1.2" + +concat-map@0.0.1: + version "0.0.1" + resolved "https://registry.yarnpkg.com/concat-map/-/concat-map-0.0.1.tgz#d8a96bd77fd68df7793a73036a3ba0d5405d477b" + integrity sha1-2Klr13/Wjfd5OnMDajug1UBdR3s= + +concat-stream@^1.5.0: + version "1.6.2" + resolved "https://registry.yarnpkg.com/concat-stream/-/concat-stream-1.6.2.tgz#904bdf194cd3122fc675c77fc4ac3d4ff0fd1a34" + integrity sha512-27HBghJxjiZtIk3Ycvn/4kbJk/1uZuJFfuPEns6LaEvpvG1f0hTea8lilrouyo9mVc2GWdcEZ8OLoGmSADlrCw== + dependencies: + buffer-from "^1.0.0" + inherits "^2.0.3" + readable-stream "^2.2.2" + typedarray "^0.0.6" + +connect-history-api-fallback@^1.5.0, connect-history-api-fallback@^1.6.0: + version "1.6.0" + resolved "https://registry.yarnpkg.com/connect-history-api-fallback/-/connect-history-api-fallback-1.6.0.tgz#8b32089359308d111115d81cad3fceab888f97bc" + integrity sha512-e54B99q/OUoH64zYYRf3HBP5z24G38h5D3qXu23JGRoigpX5Ss4r9ZnDk3g0Z8uQC2x2lPaJ+UlWBc1ZWBWdLg== + +consola@^2.6.0: + version "2.10.1" + resolved "https://registry.yarnpkg.com/consola/-/consola-2.10.1.tgz#4693edba714677c878d520e4c7e4f69306b4b927" + integrity sha512-4sxpH6SGFYLADfUip4vuY65f/gEogrzJoniVhNUYkJHtng0l8ZjnDCqxxrSVRHOHwKxsy8Vm5ONZh1wOR3/l/w== + +console-browserify@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/console-browserify/-/console-browserify-1.1.0.tgz#f0241c45730a9fc6323b206dbf38edc741d0bb10" + integrity sha1-8CQcRXMKn8YyOyBtvzjtx0HQuxA= + dependencies: + date-now "^0.1.4" + +console-control-strings@^1.0.0, console-control-strings@~1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/console-control-strings/-/console-control-strings-1.1.0.tgz#3d7cf4464db6446ea644bf4b39507f9851008e8e" + integrity sha1-PXz0Rk22RG6mRL9LOVB/mFEAjo4= + +consolidate@^0.15.1: + version "0.15.1" + resolved "https://registry.yarnpkg.com/consolidate/-/consolidate-0.15.1.tgz#21ab043235c71a07d45d9aad98593b0dba56bab7" + integrity sha512-DW46nrsMJgy9kqAbPt5rKaCr7uFtpo4mSUvLHIUbJEjm0vo+aY5QLwBUq3FK4tRnJr/X0Psc0C4jf/h+HtXSMw== + dependencies: + bluebird "^3.1.1" + +constants-browserify@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/constants-browserify/-/constants-browserify-1.0.0.tgz#c20b96d8c617748aaf1c16021760cd27fcb8cb75" + integrity sha1-wguW2MYXdIqvHBYCF2DNJ/y4y3U= + +content-disposition@0.5.3: + version "0.5.3" + resolved "https://registry.yarnpkg.com/content-disposition/-/content-disposition-0.5.3.tgz#e130caf7e7279087c5616c2007d0485698984fbd" + integrity sha512-ExO0774ikEObIAEV9kDo50o+79VCUdEB6n6lzKgGwupcVeRlhrj3qGAfwq8G6uBJjkqLrhT0qEYFcWng8z1z0g== + dependencies: + safe-buffer "5.1.2" + +content-type@~1.0.4: + version "1.0.4" + resolved "https://registry.yarnpkg.com/content-type/-/content-type-1.0.4.tgz#e138cc75e040c727b1966fe5e5f8c9aee256fe3b" + integrity sha512-hIP3EEPs8tB9AT1L+NUqtwOAps4mk2Zob89MWXMHjHWg9milF/j4osnnQLXBCBFBk/tvIG/tUc9mOUJiPBhPXA== + +convert-source-map@^1.1.0: + version "1.6.0" + resolved "https://registry.yarnpkg.com/convert-source-map/-/convert-source-map-1.6.0.tgz#51b537a8c43e0f04dec1993bffcdd504e758ac20" + integrity sha512-eFu7XigvxdZ1ETfbgPBohgyQ/Z++C0eEhTor0qRwBw9unw+L0/6V8wkSuGgzdThkiS5lSpdptOQPD8Ak40a+7A== + dependencies: + safe-buffer "~5.1.1" + +cookie-signature@1.0.6: + version "1.0.6" + resolved "https://registry.yarnpkg.com/cookie-signature/-/cookie-signature-1.0.6.tgz#e303a882b342cc3ee8ca513a79999734dab3ae2c" + integrity sha1-4wOogrNCzD7oylE6eZmXNNqzriw= + +cookie@0.4.0: + version "0.4.0" + resolved "https://registry.yarnpkg.com/cookie/-/cookie-0.4.0.tgz#beb437e7022b3b6d49019d088665303ebe9c14ba" + integrity sha512-+Hp8fLp57wnUSt0tY0tHEXh4voZRDnoIrZPqlo3DPiI4y9lwg/jqx+1Om94/W6ZaPDOUbnjOt/99w66zk+l1Xg== + +copy-concurrently@^1.0.0: + version "1.0.5" + resolved "https://registry.yarnpkg.com/copy-concurrently/-/copy-concurrently-1.0.5.tgz#92297398cae34937fcafd6ec8139c18051f0b5e0" + integrity sha512-f2domd9fsVDFtaFcbaRZuYXwtdmnzqbADSwhSWYxYB/Q8zsdUUFMXVRwXGDMWmbEzAn1kdRrtI1T/KTFOL4X2A== + dependencies: + aproba "^1.1.1" + fs-write-stream-atomic "^1.0.8" + iferr "^0.1.5" + mkdirp "^0.5.1" + rimraf "^2.5.4" + run-queue "^1.0.0" + +copy-descriptor@^0.1.0: + version "0.1.1" + resolved "https://registry.yarnpkg.com/copy-descriptor/-/copy-descriptor-0.1.1.tgz#676f6eb3c39997c2ee1ac3a924fd6124748f578d" + integrity sha1-Z29us8OZl8LuGsOpJP1hJHSPV40= + +copy-webpack-plugin@^5.0.2: + version "5.0.4" + resolved "https://registry.yarnpkg.com/copy-webpack-plugin/-/copy-webpack-plugin-5.0.4.tgz#c78126f604e24f194c6ec2f43a64e232b5d43655" + integrity sha512-YBuYGpSzoCHSSDGyHy6VJ7SHojKp6WHT4D7ItcQFNAYx2hrwkMe56e97xfVR0/ovDuMTrMffXUiltvQljtAGeg== + dependencies: + cacache "^11.3.3" + find-cache-dir "^2.1.0" + glob-parent "^3.1.0" + globby "^7.1.1" + is-glob "^4.0.1" + loader-utils "^1.2.3" + minimatch "^3.0.4" + normalize-path "^3.0.0" + p-limit "^2.2.0" + schema-utils "^1.0.0" + serialize-javascript "^1.7.0" + webpack-log "^2.0.0" + +core-js@^2.6.5: + version "2.6.9" + resolved "https://registry.yarnpkg.com/core-js/-/core-js-2.6.9.tgz#6b4b214620c834152e179323727fc19741b084f2" + integrity sha512-HOpZf6eXmnl7la+cUdMnLvUxKNqLUzJvgIziQ0DiF3JwSImNphIqdGqzj6hIKyX04MmV0poclQ7+wjWvxQyR2A== + +core-util-is@1.0.2, core-util-is@~1.0.0: + version "1.0.2" + resolved "https://registry.yarnpkg.com/core-util-is/-/core-util-is-1.0.2.tgz#b5fd54220aa2bc5ab57aab7140c940754503c1a7" + integrity sha1-tf1UIgqivFq1eqtxQMlAdUUDwac= + +cosmiconfig@^5.0.0: + version "5.2.1" + resolved "https://registry.yarnpkg.com/cosmiconfig/-/cosmiconfig-5.2.1.tgz#040f726809c591e77a17c0a3626ca45b4f168b1a" + integrity sha512-H65gsXo1SKjf8zmrJ67eJk8aIRKV5ff2D4uKZIBZShbhGSpEmsQOPW/SKMKYhSTrqR7ufy6RP69rPogdaPh/kA== + dependencies: + import-fresh "^2.0.0" + is-directory "^0.3.1" + js-yaml "^3.13.1" + parse-json "^4.0.0" + +create-ecdh@^4.0.0: + version "4.0.3" + resolved "https://registry.yarnpkg.com/create-ecdh/-/create-ecdh-4.0.3.tgz#c9111b6f33045c4697f144787f9254cdc77c45ff" + integrity sha512-GbEHQPMOswGpKXM9kCWVrremUcBmjteUaQ01T9rkKCPDXfUHX0IoP9LpHYo2NPFampa4e+/pFDc3jQdxrxQLaw== + dependencies: + bn.js "^4.1.0" + elliptic "^6.0.0" + +create-hash@^1.1.0, create-hash@^1.1.2: + version "1.2.0" + resolved "https://registry.yarnpkg.com/create-hash/-/create-hash-1.2.0.tgz#889078af11a63756bcfb59bd221996be3a9ef196" + integrity sha512-z00bCGNHDG8mHAkP7CtT1qVu+bFQUPjYq/4Iv3C3kWjTFV10zIjfSoeqXo9Asws8gwSHDGj/hl2u4OGIjapeCg== + dependencies: + cipher-base "^1.0.1" + inherits "^2.0.1" + md5.js "^1.3.4" + ripemd160 "^2.0.1" + sha.js "^2.4.0" + +create-hmac@^1.1.0, create-hmac@^1.1.2, create-hmac@^1.1.4: + version "1.1.7" + resolved "https://registry.yarnpkg.com/create-hmac/-/create-hmac-1.1.7.tgz#69170c78b3ab957147b2b8b04572e47ead2243ff" + integrity sha512-MJG9liiZ+ogc4TzUwuvbER1JRdgvUFSB5+VR/g5h82fGaIRWMWddtKBHi7/sVhfjQZ6SehlyhvQYrcYkaUIpLg== + dependencies: + cipher-base "^1.0.3" + create-hash "^1.1.0" + inherits "^2.0.1" + ripemd160 "^2.0.0" + safe-buffer "^5.0.1" + sha.js "^2.4.8" + +cross-spawn@^6.0.0, cross-spawn@^6.0.5: + version "6.0.5" + resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-6.0.5.tgz#4a5ec7c64dfae22c3a14124dbacdee846d80cbc4" + integrity sha512-eTVLrBSt7fjbDygz805pMnstIs2VTBNkRm0qxZd+M7A5XDdxVRWO5MxGBXZhjY4cqLYLdtrGqRf8mBPmzwSpWQ== + dependencies: + nice-try "^1.0.4" + path-key "^2.0.1" + semver "^5.5.0" + shebang-command "^1.2.0" + which "^1.2.9" + +crypto-browserify@^3.11.0: + version "3.12.0" + resolved "https://registry.yarnpkg.com/crypto-browserify/-/crypto-browserify-3.12.0.tgz#396cf9f3137f03e4b8e532c58f698254e00f80ec" + integrity sha512-fz4spIh+znjO2VjL+IdhEpRJ3YN6sMzITSBijk6FK2UvTqruSQW+/cCZTSNsMiZNvUeq0CqurF+dAbyiGOY6Wg== + dependencies: + browserify-cipher "^1.0.0" + browserify-sign "^4.0.0" + create-ecdh "^4.0.0" + create-hash "^1.1.0" + create-hmac "^1.1.0" + diffie-hellman "^5.0.0" + inherits "^2.0.1" + pbkdf2 "^3.0.3" + public-encrypt "^4.0.0" + randombytes "^2.0.0" + randomfill "^1.0.3" + +css-color-names@0.0.4, css-color-names@^0.0.4: + version "0.0.4" + resolved "https://registry.yarnpkg.com/css-color-names/-/css-color-names-0.0.4.tgz#808adc2e79cf84738069b646cb20ec27beb629e0" + integrity sha1-gIrcLnnPhHOAabZGyyDsJ762KeA= + +css-declaration-sorter@^4.0.1: + version "4.0.1" + resolved "https://registry.yarnpkg.com/css-declaration-sorter/-/css-declaration-sorter-4.0.1.tgz#c198940f63a76d7e36c1e71018b001721054cb22" + integrity sha512-BcxQSKTSEEQUftYpBVnsH4SF05NTuBokb19/sBt6asXGKZ/6VP7PLG1CBCkFDYOnhXhPh0jMhO6xZ71oYHXHBA== + dependencies: + postcss "^7.0.1" + timsort "^0.3.0" + +css-loader@^2.1.1: + version "2.1.1" + resolved "https://registry.yarnpkg.com/css-loader/-/css-loader-2.1.1.tgz#d8254f72e412bb2238bb44dd674ffbef497333ea" + integrity sha512-OcKJU/lt232vl1P9EEDamhoO9iKY3tIjY5GU+XDLblAykTdgs6Ux9P1hTHve8nFKy5KPpOXOsVI/hIwi3841+w== + dependencies: + camelcase "^5.2.0" + icss-utils "^4.1.0" + loader-utils "^1.2.3" + normalize-path "^3.0.0" + postcss "^7.0.14" + postcss-modules-extract-imports "^2.0.0" + postcss-modules-local-by-default "^2.0.6" + postcss-modules-scope "^2.1.0" + postcss-modules-values "^2.0.0" + postcss-value-parser "^3.3.0" + schema-utils "^1.0.0" + +css-parse@1.7.x: + version "1.7.0" + resolved "https://registry.yarnpkg.com/css-parse/-/css-parse-1.7.0.tgz#321f6cf73782a6ff751111390fc05e2c657d8c9b" + integrity sha1-Mh9s9zeCpv91ERE5D8BeLGV9jJs= + +css-select-base-adapter@^0.1.1: + version "0.1.1" + resolved "https://registry.yarnpkg.com/css-select-base-adapter/-/css-select-base-adapter-0.1.1.tgz#3b2ff4972cc362ab88561507a95408a1432135d7" + integrity sha512-jQVeeRG70QI08vSTwf1jHxp74JoZsr2XSgETae8/xC8ovSnL2WF87GTLO86Sbwdt2lK4Umg4HnnwMO4YF3Ce7w== + +css-select@^1.1.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/css-select/-/css-select-1.2.0.tgz#2b3a110539c5355f1cd8d314623e870b121ec858" + integrity sha1-KzoRBTnFNV8c2NMUYj6HCxIeyFg= + dependencies: + boolbase "~1.0.0" + css-what "2.1" + domutils "1.5.1" + nth-check "~1.0.1" + +css-select@^2.0.0: + version "2.0.2" + resolved "https://registry.yarnpkg.com/css-select/-/css-select-2.0.2.tgz#ab4386cec9e1f668855564b17c3733b43b2a5ede" + integrity sha512-dSpYaDVoWaELjvZ3mS6IKZM/y2PMPa/XYoEfYNZePL4U/XgyxZNroHEHReDx/d+VgXh9VbCTtFqLkFbmeqeaRQ== + dependencies: + boolbase "^1.0.0" + css-what "^2.1.2" + domutils "^1.7.0" + nth-check "^1.0.2" + +css-tree@1.0.0-alpha.29: + version "1.0.0-alpha.29" + resolved "https://registry.yarnpkg.com/css-tree/-/css-tree-1.0.0-alpha.29.tgz#3fa9d4ef3142cbd1c301e7664c1f352bd82f5a39" + integrity sha512-sRNb1XydwkW9IOci6iB2xmy8IGCj6r/fr+JWitvJ2JxQRPzN3T4AGGVWCMlVmVwM1gtgALJRmGIlWv5ppnGGkg== + dependencies: + mdn-data "~1.1.0" + source-map "^0.5.3" + +css-tree@1.0.0-alpha.33: + version "1.0.0-alpha.33" + resolved "https://registry.yarnpkg.com/css-tree/-/css-tree-1.0.0-alpha.33.tgz#970e20e5a91f7a378ddd0fc58d0b6c8d4f3be93e" + integrity sha512-SPt57bh5nQnpsTBsx/IXbO14sRc9xXu5MtMAVuo0BaQQmyf0NupNPPSoMaqiAF5tDFafYsTkfeH4Q/HCKXkg4w== + dependencies: + mdn-data "2.0.4" + source-map "^0.5.3" + +css-unit-converter@^1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/css-unit-converter/-/css-unit-converter-1.1.1.tgz#d9b9281adcfd8ced935bdbaba83786897f64e996" + integrity sha1-2bkoGtz9jO2TW9urqDeGiX9k6ZY= + +css-what@2.1, css-what@^2.1.2: + version "2.1.3" + resolved "https://registry.yarnpkg.com/css-what/-/css-what-2.1.3.tgz#a6d7604573365fe74686c3f311c56513d88285f2" + integrity sha512-a+EPoD+uZiNfh+5fxw2nO9QwFa6nJe2Or35fGY6Ipw1R3R4AGz1d1TEZrCegvw2YTmZ0jXirGYlzxxpYSHwpEg== + +cssesc@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/cssesc/-/cssesc-2.0.0.tgz#3b13bd1bb1cb36e1bcb5a4dcd27f54c5dcb35703" + integrity sha512-MsCAG1z9lPdoO/IUMLSBWBSVxVtJ1395VGIQ+Fc2gNdkQ1hNDnQdw3YhA71WJCBW1vdwA0cAnk/DnW6bqoEUYg== + +cssesc@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/cssesc/-/cssesc-3.0.0.tgz#37741919903b868565e1c09ea747445cd18983ee" + integrity sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg== + +cssnano-preset-default@^4.0.7: + version "4.0.7" + resolved "https://registry.yarnpkg.com/cssnano-preset-default/-/cssnano-preset-default-4.0.7.tgz#51ec662ccfca0f88b396dcd9679cdb931be17f76" + integrity sha512-x0YHHx2h6p0fCl1zY9L9roD7rnlltugGu7zXSKQx6k2rYw0Hi3IqxcoAGF7u9Q5w1nt7vK0ulxV8Lo+EvllGsA== + dependencies: + css-declaration-sorter "^4.0.1" + cssnano-util-raw-cache "^4.0.1" + postcss "^7.0.0" + postcss-calc "^7.0.1" + postcss-colormin "^4.0.3" + postcss-convert-values "^4.0.1" + postcss-discard-comments "^4.0.2" + postcss-discard-duplicates "^4.0.2" + postcss-discard-empty "^4.0.1" + postcss-discard-overridden "^4.0.1" + postcss-merge-longhand "^4.0.11" + postcss-merge-rules "^4.0.3" + postcss-minify-font-values "^4.0.2" + postcss-minify-gradients "^4.0.2" + postcss-minify-params "^4.0.2" + postcss-minify-selectors "^4.0.2" + postcss-normalize-charset "^4.0.1" + postcss-normalize-display-values "^4.0.2" + postcss-normalize-positions "^4.0.2" + postcss-normalize-repeat-style "^4.0.2" + postcss-normalize-string "^4.0.2" + postcss-normalize-timing-functions "^4.0.2" + postcss-normalize-unicode "^4.0.1" + postcss-normalize-url "^4.0.1" + postcss-normalize-whitespace "^4.0.2" + postcss-ordered-values "^4.1.2" + postcss-reduce-initial "^4.0.3" + postcss-reduce-transforms "^4.0.2" + postcss-svgo "^4.0.2" + postcss-unique-selectors "^4.0.1" + +cssnano-util-get-arguments@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/cssnano-util-get-arguments/-/cssnano-util-get-arguments-4.0.0.tgz#ed3a08299f21d75741b20f3b81f194ed49cc150f" + integrity sha1-7ToIKZ8h11dBsg87gfGU7UnMFQ8= + +cssnano-util-get-match@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/cssnano-util-get-match/-/cssnano-util-get-match-4.0.0.tgz#c0e4ca07f5386bb17ec5e52250b4f5961365156d" + integrity sha1-wOTKB/U4a7F+xeUiULT1lhNlFW0= + +cssnano-util-raw-cache@^4.0.1: + version "4.0.1" + resolved "https://registry.yarnpkg.com/cssnano-util-raw-cache/-/cssnano-util-raw-cache-4.0.1.tgz#b26d5fd5f72a11dfe7a7846fb4c67260f96bf282" + integrity sha512-qLuYtWK2b2Dy55I8ZX3ky1Z16WYsx544Q0UWViebptpwn/xDBmog2TLg4f+DBMg1rJ6JDWtn96WHbOKDWt1WQA== + dependencies: + postcss "^7.0.0" + +cssnano-util-same-parent@^4.0.0: + version "4.0.1" + resolved "https://registry.yarnpkg.com/cssnano-util-same-parent/-/cssnano-util-same-parent-4.0.1.tgz#574082fb2859d2db433855835d9a8456ea18bbf3" + integrity sha512-WcKx5OY+KoSIAxBW6UBBRay1U6vkYheCdjyVNDm85zt5K9mHoGOfsOsqIszfAqrQQFIIKgjh2+FDgIj/zsl21Q== + +cssnano@^4.1.10: + version "4.1.10" + resolved "https://registry.yarnpkg.com/cssnano/-/cssnano-4.1.10.tgz#0ac41f0b13d13d465487e111b778d42da631b8b2" + integrity sha512-5wny+F6H4/8RgNlaqab4ktc3e0/blKutmq8yNlBFXA//nSFFAqAngjNVRzUvCgYROULmZZUoosL/KSoZo5aUaQ== + dependencies: + cosmiconfig "^5.0.0" + cssnano-preset-default "^4.0.7" + is-resolvable "^1.0.0" + postcss "^7.0.0" + +csso@^3.5.1: + version "3.5.1" + resolved "https://registry.yarnpkg.com/csso/-/csso-3.5.1.tgz#7b9eb8be61628973c1b261e169d2f024008e758b" + integrity sha512-vrqULLffYU1Q2tLdJvaCYbONStnfkfimRxXNaGjxMldI0C7JPBC4rB1RyjhfdZ4m1frm8pM9uRPKH3d2knZ8gg== + dependencies: + css-tree "1.0.0-alpha.29" + +cyclist@~0.2.2: + version "0.2.2" + resolved "https://registry.yarnpkg.com/cyclist/-/cyclist-0.2.2.tgz#1b33792e11e914a2fd6d6ed6447464444e5fa640" + integrity sha1-GzN5LhHpFKL9bW7WRHRkRE5fpkA= + +dashdash@^1.12.0: + version "1.14.1" + resolved "https://registry.yarnpkg.com/dashdash/-/dashdash-1.14.1.tgz#853cfa0f7cbe2fed5de20326b8dd581035f6e2f0" + integrity sha1-hTz6D3y+L+1d4gMmuN1YEDX24vA= + dependencies: + assert-plus "^1.0.0" + +date-now@^0.1.4: + version "0.1.4" + resolved "https://registry.yarnpkg.com/date-now/-/date-now-0.1.4.tgz#eaf439fd4d4848ad74e5cc7dbef200672b9e345b" + integrity sha1-6vQ5/U1ISK105cx9vvIAZyueNFs= + +de-indent@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/de-indent/-/de-indent-1.0.2.tgz#b2038e846dc33baa5796128d0804b455b8c1e21d" + integrity sha1-sgOOhG3DO6pXlhKNCAS0VbjB4h0= + +debug@*, debug@^4.1.0, debug@^4.1.1: + version "4.1.1" + resolved "https://registry.yarnpkg.com/debug/-/debug-4.1.1.tgz#3b72260255109c6b589cee050f1d516139664791" + integrity sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw== + dependencies: + ms "^2.1.1" + +debug@2.6.9, debug@^2.2.0, debug@^2.3.3, debug@^2.6.9: + version "2.6.9" + resolved "https://registry.yarnpkg.com/debug/-/debug-2.6.9.tgz#5d128515df134ff327e90a4c93f4e077a536341f" + integrity sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA== + dependencies: + ms "2.0.0" + +debug@^3.2.5, debug@^3.2.6: + version "3.2.6" + resolved "https://registry.yarnpkg.com/debug/-/debug-3.2.6.tgz#e83d17de16d8a7efb7717edbe5fb10135eee629b" + integrity sha512-mel+jf7nrtEl5Pn1Qx46zARXKDpBbvzezse7p7LqINmdoIk8PYP5SySaxEmYv6TZ0JyEKA1hsCId6DIhgITtWQ== + dependencies: + ms "^2.1.1" + +decamelize@^1.2.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/decamelize/-/decamelize-1.2.0.tgz#f6534d15148269b20352e7bee26f501f9a191290" + integrity sha1-9lNNFRSCabIDUue+4m9QH5oZEpA= + +decode-uri-component@^0.2.0: + version "0.2.0" + resolved "https://registry.yarnpkg.com/decode-uri-component/-/decode-uri-component-0.2.0.tgz#eb3913333458775cb84cd1a1fae062106bb87545" + integrity sha1-6zkTMzRYd1y4TNGh+uBiEGu4dUU= + +deep-equal@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/deep-equal/-/deep-equal-1.0.1.tgz#f5d260292b660e084eff4cdbc9f08ad3247448b5" + integrity sha1-9dJgKStmDghO/0zbyfCK0yR0SLU= + +deep-extend@^0.6.0: + version "0.6.0" + resolved "https://registry.yarnpkg.com/deep-extend/-/deep-extend-0.6.0.tgz#c4fa7c95404a17a9c3e8ca7e1537312b736330ac" + integrity sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA== + +deepmerge@^1.5.2: + version "1.5.2" + resolved "https://registry.yarnpkg.com/deepmerge/-/deepmerge-1.5.2.tgz#10499d868844cdad4fee0842df8c7f6f0c95a753" + integrity sha512-95k0GDqvBjZavkuvzx/YqVLv/6YYa17fz6ILMSf7neqQITCPbnfEnQvEgMPNjH4kgobe7+WIL0yJEHku+H3qtQ== + +default-gateway@^4.2.0: + version "4.2.0" + resolved "https://registry.yarnpkg.com/default-gateway/-/default-gateway-4.2.0.tgz#167104c7500c2115f6dd69b0a536bb8ed720552b" + integrity sha512-h6sMrVB1VMWVrW13mSc6ia/DwYYw5MN6+exNu1OaJeFac5aSAvwM7lZ0NVfTABuSkQelr4h5oebg3KB1XPdjgA== + dependencies: + execa "^1.0.0" + ip-regex "^2.1.0" + +define-properties@^1.1.2, define-properties@^1.1.3: + version "1.1.3" + resolved "https://registry.yarnpkg.com/define-properties/-/define-properties-1.1.3.tgz#cf88da6cbee26fe6db7094f61d870cbd84cee9f1" + integrity sha512-3MqfYKj2lLzdMSf8ZIZE/V+Zuy+BgD6f164e8K2w7dgnpKArBDerGYpM46IYYcjnkdPNMjPk9A6VFB8+3SKlXQ== + dependencies: + object-keys "^1.0.12" + +define-property@^0.2.5: + version "0.2.5" + resolved "https://registry.yarnpkg.com/define-property/-/define-property-0.2.5.tgz#c35b1ef918ec3c990f9a5bc57be04aacec5c8116" + integrity sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY= + dependencies: + is-descriptor "^0.1.0" + +define-property@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/define-property/-/define-property-1.0.0.tgz#769ebaaf3f4a63aad3af9e8d304c9bbe79bfb0e6" + integrity sha1-dp66rz9KY6rTr56NMEybvnm/sOY= + dependencies: + is-descriptor "^1.0.0" + +define-property@^2.0.2: + version "2.0.2" + resolved "https://registry.yarnpkg.com/define-property/-/define-property-2.0.2.tgz#d459689e8d654ba77e02a817f8710d702cb16e9d" + integrity sha512-jwK2UV4cnPpbcG7+VRARKTZPUWowwXA8bzH5NP6ud0oeAxyYPuGZUAC7hMugpCdz4BeSZl2Dl9k66CHJ/46ZYQ== + dependencies: + is-descriptor "^1.0.2" + isobject "^3.0.1" + +del@^4.1.1: + version "4.1.1" + resolved "https://registry.yarnpkg.com/del/-/del-4.1.1.tgz#9e8f117222ea44a31ff3a156c049b99052a9f0b4" + integrity sha512-QwGuEUouP2kVwQenAsOof5Fv8K9t3D8Ca8NxcXKrIpEHjTXK5J2nXLdP+ALI1cgv8wj7KuwBhTwBkOZSJKM5XQ== + dependencies: + "@types/glob" "^7.1.1" + globby "^6.1.0" + is-path-cwd "^2.0.0" + is-path-in-cwd "^2.0.0" + p-map "^2.0.0" + pify "^4.0.1" + rimraf "^2.6.3" + +delayed-stream@~1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/delayed-stream/-/delayed-stream-1.0.0.tgz#df3ae199acadfb7d440aaae0b29e2272b24ec619" + integrity sha1-3zrhmayt+31ECqrgsp4icrJOxhk= + +delegate@^3.1.2: + version "3.2.0" + resolved "https://registry.yarnpkg.com/delegate/-/delegate-3.2.0.tgz#b66b71c3158522e8ab5744f720d8ca0c2af59166" + integrity sha512-IofjkYBZaZivn0V8nnsMJGBr4jVLxHDheKSW88PyxS5QC4Vo9ZbZVvhzlSxY87fVq3STR6r+4cGepyHkcWOQSw== + +delegates@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/delegates/-/delegates-1.0.0.tgz#84c6e159b81904fdca59a0ef44cd870d31250f9a" + integrity sha1-hMbhWbgZBP3KWaDvRM2HDTElD5o= + +depd@~1.1.2: + version "1.1.2" + resolved "https://registry.yarnpkg.com/depd/-/depd-1.1.2.tgz#9bcd52e14c097763e749b274c4346ed2e560b5a9" + integrity sha1-m81S4UwJd2PnSbJ0xDRu0uVgtak= + +des.js@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/des.js/-/des.js-1.0.0.tgz#c074d2e2aa6a8a9a07dbd61f9a15c2cd83ec8ecc" + integrity sha1-wHTS4qpqipoH29YfmhXCzYPsjsw= + dependencies: + inherits "^2.0.1" + minimalistic-assert "^1.0.0" + +destroy@~1.0.4: + version "1.0.4" + resolved "https://registry.yarnpkg.com/destroy/-/destroy-1.0.4.tgz#978857442c44749e4206613e37946205826abd80" + integrity sha1-l4hXRCxEdJ5CBmE+N5RiBYJqvYA= + +detect-libc@^1.0.2: + version "1.0.3" + resolved "https://registry.yarnpkg.com/detect-libc/-/detect-libc-1.0.3.tgz#fa137c4bd698edf55cd5cd02ac559f91a4c4ba9b" + integrity sha1-+hN8S9aY7fVc1c0CrFWfkaTEups= + +detect-node@^2.0.4: + version "2.0.4" + resolved "https://registry.yarnpkg.com/detect-node/-/detect-node-2.0.4.tgz#014ee8f8f669c5c58023da64b8179c083a28c46c" + integrity sha512-ZIzRpLJrOj7jjP2miAtgqIfmzbxa4ZOr5jJc601zklsfEx9oTzmmj2nVpIPRpNlRTIh8lc1kyViIY7BWSGNmKw== + +diacritics@^1.3.0: + version "1.3.0" + resolved "https://registry.yarnpkg.com/diacritics/-/diacritics-1.3.0.tgz#3efa87323ebb863e6696cebb0082d48ff3d6f7a1" + integrity sha1-PvqHMj67hj5mls67AILUj/PW96E= + +diffie-hellman@^5.0.0: + version "5.0.3" + resolved "https://registry.yarnpkg.com/diffie-hellman/-/diffie-hellman-5.0.3.tgz#40e8ee98f55a2149607146921c63e1ae5f3d2875" + integrity sha512-kqag/Nl+f3GwyK25fhUMYj81BUOrZ9IuJsjIcDE5icNM9FJHAVm3VcUDxdLPoQtTuUylWm6ZIknYJwwaPxsUzg== + dependencies: + bn.js "^4.1.0" + miller-rabin "^4.0.0" + randombytes "^2.0.0" + +dir-glob@^2.0.0, dir-glob@^2.2.2: + version "2.2.2" + resolved "https://registry.yarnpkg.com/dir-glob/-/dir-glob-2.2.2.tgz#fa09f0694153c8918b18ba0deafae94769fc50c4" + integrity sha512-f9LBi5QWzIW3I6e//uxZoLBlUt9kcp66qo0sSCxL6YZKc75R1c4MFCoe/LaZiBGmgujvQdxc5Bn3QhfyvK5Hsw== + dependencies: + path-type "^3.0.0" + +dns-equal@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/dns-equal/-/dns-equal-1.0.0.tgz#b39e7f1da6eb0a75ba9c17324b34753c47e0654d" + integrity sha1-s55/HabrCnW6nBcySzR1PEfgZU0= + +dns-packet@^1.3.1: + version "1.3.1" + resolved "https://registry.yarnpkg.com/dns-packet/-/dns-packet-1.3.1.tgz#12aa426981075be500b910eedcd0b47dd7deda5a" + integrity sha512-0UxfQkMhYAUaZI+xrNZOz/as5KgDU0M/fQ9b6SpkyLbk3GEswDi6PADJVaYJradtRVsRIlF1zLyOodbcTCDzUg== + dependencies: + ip "^1.1.0" + safe-buffer "^5.0.1" + +dns-txt@^2.0.2: + version "2.0.2" + resolved "https://registry.yarnpkg.com/dns-txt/-/dns-txt-2.0.2.tgz#b91d806f5d27188e4ab3e7d107d881a1cc4642b6" + integrity sha1-uR2Ab10nGI5Ks+fRB9iBocxGQrY= + dependencies: + buffer-indexof "^1.0.0" + +docsearch.js@^2.5.2: + version "2.6.3" + resolved "https://registry.yarnpkg.com/docsearch.js/-/docsearch.js-2.6.3.tgz#57cb4600d3b6553c677e7cbbe6a734593e38625d" + integrity sha512-GN+MBozuyz664ycpZY0ecdQE0ND/LSgJKhTLA0/v3arIS3S1Rpf2OJz6A35ReMsm91V5apcmzr5/kM84cvUg+A== + dependencies: + algoliasearch "^3.24.5" + autocomplete.js "0.36.0" + hogan.js "^3.0.2" + request "^2.87.0" + stack-utils "^1.0.1" + to-factory "^1.0.0" + zepto "^1.2.0" + +dom-converter@^0.2: + version "0.2.0" + resolved "https://registry.yarnpkg.com/dom-converter/-/dom-converter-0.2.0.tgz#6721a9daee2e293682955b6afe416771627bb768" + integrity sha512-gd3ypIPfOMr9h5jIKq8E3sHOTCjeirnl0WK5ZdS1AW0Odt0b1PaWaHdJ4Qk4klv+YB9aJBS7mESXjFoDQPu6DA== + dependencies: + utila "~0.4" + +dom-serializer@0: + version "0.2.1" + resolved "https://registry.yarnpkg.com/dom-serializer/-/dom-serializer-0.2.1.tgz#13650c850daffea35d8b626a4cfc4d3a17643fdb" + integrity sha512-sK3ujri04WyjwQXVoK4PU3y8ula1stq10GJZpqHIUgoGZdsGzAGu65BnU3d08aTVSvO7mGPZUc0wTEDL+qGE0Q== + dependencies: + domelementtype "^2.0.1" + entities "^2.0.0" + +dom-walk@^0.1.0: + version "0.1.1" + resolved "https://registry.yarnpkg.com/dom-walk/-/dom-walk-0.1.1.tgz#672226dc74c8f799ad35307df936aba11acd6018" + integrity sha1-ZyIm3HTI95mtNTB9+TaroRrNYBg= + +domain-browser@^1.1.1: + version "1.2.0" + resolved "https://registry.yarnpkg.com/domain-browser/-/domain-browser-1.2.0.tgz#3d31f50191a6749dd1375a7f522e823d42e54eda" + integrity sha512-jnjyiM6eRyZl2H+W8Q/zLMA481hzi0eszAaBUzIVnmYVDBbnLxVNnfu1HgEBvCbL+71FrxMl3E6lpKH7Ge3OXA== + +domelementtype@1, domelementtype@^1.3.1: + version "1.3.1" + resolved "https://registry.yarnpkg.com/domelementtype/-/domelementtype-1.3.1.tgz#d048c44b37b0d10a7f2a3d5fee3f4333d790481f" + integrity sha512-BSKB+TSpMpFI/HOxCNr1O8aMOTZ8hT3pM3GQ0w/mWRmkhEDSFJkkyzz4XQsBV44BChwGkrDfMyjVD0eA2aFV3w== + +domelementtype@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/domelementtype/-/domelementtype-2.0.1.tgz#1f8bdfe91f5a78063274e803b4bdcedf6e94f94d" + integrity sha512-5HOHUDsYZWV8FGWN0Njbr/Rn7f/eWSQi1v7+HsUVwXgn8nWWlL64zKDkS0n8ZmQ3mlWOMuXOnR+7Nx/5tMO5AQ== + +domhandler@^2.3.0: + version "2.4.2" + resolved "https://registry.yarnpkg.com/domhandler/-/domhandler-2.4.2.tgz#8805097e933d65e85546f726d60f5eb88b44f803" + integrity sha512-JiK04h0Ht5u/80fdLMCEmV4zkNh2BcoMFBmZ/91WtYZ8qVXSKjiw7fXMgFPnHcSZgOo3XdinHvmnDUeMf5R4wA== + dependencies: + domelementtype "1" + +domutils@1.5.1: + version "1.5.1" + resolved "https://registry.yarnpkg.com/domutils/-/domutils-1.5.1.tgz#dcd8488a26f563d61079e48c9f7b7e32373682cf" + integrity sha1-3NhIiib1Y9YQeeSMn3t+Mjc2gs8= + dependencies: + dom-serializer "0" + domelementtype "1" + +domutils@^1.5.1, domutils@^1.7.0: + version "1.7.0" + resolved "https://registry.yarnpkg.com/domutils/-/domutils-1.7.0.tgz#56ea341e834e06e6748af7a1cb25da67ea9f8c2a" + integrity sha512-Lgd2XcJ/NjEw+7tFvfKxOzCYKZsdct5lczQ2ZaQY8Djz7pfAD3Gbp8ySJWtreII/vDlMVmxwa6pHmdxIYgttDg== + dependencies: + dom-serializer "0" + domelementtype "1" + +dot-prop@^4.1.1: + version "4.2.0" + resolved "https://registry.yarnpkg.com/dot-prop/-/dot-prop-4.2.0.tgz#1f19e0c2e1aa0e32797c49799f2837ac6af69c57" + integrity sha512-tUMXrxlExSW6U2EXiiKGSBVdYgtV8qlHL+C10TsW4PURY/ic+eaysnSkwB4kA/mBlCyy/IKDJ+Lc3wbWeaXtuQ== + dependencies: + is-obj "^1.0.0" + +duplexify@^3.4.2, duplexify@^3.6.0: + version "3.7.1" + resolved "https://registry.yarnpkg.com/duplexify/-/duplexify-3.7.1.tgz#2a4df5317f6ccfd91f86d6fd25d8d8a103b88309" + integrity sha512-07z8uv2wMyS51kKhD1KsdXJg5WQ6t93RneqRxUHnskXVtlYYkLqM0gqStQZ3pj073g687jPCHrqNfCzawLYh5g== + dependencies: + end-of-stream "^1.0.0" + inherits "^2.0.1" + readable-stream "^2.0.0" + stream-shift "^1.0.0" + +ecc-jsbn@~0.1.1: + version "0.1.2" + resolved "https://registry.yarnpkg.com/ecc-jsbn/-/ecc-jsbn-0.1.2.tgz#3a83a904e54353287874c564b7549386849a98c9" + integrity sha1-OoOpBOVDUyh4dMVkt1SThoSamMk= + dependencies: + jsbn "~0.1.0" + safer-buffer "^2.1.0" + +ee-first@1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/ee-first/-/ee-first-1.1.1.tgz#590c61156b0ae2f4f0255732a158b266bc56b21d" + integrity sha1-WQxhFWsK4vTwJVcyoViyZrxWsh0= + +electron-to-chromium@^1.3.191: + version "1.3.232" + resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.3.232.tgz#3d812f5082b26b852bd4e98818cd86f10b6ff128" + integrity sha512-11F8S49B+8AJy5V540BofxvJ1tWP4wZZ0sOre6KF32evS1YSHXiUB7+TQ/mjrfzg1lirnlA8XDdU8CDcJrBCbA== + +elliptic@^6.0.0: + version "6.5.0" + resolved "https://registry.yarnpkg.com/elliptic/-/elliptic-6.5.0.tgz#2b8ed4c891b7de3200e14412a5b8248c7af505ca" + integrity sha512-eFOJTMyCYb7xtE/caJ6JJu+bhi67WCYNbkGSknu20pmM8Ke/bqOfdnZWxyoGN26JgfxTbXrsCkEw4KheCT/KGg== + dependencies: + bn.js "^4.4.0" + brorand "^1.0.1" + hash.js "^1.0.0" + hmac-drbg "^1.0.0" + inherits "^2.0.1" + minimalistic-assert "^1.0.0" + minimalistic-crypto-utils "^1.0.0" + +emoji-regex@^7.0.1: + version "7.0.3" + resolved "https://registry.yarnpkg.com/emoji-regex/-/emoji-regex-7.0.3.tgz#933a04052860c85e83c122479c4748a8e4c72156" + integrity sha512-CwBLREIQ7LvYFB0WyRvwhq5N5qPhc6PMjD6bYggFlI5YyDgl+0vxq5VHbMOFqLg7hfWzmu8T5Z1QofhmTIhItA== + +emojis-list@^2.0.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/emojis-list/-/emojis-list-2.1.0.tgz#4daa4d9db00f9819880c79fa457ae5b09a1fd389" + integrity sha1-TapNnbAPmBmIDHn6RXrlsJof04k= + +encodeurl@~1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/encodeurl/-/encodeurl-1.0.2.tgz#ad3ff4c86ec2d029322f5a02c3a9a606c95b3f59" + integrity sha1-rT/0yG7C0CkyL1oCw6mmBslbP1k= + +end-of-stream@^1.0.0, end-of-stream@^1.1.0: + version "1.4.1" + resolved "https://registry.yarnpkg.com/end-of-stream/-/end-of-stream-1.4.1.tgz#ed29634d19baba463b6ce6b80a37213eab71ec43" + integrity sha512-1MkrZNvWTKCaigbn+W15elq2BB/L22nqrSY5DKlo3X6+vclJm8Bb5djXJBmEX6fS3+zCh/F4VBK5Z2KxJt4s2Q== + dependencies: + once "^1.4.0" + +enhanced-resolve@^4.1.0: + version "4.1.0" + resolved "https://registry.yarnpkg.com/enhanced-resolve/-/enhanced-resolve-4.1.0.tgz#41c7e0bfdfe74ac1ffe1e57ad6a5c6c9f3742a7f" + integrity sha512-F/7vkyTtyc/llOIn8oWclcB25KdRaiPBpZYDgJHgh/UHtpgT2p2eldQgtQnLtUvfMKPKxbRaQM/hHkvLHt1Vng== + dependencies: + graceful-fs "^4.1.2" + memory-fs "^0.4.0" + tapable "^1.0.0" + +entities@^1.1.1, entities@~1.1.1: + version "1.1.2" + resolved "https://registry.yarnpkg.com/entities/-/entities-1.1.2.tgz#bdfa735299664dfafd34529ed4f8522a275fea56" + integrity sha512-f2LZMYl1Fzu7YSBKg+RoROelpOaNrcGmE9AZubeDfrCEia483oW4MI4VyFd5VNHIgQ/7qm1I0wUHK1eJnn2y2w== + +entities@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/entities/-/entities-2.0.0.tgz#68d6084cab1b079767540d80e56a39b423e4abf4" + integrity sha512-D9f7V0JSRwIxlRI2mjMqufDrRDnx8p+eEOz7aUM9SuvF8gsBzra0/6tbjl1m8eQHrZlYj6PxqE00hZ1SAIKPLw== + +envify@^4.0.0: + version "4.1.0" + resolved "https://registry.yarnpkg.com/envify/-/envify-4.1.0.tgz#f39ad3db9d6801b4e6b478b61028d3f0b6819f7e" + integrity sha512-IKRVVoAYr4pIx4yIWNsz9mOsboxlNXiu7TNBnem/K/uTHdkyzXWDzHCK7UTolqBbgaBz0tQHsD3YNls0uIIjiw== + dependencies: + esprima "^4.0.0" + through "~2.3.4" + +envinfo@^7.2.0: + version "7.3.1" + resolved "https://registry.yarnpkg.com/envinfo/-/envinfo-7.3.1.tgz#892e42f7bf858b3446d9414ad240dbaf8da52f09" + integrity sha512-GvXiDTqLYrORVSCuJCsWHPXF5BFvoWMQA9xX4YVjPT1jyS3aZEHUBwjzxU/6LTPF9ReHgVEbX7IEN5UvSXHw/A== + +errno@^0.1.3, errno@~0.1.7: + version "0.1.7" + resolved "https://registry.yarnpkg.com/errno/-/errno-0.1.7.tgz#4684d71779ad39af177e3f007996f7c67c852618" + integrity sha512-MfrRBDWzIWifgq6tJj60gkAwtLNb6sQPlcFrSOflcP1aFmmruKQ2wRnze/8V6kgyz7H3FF8Npzv78mZ7XLLflg== + dependencies: + prr "~1.0.1" + +error-ex@^1.3.1: + version "1.3.2" + resolved "https://registry.yarnpkg.com/error-ex/-/error-ex-1.3.2.tgz#b4ac40648107fdcdcfae242f428bea8a14d4f1bf" + integrity sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g== + dependencies: + is-arrayish "^0.2.1" + +es-abstract@^1.12.0, es-abstract@^1.5.1: + version "1.13.0" + resolved "https://registry.yarnpkg.com/es-abstract/-/es-abstract-1.13.0.tgz#ac86145fdd5099d8dd49558ccba2eaf9b88e24e9" + integrity sha512-vDZfg/ykNxQVwup/8E1BZhVzFfBxs9NqMzGcvIJrqg5k2/5Za2bWo40dK2J1pgLngZ7c+Shh8lwYtLGyrwPutg== + dependencies: + es-to-primitive "^1.2.0" + function-bind "^1.1.1" + has "^1.0.3" + is-callable "^1.1.4" + is-regex "^1.0.4" + object-keys "^1.0.12" + +es-to-primitive@^1.2.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/es-to-primitive/-/es-to-primitive-1.2.0.tgz#edf72478033456e8dda8ef09e00ad9650707f377" + integrity sha512-qZryBOJjV//LaxLTV6UC//WewneB3LcXOL9NP++ozKVXsIIIpm/2c13UDiD9Jp2eThsecw9m3jPqDwTyobcdbg== + dependencies: + is-callable "^1.1.4" + is-date-object "^1.0.1" + is-symbol "^1.0.2" + +es6-promise@^4.1.0: + version "4.2.8" + resolved "https://registry.yarnpkg.com/es6-promise/-/es6-promise-4.2.8.tgz#4eb21594c972bc40553d276e510539143db53e0a" + integrity sha512-HJDGx5daxeIvxdBxvG2cb9g4tEvwIk3i8+nhX0yGrYmZUzbkdg8QbDevheDB8gd0//uPj4c1EQua8Q+MViT0/w== + +escape-html@^1.0.3, escape-html@~1.0.3: + version "1.0.3" + resolved "https://registry.yarnpkg.com/escape-html/-/escape-html-1.0.3.tgz#0258eae4d3d0c0974de1c169188ef0051d1d1988" + integrity sha1-Aljq5NPQwJdN4cFpGI7wBR0dGYg= + +escape-string-regexp@^1.0.2, escape-string-regexp@^1.0.5: + version "1.0.5" + resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz#1b61c0562190a8dff6ae3bb2cf0200ca130b86d4" + integrity sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ= + +eslint-scope@^4.0.3: + version "4.0.3" + resolved "https://registry.yarnpkg.com/eslint-scope/-/eslint-scope-4.0.3.tgz#ca03833310f6889a3264781aa82e63eb9cfe7848" + integrity sha512-p7VutNr1O/QrxysMo3E45FjYDTeXBy0iTltPFNSqKAIfjDSXC+4dj+qfyuD8bfAXrW/y6lW3O76VaYNPKfpKrg== + dependencies: + esrecurse "^4.1.0" + estraverse "^4.1.1" + +esprima@^4.0.0: + version "4.0.1" + resolved "https://registry.yarnpkg.com/esprima/-/esprima-4.0.1.tgz#13b04cdb3e6c5d19df91ab6987a8695619b0aa71" + integrity sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A== + +esrecurse@^4.1.0: + version "4.2.1" + resolved "https://registry.yarnpkg.com/esrecurse/-/esrecurse-4.2.1.tgz#007a3b9fdbc2b3bb87e4879ea19c92fdbd3942cf" + integrity sha512-64RBB++fIOAXPw3P9cy89qfMlvZEXZkqqJkjqqXIvzP5ezRZjW+lPWjw35UX/3EhUPFYbg5ER4JYgDw4007/DQ== + dependencies: + estraverse "^4.1.0" + +estraverse@^4.1.0, estraverse@^4.1.1: + version "4.3.0" + resolved "https://registry.yarnpkg.com/estraverse/-/estraverse-4.3.0.tgz#398ad3f3c5a24948be7725e83d11a7de28cdbd1d" + integrity sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw== + +esutils@^2.0.2: + version "2.0.3" + resolved "https://registry.yarnpkg.com/esutils/-/esutils-2.0.3.tgz#74d2eb4de0b8da1293711910d50775b9b710ef64" + integrity sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g== + +etag@~1.8.1: + version "1.8.1" + resolved "https://registry.yarnpkg.com/etag/-/etag-1.8.1.tgz#41ae2eeb65efa62268aebfea83ac7d79299b0887" + integrity sha1-Qa4u62XvpiJorr/qg6x9eSmbCIc= + +eventemitter3@^3.0.0: + version "3.1.2" + resolved "https://registry.yarnpkg.com/eventemitter3/-/eventemitter3-3.1.2.tgz#2d3d48f9c346698fce83a85d7d664e98535df6e7" + integrity sha512-tvtQIeLVHjDkJYnzf2dgVMxfuSGJeM/7UCG17TT4EumTfNtF+0nebF/4zWOIkCreAbtNqhGEboB6BWrwqNaw4Q== + +events@^1.1.0: + version "1.1.1" + resolved "https://registry.yarnpkg.com/events/-/events-1.1.1.tgz#9ebdb7635ad099c70dcc4c2a1f5004288e8bd924" + integrity sha1-nr23Y1rQmccNzEwqH1AEKI6L2SQ= + +events@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/events/-/events-3.0.0.tgz#9a0a0dfaf62893d92b875b8f2698ca4114973e88" + integrity sha512-Dc381HFWJzEOhQ+d8pkNon++bk9h6cdAoAj4iE6Q4y6xgTzySWXlKn05/TVNpjnfRqi/X0EpJEJohPjNI3zpVA== + +eventsource@^1.0.7: + version "1.0.7" + resolved "https://registry.yarnpkg.com/eventsource/-/eventsource-1.0.7.tgz#8fbc72c93fcd34088090bc0a4e64f4b5cee6d8d0" + integrity sha512-4Ln17+vVT0k8aWq+t/bF5arcS3EpT9gYtW66EPacdj/mAFevznsnyoHLPy2BA8gbIQeIHoPsvwmfBftfcG//BQ== + dependencies: + original "^1.0.0" + +evp_bytestokey@^1.0.0, evp_bytestokey@^1.0.3: + version "1.0.3" + resolved "https://registry.yarnpkg.com/evp_bytestokey/-/evp_bytestokey-1.0.3.tgz#7fcbdb198dc71959432efe13842684e0525acb02" + integrity sha512-/f2Go4TognH/KvCISP7OUsHn85hT9nUkxxA9BEWxFn+Oj9o8ZNLm/40hdlgSLyuOimsrTKLUMEorQexp/aPQeA== + dependencies: + md5.js "^1.3.4" + safe-buffer "^5.1.1" + +execa@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/execa/-/execa-1.0.0.tgz#c6236a5bb4df6d6f15e88e7f017798216749ddd8" + integrity sha512-adbxcyWV46qiHyvSp50TKt05tB4tK3HcmF7/nxfAdhnox83seTDbwnaqKO4sXRy7roHAIFqJP/Rw/AuEbX61LA== + dependencies: + cross-spawn "^6.0.0" + get-stream "^4.0.0" + is-stream "^1.1.0" + npm-run-path "^2.0.0" + p-finally "^1.0.0" + signal-exit "^3.0.0" + strip-eof "^1.0.0" + +expand-brackets@^2.1.4: + version "2.1.4" + resolved "https://registry.yarnpkg.com/expand-brackets/-/expand-brackets-2.1.4.tgz#b77735e315ce30f6b6eff0f83b04151a22449622" + integrity sha1-t3c14xXOMPa27/D4OwQVGiJEliI= + dependencies: + debug "^2.3.3" + define-property "^0.2.5" + extend-shallow "^2.0.1" + posix-character-classes "^0.1.0" + regex-not "^1.0.0" + snapdragon "^0.8.1" + to-regex "^3.0.1" + +express@^4.17.1: + version "4.17.1" + resolved "https://registry.yarnpkg.com/express/-/express-4.17.1.tgz#4491fc38605cf51f8629d39c2b5d026f98a4c134" + integrity sha512-mHJ9O79RqluphRrcw2X/GTh3k9tVv8YcoyY4Kkh4WDMUYKRZUq0h1o0w2rrrxBqM7VoeUVqgb27xlEMXTnYt4g== + dependencies: + accepts "~1.3.7" + array-flatten "1.1.1" + body-parser "1.19.0" + content-disposition "0.5.3" + content-type "~1.0.4" + cookie "0.4.0" + cookie-signature "1.0.6" + debug "2.6.9" + depd "~1.1.2" + encodeurl "~1.0.2" + escape-html "~1.0.3" + etag "~1.8.1" + finalhandler "~1.1.2" + fresh "0.5.2" + merge-descriptors "1.0.1" + methods "~1.1.2" + on-finished "~2.3.0" + parseurl "~1.3.3" + path-to-regexp "0.1.7" + proxy-addr "~2.0.5" + qs "6.7.0" + range-parser "~1.2.1" + safe-buffer "5.1.2" + send "0.17.1" + serve-static "1.14.1" + setprototypeof "1.1.1" + statuses "~1.5.0" + type-is "~1.6.18" + utils-merge "1.0.1" + vary "~1.1.2" + +extend-shallow@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/extend-shallow/-/extend-shallow-2.0.1.tgz#51af7d614ad9a9f610ea1bafbb989d6b1c56890f" + integrity sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8= + dependencies: + is-extendable "^0.1.0" + +extend-shallow@^3.0.0, extend-shallow@^3.0.2: + version "3.0.2" + resolved "https://registry.yarnpkg.com/extend-shallow/-/extend-shallow-3.0.2.tgz#26a71aaf073b39fb2127172746131c2704028db8" + integrity sha1-Jqcarwc7OfshJxcnRhMcJwQCjbg= + dependencies: + assign-symbols "^1.0.0" + is-extendable "^1.0.1" + +extend@~3.0.2: + version "3.0.2" + resolved "https://registry.yarnpkg.com/extend/-/extend-3.0.2.tgz#f8b1136b4071fbd8eb140aff858b1019ec2915fa" + integrity sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g== + +extglob@^2.0.4: + version "2.0.4" + resolved "https://registry.yarnpkg.com/extglob/-/extglob-2.0.4.tgz#ad00fe4dc612a9232e8718711dc5cb5ab0285543" + integrity sha512-Nmb6QXkELsuBr24CJSkilo6UHHgbekK5UiZgfE6UHD3Eb27YC6oD+bhcT+tJ6cl8dmsgdQxnWlcry8ksBIBLpw== + dependencies: + array-unique "^0.3.2" + define-property "^1.0.0" + expand-brackets "^2.1.4" + extend-shallow "^2.0.1" + fragment-cache "^0.2.1" + regex-not "^1.0.0" + snapdragon "^0.8.1" + to-regex "^3.0.1" + +extsprintf@1.3.0: + version "1.3.0" + resolved "https://registry.yarnpkg.com/extsprintf/-/extsprintf-1.3.0.tgz#96918440e3041a7a414f8c52e3c574eb3c3e1e05" + integrity sha1-lpGEQOMEGnpBT4xS48V06zw+HgU= + +extsprintf@^1.2.0: + version "1.4.0" + resolved "https://registry.yarnpkg.com/extsprintf/-/extsprintf-1.4.0.tgz#e2689f8f356fad62cca65a3a91c5df5f9551692f" + integrity sha1-4mifjzVvrWLMplo6kcXfX5VRaS8= + +fast-deep-equal@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/fast-deep-equal/-/fast-deep-equal-2.0.1.tgz#7b05218ddf9667bf7f370bf7fdb2cb15fdd0aa49" + integrity sha1-ewUhjd+WZ79/Nwv3/bLLFf3Qqkk= + +fast-glob@^2.2.6: + version "2.2.7" + resolved "https://registry.yarnpkg.com/fast-glob/-/fast-glob-2.2.7.tgz#6953857c3afa475fff92ee6015d52da70a4cd39d" + integrity sha512-g1KuQwHOZAmOZMuBtHdxDtju+T2RT8jgCC9aANsbpdiDDTSnjgfuVsIBNKbUeJI3oKMRExcfNDtJl4OhbffMsw== + dependencies: + "@mrmlnc/readdir-enhanced" "^2.2.1" + "@nodelib/fs.stat" "^1.1.2" + glob-parent "^3.1.0" + is-glob "^4.0.0" + merge2 "^1.2.3" + micromatch "^3.1.10" + +fast-json-stable-stringify@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/fast-json-stable-stringify/-/fast-json-stable-stringify-2.0.0.tgz#d5142c0caee6b1189f87d3a76111064f86c8bbf2" + integrity sha1-1RQsDK7msRifh9OnYREGT4bIu/I= + +faye-websocket@^0.10.0: + version "0.10.0" + resolved "https://registry.yarnpkg.com/faye-websocket/-/faye-websocket-0.10.0.tgz#4e492f8d04dfb6f89003507f6edbf2d501e7c6f4" + integrity sha1-TkkvjQTftviQA1B/btvy1QHnxvQ= + dependencies: + websocket-driver ">=0.5.1" + +faye-websocket@~0.11.1: + version "0.11.3" + resolved "https://registry.yarnpkg.com/faye-websocket/-/faye-websocket-0.11.3.tgz#5c0e9a8968e8912c286639fde977a8b209f2508e" + integrity sha512-D2y4bovYpzziGgbHYtGCMjlJM36vAl/y+xUyn1C+FVx8szd1E+86KwVw6XvYSzOP8iMpm1X0I4xJD+QtUb36OA== + dependencies: + websocket-driver ">=0.5.1" + +figgy-pudding@^3.5.1: + version "3.5.1" + resolved "https://registry.yarnpkg.com/figgy-pudding/-/figgy-pudding-3.5.1.tgz#862470112901c727a0e495a80744bd5baa1d6790" + integrity sha512-vNKxJHTEKNThjfrdJwHc7brvM6eVevuO5nTj6ez8ZQ1qbXTvGthucRF7S4vf2cr71QVnT70V34v0S1DyQsti0w== + +figures@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/figures/-/figures-3.0.0.tgz#756275c964646163cc6f9197c7a0295dbfd04de9" + integrity sha512-HKri+WoWoUgr83pehn/SIgLOMZ9nAWC6dcGj26RY2R4F50u4+RTUz0RCrUlOV3nKRAICW1UGzyb+kcX2qK1S/g== + dependencies: + escape-string-regexp "^1.0.5" + +file-loader@^3.0.1: + version "3.0.1" + resolved "https://registry.yarnpkg.com/file-loader/-/file-loader-3.0.1.tgz#f8e0ba0b599918b51adfe45d66d1e771ad560faa" + integrity sha512-4sNIOXgtH/9WZq4NvlfU3Opn5ynUsqBwSLyM+I7UOwdGigTBYfVVQEwe/msZNX/j4pCJTIM14Fsw66Svo1oVrw== + dependencies: + loader-utils "^1.0.2" + schema-utils "^1.0.0" + +fill-range@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/fill-range/-/fill-range-4.0.0.tgz#d544811d428f98eb06a63dc402d2403c328c38f7" + integrity sha1-1USBHUKPmOsGpj3EAtJAPDKMOPc= + dependencies: + extend-shallow "^2.0.1" + is-number "^3.0.0" + repeat-string "^1.6.1" + to-regex-range "^2.1.0" + +finalhandler@~1.1.2: + version "1.1.2" + resolved "https://registry.yarnpkg.com/finalhandler/-/finalhandler-1.1.2.tgz#b7e7d000ffd11938d0fdb053506f6ebabe9f587d" + integrity sha512-aAWcW57uxVNrQZqFXjITpW3sIUQmHGG3qSb9mUah9MgMC4NeWhNOlNjXEYq3HjRAvL6arUviZGGJsBg6z0zsWA== + dependencies: + debug "2.6.9" + encodeurl "~1.0.2" + escape-html "~1.0.3" + on-finished "~2.3.0" + parseurl "~1.3.3" + statuses "~1.5.0" + unpipe "~1.0.0" + +find-babel-config@^1.1.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/find-babel-config/-/find-babel-config-1.2.0.tgz#a9b7b317eb5b9860cda9d54740a8c8337a2283a2" + integrity sha512-jB2CHJeqy6a820ssiqwrKMeyC6nNdmrcgkKWJWmpoxpE8RKciYJXCcXRq1h2AzCo5I5BJeN2tkGEO3hLTuePRA== + dependencies: + json5 "^0.5.1" + path-exists "^3.0.0" + +find-cache-dir@^2.0.0, find-cache-dir@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/find-cache-dir/-/find-cache-dir-2.1.0.tgz#8d0f94cd13fe43c6c7c261a0d86115ca918c05f7" + integrity sha512-Tq6PixE0w/VMFfCgbONnkiQIVol/JJL7nRMi20fqzA4NRs9AfeqMGeRdPi3wIhYkxjeBaWh2rxwapn5Tu3IqOQ== + dependencies: + commondir "^1.0.1" + make-dir "^2.0.0" + pkg-dir "^3.0.0" + +find-up@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/find-up/-/find-up-2.1.0.tgz#45d1b7e506c717ddd482775a2b77920a3c0c57a7" + integrity sha1-RdG35QbHF93UgndaK3eSCjwMV6c= + dependencies: + locate-path "^2.0.0" + +find-up@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/find-up/-/find-up-3.0.0.tgz#49169f1d7993430646da61ecc5ae355c21c97b73" + integrity sha512-1yD6RmLI1XBfxugvORwlck6f75tYL+iR0jqwsOrOxMZyGYqUuDhJ0l4AXdO1iX/FTs9cBAMEk1gWSEx1kSbylg== + dependencies: + locate-path "^3.0.0" + +flush-write-stream@^1.0.0: + version "1.1.1" + resolved "https://registry.yarnpkg.com/flush-write-stream/-/flush-write-stream-1.1.1.tgz#8dd7d873a1babc207d94ead0c2e0e44276ebf2e8" + integrity sha512-3Z4XhFZ3992uIq0XOqb9AreonueSYphE6oYbpt5+3u06JWklbsPkNv3ZKkP9Bz/r+1MWCaMoSQ28P85+1Yc77w== + dependencies: + inherits "^2.0.3" + readable-stream "^2.3.6" + +follow-redirects@^1.0.0: + version "1.7.0" + resolved "https://registry.yarnpkg.com/follow-redirects/-/follow-redirects-1.7.0.tgz#489ebc198dc0e7f64167bd23b03c4c19b5784c76" + integrity sha512-m/pZQy4Gj287eNy94nivy5wchN3Kp+Q5WgUPNy5lJSZ3sgkVKSYV/ZChMAQVIgx1SqfZ2zBZtPA2YlXIWxxJOQ== + dependencies: + debug "^3.2.6" + +for-in@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/for-in/-/for-in-1.0.2.tgz#81068d295a8142ec0ac726c6e2200c30fb6d5e80" + integrity sha1-gQaNKVqBQuwKxybG4iAMMPttXoA= + +foreach@^2.0.5: + version "2.0.5" + resolved "https://registry.yarnpkg.com/foreach/-/foreach-2.0.5.tgz#0bee005018aeb260d0a3af3ae658dd0136ec1b99" + integrity sha1-C+4AUBiusmDQo6865ljdATbsG5k= + +forever-agent@~0.6.1: + version "0.6.1" + resolved "https://registry.yarnpkg.com/forever-agent/-/forever-agent-0.6.1.tgz#fbc71f0c41adeb37f96c577ad1ed42d8fdacca91" + integrity sha1-+8cfDEGt6zf5bFd60e1C2P2sypE= + +form-data@~2.3.2: + version "2.3.3" + resolved "https://registry.yarnpkg.com/form-data/-/form-data-2.3.3.tgz#dcce52c05f644f298c6a7ab936bd724ceffbf3a6" + integrity sha512-1lLKB2Mu3aGP1Q/2eCOx0fNbRMe7XdwktwOruhfqqd0rIJWwN4Dh+E3hrPSlDCXnSR7UtZ1N38rVXm+6+MEhJQ== + dependencies: + asynckit "^0.4.0" + combined-stream "^1.0.6" + mime-types "^2.1.12" + +forwarded@~0.1.2: + version "0.1.2" + resolved "https://registry.yarnpkg.com/forwarded/-/forwarded-0.1.2.tgz#98c23dab1175657b8c0573e8ceccd91b0ff18c84" + integrity sha1-mMI9qxF1ZXuMBXPozszZGw/xjIQ= + +fragment-cache@^0.2.1: + version "0.2.1" + resolved "https://registry.yarnpkg.com/fragment-cache/-/fragment-cache-0.2.1.tgz#4290fad27f13e89be7f33799c6bc5a0abfff0d19" + integrity sha1-QpD60n8T6Jvn8zeZxrxaCr//DRk= + dependencies: + map-cache "^0.2.2" + +fresh@0.5.2: + version "0.5.2" + resolved "https://registry.yarnpkg.com/fresh/-/fresh-0.5.2.tgz#3d8cadd90d976569fa835ab1f8e4b23a105605a7" + integrity sha1-PYyt2Q2XZWn6g1qx+OSyOhBWBac= + +from2@^2.1.0: + version "2.3.0" + resolved "https://registry.yarnpkg.com/from2/-/from2-2.3.0.tgz#8bfb5502bde4a4d36cfdeea007fcca21d7e382af" + integrity sha1-i/tVAr3kpNNs/e6gB/zKIdfjgq8= + dependencies: + inherits "^2.0.1" + readable-stream "^2.0.0" + +fs-extra@^7.0.1: + version "7.0.1" + resolved "https://registry.yarnpkg.com/fs-extra/-/fs-extra-7.0.1.tgz#4f189c44aa123b895f722804f55ea23eadc348e9" + integrity sha512-YJDaCJZEnBmcbw13fvdAM9AwNOJwOzrE4pqMqBq5nFiEqXUqHwlK4B+3pUw6JNvfSPtX05xFHtYy/1ni01eGCw== + dependencies: + graceful-fs "^4.1.2" + jsonfile "^4.0.0" + universalify "^0.1.0" + +fs-minipass@^1.2.5: + version "1.2.6" + resolved "https://registry.yarnpkg.com/fs-minipass/-/fs-minipass-1.2.6.tgz#2c5cc30ded81282bfe8a0d7c7c1853ddeb102c07" + integrity sha512-crhvyXcMejjv3Z5d2Fa9sf5xLYVCF5O1c71QxbVnbLsmYMBEvDAftewesN/HhY03YRoA7zOMxjNGrF5svGaaeQ== + dependencies: + minipass "^2.2.1" + +fs-write-stream-atomic@^1.0.8: + version "1.0.10" + resolved "https://registry.yarnpkg.com/fs-write-stream-atomic/-/fs-write-stream-atomic-1.0.10.tgz#b47df53493ef911df75731e70a9ded0189db40c9" + integrity sha1-tH31NJPvkR33VzHnCp3tAYnbQMk= + dependencies: + graceful-fs "^4.1.2" + iferr "^0.1.5" + imurmurhash "^0.1.4" + readable-stream "1 || 2" + +fs.realpath@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/fs.realpath/-/fs.realpath-1.0.0.tgz#1504ad2523158caa40db4a2787cb01411994ea4f" + integrity sha1-FQStJSMVjKpA20onh8sBQRmU6k8= + +fsevents@^1.2.7: + version "1.2.9" + resolved "https://registry.yarnpkg.com/fsevents/-/fsevents-1.2.9.tgz#3f5ed66583ccd6f400b5a00db6f7e861363e388f" + integrity sha512-oeyj2H3EjjonWcFjD5NvZNE9Rqe4UW+nQBU2HNeKw0koVLEFIhtyETyAakeAM3de7Z/SW5kcA+fZUait9EApnw== + dependencies: + nan "^2.12.1" + node-pre-gyp "^0.12.0" + +function-bind@^1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/function-bind/-/function-bind-1.1.1.tgz#a56899d3ea3c9bab874bb9773b7c5ede92f4895d" + integrity sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A== + +gauge@~2.7.3: + version "2.7.4" + resolved "https://registry.yarnpkg.com/gauge/-/gauge-2.7.4.tgz#2c03405c7538c39d7eb37b317022e325fb018bf7" + integrity sha1-LANAXHU4w51+s3sxcCLjJfsBi/c= + dependencies: + aproba "^1.0.3" + console-control-strings "^1.0.0" + has-unicode "^2.0.0" + object-assign "^4.1.0" + signal-exit "^3.0.0" + string-width "^1.0.1" + strip-ansi "^3.0.1" + wide-align "^1.1.0" + +get-caller-file@^1.0.1: + version "1.0.3" + resolved "https://registry.yarnpkg.com/get-caller-file/-/get-caller-file-1.0.3.tgz#f978fa4c90d1dfe7ff2d6beda2a515e713bdcf4a" + integrity sha512-3t6rVToeoZfYSGd8YoLFR2DJkiQrIiUrGcjvFX2mDw3bn6k2OtwHN0TNCLbBO+w8qTvimhDkv+LSscbJY1vE6w== + +get-stream@^4.0.0: + version "4.1.0" + resolved "https://registry.yarnpkg.com/get-stream/-/get-stream-4.1.0.tgz#c1b255575f3dc21d59bfc79cd3d2b46b1c3a54b5" + integrity sha512-GMat4EJ5161kIy2HevLlr4luNjBgvmj413KaQA7jt4V8B4RDsfpHk7WQ9GVqfYyyx8OS/L66Kox+rJRNklLK7w== + dependencies: + pump "^3.0.0" + +get-value@^2.0.3, get-value@^2.0.6: + version "2.0.6" + resolved "https://registry.yarnpkg.com/get-value/-/get-value-2.0.6.tgz#dc15ca1c672387ca76bd37ac0a395ba2042a2c28" + integrity sha1-3BXKHGcjh8p2vTesCjlbogQqLCg= + +getpass@^0.1.1: + version "0.1.7" + resolved "https://registry.yarnpkg.com/getpass/-/getpass-0.1.7.tgz#5eff8e3e684d569ae4cb2b1282604e8ba62149fa" + integrity sha1-Xv+OPmhNVprkyysSgmBOi6YhSfo= + dependencies: + assert-plus "^1.0.0" + +glob-parent@^3.1.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/glob-parent/-/glob-parent-3.1.0.tgz#9e6af6299d8d3bd2bd40430832bd113df906c5ae" + integrity sha1-nmr2KZ2NO9K9QEMIMr0RPfkGxa4= + dependencies: + is-glob "^3.1.0" + path-dirname "^1.0.0" + +glob-to-regexp@^0.3.0: + version "0.3.0" + resolved "https://registry.yarnpkg.com/glob-to-regexp/-/glob-to-regexp-0.3.0.tgz#8c5a1494d2066c570cc3bfe4496175acc4d502ab" + integrity sha1-jFoUlNIGbFcMw7/kSWF1rMTVAqs= + +glob@7.0.x: + version "7.0.6" + resolved "https://registry.yarnpkg.com/glob/-/glob-7.0.6.tgz#211bafaf49e525b8cd93260d14ab136152b3f57a" + integrity sha1-IRuvr0nlJbjNkyYNFKsTYVKz9Xo= + dependencies: + fs.realpath "^1.0.0" + inflight "^1.0.4" + inherits "2" + minimatch "^3.0.2" + once "^1.3.0" + path-is-absolute "^1.0.0" + +glob@^7.0.3, glob@^7.1.2, glob@^7.1.3, glob@^7.1.4: + version "7.1.4" + resolved "https://registry.yarnpkg.com/glob/-/glob-7.1.4.tgz#aa608a2f6c577ad357e1ae5a5c26d9a8d1969255" + integrity sha512-hkLPepehmnKk41pUGm3sYxoFs/umurYfYJCerbXEyFIWcAzvpipAgVkBqqT9RBKMGjnq6kMuyYwha6csxbiM1A== + dependencies: + fs.realpath "^1.0.0" + inflight "^1.0.4" + inherits "2" + minimatch "^3.0.4" + once "^1.3.0" + path-is-absolute "^1.0.0" + +global@^4.3.2: + version "4.4.0" + resolved "https://registry.yarnpkg.com/global/-/global-4.4.0.tgz#3e7b105179006a323ed71aafca3e9c57a5cc6406" + integrity sha512-wv/LAoHdRE3BeTGz53FAamhGlPLhlssK45usmGFThIi4XqnBmjKQ16u+RNbP7WvigRZDxUsM0J3gcQ5yicaL0w== + dependencies: + min-document "^2.19.0" + process "^0.11.10" + +globals@^11.1.0: + version "11.12.0" + resolved "https://registry.yarnpkg.com/globals/-/globals-11.12.0.tgz#ab8795338868a0babd8525758018c2a7eb95c42e" + integrity sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA== + +globby@^6.1.0: + version "6.1.0" + resolved "https://registry.yarnpkg.com/globby/-/globby-6.1.0.tgz#f5a6d70e8395e21c858fb0489d64df02424d506c" + integrity sha1-9abXDoOV4hyFj7BInWTfAkJNUGw= + dependencies: + array-union "^1.0.1" + glob "^7.0.3" + object-assign "^4.0.1" + pify "^2.0.0" + pinkie-promise "^2.0.0" + +globby@^7.1.1: + version "7.1.1" + resolved "https://registry.yarnpkg.com/globby/-/globby-7.1.1.tgz#fb2ccff9401f8600945dfada97440cca972b8680" + integrity sha1-+yzP+UAfhgCUXfral0QMypcrhoA= + dependencies: + array-union "^1.0.1" + dir-glob "^2.0.0" + glob "^7.1.2" + ignore "^3.3.5" + pify "^3.0.0" + slash "^1.0.0" + +globby@^9.2.0: + version "9.2.0" + resolved "https://registry.yarnpkg.com/globby/-/globby-9.2.0.tgz#fd029a706c703d29bdd170f4b6db3a3f7a7cb63d" + integrity sha512-ollPHROa5mcxDEkwg6bPt3QbEf4pDQSNtd6JPL1YvOvAo/7/0VAm9TccUeoTmarjPw4pfUthSCqcyfNB1I3ZSg== + dependencies: + "@types/glob" "^7.1.1" + array-union "^1.0.2" + dir-glob "^2.2.2" + fast-glob "^2.2.6" + glob "^7.1.3" + ignore "^4.0.3" + pify "^4.0.1" + slash "^2.0.0" + +good-listener@^1.2.2: + version "1.2.2" + resolved "https://registry.yarnpkg.com/good-listener/-/good-listener-1.2.2.tgz#d53b30cdf9313dffb7dc9a0d477096aa6d145c50" + integrity sha1-1TswzfkxPf+33JoNR3CWqm0UXFA= + dependencies: + delegate "^3.1.2" + +graceful-fs@^4.1.11, graceful-fs@^4.1.15, graceful-fs@^4.1.2, graceful-fs@^4.1.6: + version "4.2.2" + resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.2.2.tgz#6f0952605d0140c1cfdb138ed005775b92d67b02" + integrity sha512-IItsdsea19BoLC7ELy13q1iJFNmd7ofZH5+X/pJr90/nRoPEX0DJo1dHDbgtYWOhJhcCgMDTOw84RZ72q6lB+Q== + +gray-matter@^4.0.1: + version "4.0.2" + resolved "https://registry.yarnpkg.com/gray-matter/-/gray-matter-4.0.2.tgz#9aa379e3acaf421193fce7d2a28cebd4518ac454" + integrity sha512-7hB/+LxrOjq/dd8APlK0r24uL/67w7SkYnfwhNFwg/VDIGWGmduTDYf3WNstLW2fbbmRwrDGCVSJ2isuf2+4Hw== + dependencies: + js-yaml "^3.11.0" + kind-of "^6.0.2" + section-matter "^1.0.0" + strip-bom-string "^1.0.0" + +handle-thing@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/handle-thing/-/handle-thing-2.0.0.tgz#0e039695ff50c93fc288557d696f3c1dc6776754" + integrity sha512-d4sze1JNC454Wdo2fkuyzCr6aHcbL6PGGuFAz0Li/NcOm1tCHGnWDRmJP85dh9IhQErTc2svWFEX5xHIOo//kQ== + +har-schema@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/har-schema/-/har-schema-2.0.0.tgz#a94c2224ebcac04782a0d9035521f24735b7ec92" + integrity sha1-qUwiJOvKwEeCoNkDVSHyRzW37JI= + +har-validator@~5.1.0: + version "5.1.3" + resolved "https://registry.yarnpkg.com/har-validator/-/har-validator-5.1.3.tgz#1ef89ebd3e4996557675eed9893110dc350fa080" + integrity sha512-sNvOCzEQNr/qrvJgc3UG/kD4QtlHycrzwS+6mfTrrSq97BvaYcPZZI1ZSqGSPR73Cxn4LKTD4PttRwfU7jWq5g== + dependencies: + ajv "^6.5.5" + har-schema "^2.0.0" + +has-ansi@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/has-ansi/-/has-ansi-2.0.0.tgz#34f5049ce1ecdf2b0649af3ef24e45ed35416d91" + integrity sha1-NPUEnOHs3ysGSa8+8k5F7TVBbZE= + dependencies: + ansi-regex "^2.0.0" + +has-flag@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/has-flag/-/has-flag-3.0.0.tgz#b5d454dc2199ae225699f3467e5a07f3b955bafd" + integrity sha1-tdRU3CGZriJWmfNGfloH87lVuv0= + +has-symbols@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/has-symbols/-/has-symbols-1.0.0.tgz#ba1a8f1af2a0fc39650f5c850367704122063b44" + integrity sha1-uhqPGvKg/DllD1yFA2dwQSIGO0Q= + +has-unicode@^2.0.0: + version "2.0.1" + resolved "https://registry.yarnpkg.com/has-unicode/-/has-unicode-2.0.1.tgz#e0e6fe6a28cf51138855e086d1691e771de2a8b9" + integrity sha1-4Ob+aijPUROIVeCG0Wkedx3iqLk= + +has-value@^0.3.1: + version "0.3.1" + resolved "https://registry.yarnpkg.com/has-value/-/has-value-0.3.1.tgz#7b1f58bada62ca827ec0a2078025654845995e1f" + integrity sha1-ex9YutpiyoJ+wKIHgCVlSEWZXh8= + dependencies: + get-value "^2.0.3" + has-values "^0.1.4" + isobject "^2.0.0" + +has-value@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/has-value/-/has-value-1.0.0.tgz#18b281da585b1c5c51def24c930ed29a0be6b177" + integrity sha1-GLKB2lhbHFxR3vJMkw7SmgvmsXc= + dependencies: + get-value "^2.0.6" + has-values "^1.0.0" + isobject "^3.0.0" + +has-values@^0.1.4: + version "0.1.4" + resolved "https://registry.yarnpkg.com/has-values/-/has-values-0.1.4.tgz#6d61de95d91dfca9b9a02089ad384bff8f62b771" + integrity sha1-bWHeldkd/Km5oCCJrThL/49it3E= + +has-values@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/has-values/-/has-values-1.0.0.tgz#95b0b63fec2146619a6fe57fe75628d5a39efe4f" + integrity sha1-lbC2P+whRmGab+V/51Yo1aOe/k8= + dependencies: + is-number "^3.0.0" + kind-of "^4.0.0" + +has@^1.0.0, has@^1.0.1, has@^1.0.3: + version "1.0.3" + resolved "https://registry.yarnpkg.com/has/-/has-1.0.3.tgz#722d7cbfc1f6aa8241f16dd814e011e1f41e8796" + integrity sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw== + dependencies: + function-bind "^1.1.1" + +hash-base@^3.0.0: + version "3.0.4" + resolved "https://registry.yarnpkg.com/hash-base/-/hash-base-3.0.4.tgz#5fc8686847ecd73499403319a6b0a3f3f6ae4918" + integrity sha1-X8hoaEfs1zSZQDMZprCj8/auSRg= + dependencies: + inherits "^2.0.1" + safe-buffer "^5.0.1" + +hash-sum@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/hash-sum/-/hash-sum-1.0.2.tgz#33b40777754c6432573c120cc3808bbd10d47f04" + integrity sha1-M7QHd3VMZDJXPBIMw4CLvRDUfwQ= + +hash.js@^1.0.0, hash.js@^1.0.3: + version "1.1.7" + resolved "https://registry.yarnpkg.com/hash.js/-/hash.js-1.1.7.tgz#0babca538e8d4ee4a0f8988d68866537a003cf42" + integrity sha512-taOaskGt4z4SOANNseOviYDvjEJinIkRgmp7LbKP2YTTmVxWBl87s/uzK9r+44BclBSp2X7K1hqeNfz9JbBeXA== + dependencies: + inherits "^2.0.3" + minimalistic-assert "^1.0.1" + +he@1.2.x, he@^1.1.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/he/-/he-1.2.0.tgz#84ae65fa7eafb165fddb61566ae14baf05664f0f" + integrity sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw== + +hex-color-regex@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/hex-color-regex/-/hex-color-regex-1.1.0.tgz#4c06fccb4602fe2602b3c93df82d7e7dbf1a8a8e" + integrity sha512-l9sfDFsuqtOqKDsQdqrMRk0U85RZc0RtOR9yPI7mRVOa4FsR/BVnZ0shmQRM96Ji99kYZP/7hn1cedc1+ApsTQ== + +hmac-drbg@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/hmac-drbg/-/hmac-drbg-1.0.1.tgz#d2745701025a6c775a6c545793ed502fc0c649a1" + integrity sha1-0nRXAQJabHdabFRXk+1QL8DGSaE= + dependencies: + hash.js "^1.0.3" + minimalistic-assert "^1.0.0" + minimalistic-crypto-utils "^1.0.1" + +hogan.js@^3.0.2: + version "3.0.2" + resolved "https://registry.yarnpkg.com/hogan.js/-/hogan.js-3.0.2.tgz#4cd9e1abd4294146e7679e41d7898732b02c7bfd" + integrity sha1-TNnhq9QpQUbnZ55B14mHMrAse/0= + dependencies: + mkdirp "0.3.0" + nopt "1.0.10" + +hpack.js@^2.1.6: + version "2.1.6" + resolved "https://registry.yarnpkg.com/hpack.js/-/hpack.js-2.1.6.tgz#87774c0949e513f42e84575b3c45681fade2a0b2" + integrity sha1-h3dMCUnlE/QuhFdbPEVoH63ioLI= + dependencies: + inherits "^2.0.1" + obuf "^1.0.0" + readable-stream "^2.0.1" + wbuf "^1.1.0" + +hsl-regex@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/hsl-regex/-/hsl-regex-1.0.0.tgz#d49330c789ed819e276a4c0d272dffa30b18fe6e" + integrity sha1-1JMwx4ntgZ4nakwNJy3/owsY/m4= + +hsla-regex@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/hsla-regex/-/hsla-regex-1.0.0.tgz#c1ce7a3168c8c6614033a4b5f7877f3b225f9c38" + integrity sha1-wc56MWjIxmFAM6S194d/OyJfnDg= + +html-comment-regex@^1.1.0: + version "1.1.2" + resolved "https://registry.yarnpkg.com/html-comment-regex/-/html-comment-regex-1.1.2.tgz#97d4688aeb5c81886a364faa0cad1dda14d433a7" + integrity sha512-P+M65QY2JQ5Y0G9KKdlDpo0zK+/OHptU5AaBwUfAIDJZk1MYf32Frm84EcOytfJE0t5JvkAnKlmjsXDnWzCJmQ== + +html-entities@^1.2.1: + version "1.2.1" + resolved "https://registry.yarnpkg.com/html-entities/-/html-entities-1.2.1.tgz#0df29351f0721163515dfb9e5543e5f6eed5162f" + integrity sha1-DfKTUfByEWNRXfueVUPl9u7VFi8= + +html-minifier@^3.2.3: + version "3.5.21" + resolved "https://registry.yarnpkg.com/html-minifier/-/html-minifier-3.5.21.tgz#d0040e054730e354db008463593194015212d20c" + integrity sha512-LKUKwuJDhxNa3uf/LPR/KVjm/l3rBqtYeCOAekvG8F1vItxMUpueGd94i/asDDr8/1u7InxzFA5EeGjhhG5mMA== + dependencies: + camel-case "3.0.x" + clean-css "4.2.x" + commander "2.17.x" + he "1.2.x" + param-case "2.1.x" + relateurl "0.2.x" + uglify-js "3.4.x" + +html-tags@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/html-tags/-/html-tags-2.0.0.tgz#10b30a386085f43cede353cc8fa7cb0deeea668b" + integrity sha1-ELMKOGCF9Dzt41PMj6fLDe7qZos= + +htmlparser2@^3.3.0: + version "3.10.1" + resolved "https://registry.yarnpkg.com/htmlparser2/-/htmlparser2-3.10.1.tgz#bd679dc3f59897b6a34bb10749c855bb53a9392f" + integrity sha512-IgieNijUMbkDovyoKObU1DUhm1iwNYE/fuifEoEHfd1oZKZDaONBSkal7Y01shxsM49R4XaMdGez3WnF9UfiCQ== + dependencies: + domelementtype "^1.3.1" + domhandler "^2.3.0" + domutils "^1.5.1" + entities "^1.1.1" + inherits "^2.0.1" + readable-stream "^3.1.1" + +http-deceiver@^1.2.7: + version "1.2.7" + resolved "https://registry.yarnpkg.com/http-deceiver/-/http-deceiver-1.2.7.tgz#fa7168944ab9a519d337cb0bec7284dc3e723d87" + integrity sha1-+nFolEq5pRnTN8sL7HKE3D5yPYc= + +http-errors@1.7.2: + version "1.7.2" + resolved "https://registry.yarnpkg.com/http-errors/-/http-errors-1.7.2.tgz#4f5029cf13239f31036e5b2e55292bcfbcc85c8f" + integrity sha512-uUQBt3H/cSIVfch6i1EuPNy/YsRSOUBXTVfZ+yR7Zjez3qjBz6i9+i4zjNaoqcoFVI4lQJ5plg63TvGfRSDCRg== + dependencies: + depd "~1.1.2" + inherits "2.0.3" + setprototypeof "1.1.1" + statuses ">= 1.5.0 < 2" + toidentifier "1.0.0" + +http-errors@~1.6.2: + version "1.6.3" + resolved "https://registry.yarnpkg.com/http-errors/-/http-errors-1.6.3.tgz#8b55680bb4be283a0b5bf4ea2e38580be1d9320d" + integrity sha1-i1VoC7S+KDoLW/TqLjhYC+HZMg0= + dependencies: + depd "~1.1.2" + inherits "2.0.3" + setprototypeof "1.1.0" + statuses ">= 1.4.0 < 2" + +http-errors@~1.7.2: + version "1.7.3" + resolved "https://registry.yarnpkg.com/http-errors/-/http-errors-1.7.3.tgz#6c619e4f9c60308c38519498c14fbb10aacebb06" + integrity sha512-ZTTX0MWrsQ2ZAhA1cejAwDLycFsd7I7nVtnkT3Ol0aqodaKW+0CTZDQ1uBv5whptCnc8e8HeRRJxRs0kmm/Qfw== + dependencies: + depd "~1.1.2" + inherits "2.0.4" + setprototypeof "1.1.1" + statuses ">= 1.5.0 < 2" + toidentifier "1.0.0" + +"http-parser-js@>=0.4.0 <0.4.11": + version "0.4.10" + resolved "https://registry.yarnpkg.com/http-parser-js/-/http-parser-js-0.4.10.tgz#92c9c1374c35085f75db359ec56cc257cbb93fa4" + integrity sha1-ksnBN0w1CF912zWexWzCV8u5P6Q= + +http-proxy-middleware@^0.19.1: + version "0.19.1" + resolved "https://registry.yarnpkg.com/http-proxy-middleware/-/http-proxy-middleware-0.19.1.tgz#183c7dc4aa1479150306498c210cdaf96080a43a" + integrity sha512-yHYTgWMQO8VvwNS22eLLloAkvungsKdKTLO8AJlftYIKNfJr3GK3zK0ZCfzDDGUBttdGc8xFy1mCitvNKQtC3Q== + dependencies: + http-proxy "^1.17.0" + is-glob "^4.0.0" + lodash "^4.17.11" + micromatch "^3.1.10" + +http-proxy@^1.17.0: + version "1.17.0" + resolved "https://registry.yarnpkg.com/http-proxy/-/http-proxy-1.17.0.tgz#7ad38494658f84605e2f6db4436df410f4e5be9a" + integrity sha512-Taqn+3nNvYRfJ3bGvKfBSRwy1v6eePlm3oc/aWVxZp57DQr5Eq3xhKJi7Z4hZpS8PC3H4qI+Yly5EmFacGuA/g== + dependencies: + eventemitter3 "^3.0.0" + follow-redirects "^1.0.0" + requires-port "^1.0.0" + +http-signature@~1.2.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/http-signature/-/http-signature-1.2.0.tgz#9aecd925114772f3d95b65a60abb8f7c18fbace1" + integrity sha1-muzZJRFHcvPZW2WmCruPfBj7rOE= + dependencies: + assert-plus "^1.0.0" + jsprim "^1.2.2" + sshpk "^1.7.0" + +https-browserify@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/https-browserify/-/https-browserify-1.0.0.tgz#ec06c10e0a34c0f2faf199f7fd7fc78fffd03c73" + integrity sha1-7AbBDgo0wPL68Zn3/X/Hj//QPHM= + +iconv-lite@0.4.24, iconv-lite@^0.4.4: + version "0.4.24" + resolved "https://registry.yarnpkg.com/iconv-lite/-/iconv-lite-0.4.24.tgz#2022b4b25fbddc21d2f524974a474aafe733908b" + integrity sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA== + dependencies: + safer-buffer ">= 2.1.2 < 3" + +icss-replace-symbols@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/icss-replace-symbols/-/icss-replace-symbols-1.1.0.tgz#06ea6f83679a7749e386cfe1fe812ae5db223ded" + integrity sha1-Bupvg2ead0njhs/h/oEq5dsiPe0= + +icss-utils@^4.1.0: + version "4.1.1" + resolved "https://registry.yarnpkg.com/icss-utils/-/icss-utils-4.1.1.tgz#21170b53789ee27447c2f47dd683081403f9a467" + integrity sha512-4aFq7wvWyMHKgxsH8QQtGpvbASCf+eM3wPRLI6R+MgAnTCZ6STYsRvttLvRWK0Nfif5piF394St3HeJDaljGPA== + dependencies: + postcss "^7.0.14" + +ieee754@^1.1.4: + version "1.1.13" + resolved "https://registry.yarnpkg.com/ieee754/-/ieee754-1.1.13.tgz#ec168558e95aa181fd87d37f55c32bbcb6708b84" + integrity sha512-4vf7I2LYV/HaWerSo3XmlMkp5eZ83i+/CDluXi/IGTs/O1sejBNhTtnxzmRZfvOUqj7lZjqHkeTvpgSFDlWZTg== + +iferr@^0.1.5: + version "0.1.5" + resolved "https://registry.yarnpkg.com/iferr/-/iferr-0.1.5.tgz#c60eed69e6d8fdb6b3104a1fcbca1c192dc5b501" + integrity sha1-xg7taebY/bazEEofy8ocGS3FtQE= + +ignore-walk@^3.0.1: + version "3.0.1" + resolved "https://registry.yarnpkg.com/ignore-walk/-/ignore-walk-3.0.1.tgz#a83e62e7d272ac0e3b551aaa82831a19b69f82f8" + integrity sha512-DTVlMx3IYPe0/JJcYP7Gxg7ttZZu3IInhuEhbchuqneY9wWe5Ojy2mXLBaQFUQmo0AW2r3qG7m1mg86js+gnlQ== + dependencies: + minimatch "^3.0.4" + +ignore@^3.3.5: + version "3.3.10" + resolved "https://registry.yarnpkg.com/ignore/-/ignore-3.3.10.tgz#0a97fb876986e8081c631160f8f9f389157f0043" + integrity sha512-Pgs951kaMm5GXP7MOvxERINe3gsaVjUWFm+UZPSq9xYriQAksyhg0csnS0KXSNRD5NmNdapXEpjxG49+AKh/ug== + +ignore@^4.0.3: + version "4.0.6" + resolved "https://registry.yarnpkg.com/ignore/-/ignore-4.0.6.tgz#750e3db5862087b4737ebac8207ffd1ef27b25fc" + integrity sha512-cyFDKrqc/YdcWFniJhzI42+AzS+gNwmUzOSFcRCQYwySuBBBy/KjuxWLZ/FHEH6Moq1NizMOBWyTcv8O4OZIMg== + +immediate@^3.2.3: + version "3.2.3" + resolved "https://registry.yarnpkg.com/immediate/-/immediate-3.2.3.tgz#d140fa8f614659bd6541233097ddaac25cdd991c" + integrity sha1-0UD6j2FGWb1lQSMwl92qwlzdmRw= + +import-cwd@^2.0.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/import-cwd/-/import-cwd-2.1.0.tgz#aa6cf36e722761285cb371ec6519f53e2435b0a9" + integrity sha1-qmzzbnInYShcs3HsZRn1PiQ1sKk= + dependencies: + import-from "^2.1.0" + +import-fresh@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/import-fresh/-/import-fresh-2.0.0.tgz#d81355c15612d386c61f9ddd3922d4304822a546" + integrity sha1-2BNVwVYS04bGH53dOSLUMEgipUY= + dependencies: + caller-path "^2.0.0" + resolve-from "^3.0.0" + +import-from@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/import-from/-/import-from-2.1.0.tgz#335db7f2a7affd53aaa471d4b8021dee36b7f3b1" + integrity sha1-M1238qev/VOqpHHUuAId7ja387E= + dependencies: + resolve-from "^3.0.0" + +import-local@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/import-local/-/import-local-2.0.0.tgz#55070be38a5993cf18ef6db7e961f5bee5c5a09d" + integrity sha512-b6s04m3O+s3CGSbqDIyP4R6aAwAeYlVq9+WUWep6iHa8ETRf9yei1U48C5MmfJmV9AiLYYBKPMq/W+/WRpQmCQ== + dependencies: + pkg-dir "^3.0.0" + resolve-cwd "^2.0.0" + +imurmurhash@^0.1.4: + version "0.1.4" + resolved "https://registry.yarnpkg.com/imurmurhash/-/imurmurhash-0.1.4.tgz#9218b9b2b928a238b13dc4fb6b6d576f231453ea" + integrity sha1-khi5srkoojixPcT7a21XbyMUU+o= + +indexes-of@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/indexes-of/-/indexes-of-1.0.1.tgz#f30f716c8e2bd346c7b67d3df3915566a7c05607" + integrity sha1-8w9xbI4r00bHtn0985FVZqfAVgc= + +infer-owner@^1.0.3: + version "1.0.4" + resolved "https://registry.yarnpkg.com/infer-owner/-/infer-owner-1.0.4.tgz#c4cefcaa8e51051c2a40ba2ce8a3d27295af9467" + integrity sha512-IClj+Xz94+d7irH5qRyfJonOdfTzuDaifE6ZPWfx0N0+/ATZCbuTPq2prFl526urkQd90WyUKIh1DfBQ2hMz9A== + +inflight@^1.0.4: + version "1.0.6" + resolved "https://registry.yarnpkg.com/inflight/-/inflight-1.0.6.tgz#49bd6331d7d02d0c09bc910a1075ba8165b56df9" + integrity sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk= + dependencies: + once "^1.3.0" + wrappy "1" + +inherits@2, inherits@2.0.4, inherits@^2.0.1, inherits@^2.0.3, inherits@~2.0.1, inherits@~2.0.3: + version "2.0.4" + resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.4.tgz#0fa2c64f932917c3433a0ded55363aae37416b7c" + integrity sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ== + +inherits@2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.1.tgz#b17d08d326b4423e568eff719f91b0b1cbdf69f1" + integrity sha1-sX0I0ya0Qj5Wjv9xn5GwscvfafE= + +inherits@2.0.3: + version "2.0.3" + resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.3.tgz#633c2c83e3da42a502f52466022480f4208261de" + integrity sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4= + +ini@~1.3.0: + version "1.3.5" + resolved "https://registry.yarnpkg.com/ini/-/ini-1.3.5.tgz#eee25f56db1c9ec6085e0c22778083f596abf927" + integrity sha512-RZY5huIKCMRWDUqZlEi72f/lmXKMvuszcMBduliQ3nnWbx9X/ZBQO7DijMEYS9EhHBb2qacRUMtC7svLwe0lcw== + +internal-ip@^4.3.0: + version "4.3.0" + resolved "https://registry.yarnpkg.com/internal-ip/-/internal-ip-4.3.0.tgz#845452baad9d2ca3b69c635a137acb9a0dad0907" + integrity sha512-S1zBo1D6zcsyuC6PMmY5+55YMILQ9av8lotMx447Bq6SAgo/sDK6y6uUKmuYhW7eacnIhFfsPmCNYdDzsnnDCg== + dependencies: + default-gateway "^4.2.0" + ipaddr.js "^1.9.0" + +invariant@^2.2.2: + version "2.2.4" + resolved "https://registry.yarnpkg.com/invariant/-/invariant-2.2.4.tgz#610f3c92c9359ce1db616e538008d23ff35158e6" + integrity sha512-phJfQVBuaJM5raOpJjSfkiD6BpbCE4Ns//LaXl6wGYtUBY83nWS6Rf9tXm2e8VaK60JEjYldbPif/A2B1C2gNA== + dependencies: + loose-envify "^1.0.0" + +invert-kv@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/invert-kv/-/invert-kv-2.0.0.tgz#7393f5afa59ec9ff5f67a27620d11c226e3eec02" + integrity sha512-wPVv/y/QQ/Uiirj/vh3oP+1Ww+AWehmi1g5fFWGPF6IpCBCDVrhgHRMvrLfdYcwDh3QJbGXDW4JAuzxElLSqKA== + +ip-regex@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/ip-regex/-/ip-regex-2.1.0.tgz#fa78bf5d2e6913c911ce9f819ee5146bb6d844e9" + integrity sha1-+ni/XS5pE8kRzp+BnuUUa7bYROk= + +ip@^1.1.0, ip@^1.1.5: + version "1.1.5" + resolved "https://registry.yarnpkg.com/ip/-/ip-1.1.5.tgz#bdded70114290828c0a039e72ef25f5aaec4354a" + integrity sha1-vd7XARQpCCjAoDnnLvJfWq7ENUo= + +ipaddr.js@1.9.0: + version "1.9.0" + resolved "https://registry.yarnpkg.com/ipaddr.js/-/ipaddr.js-1.9.0.tgz#37df74e430a0e47550fe54a2defe30d8acd95f65" + integrity sha512-M4Sjn6N/+O6/IXSJseKqHoFc+5FdGJ22sXqnjTpdZweHK64MzEPAyQZyEU3R/KRv2GLoa7nNtg/C2Ev6m7z+eA== + +ipaddr.js@^1.9.0: + version "1.9.1" + resolved "https://registry.yarnpkg.com/ipaddr.js/-/ipaddr.js-1.9.1.tgz#bff38543eeb8984825079ff3a2a8e6cbd46781b3" + integrity sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g== + +is-absolute-url@^2.0.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/is-absolute-url/-/is-absolute-url-2.1.0.tgz#50530dfb84fcc9aa7dbe7852e83a37b93b9f2aa6" + integrity sha1-UFMN+4T8yap9vnhS6Do3uTufKqY= + +is-absolute-url@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/is-absolute-url/-/is-absolute-url-3.0.0.tgz#eb21d69df2ed8ef72a3e6f243e216563036a0913" + integrity sha512-3OkP8XrM2Xq4/IxsJnClfMp3OaM3TAatLPLKPeWcxLBTrpe6hihwtX+XZfJTcXg/FTRi4qjy0y/C5qiyNxY24g== + +is-accessor-descriptor@^0.1.6: + version "0.1.6" + resolved "https://registry.yarnpkg.com/is-accessor-descriptor/-/is-accessor-descriptor-0.1.6.tgz#a9e12cb3ae8d876727eeef3843f8a0897b5c98d6" + integrity sha1-qeEss66Nh2cn7u84Q/igiXtcmNY= + dependencies: + kind-of "^3.0.2" + +is-accessor-descriptor@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/is-accessor-descriptor/-/is-accessor-descriptor-1.0.0.tgz#169c2f6d3df1f992618072365c9b0ea1f6878656" + integrity sha512-m5hnHTkcVsPfqx3AKlyttIPb7J+XykHvJP2B9bZDjlhLIoEq4XoK64Vg7boZlVWYK6LUY94dYPEE7Lh0ZkZKcQ== + dependencies: + kind-of "^6.0.0" + +is-arrayish@^0.2.1: + version "0.2.1" + resolved "https://registry.yarnpkg.com/is-arrayish/-/is-arrayish-0.2.1.tgz#77c99840527aa8ecb1a8ba697b80645a7a926a9d" + integrity sha1-d8mYQFJ6qOyxqLppe4BkWnqSap0= + +is-arrayish@^0.3.1: + version "0.3.2" + resolved "https://registry.yarnpkg.com/is-arrayish/-/is-arrayish-0.3.2.tgz#4574a2ae56f7ab206896fb431eaeed066fdf8f03" + integrity sha512-eVRqCvVlZbuw3GrM63ovNSNAeA1K16kaR/LRY/92w0zxQ5/1YzwblUX652i4Xs9RwAGjW9d9y6X88t8OaAJfWQ== + +is-binary-path@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/is-binary-path/-/is-binary-path-1.0.1.tgz#75f16642b480f187a711c814161fd3a4a7655898" + integrity sha1-dfFmQrSA8YenEcgUFh/TpKdlWJg= + dependencies: + binary-extensions "^1.0.0" + +is-buffer@^1.1.5: + version "1.1.6" + resolved "https://registry.yarnpkg.com/is-buffer/-/is-buffer-1.1.6.tgz#efaa2ea9daa0d7ab2ea13a97b2b8ad51fefbe8be" + integrity sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w== + +is-callable@^1.1.4: + version "1.1.4" + resolved "https://registry.yarnpkg.com/is-callable/-/is-callable-1.1.4.tgz#1e1adf219e1eeb684d691f9d6a05ff0d30a24d75" + integrity sha512-r5p9sxJjYnArLjObpjA4xu5EKI3CuKHkJXMhT7kwbpUyIFD1n5PMAsoPvWnvtZiNz7LjkYDRZhd7FlI0eMijEA== + +is-color-stop@^1.0.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/is-color-stop/-/is-color-stop-1.1.0.tgz#cfff471aee4dd5c9e158598fbe12967b5cdad345" + integrity sha1-z/9HGu5N1cnhWFmPvhKWe1za00U= + dependencies: + css-color-names "^0.0.4" + hex-color-regex "^1.1.0" + hsl-regex "^1.0.0" + hsla-regex "^1.0.0" + rgb-regex "^1.0.1" + rgba-regex "^1.0.0" + +is-data-descriptor@^0.1.4: + version "0.1.4" + resolved "https://registry.yarnpkg.com/is-data-descriptor/-/is-data-descriptor-0.1.4.tgz#0b5ee648388e2c860282e793f1856fec3f301b56" + integrity sha1-C17mSDiOLIYCgueT8YVv7D8wG1Y= + dependencies: + kind-of "^3.0.2" + +is-data-descriptor@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/is-data-descriptor/-/is-data-descriptor-1.0.0.tgz#d84876321d0e7add03990406abbbbd36ba9268c7" + integrity sha512-jbRXy1FmtAoCjQkVmIVYwuuqDFUbaOeDjmed1tOGPrsMhtJA4rD9tkgA0F1qJ3gRFRXcHYVkdeaP50Q5rE/jLQ== + dependencies: + kind-of "^6.0.0" + +is-date-object@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/is-date-object/-/is-date-object-1.0.1.tgz#9aa20eb6aeebbff77fbd33e74ca01b33581d3a16" + integrity sha1-mqIOtq7rv/d/vTPnTKAbM1gdOhY= + +is-descriptor@^0.1.0: + version "0.1.6" + resolved "https://registry.yarnpkg.com/is-descriptor/-/is-descriptor-0.1.6.tgz#366d8240dde487ca51823b1ab9f07a10a78251ca" + integrity sha512-avDYr0SB3DwO9zsMov0gKCESFYqCnE4hq/4z3TdUlukEy5t9C0YRq7HLrsN52NAcqXKaepeCD0n+B0arnVG3Hg== + dependencies: + is-accessor-descriptor "^0.1.6" + is-data-descriptor "^0.1.4" + kind-of "^5.0.0" + +is-descriptor@^1.0.0, is-descriptor@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/is-descriptor/-/is-descriptor-1.0.2.tgz#3b159746a66604b04f8c81524ba365c5f14d86ec" + integrity sha512-2eis5WqQGV7peooDyLmNEPUrps9+SXX5c9pL3xEB+4e9HnGuDa7mB7kHxHw4CbqS9k1T2hOH3miL8n8WtiYVtg== + dependencies: + is-accessor-descriptor "^1.0.0" + is-data-descriptor "^1.0.0" + kind-of "^6.0.2" + +is-directory@^0.3.1: + version "0.3.1" + resolved "https://registry.yarnpkg.com/is-directory/-/is-directory-0.3.1.tgz#61339b6f2475fc772fd9c9d83f5c8575dc154ae1" + integrity sha1-YTObbyR1/Hcv2cnYP1yFddwVSuE= + +is-extendable@^0.1.0, is-extendable@^0.1.1: + version "0.1.1" + resolved "https://registry.yarnpkg.com/is-extendable/-/is-extendable-0.1.1.tgz#62b110e289a471418e3ec36a617d472e301dfc89" + integrity sha1-YrEQ4omkcUGOPsNqYX1HLjAd/Ik= + +is-extendable@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/is-extendable/-/is-extendable-1.0.1.tgz#a7470f9e426733d81bd81e1155264e3a3507cab4" + integrity sha512-arnXMxT1hhoKo9k1LZdmlNyJdDDfy2v0fXjFlmok4+i8ul/6WlbVge9bhM74OpNPQPMGUToDtz+KXa1PneJxOA== + dependencies: + is-plain-object "^2.0.4" + +is-extglob@^2.1.0, is-extglob@^2.1.1: + version "2.1.1" + resolved "https://registry.yarnpkg.com/is-extglob/-/is-extglob-2.1.1.tgz#a88c02535791f02ed37c76a1b9ea9773c833f8c2" + integrity sha1-qIwCU1eR8C7TfHahueqXc8gz+MI= + +is-fullwidth-code-point@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/is-fullwidth-code-point/-/is-fullwidth-code-point-1.0.0.tgz#ef9e31386f031a7f0d643af82fde50c457ef00cb" + integrity sha1-754xOG8DGn8NZDr4L95QxFfvAMs= + dependencies: + number-is-nan "^1.0.0" + +is-fullwidth-code-point@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz#a3b30a5c4f199183167aaab93beefae3ddfb654f" + integrity sha1-o7MKXE8ZkYMWeqq5O+764937ZU8= + +is-glob@^3.1.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/is-glob/-/is-glob-3.1.0.tgz#7ba5ae24217804ac70707b96922567486cc3e84a" + integrity sha1-e6WuJCF4BKxwcHuWkiVnSGzD6Eo= + dependencies: + is-extglob "^2.1.0" + +is-glob@^4.0.0, is-glob@^4.0.1: + version "4.0.1" + resolved "https://registry.yarnpkg.com/is-glob/-/is-glob-4.0.1.tgz#7567dbe9f2f5e2467bc77ab83c4a29482407a5dc" + integrity sha512-5G0tKtBTFImOqDnLB2hG6Bp2qcKEFduo4tZu9MT/H6NQv/ghhy30o55ufafxJ/LdH79LLs2Kfrn85TLKyA7BUg== + dependencies: + is-extglob "^2.1.1" + +is-number@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/is-number/-/is-number-3.0.0.tgz#24fd6201a4782cf50561c810276afc7d12d71195" + integrity sha1-JP1iAaR4LPUFYcgQJ2r8fRLXEZU= + dependencies: + kind-of "^3.0.2" + +is-obj@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/is-obj/-/is-obj-1.0.1.tgz#3e4729ac1f5fde025cd7d83a896dab9f4f67db0f" + integrity sha1-PkcprB9f3gJc19g6iW2rn09n2w8= + +is-path-cwd@^2.0.0: + version "2.2.0" + resolved "https://registry.yarnpkg.com/is-path-cwd/-/is-path-cwd-2.2.0.tgz#67d43b82664a7b5191fd9119127eb300048a9fdb" + integrity sha512-w942bTcih8fdJPJmQHFzkS76NEP8Kzzvmw92cXsazb8intwLqPibPPdXf4ANdKV3rYMuuQYGIWtvz9JilB3NFQ== + +is-path-in-cwd@^2.0.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/is-path-in-cwd/-/is-path-in-cwd-2.1.0.tgz#bfe2dca26c69f397265a4009963602935a053acb" + integrity sha512-rNocXHgipO+rvnP6dk3zI20RpOtrAM/kzbB258Uw5BWr3TpXi861yzjo16Dn4hUox07iw5AyeMLHWsujkjzvRQ== + dependencies: + is-path-inside "^2.1.0" + +is-path-inside@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/is-path-inside/-/is-path-inside-2.1.0.tgz#7c9810587d659a40d27bcdb4d5616eab059494b2" + integrity sha512-wiyhTzfDWsvwAW53OBWF5zuvaOGlZ6PwYxAbPVDhpm+gM09xKQGjBq/8uYN12aDvMxnAnq3dxTyoSoRNmg5YFg== + dependencies: + path-is-inside "^1.0.2" + +is-plain-obj@^1.0.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/is-plain-obj/-/is-plain-obj-1.1.0.tgz#71a50c8429dfca773c92a390a4a03b39fcd51d3e" + integrity sha1-caUMhCnfync8kqOQpKA7OfzVHT4= + +is-plain-object@^2.0.3, is-plain-object@^2.0.4: + version "2.0.4" + resolved "https://registry.yarnpkg.com/is-plain-object/-/is-plain-object-2.0.4.tgz#2c163b3fafb1b606d9d17928f05c2a1c38e07677" + integrity sha512-h5PpgXkWitc38BBMYawTYMWJHFZJVnBquFE57xFpjB8pJFiF6gZ+bU+WyI/yqXiFR5mdLsgYNaPe8uao6Uv9Og== + dependencies: + isobject "^3.0.1" + +is-regex@^1.0.4: + version "1.0.4" + resolved "https://registry.yarnpkg.com/is-regex/-/is-regex-1.0.4.tgz#5517489b547091b0930e095654ced25ee97e9491" + integrity sha1-VRdIm1RwkbCTDglWVM7SXul+lJE= + dependencies: + has "^1.0.1" + +is-resolvable@^1.0.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/is-resolvable/-/is-resolvable-1.1.0.tgz#fb18f87ce1feb925169c9a407c19318a3206ed88" + integrity sha512-qgDYXFSR5WvEfuS5dMj6oTMEbrrSaM0CrFk2Yiq/gXnBvD9pMa2jGXxyhGLfvhZpuMZe18CJpFxAt3CRs42NMg== + +is-stream@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/is-stream/-/is-stream-1.1.0.tgz#12d4a3dd4e68e0b79ceb8dbc84173ae80d91ca44" + integrity sha1-EtSj3U5o4Lec6428hBc66A2RykQ= + +is-svg@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/is-svg/-/is-svg-3.0.0.tgz#9321dbd29c212e5ca99c4fa9794c714bcafa2f75" + integrity sha512-gi4iHK53LR2ujhLVVj+37Ykh9GLqYHX6JOVXbLAucaG/Cqw9xwdFOjDM2qeifLs1sF1npXXFvDu0r5HNgCMrzQ== + dependencies: + html-comment-regex "^1.1.0" + +is-symbol@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/is-symbol/-/is-symbol-1.0.2.tgz#a055f6ae57192caee329e7a860118b497a950f38" + integrity sha512-HS8bZ9ox60yCJLH9snBpIwv9pYUAkcuLhSA1oero1UB5y9aiQpRA8y2ex945AOtCZL1lJDeIk3G5LthswI46Lw== + dependencies: + has-symbols "^1.0.0" + +is-typedarray@~1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/is-typedarray/-/is-typedarray-1.0.0.tgz#e479c80858df0c1b11ddda6940f96011fcda4a9a" + integrity sha1-5HnICFjfDBsR3dppQPlgEfzaSpo= + +is-windows@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/is-windows/-/is-windows-1.0.2.tgz#d1850eb9791ecd18e6182ce12a30f396634bb19d" + integrity sha512-eXK1UInq2bPmjyX6e3VHIzMLobc4J94i4AWn+Hpq3OU5KkrRC96OAcR3PRJ/pGu6m8TRnBHP9dkXQVsT/COVIA== + +is-wsl@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/is-wsl/-/is-wsl-1.1.0.tgz#1f16e4aa22b04d1336b66188a66af3c600c3a66d" + integrity sha1-HxbkqiKwTRM2tmGIpmrzxgDDpm0= + +isarray@1.0.0, isarray@^1.0.0, isarray@~1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/isarray/-/isarray-1.0.0.tgz#bb935d48582cba168c06834957a54a3e07124f11" + integrity sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE= + +isarray@^2.0.1: + version "2.0.5" + resolved "https://registry.yarnpkg.com/isarray/-/isarray-2.0.5.tgz#8af1e4c1221244cc62459faf38940d4e644a5723" + integrity sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw== + +isexe@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/isexe/-/isexe-2.0.0.tgz#e8fbf374dc556ff8947a10dcb0572d633f2cfa10" + integrity sha1-6PvzdNxVb/iUehDcsFctYz8s+hA= + +isobject@^2.0.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/isobject/-/isobject-2.1.0.tgz#f065561096a3f1da2ef46272f815c840d87e0c89" + integrity sha1-8GVWEJaj8dou9GJy+BXIQNh+DIk= + dependencies: + isarray "1.0.0" + +isobject@^3.0.0, isobject@^3.0.1: + version "3.0.1" + resolved "https://registry.yarnpkg.com/isobject/-/isobject-3.0.1.tgz#4e431e92b11a9731636aa1f9c8d1ccbcfdab78df" + integrity sha1-TkMekrEalzFjaqH5yNHMvP2reN8= + +isstream@~0.1.2: + version "0.1.2" + resolved "https://registry.yarnpkg.com/isstream/-/isstream-0.1.2.tgz#47e63f7af55afa6f92e1500e690eb8b8529c099a" + integrity sha1-R+Y/evVa+m+S4VAOaQ64uFKcCZo= + +javascript-stringify@^1.6.0: + version "1.6.0" + resolved "https://registry.yarnpkg.com/javascript-stringify/-/javascript-stringify-1.6.0.tgz#142d111f3a6e3dae8f4a9afd77d45855b5a9cce3" + integrity sha1-FC0RHzpuPa6PSpr9d9RYVbWpzOM= + +js-levenshtein@^1.1.3: + version "1.1.6" + resolved "https://registry.yarnpkg.com/js-levenshtein/-/js-levenshtein-1.1.6.tgz#c6cee58eb3550372df8deb85fad5ce66ce01d59d" + integrity sha512-X2BB11YZtrRqY4EnQcLX5Rh373zbK4alC1FW7D7MBhL2gtcC17cTnr6DmfHZeS0s2rTHjUTMMHfG7gO8SSdw+g== + +"js-tokens@^3.0.0 || ^4.0.0", js-tokens@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/js-tokens/-/js-tokens-4.0.0.tgz#19203fb59991df98e3a287050d4647cdeaf32499" + integrity sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ== + +js-yaml@^3.11.0, js-yaml@^3.13.1: + version "3.13.1" + resolved "https://registry.yarnpkg.com/js-yaml/-/js-yaml-3.13.1.tgz#aff151b30bfdfa8e49e05da22e7415e9dfa37847" + integrity sha512-YfbcO7jXDdyj0DGxYVSlSeQNHbD7XPWvrVWeVUujrQEoZzWJIRrCPoyk6kL6IAjAG2IolMK4T0hNUe0HOUs5Jw== + dependencies: + argparse "^1.0.7" + esprima "^4.0.0" + +jsbn@~0.1.0: + version "0.1.1" + resolved "https://registry.yarnpkg.com/jsbn/-/jsbn-0.1.1.tgz#a5e654c2e5a2deb5f201d96cefbca80c0ef2f513" + integrity sha1-peZUwuWi3rXyAdls77yoDA7y9RM= + +jsesc@^2.5.1: + version "2.5.2" + resolved "https://registry.yarnpkg.com/jsesc/-/jsesc-2.5.2.tgz#80564d2e483dacf6e8ef209650a67df3f0c283a4" + integrity sha512-OYu7XEzjkCQ3C5Ps3QIZsQfNpqoJyZZA99wd9aWd05NCtC5pWOkShK2mkL6HXQR6/Cy2lbNdPlZBpuQHXE63gA== + +jsesc@~0.5.0: + version "0.5.0" + resolved "https://registry.yarnpkg.com/jsesc/-/jsesc-0.5.0.tgz#e7dee66e35d6fc16f710fe91d5cf69f70f08911d" + integrity sha1-597mbjXW/Bb3EP6R1c9p9w8IkR0= + +json-parse-better-errors@^1.0.1, json-parse-better-errors@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/json-parse-better-errors/-/json-parse-better-errors-1.0.2.tgz#bb867cfb3450e69107c131d1c514bab3dc8bcaa9" + integrity sha512-mrqyZKfX5EhL7hvqcV6WG1yYjnjeuYDzDhhcAAUrq8Po85NBQBJP+ZDUT75qZQ98IkUoBqdkExkukOU7Ts2wrw== + +json-schema-traverse@^0.4.1: + version "0.4.1" + resolved "https://registry.yarnpkg.com/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz#69f6a87d9513ab8bb8fe63bdb0979c448e684660" + integrity sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg== + +json-schema@0.2.3: + version "0.2.3" + resolved "https://registry.yarnpkg.com/json-schema/-/json-schema-0.2.3.tgz#b480c892e59a2f05954ce727bd3f2a4e882f9e13" + integrity sha1-tIDIkuWaLwWVTOcnvT8qTogvnhM= + +json-stringify-safe@~5.0.1: + version "5.0.1" + resolved "https://registry.yarnpkg.com/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz#1296a2d58fd45f19a0f6ce01d65701e2c735b6eb" + integrity sha1-Epai1Y/UXxmg9s4B1lcB4sc1tus= + +json3@^3.3.2: + version "3.3.3" + resolved "https://registry.yarnpkg.com/json3/-/json3-3.3.3.tgz#7fc10e375fc5ae42c4705a5cc0aa6f62be305b81" + integrity sha512-c7/8mbUsKigAbLkD5B010BK4D9LZm7A1pNItkEwiUZRpIN66exu/e7YQWysGun+TRKaJp8MhemM+VkfWv42aCA== + +json5@^0.5.0, json5@^0.5.1: + version "0.5.1" + resolved "https://registry.yarnpkg.com/json5/-/json5-0.5.1.tgz#1eade7acc012034ad84e2396767ead9fa5495821" + integrity sha1-Hq3nrMASA0rYTiOWdn6tn6VJWCE= + +json5@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/json5/-/json5-1.0.1.tgz#779fb0018604fa854eacbf6252180d83543e3dbe" + integrity sha512-aKS4WQjPenRxiQsC93MNfjx+nbF4PAdYzmd/1JIj8HYzqfbu86beTuNgXDzPknWk0n0uARlyewZo4s++ES36Ow== + dependencies: + minimist "^1.2.0" + +json5@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/json5/-/json5-2.1.0.tgz#e7a0c62c48285c628d20a10b85c89bb807c32850" + integrity sha512-8Mh9h6xViijj36g7Dxi+Y4S6hNGV96vcJZr/SrlHh1LR/pEn/8j/+qIBbs44YKl69Lrfctp4QD+AdWLTMqEZAQ== + dependencies: + minimist "^1.2.0" + +jsonfile@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/jsonfile/-/jsonfile-4.0.0.tgz#8771aae0799b64076b76640fca058f9c10e33ecb" + integrity sha1-h3Gq4HmbZAdrdmQPygWPnBDjPss= + optionalDependencies: + graceful-fs "^4.1.6" + +jsprim@^1.2.2: + version "1.4.1" + resolved "https://registry.yarnpkg.com/jsprim/-/jsprim-1.4.1.tgz#313e66bc1e5cc06e438bc1b7499c2e5c56acb6a2" + integrity sha1-MT5mvB5cwG5Di8G3SZwuXFastqI= + dependencies: + assert-plus "1.0.0" + extsprintf "1.3.0" + json-schema "0.2.3" + verror "1.10.0" + +killable@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/killable/-/killable-1.0.1.tgz#4c8ce441187a061c7474fb87ca08e2a638194892" + integrity sha512-LzqtLKlUwirEUyl/nicirVmNiPvYs7l5n8wOPP7fyJVpUPkvCnW/vuiXGpylGUlnPDnB7311rARzAt3Mhswpjg== + +kind-of@^3.0.2, kind-of@^3.0.3, kind-of@^3.2.0: + version "3.2.2" + resolved "https://registry.yarnpkg.com/kind-of/-/kind-of-3.2.2.tgz#31ea21a734bab9bbb0f32466d893aea51e4a3c64" + integrity sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ= + dependencies: + is-buffer "^1.1.5" + +kind-of@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/kind-of/-/kind-of-4.0.0.tgz#20813df3d712928b207378691a45066fae72dd57" + integrity sha1-IIE989cSkosgc3hpGkUGb65y3Vc= + dependencies: + is-buffer "^1.1.5" + +kind-of@^5.0.0: + version "5.1.0" + resolved "https://registry.yarnpkg.com/kind-of/-/kind-of-5.1.0.tgz#729c91e2d857b7a419a1f9aa65685c4c33f5845d" + integrity sha512-NGEErnH6F2vUuXDh+OlbcKW7/wOcfdRHaZ7VWtqCztfHri/++YKmP51OdWeGPuqCOba6kk2OTe5d02VmTB80Pw== + +kind-of@^6.0.0, kind-of@^6.0.2: + version "6.0.2" + resolved "https://registry.yarnpkg.com/kind-of/-/kind-of-6.0.2.tgz#01146b36a6218e64e58f3a8d66de5d7fc6f6d051" + integrity sha512-s5kLOcnH0XqDO+FvuaLX8DDjZ18CGFk7VygH40QoKPUQhW4e2rvM0rwUq0t8IQDOwYSeLK01U90OjzBTme2QqA== + +last-call-webpack-plugin@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/last-call-webpack-plugin/-/last-call-webpack-plugin-3.0.0.tgz#9742df0e10e3cf46e5c0381c2de90d3a7a2d7555" + integrity sha512-7KI2l2GIZa9p2spzPIVZBYyNKkN+e/SQPpnjlTiPhdbDW3F86tdKKELxKpzJ5sgU19wQWsACULZmpTPYHeWO5w== + dependencies: + lodash "^4.17.5" + webpack-sources "^1.1.0" + +lcid@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/lcid/-/lcid-2.0.0.tgz#6ef5d2df60e52f82eb228a4c373e8d1f397253cf" + integrity sha512-avPEb8P8EGnwXKClwsNUgryVjllcRqtMYa49NTsbQagYuT1DcXnl1915oxWjoyGrXR6zH/Y0Zc96xWsPcoDKeA== + dependencies: + invert-kv "^2.0.0" + +linkify-it@^2.0.0: + version "2.2.0" + resolved "https://registry.yarnpkg.com/linkify-it/-/linkify-it-2.2.0.tgz#e3b54697e78bf915c70a38acd78fd09e0058b1cf" + integrity sha512-GnAl/knGn+i1U/wjBz3akz2stz+HrHLsxMwHQGofCDfPvlf+gDKN58UtfmUquTY4/MXeE2x7k19KQmeoZi94Iw== + dependencies: + uc.micro "^1.0.1" + +load-script@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/load-script/-/load-script-1.0.0.tgz#0491939e0bee5643ee494a7e3da3d2bac70c6ca4" + integrity sha1-BJGTngvuVkPuSUp+PaPSuscMbKQ= + +loader-runner@^2.4.0: + version "2.4.0" + resolved "https://registry.yarnpkg.com/loader-runner/-/loader-runner-2.4.0.tgz#ed47066bfe534d7e84c4c7b9998c2a75607d9357" + integrity sha512-Jsmr89RcXGIwivFY21FcRrisYZfvLMTWx5kOLc+JTxtpBOG6xML0vzbc6SEQG2FO9/4Fc3wW4LVcB5DmGflaRw== + +loader-utils@^0.2.16: + version "0.2.17" + resolved "https://registry.yarnpkg.com/loader-utils/-/loader-utils-0.2.17.tgz#f86e6374d43205a6e6c60e9196f17c0299bfb348" + integrity sha1-+G5jdNQyBabmxg6RlvF8Apm/s0g= + dependencies: + big.js "^3.1.3" + emojis-list "^2.0.0" + json5 "^0.5.0" + object-assign "^4.0.1" + +loader-utils@^1.0.2, loader-utils@^1.1.0, loader-utils@^1.2.3: + version "1.2.3" + resolved "https://registry.yarnpkg.com/loader-utils/-/loader-utils-1.2.3.tgz#1ff5dc6911c9f0a062531a4c04b609406108c2c7" + integrity sha512-fkpz8ejdnEMG3s37wGL07iSBDg99O9D5yflE9RGNH3hRdx9SOwYfnGYdZOUIZitN8E+E2vkq3MUMYMvPYl5ZZA== + dependencies: + big.js "^5.2.2" + emojis-list "^2.0.0" + json5 "^1.0.1" + +locate-path@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/locate-path/-/locate-path-2.0.0.tgz#2b568b265eec944c6d9c0de9c3dbbbca0354cd8e" + integrity sha1-K1aLJl7slExtnA3pw9u7ygNUzY4= + dependencies: + p-locate "^2.0.0" + path-exists "^3.0.0" + +locate-path@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/locate-path/-/locate-path-3.0.0.tgz#dbec3b3ab759758071b58fe59fc41871af21400e" + integrity sha512-7AO748wWnIhNqAuaty2ZWHkQHRSNfPVIsPIfwEOWO22AmaoVrWavlOcMR5nzTLNYvp36X220/maaRsrec1G65A== + dependencies: + p-locate "^3.0.0" + path-exists "^3.0.0" + +lodash._reinterpolate@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/lodash._reinterpolate/-/lodash._reinterpolate-3.0.0.tgz#0ccf2d89166af03b3663c796538b75ac6e114d9d" + integrity sha1-DM8tiRZq8Ds2Y8eWU4t1rG4RTZ0= + +lodash.clonedeep@^4.5.0: + version "4.5.0" + resolved "https://registry.yarnpkg.com/lodash.clonedeep/-/lodash.clonedeep-4.5.0.tgz#e23f3f9c4f8fbdde872529c1071857a086e5ccef" + integrity sha1-4j8/nE+Pvd6HJSnBBxhXoIblzO8= + +lodash.kebabcase@^4.1.1: + version "4.1.1" + resolved "https://registry.yarnpkg.com/lodash.kebabcase/-/lodash.kebabcase-4.1.1.tgz#8489b1cb0d29ff88195cceca448ff6d6cc295c36" + integrity sha1-hImxyw0p/4gZXM7KRI/21swpXDY= + +lodash.memoize@^4.1.2: + version "4.1.2" + resolved "https://registry.yarnpkg.com/lodash.memoize/-/lodash.memoize-4.1.2.tgz#bcc6c49a42a2840ed997f323eada5ecd182e0bfe" + integrity sha1-vMbEmkKihA7Zl/Mj6tpezRguC/4= + +lodash.template@^4.4.0: + version "4.5.0" + resolved "https://registry.yarnpkg.com/lodash.template/-/lodash.template-4.5.0.tgz#f976195cf3f347d0d5f52483569fe8031ccce8ab" + integrity sha512-84vYFxIkmidUiFxidA/KjjH9pAycqW+h980j7Fuz5qxRtO9pgB7MDFTdys1N7A5mcucRiDyEq4fusljItR1T/A== + dependencies: + lodash._reinterpolate "^3.0.0" + lodash.templatesettings "^4.0.0" + +lodash.templatesettings@^4.0.0: + version "4.2.0" + resolved "https://registry.yarnpkg.com/lodash.templatesettings/-/lodash.templatesettings-4.2.0.tgz#e481310f049d3cf6d47e912ad09313b154f0fb33" + integrity sha512-stgLz+i3Aa9mZgnjr/O+v9ruKZsPsndy7qPZOchbqk2cnTU1ZaldKK+v7m54WoKIyxiuMZTKT2H81F8BeAc3ZQ== + dependencies: + lodash._reinterpolate "^3.0.0" + +lodash.throttle@^4.1.1: + version "4.1.1" + resolved "https://registry.yarnpkg.com/lodash.throttle/-/lodash.throttle-4.1.1.tgz#c23e91b710242ac70c37f1e1cda9274cc39bf2f4" + integrity sha1-wj6RtxAkKscMN/HhzaknTMOb8vQ= + +lodash.uniq@^4.5.0: + version "4.5.0" + resolved "https://registry.yarnpkg.com/lodash.uniq/-/lodash.uniq-4.5.0.tgz#d0225373aeb652adc1bc82e4945339a842754773" + integrity sha1-0CJTc662Uq3BvILklFM5qEJ1R3M= + +lodash@^4.17.11, lodash@^4.17.13, lodash@^4.17.3, lodash@^4.17.5: + version "4.17.15" + resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.15.tgz#b447f6670a0455bbfeedd11392eff330ea097548" + integrity sha512-8xOcRHvCjnocdS5cpwXQXVzmmh5e5+saE2QGoeQmbKmRS6J3VQppPOIt0MnmE+4xlZoumy0GPG0D0MVIQbNA1A== + +loglevel@^1.6.3: + version "1.6.3" + resolved "https://registry.yarnpkg.com/loglevel/-/loglevel-1.6.3.tgz#77f2eb64be55a404c9fd04ad16d57c1d6d6b1280" + integrity sha512-LoEDv5pgpvWgPF4kNYuIp0qqSJVWak/dML0RY74xlzMZiT9w77teNAwKYKWBTYjlokMirg+o3jBwp+vlLrcfAA== + +loose-envify@^1.0.0: + version "1.4.0" + resolved "https://registry.yarnpkg.com/loose-envify/-/loose-envify-1.4.0.tgz#71ee51fa7be4caec1a63839f7e682d8132d30caf" + integrity sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q== + dependencies: + js-tokens "^3.0.0 || ^4.0.0" + +lower-case@^1.1.1: + version "1.1.4" + resolved "https://registry.yarnpkg.com/lower-case/-/lower-case-1.1.4.tgz#9a2cabd1b9e8e0ae993a4bf7d5875c39c42e8eac" + integrity sha1-miyr0bno4K6ZOkv31YdcOcQujqw= + +lru-cache@^4.1.2: + version "4.1.5" + resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-4.1.5.tgz#8bbe50ea85bed59bc9e33dcab8235ee9bcf443cd" + integrity sha512-sWZlbEP2OsHNkXrMl5GYk/jKk70MBng6UU4YI/qGDYbgf6YbP4EvmqISbXCoJiRKs+1bSpFHVgQxvJ17F2li5g== + dependencies: + pseudomap "^1.0.2" + yallist "^2.1.2" + +lru-cache@^5.1.1: + version "5.1.1" + resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-5.1.1.tgz#1da27e6710271947695daf6848e847f01d84b920" + integrity sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w== + dependencies: + yallist "^3.0.2" + +make-dir@^2.0.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/make-dir/-/make-dir-2.1.0.tgz#5f0310e18b8be898cc07009295a30ae41e91e6f5" + integrity sha512-LS9X+dc8KLxXCb8dni79fLIIUA5VyZoyjSMCwTluaXA0o27cCK0bhXkpgw+sTXVpPy/lSO57ilRixqk0vDmtRA== + dependencies: + pify "^4.0.1" + semver "^5.6.0" + +mamacro@^0.0.3: + version "0.0.3" + resolved "https://registry.yarnpkg.com/mamacro/-/mamacro-0.0.3.tgz#ad2c9576197c9f1abf308d0787865bd975a3f3e4" + integrity sha512-qMEwh+UujcQ+kbz3T6V+wAmO2U8veoq2w+3wY8MquqwVA3jChfwY+Tk52GZKDfACEPjuZ7r2oJLejwpt8jtwTA== + +map-age-cleaner@^0.1.1: + version "0.1.3" + resolved "https://registry.yarnpkg.com/map-age-cleaner/-/map-age-cleaner-0.1.3.tgz#7d583a7306434c055fe474b0f45078e6e1b4b92a" + integrity sha512-bJzx6nMoP6PDLPBFmg7+xRKeFZvFboMrGlxmNj9ClvX53KrmvM5bXFXEWjbz4cz1AFn+jWJ9z/DJSz7hrs0w3w== + dependencies: + p-defer "^1.0.0" + +map-cache@^0.2.2: + version "0.2.2" + resolved "https://registry.yarnpkg.com/map-cache/-/map-cache-0.2.2.tgz#c32abd0bd6525d9b051645bb4f26ac5dc98a0dbf" + integrity sha1-wyq9C9ZSXZsFFkW7TyasXcmKDb8= + +map-visit@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/map-visit/-/map-visit-1.0.0.tgz#ecdca8f13144e660f1b5bd41f12f3479d98dfb8f" + integrity sha1-7Nyo8TFE5mDxtb1B8S80edmN+48= + dependencies: + object-visit "^1.0.0" + +markdown-it-anchor@^5.0.2: + version "5.2.4" + resolved "https://registry.yarnpkg.com/markdown-it-anchor/-/markdown-it-anchor-5.2.4.tgz#d39306fe4c199705b4479d3036842cf34dcba24f" + integrity sha512-n8zCGjxA3T+Mx1pG8HEgbJbkB8JFUuRkeTZQuIM8iPY6oQ8sWOPRZJDFC9a/pNg2QkHEjjGkhBEl/RSyzaDZ3A== + +markdown-it-chain@^1.3.0: + version "1.3.0" + resolved "https://registry.yarnpkg.com/markdown-it-chain/-/markdown-it-chain-1.3.0.tgz#ccf6fe86c10266bafb4e547380dfd7f277cc17bc" + integrity sha512-XClV8I1TKy8L2qsT9iX3qiV+50ZtcInGXI80CA+DP62sMs7hXlyV/RM3hfwy5O3Ad0sJm9xIwQELgANfESo8mQ== + dependencies: + webpack-chain "^4.9.0" + +markdown-it-container@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/markdown-it-container/-/markdown-it-container-2.0.0.tgz#0019b43fd02eefece2f1960a2895fba81a404695" + integrity sha1-ABm0P9Au7+zi8ZYKKJX7qBpARpU= + +markdown-it-emoji@^1.4.0: + version "1.4.0" + resolved "https://registry.yarnpkg.com/markdown-it-emoji/-/markdown-it-emoji-1.4.0.tgz#9bee0e9a990a963ba96df6980c4fddb05dfb4dcc" + integrity sha1-m+4OmpkKljupbfaYDE/dsF37Tcw= + +markdown-it-table-of-contents@^0.4.0: + version "0.4.4" + resolved "https://registry.yarnpkg.com/markdown-it-table-of-contents/-/markdown-it-table-of-contents-0.4.4.tgz#3dc7ce8b8fc17e5981c77cc398d1782319f37fbc" + integrity sha512-TAIHTHPwa9+ltKvKPWulm/beozQU41Ab+FIefRaQV1NRnpzwcV9QOe6wXQS5WLivm5Q/nlo0rl6laGkMDZE7Gw== + +markdown-it@^8.4.1: + version "8.4.2" + resolved "https://registry.yarnpkg.com/markdown-it/-/markdown-it-8.4.2.tgz#386f98998dc15a37722aa7722084f4020bdd9b54" + integrity sha512-GcRz3AWTqSUphY3vsUqQSFMbgR38a4Lh3GWlHRh/7MRwz8mcu9n2IO7HOh+bXHrR9kOPDl5RNCaEsrneb+xhHQ== + dependencies: + argparse "^1.0.7" + entities "~1.1.1" + linkify-it "^2.0.0" + mdurl "^1.0.1" + uc.micro "^1.0.5" + +md5.js@^1.3.4: + version "1.3.5" + resolved "https://registry.yarnpkg.com/md5.js/-/md5.js-1.3.5.tgz#b5d07b8e3216e3e27cd728d72f70d1e6a342005f" + integrity sha512-xitP+WxNPcTTOgnTJcrhM0xvdPepipPSf3I8EIpGKeFLjt3PlJLIDG3u8EX53ZIubkb+5U2+3rELYpEhHhzdkg== + dependencies: + hash-base "^3.0.0" + inherits "^2.0.1" + safe-buffer "^5.1.2" + +mdn-data@2.0.4: + version "2.0.4" + resolved "https://registry.yarnpkg.com/mdn-data/-/mdn-data-2.0.4.tgz#699b3c38ac6f1d728091a64650b65d388502fd5b" + integrity sha512-iV3XNKw06j5Q7mi6h+9vbx23Tv7JkjEVgKHW4pimwyDGWm0OIQntJJ+u1C6mg6mK1EaTv42XQ7w76yuzH7M2cA== + +mdn-data@~1.1.0: + version "1.1.4" + resolved "https://registry.yarnpkg.com/mdn-data/-/mdn-data-1.1.4.tgz#50b5d4ffc4575276573c4eedb8780812a8419f01" + integrity sha512-FSYbp3lyKjyj3E7fMl6rYvUdX0FBXaluGqlFoYESWQlyUTq8R+wp0rkFxoYFqZlHCvsUXGjyJmLQSnXToYhOSA== + +mdurl@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/mdurl/-/mdurl-1.0.1.tgz#fe85b2ec75a59037f2adfec100fd6c601761152e" + integrity sha1-/oWy7HWlkDfyrf7BAP1sYBdhFS4= + +media-typer@0.3.0: + version "0.3.0" + resolved "https://registry.yarnpkg.com/media-typer/-/media-typer-0.3.0.tgz#8710d7af0aa626f8fffa1ce00168545263255748" + integrity sha1-hxDXrwqmJvj/+hzgAWhUUmMlV0g= + +mem@^4.0.0: + version "4.3.0" + resolved "https://registry.yarnpkg.com/mem/-/mem-4.3.0.tgz#461af497bc4ae09608cdb2e60eefb69bff744178" + integrity sha512-qX2bG48pTqYRVmDB37rn/6PT7LcR8T7oAX3bf99u1Tt1nzxYfxkgqDwUwolPlXweM0XzBOBFzSx4kfp7KP1s/w== + dependencies: + map-age-cleaner "^0.1.1" + mimic-fn "^2.0.0" + p-is-promise "^2.0.0" + +memory-fs@^0.4.0, memory-fs@^0.4.1: + version "0.4.1" + resolved "https://registry.yarnpkg.com/memory-fs/-/memory-fs-0.4.1.tgz#3a9a20b8462523e447cfbc7e8bb80ed667bfc552" + integrity sha1-OpoguEYlI+RHz7x+i7gO1me/xVI= + dependencies: + errno "^0.1.3" + readable-stream "^2.0.1" + +merge-descriptors@1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/merge-descriptors/-/merge-descriptors-1.0.1.tgz#b00aaa556dd8b44568150ec9d1b953f3f90cbb61" + integrity sha1-sAqqVW3YtEVoFQ7J0blT8/kMu2E= + +merge-source-map@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/merge-source-map/-/merge-source-map-1.1.0.tgz#2fdde7e6020939f70906a68f2d7ae685e4c8c646" + integrity sha512-Qkcp7P2ygktpMPh2mCQZaf3jhN6D3Z/qVZHSdWvQ+2Ef5HgRAPBO57A77+ENm0CPx2+1Ce/MYKi3ymqdfuqibw== + dependencies: + source-map "^0.6.1" + +merge2@^1.2.3: + version "1.2.4" + resolved "https://registry.yarnpkg.com/merge2/-/merge2-1.2.4.tgz#c9269589e6885a60cf80605d9522d4b67ca646e3" + integrity sha512-FYE8xI+6pjFOhokZu0We3S5NKCirLbCzSh2Usf3qEyr4X8U+0jNg9P8RZ4qz+V2UoECLVwSyzU3LxXBaLGtD3A== + +methods@~1.1.2: + version "1.1.2" + resolved "https://registry.yarnpkg.com/methods/-/methods-1.1.2.tgz#5529a4d67654134edcc5266656835b0f851afcee" + integrity sha1-VSmk1nZUE07cxSZmVoNbD4Ua/O4= + +micromatch@^3.1.10, micromatch@^3.1.4: + version "3.1.10" + resolved "https://registry.yarnpkg.com/micromatch/-/micromatch-3.1.10.tgz#70859bc95c9840952f359a068a3fc49f9ecfac23" + integrity sha512-MWikgl9n9M3w+bpsY3He8L+w9eF9338xRl8IAO5viDizwSzziFEyUzo2xrrloB64ADbTf8uA8vRqqttDTOmccg== + dependencies: + arr-diff "^4.0.0" + array-unique "^0.3.2" + braces "^2.3.1" + define-property "^2.0.2" + extend-shallow "^3.0.2" + extglob "^2.0.4" + fragment-cache "^0.2.1" + kind-of "^6.0.2" + nanomatch "^1.2.9" + object.pick "^1.3.0" + regex-not "^1.0.0" + snapdragon "^0.8.1" + to-regex "^3.0.2" + +miller-rabin@^4.0.0: + version "4.0.1" + resolved "https://registry.yarnpkg.com/miller-rabin/-/miller-rabin-4.0.1.tgz#f080351c865b0dc562a8462966daa53543c78a4d" + integrity sha512-115fLhvZVqWwHPbClyntxEVfVDfl9DLLTuJvq3g2O/Oxi8AiNouAHvDSzHS0viUJc+V5vm3eq91Xwqn9dp4jRA== + dependencies: + bn.js "^4.0.0" + brorand "^1.0.1" + +mime-db@1.40.0, "mime-db@>= 1.40.0 < 2": + version "1.40.0" + resolved "https://registry.yarnpkg.com/mime-db/-/mime-db-1.40.0.tgz#a65057e998db090f732a68f6c276d387d4126c32" + integrity sha512-jYdeOMPy9vnxEqFRRo6ZvTZ8d9oPb+k18PKoYNYUe2stVEBPPwsln/qWzdbmaIvnhZ9v2P+CuecK+fpUfsV2mA== + +mime-types@^2.1.12, mime-types@~2.1.17, mime-types@~2.1.19, mime-types@~2.1.24: + version "2.1.24" + resolved "https://registry.yarnpkg.com/mime-types/-/mime-types-2.1.24.tgz#b6f8d0b3e951efb77dedeca194cff6d16f676f81" + integrity sha512-WaFHS3MCl5fapm3oLxU4eYDw77IQM2ACcxQ9RIxfaC3ooc6PFuBMGZZsYpvoXS5D5QTWPieo1jjLdAm3TBP3cQ== + dependencies: + mime-db "1.40.0" + +mime@1.6.0: + version "1.6.0" + resolved "https://registry.yarnpkg.com/mime/-/mime-1.6.0.tgz#32cd9e5c64553bd58d19a568af452acff04981b1" + integrity sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg== + +mime@^2.0.3, mime@^2.4.2: + version "2.4.4" + resolved "https://registry.yarnpkg.com/mime/-/mime-2.4.4.tgz#bd7b91135fc6b01cde3e9bae33d659b63d8857e5" + integrity sha512-LRxmNwziLPT828z+4YkNzloCFC2YM4wrB99k+AV5ZbEyfGNWfG8SO1FUXLmLDBSo89NrJZ4DIWeLjy1CHGhMGA== + +mimic-fn@^2.0.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/mimic-fn/-/mimic-fn-2.1.0.tgz#7ed2c2ccccaf84d3ffcb7a69b57711fc2083401b" + integrity sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg== + +min-document@^2.19.0: + version "2.19.0" + resolved "https://registry.yarnpkg.com/min-document/-/min-document-2.19.0.tgz#7bd282e3f5842ed295bb748cdd9f1ffa2c824685" + integrity sha1-e9KC4/WELtKVu3SM3Z8f+iyCRoU= + dependencies: + dom-walk "^0.1.0" + +mini-css-extract-plugin@0.6.0: + version "0.6.0" + resolved "https://registry.yarnpkg.com/mini-css-extract-plugin/-/mini-css-extract-plugin-0.6.0.tgz#a3f13372d6fcde912f3ee4cd039665704801e3b9" + integrity sha512-79q5P7YGI6rdnVyIAV4NXpBQJFWdkzJxCim3Kog4078fM0piAaFlwocqbejdWtLW1cEzCexPrh6EdyFsPgVdAw== + dependencies: + loader-utils "^1.1.0" + normalize-url "^2.0.1" + schema-utils "^1.0.0" + webpack-sources "^1.1.0" + +minimalistic-assert@^1.0.0, minimalistic-assert@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/minimalistic-assert/-/minimalistic-assert-1.0.1.tgz#2e194de044626d4a10e7f7fbc00ce73e83e4d5c7" + integrity sha512-UtJcAD4yEaGtjPezWuO9wC4nwUnVH/8/Im3yEHQP4b67cXlD/Qr9hdITCU1xDbSEXg2XKNaP8jsReV7vQd00/A== + +minimalistic-crypto-utils@^1.0.0, minimalistic-crypto-utils@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/minimalistic-crypto-utils/-/minimalistic-crypto-utils-1.0.1.tgz#f6c00c1c0b082246e5c4d99dfb8c7c083b2b582a" + integrity sha1-9sAMHAsIIkblxNmd+4x8CDsrWCo= + +minimatch@^3.0.2, minimatch@^3.0.4: + version "3.0.4" + resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-3.0.4.tgz#5166e286457f03306064be5497e8dbb0c3d32083" + integrity sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA== + dependencies: + brace-expansion "^1.1.7" + +minimist@0.0.8: + version "0.0.8" + resolved "https://registry.yarnpkg.com/minimist/-/minimist-0.0.8.tgz#857fcabfc3397d2625b8228262e86aa7a011b05d" + integrity sha1-hX/Kv8M5fSYluCKCYuhqp6ARsF0= + +minimist@^1.2.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.0.tgz#a35008b20f41383eec1fb914f4cd5df79a264284" + integrity sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ= + +minipass@^2.2.1, minipass@^2.3.5: + version "2.3.5" + resolved "https://registry.yarnpkg.com/minipass/-/minipass-2.3.5.tgz#cacebe492022497f656b0f0f51e2682a9ed2d848" + integrity sha512-Gi1W4k059gyRbyVUZQ4mEqLm0YIUiGYfvxhF6SIlk3ui1WVxMTGfGdQ2SInh3PDrRTVvPKgULkpJtT4RH10+VA== + dependencies: + safe-buffer "^5.1.2" + yallist "^3.0.0" + +minizlib@^1.2.1: + version "1.2.1" + resolved "https://registry.yarnpkg.com/minizlib/-/minizlib-1.2.1.tgz#dd27ea6136243c7c880684e8672bb3a45fd9b614" + integrity sha512-7+4oTUOWKg7AuL3vloEWekXY2/D20cevzsrNT2kGWm+39J9hGTCBv8VI5Pm5lXZ/o3/mdR4f8rflAPhnQb8mPA== + dependencies: + minipass "^2.2.1" + +mississippi@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/mississippi/-/mississippi-3.0.0.tgz#ea0a3291f97e0b5e8776b363d5f0a12d94c67022" + integrity sha512-x471SsVjUtBRtcvd4BzKE9kFC+/2TeWgKCgw0bZcw1b9l2X3QX5vCWgF+KaZaYm87Ss//rHnWryupDrgLvmSkA== + dependencies: + concat-stream "^1.5.0" + duplexify "^3.4.2" + end-of-stream "^1.1.0" + flush-write-stream "^1.0.0" + from2 "^2.1.0" + parallel-transform "^1.1.0" + pump "^3.0.0" + pumpify "^1.3.3" + stream-each "^1.1.0" + through2 "^2.0.0" + +mixin-deep@^1.2.0: + version "1.3.2" + resolved "https://registry.yarnpkg.com/mixin-deep/-/mixin-deep-1.3.2.tgz#1120b43dc359a785dce65b55b82e257ccf479566" + integrity sha512-WRoDn//mXBiJ1H40rqa3vH0toePwSsGb45iInWlTySa+Uu4k3tYUSxa2v1KqAiLtvlrSzaExqS1gtk96A9zvEA== + dependencies: + for-in "^1.0.2" + is-extendable "^1.0.1" + +mkdirp@0.3.0: + version "0.3.0" + resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-0.3.0.tgz#1bbf5ab1ba827af23575143490426455f481fe1e" + integrity sha1-G79asbqCevI1dRQ0kEJkVfSB/h4= + +mkdirp@0.5.x, mkdirp@^0.5.0, mkdirp@^0.5.1, mkdirp@~0.5.1: + version "0.5.1" + resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-0.5.1.tgz#30057438eac6cf7f8c4767f38648d6697d75c903" + integrity sha1-MAV0OOrGz3+MR2fzhkjWaX11yQM= + dependencies: + minimist "0.0.8" + +move-concurrently@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/move-concurrently/-/move-concurrently-1.0.1.tgz#be2c005fda32e0b29af1f05d7c4b33214c701f92" + integrity sha1-viwAX9oy4LKa8fBdfEszIUxwH5I= + dependencies: + aproba "^1.1.1" + copy-concurrently "^1.0.0" + fs-write-stream-atomic "^1.0.8" + mkdirp "^0.5.1" + rimraf "^2.5.4" + run-queue "^1.0.3" + +ms@2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/ms/-/ms-2.0.0.tgz#5608aeadfc00be6c2901df5f9861788de0d597c8" + integrity sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g= + +ms@2.1.1: + version "2.1.1" + resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.1.tgz#30a5864eb3ebb0a66f2ebe6d727af06a09d86e0a" + integrity sha512-tgp+dl5cGk28utYktBsrFqA7HKgrhgPsg6Z/EfhWI4gl1Hwq8B/GmY/0oXZ6nF8hDVesS/FpnYaD/kOWhYQvyg== + +ms@^2.1.1: + version "2.1.2" + resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.2.tgz#d09d1f357b443f493382a8eb3ccd183872ae6009" + integrity sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w== + +multicast-dns-service-types@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/multicast-dns-service-types/-/multicast-dns-service-types-1.1.0.tgz#899f11d9686e5e05cb91b35d5f0e63b773cfc901" + integrity sha1-iZ8R2WhuXgXLkbNdXw5jt3PPyQE= + +multicast-dns@^6.0.1: + version "6.2.3" + resolved "https://registry.yarnpkg.com/multicast-dns/-/multicast-dns-6.2.3.tgz#a0ec7bd9055c4282f790c3c82f4e28db3b31b229" + integrity sha512-ji6J5enbMyGRHIAkAOu3WdV8nggqviKCEKtXcOqfphZZtQrmHKycfynJ2V7eVPUA4NhJ6V7Wf4TmGbTwKE9B6g== + dependencies: + dns-packet "^1.3.1" + thunky "^1.0.2" + +nan@^2.12.1: + version "2.14.0" + resolved "https://registry.yarnpkg.com/nan/-/nan-2.14.0.tgz#7818f722027b2459a86f0295d434d1fc2336c52c" + integrity sha512-INOFj37C7k3AfaNTtX8RhsTw7qRy7eLET14cROi9+5HAVbbHuIWUHEauBv5qT4Av2tWasiTY1Jw6puUNqRJXQg== + +nanomatch@^1.2.9: + version "1.2.13" + resolved "https://registry.yarnpkg.com/nanomatch/-/nanomatch-1.2.13.tgz#b87a8aa4fc0de8fe6be88895b38983ff265bd119" + integrity sha512-fpoe2T0RbHwBTBUOftAfBPaDEi06ufaUai0mE6Yn1kacc3SnTErfb/h+X94VXzI64rKFHYImXSvdwGGCmwOqCA== + dependencies: + arr-diff "^4.0.0" + array-unique "^0.3.2" + define-property "^2.0.2" + extend-shallow "^3.0.2" + fragment-cache "^0.2.1" + is-windows "^1.0.2" + kind-of "^6.0.2" + object.pick "^1.3.0" + regex-not "^1.0.0" + snapdragon "^0.8.1" + to-regex "^3.0.1" + +needle@^2.2.1: + version "2.4.0" + resolved "https://registry.yarnpkg.com/needle/-/needle-2.4.0.tgz#6833e74975c444642590e15a750288c5f939b57c" + integrity sha512-4Hnwzr3mi5L97hMYeNl8wRW/Onhy4nUKR/lVemJ8gJedxxUyBLm9kkrDColJvoSfwi0jCNhD+xCdOtiGDQiRZg== + dependencies: + debug "^3.2.6" + iconv-lite "^0.4.4" + sax "^1.2.4" + +negotiator@0.6.2: + version "0.6.2" + resolved "https://registry.yarnpkg.com/negotiator/-/negotiator-0.6.2.tgz#feacf7ccf525a77ae9634436a64883ffeca346fb" + integrity sha512-hZXc7K2e+PgeI1eDBe/10Ard4ekbfrrqG8Ep+8Jmf4JID2bNg7NvCPOZN+kfF574pFQI7mum2AUqDidoKqcTOw== + +neo-async@^2.5.0, neo-async@^2.6.1: + version "2.6.1" + resolved "https://registry.yarnpkg.com/neo-async/-/neo-async-2.6.1.tgz#ac27ada66167fa8849a6addd837f6b189ad2081c" + integrity sha512-iyam8fBuCUpWeKPGpaNMetEocMt364qkCsfL9JuhjXX6dRnguRVOfk2GZaDpPjcOKiiXCPINZC1GczQ7iTq3Zw== + +nice-try@^1.0.4: + version "1.0.5" + resolved "https://registry.yarnpkg.com/nice-try/-/nice-try-1.0.5.tgz#a3378a7696ce7d223e88fc9b764bd7ef1089e366" + integrity sha512-1nh45deeb5olNY7eX82BkPO7SSxR5SSYJiPTrTdFUVYwAl8CKMA5N9PjTYkHiRjisVcxcQ1HXdLhx2qxxJzLNQ== + +no-case@^2.2.0: + version "2.3.2" + resolved "https://registry.yarnpkg.com/no-case/-/no-case-2.3.2.tgz#60b813396be39b3f1288a4c1ed5d1e7d28b464ac" + integrity sha512-rmTZ9kz+f3rCvK2TD1Ue/oZlns7OGoIWP4fc3llxxRXlOkHKoWPPWJOfFYpITabSow43QJbRIoHQXtt10VldyQ== + dependencies: + lower-case "^1.1.1" + +node-forge@0.7.5: + version "0.7.5" + resolved "https://registry.yarnpkg.com/node-forge/-/node-forge-0.7.5.tgz#6c152c345ce11c52f465c2abd957e8639cd674df" + integrity sha512-MmbQJ2MTESTjt3Gi/3yG1wGpIMhUfcIypUCGtTizFR9IiccFwxSpfp0vtIZlkFclEqERemxfnSdZEMR9VqqEFQ== + +node-libs-browser@^2.2.1: + version "2.2.1" + resolved "https://registry.yarnpkg.com/node-libs-browser/-/node-libs-browser-2.2.1.tgz#b64f513d18338625f90346d27b0d235e631f6425" + integrity sha512-h/zcD8H9kaDZ9ALUWwlBUDo6TKF8a7qBSCSEGfjTVIYeqsioSKaAX+BN7NgiMGp6iSIXZ3PxgCu8KS3b71YK5Q== + dependencies: + assert "^1.1.1" + browserify-zlib "^0.2.0" + buffer "^4.3.0" + console-browserify "^1.1.0" + constants-browserify "^1.0.0" + crypto-browserify "^3.11.0" + domain-browser "^1.1.1" + events "^3.0.0" + https-browserify "^1.0.0" + os-browserify "^0.3.0" + path-browserify "0.0.1" + process "^0.11.10" + punycode "^1.2.4" + querystring-es3 "^0.2.0" + readable-stream "^2.3.3" + stream-browserify "^2.0.1" + stream-http "^2.7.2" + string_decoder "^1.0.0" + timers-browserify "^2.0.4" + tty-browserify "0.0.0" + url "^0.11.0" + util "^0.11.0" + vm-browserify "^1.0.1" + +node-pre-gyp@^0.12.0: + version "0.12.0" + resolved "https://registry.yarnpkg.com/node-pre-gyp/-/node-pre-gyp-0.12.0.tgz#39ba4bb1439da030295f899e3b520b7785766149" + integrity sha512-4KghwV8vH5k+g2ylT+sLTjy5wmUOb9vPhnM8NHvRf9dHmnW/CndrFXy2aRPaPST6dugXSdHXfeaHQm77PIz/1A== + dependencies: + detect-libc "^1.0.2" + mkdirp "^0.5.1" + needle "^2.2.1" + nopt "^4.0.1" + npm-packlist "^1.1.6" + npmlog "^4.0.2" + rc "^1.2.7" + rimraf "^2.6.1" + semver "^5.3.0" + tar "^4" + +node-releases@^1.1.25: + version "1.1.27" + resolved "https://registry.yarnpkg.com/node-releases/-/node-releases-1.1.27.tgz#b19ec8add2afe9a826a99dceccc516104c1edaf4" + integrity sha512-9iXUqHKSGo6ph/tdXVbHFbhRVQln4ZDTIBJCzsa90HimnBYc5jw8RWYt4wBYFHehGyC3koIz5O4mb2fHrbPOuA== + dependencies: + semver "^5.3.0" + +nopt@1.0.10: + version "1.0.10" + resolved "https://registry.yarnpkg.com/nopt/-/nopt-1.0.10.tgz#6ddd21bd2a31417b92727dd585f8a6f37608ebee" + integrity sha1-bd0hvSoxQXuScn3Vhfim83YI6+4= + dependencies: + abbrev "1" + +nopt@^4.0.1: + version "4.0.1" + resolved "https://registry.yarnpkg.com/nopt/-/nopt-4.0.1.tgz#d0d4685afd5415193c8c7505602d0d17cd64474d" + integrity sha1-0NRoWv1UFRk8jHUFYC0NF81kR00= + dependencies: + abbrev "1" + osenv "^0.1.4" + +normalize-path@^2.1.1: + version "2.1.1" + resolved "https://registry.yarnpkg.com/normalize-path/-/normalize-path-2.1.1.tgz#1ab28b556e198363a8c1a6f7e6fa20137fe6aed9" + integrity sha1-GrKLVW4Zg2Oowab35vogE3/mrtk= + dependencies: + remove-trailing-separator "^1.0.1" + +normalize-path@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/normalize-path/-/normalize-path-3.0.0.tgz#0dcd69ff23a1c9b11fd0978316644a0388216a65" + integrity sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA== + +normalize-range@^0.1.2: + version "0.1.2" + resolved "https://registry.yarnpkg.com/normalize-range/-/normalize-range-0.1.2.tgz#2d10c06bdfd312ea9777695a4d28439456b75942" + integrity sha1-LRDAa9/TEuqXd2laTShDlFa3WUI= + +normalize-url@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/normalize-url/-/normalize-url-2.0.1.tgz#835a9da1551fa26f70e92329069a23aa6574d7e6" + integrity sha512-D6MUW4K/VzoJ4rJ01JFKxDrtY1v9wrgzCX5f2qj/lzH1m/lW6MhUZFKerVsnyjOhOsYzI9Kqqak+10l4LvLpMw== + dependencies: + prepend-http "^2.0.0" + query-string "^5.0.1" + sort-keys "^2.0.0" + +normalize-url@^3.0.0: + version "3.3.0" + resolved "https://registry.yarnpkg.com/normalize-url/-/normalize-url-3.3.0.tgz#b2e1c4dc4f7c6d57743df733a4f5978d18650559" + integrity sha512-U+JJi7duF1o+u2pynbp2zXDW2/PADgC30f0GsHZtRh+HOcXHnw137TrNlyxxRvWW5fjKd3bcLHPxofWuCjaeZg== + +npm-bundled@^1.0.1: + version "1.0.6" + resolved "https://registry.yarnpkg.com/npm-bundled/-/npm-bundled-1.0.6.tgz#e7ba9aadcef962bb61248f91721cd932b3fe6bdd" + integrity sha512-8/JCaftHwbd//k6y2rEWp6k1wxVfpFzB6t1p825+cUb7Ym2XQfhwIC5KwhrvzZRJu+LtDE585zVaS32+CGtf0g== + +npm-packlist@^1.1.6: + version "1.4.4" + resolved "https://registry.yarnpkg.com/npm-packlist/-/npm-packlist-1.4.4.tgz#866224233850ac534b63d1a6e76050092b5d2f44" + integrity sha512-zTLo8UcVYtDU3gdeaFu2Xu0n0EvelfHDGuqtNIn5RO7yQj4H1TqNdBc/yZjxnWA0PVB8D3Woyp0i5B43JwQ6Vw== + dependencies: + ignore-walk "^3.0.1" + npm-bundled "^1.0.1" + +npm-run-path@^2.0.0: + version "2.0.2" + resolved "https://registry.yarnpkg.com/npm-run-path/-/npm-run-path-2.0.2.tgz#35a9232dfa35d7067b4cb2ddf2357b1871536c5f" + integrity sha1-NakjLfo11wZ7TLLd8jV7GHFTbF8= + dependencies: + path-key "^2.0.0" + +npmlog@^4.0.2: + version "4.1.2" + resolved "https://registry.yarnpkg.com/npmlog/-/npmlog-4.1.2.tgz#08a7f2a8bf734604779a9efa4ad5cc717abb954b" + integrity sha512-2uUqazuKlTaSI/dC8AzicUck7+IrEaOnN/e0jd3Xtt1KcGpwx30v50mL7oPyr/h9bL3E4aZccVwpwP+5W9Vjkg== + dependencies: + are-we-there-yet "~1.1.2" + console-control-strings "~1.1.0" + gauge "~2.7.3" + set-blocking "~2.0.0" + +nprogress@^0.2.0: + version "0.2.0" + resolved "https://registry.yarnpkg.com/nprogress/-/nprogress-0.2.0.tgz#cb8f34c53213d895723fcbab907e9422adbcafb1" + integrity sha1-y480xTIT2JVyP8urkH6UIq28r7E= + +nth-check@^1.0.2, nth-check@~1.0.1: + version "1.0.2" + resolved "https://registry.yarnpkg.com/nth-check/-/nth-check-1.0.2.tgz#b2bd295c37e3dd58a3bf0700376663ba4d9cf05c" + integrity sha512-WeBOdju8SnzPN5vTUJYxYUxLeXpCaVP5i5e0LF8fg7WORF2Wd7wFX/pk0tYZk7s8T+J7VLy0Da6J1+wCT0AtHg== + dependencies: + boolbase "~1.0.0" + +num2fraction@^1.2.2: + version "1.2.2" + resolved "https://registry.yarnpkg.com/num2fraction/-/num2fraction-1.2.2.tgz#6f682b6a027a4e9ddfa4564cd2589d1d4e669ede" + integrity sha1-b2gragJ6Tp3fpFZM0lidHU5mnt4= + +number-is-nan@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/number-is-nan/-/number-is-nan-1.0.1.tgz#097b602b53422a522c1afb8790318336941a011d" + integrity sha1-CXtgK1NCKlIsGvuHkDGDNpQaAR0= + +oauth-sign@~0.9.0: + version "0.9.0" + resolved "https://registry.yarnpkg.com/oauth-sign/-/oauth-sign-0.9.0.tgz#47a7b016baa68b5fa0ecf3dee08a85c679ac6455" + integrity sha512-fexhUFFPTGV8ybAtSIGbV6gOkSv8UtRbDBnAyLQw4QPKkgNlsH2ByPGtMUqdWkos6YCRmAqViwgZrJc/mRDzZQ== + +object-assign@^4.0.1, object-assign@^4.1.0, object-assign@^4.1.1: + version "4.1.1" + resolved "https://registry.yarnpkg.com/object-assign/-/object-assign-4.1.1.tgz#2109adc7965887cfc05cbbd442cac8bfbb360863" + integrity sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM= + +object-copy@^0.1.0: + version "0.1.0" + resolved "https://registry.yarnpkg.com/object-copy/-/object-copy-0.1.0.tgz#7e7d858b781bd7c991a41ba975ed3812754e998c" + integrity sha1-fn2Fi3gb18mRpBupde04EnVOmYw= + dependencies: + copy-descriptor "^0.1.0" + define-property "^0.2.5" + kind-of "^3.0.3" + +object-keys@^1.0.11, object-keys@^1.0.12, object-keys@^1.1.0: + version "1.1.1" + resolved "https://registry.yarnpkg.com/object-keys/-/object-keys-1.1.1.tgz#1c47f272df277f3b1daf061677d9c82e2322c60e" + integrity sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA== + +object-visit@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/object-visit/-/object-visit-1.0.1.tgz#f79c4493af0c5377b59fe39d395e41042dd045bb" + integrity sha1-95xEk68MU3e1n+OdOV5BBC3QRbs= + dependencies: + isobject "^3.0.0" + +object.assign@^4.1.0: + version "4.1.0" + resolved "https://registry.yarnpkg.com/object.assign/-/object.assign-4.1.0.tgz#968bf1100d7956bb3ca086f006f846b3bc4008da" + integrity sha512-exHJeq6kBKj58mqGyTQ9DFvrZC/eR6OwxzoM9YRoGBqrXYonaFyGiFMuc9VZrXf7DarreEwMpurG3dd+CNyW5w== + dependencies: + define-properties "^1.1.2" + function-bind "^1.1.1" + has-symbols "^1.0.0" + object-keys "^1.0.11" + +object.getownpropertydescriptors@^2.0.3: + version "2.0.3" + resolved "https://registry.yarnpkg.com/object.getownpropertydescriptors/-/object.getownpropertydescriptors-2.0.3.tgz#8758c846f5b407adab0f236e0986f14b051caa16" + integrity sha1-h1jIRvW0B62rDyNuCYbxSwUcqhY= + dependencies: + define-properties "^1.1.2" + es-abstract "^1.5.1" + +object.pick@^1.3.0: + version "1.3.0" + resolved "https://registry.yarnpkg.com/object.pick/-/object.pick-1.3.0.tgz#87a10ac4c1694bd2e1cbf53591a66141fb5dd747" + integrity sha1-h6EKxMFpS9Lhy/U1kaZhQftd10c= + dependencies: + isobject "^3.0.1" + +object.values@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/object.values/-/object.values-1.1.0.tgz#bf6810ef5da3e5325790eaaa2be213ea84624da9" + integrity sha512-8mf0nKLAoFX6VlNVdhGj31SVYpaNFtUnuoOXWyFEstsWRgU837AK+JYM0iAxwkSzGRbwn8cbFmgbyxj1j4VbXg== + dependencies: + define-properties "^1.1.3" + es-abstract "^1.12.0" + function-bind "^1.1.1" + has "^1.0.3" + +obuf@^1.0.0, obuf@^1.1.2: + version "1.1.2" + resolved "https://registry.yarnpkg.com/obuf/-/obuf-1.1.2.tgz#09bea3343d41859ebd446292d11c9d4db619084e" + integrity sha512-PX1wu0AmAdPqOL1mWhqmlOd8kOIZQwGZw6rh7uby9fTc5lhaOWFLX3I6R1hrF9k3zUY40e6igsLGkDXK92LJNg== + +on-finished@~2.3.0: + version "2.3.0" + resolved "https://registry.yarnpkg.com/on-finished/-/on-finished-2.3.0.tgz#20f1336481b083cd75337992a16971aa2d906947" + integrity sha1-IPEzZIGwg811M3mSoWlxqi2QaUc= + dependencies: + ee-first "1.1.1" + +on-headers@~1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/on-headers/-/on-headers-1.0.2.tgz#772b0ae6aaa525c399e489adfad90c403eb3c28f" + integrity sha512-pZAE+FJLoyITytdqK0U5s+FIpjN0JP3OzFi/u8Rx+EV5/W+JTWGXG8xFzevE7AjBfDqHv/8vL8qQsIhHnqRkrA== + +once@^1.3.0, once@^1.3.1, once@^1.4.0: + version "1.4.0" + resolved "https://registry.yarnpkg.com/once/-/once-1.4.0.tgz#583b1aa775961d4b113ac17d9c50baef9dd76bd1" + integrity sha1-WDsap3WWHUsROsF9nFC6753Xa9E= + dependencies: + wrappy "1" + +opencollective-postinstall@^2.0.2: + version "2.0.2" + resolved "https://registry.yarnpkg.com/opencollective-postinstall/-/opencollective-postinstall-2.0.2.tgz#5657f1bede69b6e33a45939b061eb53d3c6c3a89" + integrity sha512-pVOEP16TrAO2/fjej1IdOyupJY8KDUM1CvsaScRbw6oddvpQoOfGk4ywha0HKKVAD6RkW4x6Q+tNBwhf3Bgpuw== + +opn@^5.5.0: + version "5.5.0" + resolved "https://registry.yarnpkg.com/opn/-/opn-5.5.0.tgz#fc7164fab56d235904c51c3b27da6758ca3b9bfc" + integrity sha512-PqHpggC9bLV0VeWcdKhkpxY+3JTzetLSqTCWL/z/tFIbI6G8JCjondXklT1JinczLz2Xib62sSp0T/gKT4KksA== + dependencies: + is-wsl "^1.1.0" + +optimize-css-assets-webpack-plugin@^5.0.1: + version "5.0.3" + resolved "https://registry.yarnpkg.com/optimize-css-assets-webpack-plugin/-/optimize-css-assets-webpack-plugin-5.0.3.tgz#e2f1d4d94ad8c0af8967ebd7cf138dcb1ef14572" + integrity sha512-q9fbvCRS6EYtUKKSwI87qm2IxlyJK5b4dygW1rKUBT6mMDhdG5e5bZT63v6tnJR9F9FB/H5a0HTmtw+laUBxKA== + dependencies: + cssnano "^4.1.10" + last-call-webpack-plugin "^3.0.0" + +original@^1.0.0: + version "1.0.2" + resolved "https://registry.yarnpkg.com/original/-/original-1.0.2.tgz#e442a61cffe1c5fd20a65f3261c26663b303f25f" + integrity sha512-hyBVl6iqqUOJ8FqRe+l/gS8H+kKYjrEndd5Pm1MfBtsEKA038HkkdbAl/72EAXGyonD/PFsvmVG+EvcIpliMBg== + dependencies: + url-parse "^1.4.3" + +os-browserify@^0.3.0: + version "0.3.0" + resolved "https://registry.yarnpkg.com/os-browserify/-/os-browserify-0.3.0.tgz#854373c7f5c2315914fc9bfc6bd8238fdda1ec27" + integrity sha1-hUNzx/XCMVkU/Jv8a9gjj92h7Cc= + +os-homedir@^1.0.0: + version "1.0.2" + resolved "https://registry.yarnpkg.com/os-homedir/-/os-homedir-1.0.2.tgz#ffbc4988336e0e833de0c168c7ef152121aa7fb3" + integrity sha1-/7xJiDNuDoM94MFox+8VISGqf7M= + +os-locale@^3.0.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/os-locale/-/os-locale-3.1.0.tgz#a802a6ee17f24c10483ab9935719cef4ed16bf1a" + integrity sha512-Z8l3R4wYWM40/52Z+S265okfFj8Kt2cC2MKY+xNi3kFs+XGI7WXu/I309QQQYbRW4ijiZ+yxs9pqEhJh0DqW3Q== + dependencies: + execa "^1.0.0" + lcid "^2.0.0" + mem "^4.0.0" + +os-tmpdir@^1.0.0: + version "1.0.2" + resolved "https://registry.yarnpkg.com/os-tmpdir/-/os-tmpdir-1.0.2.tgz#bbe67406c79aa85c5cfec766fe5734555dfa1274" + integrity sha1-u+Z0BseaqFxc/sdm/lc0VV36EnQ= + +osenv@^0.1.4: + version "0.1.5" + resolved "https://registry.yarnpkg.com/osenv/-/osenv-0.1.5.tgz#85cdfafaeb28e8677f416e287592b5f3f49ea410" + integrity sha512-0CWcCECdMVc2Rw3U5w9ZjqX6ga6ubk1xDVKxtBQPK7wis/0F2r9T6k4ydGYhecl7YUBxBVxhL5oisPsNxAPe2g== + dependencies: + os-homedir "^1.0.0" + os-tmpdir "^1.0.0" + +p-defer@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/p-defer/-/p-defer-1.0.0.tgz#9f6eb182f6c9aa8cd743004a7d4f96b196b0fb0c" + integrity sha1-n26xgvbJqozXQwBKfU+WsZaw+ww= + +p-finally@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/p-finally/-/p-finally-1.0.0.tgz#3fbcfb15b899a44123b34b6dcc18b724336a2cae" + integrity sha1-P7z7FbiZpEEjs0ttzBi3JDNqLK4= + +p-is-promise@^2.0.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/p-is-promise/-/p-is-promise-2.1.0.tgz#918cebaea248a62cf7ffab8e3bca8c5f882fc42e" + integrity sha512-Y3W0wlRPK8ZMRbNq97l4M5otioeA5lm1z7bkNkxCka8HSPjR0xRWmpCmc9utiaLP9Jb1eD8BgeIxTW4AIF45Pg== + +p-limit@^1.1.0: + version "1.3.0" + resolved "https://registry.yarnpkg.com/p-limit/-/p-limit-1.3.0.tgz#b86bd5f0c25690911c7590fcbfc2010d54b3ccb8" + integrity sha512-vvcXsLAJ9Dr5rQOPk7toZQZJApBl2K4J6dANSsEuh6QI41JYcsS/qhTGa9ErIUUgK3WNQoJYvylxvjqmiqEA9Q== + dependencies: + p-try "^1.0.0" + +p-limit@^2.0.0, p-limit@^2.2.0: + version "2.2.0" + resolved "https://registry.yarnpkg.com/p-limit/-/p-limit-2.2.0.tgz#417c9941e6027a9abcba5092dd2904e255b5fbc2" + integrity sha512-pZbTJpoUsCzV48Mc9Nh51VbwO0X9cuPFE8gYwx9BTCt9SF8/b7Zljd2fVgOxhIF/HDTKgpVzs+GPhyKfjLLFRQ== + dependencies: + p-try "^2.0.0" + +p-locate@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/p-locate/-/p-locate-2.0.0.tgz#20a0103b222a70c8fd39cc2e580680f3dde5ec43" + integrity sha1-IKAQOyIqcMj9OcwuWAaA893l7EM= + dependencies: + p-limit "^1.1.0" + +p-locate@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/p-locate/-/p-locate-3.0.0.tgz#322d69a05c0264b25997d9f40cd8a891ab0064a4" + integrity sha512-x+12w/To+4GFfgJhBEpiDcLozRJGegY+Ei7/z0tSLkMmxGZNybVMSfWj9aJn8Z5Fc7dBUNJOOVgPv2H7IwulSQ== + dependencies: + p-limit "^2.0.0" + +p-map@^2.0.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/p-map/-/p-map-2.1.0.tgz#310928feef9c9ecc65b68b17693018a665cea175" + integrity sha512-y3b8Kpd8OAN444hxfBbFfj1FY/RjtTd8tzYwhUqNYXx0fXx2iX4maP4Qr6qhIKbQXI02wTLAda4fYUbDagTUFw== + +p-retry@^3.0.1: + version "3.0.1" + resolved "https://registry.yarnpkg.com/p-retry/-/p-retry-3.0.1.tgz#316b4c8893e2c8dc1cfa891f406c4b422bebf328" + integrity sha512-XE6G4+YTTkT2a0UWb2kjZe8xNwf8bIbnqpc/IS/idOBVhyves0mK5OJgeocjx7q5pvX/6m23xuzVPYT1uGM73w== + dependencies: + retry "^0.12.0" + +p-try@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/p-try/-/p-try-1.0.0.tgz#cbc79cdbaf8fd4228e13f621f2b1a237c1b207b3" + integrity sha1-y8ec26+P1CKOE/Yh8rGiN8GyB7M= + +p-try@^2.0.0: + version "2.2.0" + resolved "https://registry.yarnpkg.com/p-try/-/p-try-2.2.0.tgz#cb2868540e313d61de58fafbe35ce9004d5540e6" + integrity sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ== + +pako@~1.0.5: + version "1.0.10" + resolved "https://registry.yarnpkg.com/pako/-/pako-1.0.10.tgz#4328badb5086a426aa90f541977d4955da5c9732" + integrity sha512-0DTvPVU3ed8+HNXOu5Bs+o//Mbdj9VNQMUOe9oKCwh8l0GNwpTDMKCWbRjgtD291AWnkAgkqA/LOnQS8AmS1tw== + +parallel-transform@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/parallel-transform/-/parallel-transform-1.1.0.tgz#d410f065b05da23081fcd10f28854c29bda33b06" + integrity sha1-1BDwZbBdojCB/NEPKIVMKb2jOwY= + dependencies: + cyclist "~0.2.2" + inherits "^2.0.3" + readable-stream "^2.1.5" + +param-case@2.1.x: + version "2.1.1" + resolved "https://registry.yarnpkg.com/param-case/-/param-case-2.1.1.tgz#df94fd8cf6531ecf75e6bef9a0858fbc72be2247" + integrity sha1-35T9jPZTHs915r75oIWPvHK+Ikc= + dependencies: + no-case "^2.2.0" + +parse-asn1@^5.0.0: + version "5.1.4" + resolved "https://registry.yarnpkg.com/parse-asn1/-/parse-asn1-5.1.4.tgz#37f6628f823fbdeb2273b4d540434a22f3ef1fcc" + integrity sha512-Qs5duJcuvNExRfFZ99HDD3z4mAi3r9Wl/FOjEOijlxwCZs7E7mW2vjTpgQ4J8LpTF8x5v+1Vn5UQFejmWT11aw== + dependencies: + asn1.js "^4.0.0" + browserify-aes "^1.0.0" + create-hash "^1.1.0" + evp_bytestokey "^1.0.0" + pbkdf2 "^3.0.3" + safe-buffer "^5.1.1" + +parse-json@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/parse-json/-/parse-json-4.0.0.tgz#be35f5425be1f7f6c747184f98a788cb99477ee0" + integrity sha1-vjX1Qlvh9/bHRxhPmKeIy5lHfuA= + dependencies: + error-ex "^1.3.1" + json-parse-better-errors "^1.0.1" + +parseurl@~1.3.2, parseurl@~1.3.3: + version "1.3.3" + resolved "https://registry.yarnpkg.com/parseurl/-/parseurl-1.3.3.tgz#9da19e7bee8d12dff0513ed5b76957793bc2e8d4" + integrity sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ== + +pascalcase@^0.1.1: + version "0.1.1" + resolved "https://registry.yarnpkg.com/pascalcase/-/pascalcase-0.1.1.tgz#b363e55e8006ca6fe21784d2db22bd15d7917f14" + integrity sha1-s2PlXoAGym/iF4TS2yK9FdeRfxQ= + +path-browserify@0.0.1: + version "0.0.1" + resolved "https://registry.yarnpkg.com/path-browserify/-/path-browserify-0.0.1.tgz#e6c4ddd7ed3aa27c68a20cc4e50e1a4ee83bbc4a" + integrity sha512-BapA40NHICOS+USX9SN4tyhq+A2RrN/Ws5F0Z5aMHDp98Fl86lX8Oti8B7uN93L4Ifv4fHOEA+pQw87gmMO/lQ== + +path-dirname@^1.0.0: + version "1.0.2" + resolved "https://registry.yarnpkg.com/path-dirname/-/path-dirname-1.0.2.tgz#cc33d24d525e099a5388c0336c6e32b9160609e0" + integrity sha1-zDPSTVJeCZpTiMAzbG4yuRYGCeA= + +path-exists@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/path-exists/-/path-exists-3.0.0.tgz#ce0ebeaa5f78cb18925ea7d810d7b59b010fd515" + integrity sha1-zg6+ql94yxiSXqfYENe1mwEP1RU= + +path-is-absolute@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/path-is-absolute/-/path-is-absolute-1.0.1.tgz#174b9268735534ffbc7ace6bf53a5a9e1b5c5f5f" + integrity sha1-F0uSaHNVNP+8es5r9TpanhtcX18= + +path-is-inside@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/path-is-inside/-/path-is-inside-1.0.2.tgz#365417dede44430d1c11af61027facf074bdfc53" + integrity sha1-NlQX3t5EQw0cEa9hAn+s8HS9/FM= + +path-key@^2.0.0, path-key@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/path-key/-/path-key-2.0.1.tgz#411cadb574c5a140d3a4b1910d40d80cc9f40b40" + integrity sha1-QRyttXTFoUDTpLGRDUDYDMn0C0A= + +path-parse@^1.0.6: + version "1.0.6" + resolved "https://registry.yarnpkg.com/path-parse/-/path-parse-1.0.6.tgz#d62dbb5679405d72c4737ec58600e9ddcf06d24c" + integrity sha512-GSmOT2EbHrINBf9SR7CDELwlJ8AENk3Qn7OikK4nFYAu3Ote2+JYNVvkpAEQm3/TLNEJFD/xZJjzyxg3KBWOzw== + +path-to-regexp@0.1.7: + version "0.1.7" + resolved "https://registry.yarnpkg.com/path-to-regexp/-/path-to-regexp-0.1.7.tgz#df604178005f522f15eb4490e7247a1bfaa67f8c" + integrity sha1-32BBeABfUi8V60SQ5yR6G/qmf4w= + +path-type@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/path-type/-/path-type-3.0.0.tgz#cef31dc8e0a1a3bb0d105c0cd97cf3bf47f4e36f" + integrity sha512-T2ZUsdZFHgA3u4e5PfPbjd7HDDpxPnQb5jN0SrDsjNSuVXHJqtwTnWqG0B1jZrgmJ/7lj1EmVIByWt1gxGkWvg== + dependencies: + pify "^3.0.0" + +pbkdf2@^3.0.3: + version "3.0.17" + resolved "https://registry.yarnpkg.com/pbkdf2/-/pbkdf2-3.0.17.tgz#976c206530617b14ebb32114239f7b09336e93a6" + integrity sha512-U/il5MsrZp7mGg3mSQfn742na2T+1/vHDCG5/iTI3X9MKUuYUZVLQhyRsg06mCgDBTd57TxzgZt7P+fYfjRLtA== + dependencies: + create-hash "^1.1.2" + create-hmac "^1.1.4" + ripemd160 "^2.0.1" + safe-buffer "^5.0.1" + sha.js "^2.4.8" + +performance-now@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/performance-now/-/performance-now-2.1.0.tgz#6309f4e0e5fa913ec1c69307ae364b4b377c9e7b" + integrity sha1-Ywn04OX6kT7BxpMHrjZLSzd8nns= + +pify@^2.0.0: + version "2.3.0" + resolved "https://registry.yarnpkg.com/pify/-/pify-2.3.0.tgz#ed141a6ac043a849ea588498e7dca8b15330e90c" + integrity sha1-7RQaasBDqEnqWISY59yosVMw6Qw= + +pify@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/pify/-/pify-3.0.0.tgz#e5a4acd2c101fdf3d9a4d07f0dbc4db49dd28176" + integrity sha1-5aSs0sEB/fPZpNB/DbxNtJ3SgXY= + +pify@^4.0.1: + version "4.0.1" + resolved "https://registry.yarnpkg.com/pify/-/pify-4.0.1.tgz#4b2cd25c50d598735c50292224fd8c6df41e3231" + integrity sha512-uB80kBFb/tfd68bVleG9T5GGsGPjJrLAUpR5PZIrhBnIaRTQRjqdJSsIKkOP6OAIFbj7GOrcudc5pNjZ+geV2g== + +pinkie-promise@^2.0.0: + version "2.0.1" + resolved "https://registry.yarnpkg.com/pinkie-promise/-/pinkie-promise-2.0.1.tgz#2135d6dfa7a358c069ac9b178776288228450ffa" + integrity sha1-ITXW36ejWMBprJsXh3YogihFD/o= + dependencies: + pinkie "^2.0.0" + +pinkie@^2.0.0: + version "2.0.4" + resolved "https://registry.yarnpkg.com/pinkie/-/pinkie-2.0.4.tgz#72556b80cfa0d48a974e80e77248e80ed4f7f870" + integrity sha1-clVrgM+g1IqXToDnckjoDtT3+HA= + +pkg-dir@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/pkg-dir/-/pkg-dir-3.0.0.tgz#2749020f239ed990881b1f71210d51eb6523bea3" + integrity sha512-/E57AYkoeQ25qkxMj5PBOVgF8Kiu/h7cYS30Z5+R7WaiCCBfLq58ZI/dSeaEKb9WVJV5n/03QwrN3IeWIFllvw== + dependencies: + find-up "^3.0.0" + +pkg-up@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/pkg-up/-/pkg-up-2.0.0.tgz#c819ac728059a461cab1c3889a2be3c49a004d7f" + integrity sha1-yBmscoBZpGHKscOImivjxJoATX8= + dependencies: + find-up "^2.1.0" + +portfinder@^1.0.13, portfinder@^1.0.21: + version "1.0.21" + resolved "https://registry.yarnpkg.com/portfinder/-/portfinder-1.0.21.tgz#60e1397b95ac170749db70034ece306b9a27e324" + integrity sha512-ESabpDCzmBS3ekHbmpAIiESq3udRsCBGiBZLsC+HgBKv2ezb0R4oG+7RnYEVZ/ZCfhel5Tx3UzdNWA0Lox2QCA== + dependencies: + async "^1.5.2" + debug "^2.2.0" + mkdirp "0.5.x" + +posix-character-classes@^0.1.0: + version "0.1.1" + resolved "https://registry.yarnpkg.com/posix-character-classes/-/posix-character-classes-0.1.1.tgz#01eac0fe3b5af71a2a6c02feabb8c1fef7e00eab" + integrity sha1-AerA/jta9xoqbAL+q7jB/vfgDqs= + +postcss-calc@^7.0.1: + version "7.0.1" + resolved "https://registry.yarnpkg.com/postcss-calc/-/postcss-calc-7.0.1.tgz#36d77bab023b0ecbb9789d84dcb23c4941145436" + integrity sha512-oXqx0m6tb4N3JGdmeMSc/i91KppbYsFZKdH0xMOqK8V1rJlzrKlTdokz8ozUXLVejydRN6u2IddxpcijRj2FqQ== + dependencies: + css-unit-converter "^1.1.1" + postcss "^7.0.5" + postcss-selector-parser "^5.0.0-rc.4" + postcss-value-parser "^3.3.1" + +postcss-colormin@^4.0.3: + version "4.0.3" + resolved "https://registry.yarnpkg.com/postcss-colormin/-/postcss-colormin-4.0.3.tgz#ae060bce93ed794ac71264f08132d550956bd381" + integrity sha512-WyQFAdDZpExQh32j0U0feWisZ0dmOtPl44qYmJKkq9xFWY3p+4qnRzCHeNrkeRhwPHz9bQ3mo0/yVkaply0MNw== + dependencies: + browserslist "^4.0.0" + color "^3.0.0" + has "^1.0.0" + postcss "^7.0.0" + postcss-value-parser "^3.0.0" + +postcss-convert-values@^4.0.1: + version "4.0.1" + resolved "https://registry.yarnpkg.com/postcss-convert-values/-/postcss-convert-values-4.0.1.tgz#ca3813ed4da0f812f9d43703584e449ebe189a7f" + integrity sha512-Kisdo1y77KUC0Jmn0OXU/COOJbzM8cImvw1ZFsBgBgMgb1iL23Zs/LXRe3r+EZqM3vGYKdQ2YJVQ5VkJI+zEJQ== + dependencies: + postcss "^7.0.0" + postcss-value-parser "^3.0.0" + +postcss-discard-comments@^4.0.2: + version "4.0.2" + resolved "https://registry.yarnpkg.com/postcss-discard-comments/-/postcss-discard-comments-4.0.2.tgz#1fbabd2c246bff6aaad7997b2b0918f4d7af4033" + integrity sha512-RJutN259iuRf3IW7GZyLM5Sw4GLTOH8FmsXBnv8Ab/Tc2k4SR4qbV4DNbyyY4+Sjo362SyDmW2DQ7lBSChrpkg== + dependencies: + postcss "^7.0.0" + +postcss-discard-duplicates@^4.0.2: + version "4.0.2" + resolved "https://registry.yarnpkg.com/postcss-discard-duplicates/-/postcss-discard-duplicates-4.0.2.tgz#3fe133cd3c82282e550fc9b239176a9207b784eb" + integrity sha512-ZNQfR1gPNAiXZhgENFfEglF93pciw0WxMkJeVmw8eF+JZBbMD7jp6C67GqJAXVZP2BWbOztKfbsdmMp/k8c6oQ== + dependencies: + postcss "^7.0.0" + +postcss-discard-empty@^4.0.1: + version "4.0.1" + resolved "https://registry.yarnpkg.com/postcss-discard-empty/-/postcss-discard-empty-4.0.1.tgz#c8c951e9f73ed9428019458444a02ad90bb9f765" + integrity sha512-B9miTzbznhDjTfjvipfHoqbWKwd0Mj+/fL5s1QOz06wufguil+Xheo4XpOnc4NqKYBCNqqEzgPv2aPBIJLox0w== + dependencies: + postcss "^7.0.0" + +postcss-discard-overridden@^4.0.1: + version "4.0.1" + resolved "https://registry.yarnpkg.com/postcss-discard-overridden/-/postcss-discard-overridden-4.0.1.tgz#652aef8a96726f029f5e3e00146ee7a4e755ff57" + integrity sha512-IYY2bEDD7g1XM1IDEsUT4//iEYCxAmP5oDSFMVU/JVvT7gh+l4fmjciLqGgwjdWpQIdb0Che2VX00QObS5+cTg== + dependencies: + postcss "^7.0.0" + +postcss-load-config@^2.0.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/postcss-load-config/-/postcss-load-config-2.1.0.tgz#c84d692b7bb7b41ddced94ee62e8ab31b417b003" + integrity sha512-4pV3JJVPLd5+RueiVVB+gFOAa7GWc25XQcMp86Zexzke69mKf6Nx9LRcQywdz7yZI9n1udOxmLuAwTBypypF8Q== + dependencies: + cosmiconfig "^5.0.0" + import-cwd "^2.0.0" + +postcss-loader@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/postcss-loader/-/postcss-loader-3.0.0.tgz#6b97943e47c72d845fa9e03f273773d4e8dd6c2d" + integrity sha512-cLWoDEY5OwHcAjDnkyRQzAXfs2jrKjXpO/HQFcc5b5u/r7aa471wdmChmwfnv7x2u840iat/wi0lQ5nbRgSkUA== + dependencies: + loader-utils "^1.1.0" + postcss "^7.0.0" + postcss-load-config "^2.0.0" + schema-utils "^1.0.0" + +postcss-merge-longhand@^4.0.11: + version "4.0.11" + resolved "https://registry.yarnpkg.com/postcss-merge-longhand/-/postcss-merge-longhand-4.0.11.tgz#62f49a13e4a0ee04e7b98f42bb16062ca2549e24" + integrity sha512-alx/zmoeXvJjp7L4mxEMjh8lxVlDFX1gqWHzaaQewwMZiVhLo42TEClKaeHbRf6J7j82ZOdTJ808RtN0ZOZwvw== + dependencies: + css-color-names "0.0.4" + postcss "^7.0.0" + postcss-value-parser "^3.0.0" + stylehacks "^4.0.0" + +postcss-merge-rules@^4.0.3: + version "4.0.3" + resolved "https://registry.yarnpkg.com/postcss-merge-rules/-/postcss-merge-rules-4.0.3.tgz#362bea4ff5a1f98e4075a713c6cb25aefef9a650" + integrity sha512-U7e3r1SbvYzO0Jr3UT/zKBVgYYyhAz0aitvGIYOYK5CPmkNih+WDSsS5tvPrJ8YMQYlEMvsZIiqmn7HdFUaeEQ== + dependencies: + browserslist "^4.0.0" + caniuse-api "^3.0.0" + cssnano-util-same-parent "^4.0.0" + postcss "^7.0.0" + postcss-selector-parser "^3.0.0" + vendors "^1.0.0" + +postcss-minify-font-values@^4.0.2: + version "4.0.2" + resolved "https://registry.yarnpkg.com/postcss-minify-font-values/-/postcss-minify-font-values-4.0.2.tgz#cd4c344cce474343fac5d82206ab2cbcb8afd5a6" + integrity sha512-j85oO6OnRU9zPf04+PZv1LYIYOprWm6IA6zkXkrJXyRveDEuQggG6tvoy8ir8ZwjLxLuGfNkCZEQG7zan+Hbtg== + dependencies: + postcss "^7.0.0" + postcss-value-parser "^3.0.0" + +postcss-minify-gradients@^4.0.2: + version "4.0.2" + resolved "https://registry.yarnpkg.com/postcss-minify-gradients/-/postcss-minify-gradients-4.0.2.tgz#93b29c2ff5099c535eecda56c4aa6e665a663471" + integrity sha512-qKPfwlONdcf/AndP1U8SJ/uzIJtowHlMaSioKzebAXSG4iJthlWC9iSWznQcX4f66gIWX44RSA841HTHj3wK+Q== + dependencies: + cssnano-util-get-arguments "^4.0.0" + is-color-stop "^1.0.0" + postcss "^7.0.0" + postcss-value-parser "^3.0.0" + +postcss-minify-params@^4.0.2: + version "4.0.2" + resolved "https://registry.yarnpkg.com/postcss-minify-params/-/postcss-minify-params-4.0.2.tgz#6b9cef030c11e35261f95f618c90036d680db874" + integrity sha512-G7eWyzEx0xL4/wiBBJxJOz48zAKV2WG3iZOqVhPet/9geefm/Px5uo1fzlHu+DOjT+m0Mmiz3jkQzVHe6wxAWg== + dependencies: + alphanum-sort "^1.0.0" + browserslist "^4.0.0" + cssnano-util-get-arguments "^4.0.0" + postcss "^7.0.0" + postcss-value-parser "^3.0.0" + uniqs "^2.0.0" + +postcss-minify-selectors@^4.0.2: + version "4.0.2" + resolved "https://registry.yarnpkg.com/postcss-minify-selectors/-/postcss-minify-selectors-4.0.2.tgz#e2e5eb40bfee500d0cd9243500f5f8ea4262fbd8" + integrity sha512-D5S1iViljXBj9kflQo4YutWnJmwm8VvIsU1GeXJGiG9j8CIg9zs4voPMdQDUmIxetUOh60VilsNzCiAFTOqu3g== + dependencies: + alphanum-sort "^1.0.0" + has "^1.0.0" + postcss "^7.0.0" + postcss-selector-parser "^3.0.0" + +postcss-modules-extract-imports@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/postcss-modules-extract-imports/-/postcss-modules-extract-imports-2.0.0.tgz#818719a1ae1da325f9832446b01136eeb493cd7e" + integrity sha512-LaYLDNS4SG8Q5WAWqIJgdHPJrDDr/Lv775rMBFUbgjTz6j34lUznACHcdRWroPvXANP2Vj7yNK57vp9eFqzLWQ== + dependencies: + postcss "^7.0.5" + +postcss-modules-local-by-default@^2.0.6: + version "2.0.6" + resolved "https://registry.yarnpkg.com/postcss-modules-local-by-default/-/postcss-modules-local-by-default-2.0.6.tgz#dd9953f6dd476b5fd1ef2d8830c8929760b56e63" + integrity sha512-oLUV5YNkeIBa0yQl7EYnxMgy4N6noxmiwZStaEJUSe2xPMcdNc8WmBQuQCx18H5psYbVxz8zoHk0RAAYZXP9gA== + dependencies: + postcss "^7.0.6" + postcss-selector-parser "^6.0.0" + postcss-value-parser "^3.3.1" + +postcss-modules-scope@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/postcss-modules-scope/-/postcss-modules-scope-2.1.0.tgz#ad3f5bf7856114f6fcab901b0502e2a2bc39d4eb" + integrity sha512-91Rjps0JnmtUB0cujlc8KIKCsJXWjzuxGeT/+Q2i2HXKZ7nBUeF9YQTZZTNvHVoNYj1AthsjnGLtqDUE0Op79A== + dependencies: + postcss "^7.0.6" + postcss-selector-parser "^6.0.0" + +postcss-modules-values@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/postcss-modules-values/-/postcss-modules-values-2.0.0.tgz#479b46dc0c5ca3dc7fa5270851836b9ec7152f64" + integrity sha512-Ki7JZa7ff1N3EIMlPnGTZfUMe69FFwiQPnVSXC9mnn3jozCRBYIxiZd44yJOV2AmabOo4qFf8s0dC/+lweG7+w== + dependencies: + icss-replace-symbols "^1.1.0" + postcss "^7.0.6" + +postcss-normalize-charset@^4.0.1: + version "4.0.1" + resolved "https://registry.yarnpkg.com/postcss-normalize-charset/-/postcss-normalize-charset-4.0.1.tgz#8b35add3aee83a136b0471e0d59be58a50285dd4" + integrity sha512-gMXCrrlWh6G27U0hF3vNvR3w8I1s2wOBILvA87iNXaPvSNo5uZAMYsZG7XjCUf1eVxuPfyL4TJ7++SGZLc9A3g== + dependencies: + postcss "^7.0.0" + +postcss-normalize-display-values@^4.0.2: + version "4.0.2" + resolved "https://registry.yarnpkg.com/postcss-normalize-display-values/-/postcss-normalize-display-values-4.0.2.tgz#0dbe04a4ce9063d4667ed2be476bb830c825935a" + integrity sha512-3F2jcsaMW7+VtRMAqf/3m4cPFhPD3EFRgNs18u+k3lTJJlVe7d0YPO+bnwqo2xg8YiRpDXJI2u8A0wqJxMsQuQ== + dependencies: + cssnano-util-get-match "^4.0.0" + postcss "^7.0.0" + postcss-value-parser "^3.0.0" + +postcss-normalize-positions@^4.0.2: + version "4.0.2" + resolved "https://registry.yarnpkg.com/postcss-normalize-positions/-/postcss-normalize-positions-4.0.2.tgz#05f757f84f260437378368a91f8932d4b102917f" + integrity sha512-Dlf3/9AxpxE+NF1fJxYDeggi5WwV35MXGFnnoccP/9qDtFrTArZ0D0R+iKcg5WsUd8nUYMIl8yXDCtcrT8JrdA== + dependencies: + cssnano-util-get-arguments "^4.0.0" + has "^1.0.0" + postcss "^7.0.0" + postcss-value-parser "^3.0.0" + +postcss-normalize-repeat-style@^4.0.2: + version "4.0.2" + resolved "https://registry.yarnpkg.com/postcss-normalize-repeat-style/-/postcss-normalize-repeat-style-4.0.2.tgz#c4ebbc289f3991a028d44751cbdd11918b17910c" + integrity sha512-qvigdYYMpSuoFs3Is/f5nHdRLJN/ITA7huIoCyqqENJe9PvPmLhNLMu7QTjPdtnVf6OcYYO5SHonx4+fbJE1+Q== + dependencies: + cssnano-util-get-arguments "^4.0.0" + cssnano-util-get-match "^4.0.0" + postcss "^7.0.0" + postcss-value-parser "^3.0.0" + +postcss-normalize-string@^4.0.2: + version "4.0.2" + resolved "https://registry.yarnpkg.com/postcss-normalize-string/-/postcss-normalize-string-4.0.2.tgz#cd44c40ab07a0c7a36dc5e99aace1eca4ec2690c" + integrity sha512-RrERod97Dnwqq49WNz8qo66ps0swYZDSb6rM57kN2J+aoyEAJfZ6bMx0sx/F9TIEX0xthPGCmeyiam/jXif0eA== + dependencies: + has "^1.0.0" + postcss "^7.0.0" + postcss-value-parser "^3.0.0" + +postcss-normalize-timing-functions@^4.0.2: + version "4.0.2" + resolved "https://registry.yarnpkg.com/postcss-normalize-timing-functions/-/postcss-normalize-timing-functions-4.0.2.tgz#8e009ca2a3949cdaf8ad23e6b6ab99cb5e7d28d9" + integrity sha512-acwJY95edP762e++00Ehq9L4sZCEcOPyaHwoaFOhIwWCDfik6YvqsYNxckee65JHLKzuNSSmAdxwD2Cud1Z54A== + dependencies: + cssnano-util-get-match "^4.0.0" + postcss "^7.0.0" + postcss-value-parser "^3.0.0" + +postcss-normalize-unicode@^4.0.1: + version "4.0.1" + resolved "https://registry.yarnpkg.com/postcss-normalize-unicode/-/postcss-normalize-unicode-4.0.1.tgz#841bd48fdcf3019ad4baa7493a3d363b52ae1cfb" + integrity sha512-od18Uq2wCYn+vZ/qCOeutvHjB5jm57ToxRaMeNuf0nWVHaP9Hua56QyMF6fs/4FSUnVIw0CBPsU0K4LnBPwYwg== + dependencies: + browserslist "^4.0.0" + postcss "^7.0.0" + postcss-value-parser "^3.0.0" + +postcss-normalize-url@^4.0.1: + version "4.0.1" + resolved "https://registry.yarnpkg.com/postcss-normalize-url/-/postcss-normalize-url-4.0.1.tgz#10e437f86bc7c7e58f7b9652ed878daaa95faae1" + integrity sha512-p5oVaF4+IHwu7VpMan/SSpmpYxcJMtkGppYf0VbdH5B6hN8YNmVyJLuY9FmLQTzY3fag5ESUUHDqM+heid0UVA== + dependencies: + is-absolute-url "^2.0.0" + normalize-url "^3.0.0" + postcss "^7.0.0" + postcss-value-parser "^3.0.0" + +postcss-normalize-whitespace@^4.0.2: + version "4.0.2" + resolved "https://registry.yarnpkg.com/postcss-normalize-whitespace/-/postcss-normalize-whitespace-4.0.2.tgz#bf1d4070fe4fcea87d1348e825d8cc0c5faa7d82" + integrity sha512-tO8QIgrsI3p95r8fyqKV+ufKlSHh9hMJqACqbv2XknufqEDhDvbguXGBBqxw9nsQoXWf0qOqppziKJKHMD4GtA== + dependencies: + postcss "^7.0.0" + postcss-value-parser "^3.0.0" + +postcss-ordered-values@^4.1.2: + version "4.1.2" + resolved "https://registry.yarnpkg.com/postcss-ordered-values/-/postcss-ordered-values-4.1.2.tgz#0cf75c820ec7d5c4d280189559e0b571ebac0eee" + integrity sha512-2fCObh5UanxvSxeXrtLtlwVThBvHn6MQcu4ksNT2tsaV2Fg76R2CV98W7wNSlX+5/pFwEyaDwKLLoEV7uRybAw== + dependencies: + cssnano-util-get-arguments "^4.0.0" + postcss "^7.0.0" + postcss-value-parser "^3.0.0" + +postcss-reduce-initial@^4.0.3: + version "4.0.3" + resolved "https://registry.yarnpkg.com/postcss-reduce-initial/-/postcss-reduce-initial-4.0.3.tgz#7fd42ebea5e9c814609639e2c2e84ae270ba48df" + integrity sha512-gKWmR5aUulSjbzOfD9AlJiHCGH6AEVLaM0AV+aSioxUDd16qXP1PCh8d1/BGVvpdWn8k/HiK7n6TjeoXN1F7DA== + dependencies: + browserslist "^4.0.0" + caniuse-api "^3.0.0" + has "^1.0.0" + postcss "^7.0.0" + +postcss-reduce-transforms@^4.0.2: + version "4.0.2" + resolved "https://registry.yarnpkg.com/postcss-reduce-transforms/-/postcss-reduce-transforms-4.0.2.tgz#17efa405eacc6e07be3414a5ca2d1074681d4e29" + integrity sha512-EEVig1Q2QJ4ELpJXMZR8Vt5DQx8/mo+dGWSR7vWXqcob2gQLyQGsionYcGKATXvQzMPn6DSN1vTN7yFximdIAg== + dependencies: + cssnano-util-get-match "^4.0.0" + has "^1.0.0" + postcss "^7.0.0" + postcss-value-parser "^3.0.0" + +postcss-safe-parser@^4.0.1: + version "4.0.1" + resolved "https://registry.yarnpkg.com/postcss-safe-parser/-/postcss-safe-parser-4.0.1.tgz#8756d9e4c36fdce2c72b091bbc8ca176ab1fcdea" + integrity sha512-xZsFA3uX8MO3yAda03QrG3/Eg1LN3EPfjjf07vke/46HERLZyHrTsQ9E1r1w1W//fWEhtYNndo2hQplN2cVpCQ== + dependencies: + postcss "^7.0.0" + +postcss-selector-parser@^3.0.0: + version "3.1.1" + resolved "https://registry.yarnpkg.com/postcss-selector-parser/-/postcss-selector-parser-3.1.1.tgz#4f875f4afb0c96573d5cf4d74011aee250a7e865" + integrity sha1-T4dfSvsMllc9XPTXQBGu4lCn6GU= + dependencies: + dot-prop "^4.1.1" + indexes-of "^1.0.1" + uniq "^1.0.1" + +postcss-selector-parser@^5.0.0, postcss-selector-parser@^5.0.0-rc.4: + version "5.0.0" + resolved "https://registry.yarnpkg.com/postcss-selector-parser/-/postcss-selector-parser-5.0.0.tgz#249044356697b33b64f1a8f7c80922dddee7195c" + integrity sha512-w+zLE5Jhg6Liz8+rQOWEAwtwkyqpfnmsinXjXg6cY7YIONZZtgvE0v2O0uhQBs0peNomOJwWRKt6JBfTdTd3OQ== + dependencies: + cssesc "^2.0.0" + indexes-of "^1.0.1" + uniq "^1.0.1" + +postcss-selector-parser@^6.0.0: + version "6.0.2" + resolved "https://registry.yarnpkg.com/postcss-selector-parser/-/postcss-selector-parser-6.0.2.tgz#934cf799d016c83411859e09dcecade01286ec5c" + integrity sha512-36P2QR59jDTOAiIkqEprfJDsoNrvwFei3eCqKd1Y0tUsBimsq39BLp7RD+JWny3WgB1zGhJX8XVePwm9k4wdBg== + dependencies: + cssesc "^3.0.0" + indexes-of "^1.0.1" + uniq "^1.0.1" + +postcss-svgo@^4.0.2: + version "4.0.2" + resolved "https://registry.yarnpkg.com/postcss-svgo/-/postcss-svgo-4.0.2.tgz#17b997bc711b333bab143aaed3b8d3d6e3d38258" + integrity sha512-C6wyjo3VwFm0QgBy+Fu7gCYOkCmgmClghO+pjcxvrcBKtiKt0uCF+hvbMO1fyv5BMImRK90SMb+dwUnfbGd+jw== + dependencies: + is-svg "^3.0.0" + postcss "^7.0.0" + postcss-value-parser "^3.0.0" + svgo "^1.0.0" + +postcss-unique-selectors@^4.0.1: + version "4.0.1" + resolved "https://registry.yarnpkg.com/postcss-unique-selectors/-/postcss-unique-selectors-4.0.1.tgz#9446911f3289bfd64c6d680f073c03b1f9ee4bac" + integrity sha512-+JanVaryLo9QwZjKrmJgkI4Fn8SBgRO6WXQBJi7KiAVPlmxikB5Jzc4EvXMT2H0/m0RjrVVm9rGNhZddm/8Spg== + dependencies: + alphanum-sort "^1.0.0" + postcss "^7.0.0" + uniqs "^2.0.0" + +postcss-value-parser@^3.0.0, postcss-value-parser@^3.3.0, postcss-value-parser@^3.3.1: + version "3.3.1" + resolved "https://registry.yarnpkg.com/postcss-value-parser/-/postcss-value-parser-3.3.1.tgz#9ff822547e2893213cf1c30efa51ac5fd1ba8281" + integrity sha512-pISE66AbVkp4fDQ7VHBwRNXzAAKJjw4Vw7nWI/+Q3vuly7SNfgYXvm6i5IgFylHGK5sP/xHAbB7N49OS4gWNyQ== + +postcss-value-parser@^4.0.0: + version "4.0.2" + resolved "https://registry.yarnpkg.com/postcss-value-parser/-/postcss-value-parser-4.0.2.tgz#482282c09a42706d1fc9a069b73f44ec08391dc9" + integrity sha512-LmeoohTpp/K4UiyQCwuGWlONxXamGzCMtFxLq4W1nZVGIQLYvMCJx3yAF9qyyuFpflABI9yVdtJAqbihOsCsJQ== + +postcss@^7.0.0, postcss@^7.0.1, postcss@^7.0.14, postcss@^7.0.17, postcss@^7.0.5, postcss@^7.0.6: + version "7.0.17" + resolved "https://registry.yarnpkg.com/postcss/-/postcss-7.0.17.tgz#4da1bdff5322d4a0acaab4d87f3e782436bad31f" + integrity sha512-546ZowA+KZ3OasvQZHsbuEpysvwTZNGJv9EfyCQdsIDltPSWHAeTQ5fQy/Npi2ZDtLI3zs7Ps/p6wThErhm9fQ== + dependencies: + chalk "^2.4.2" + source-map "^0.6.1" + supports-color "^6.1.0" + +prepend-http@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/prepend-http/-/prepend-http-2.0.0.tgz#e92434bfa5ea8c19f41cdfd401d741a3c819d897" + integrity sha1-6SQ0v6XqjBn0HN/UAddBo8gZ2Jc= + +prettier@1.16.3: + version "1.16.3" + resolved "https://registry.yarnpkg.com/prettier/-/prettier-1.16.3.tgz#8c62168453badef702f34b45b6ee899574a6a65d" + integrity sha512-kn/GU6SMRYPxUakNXhpP0EedT/KmaPzr0H5lIsDogrykbaxOpOfAFfk5XA7DZrJyMAv1wlMV3CPcZruGXVVUZw== + +pretty-error@^2.0.2: + version "2.1.1" + resolved "https://registry.yarnpkg.com/pretty-error/-/pretty-error-2.1.1.tgz#5f4f87c8f91e5ae3f3ba87ab4cf5e03b1a17f1a3" + integrity sha1-X0+HyPkeWuPzuoerTPXgOxoX8aM= + dependencies: + renderkid "^2.0.1" + utila "~0.4" + +pretty-time@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/pretty-time/-/pretty-time-1.1.0.tgz#ffb7429afabb8535c346a34e41873adf3d74dd0e" + integrity sha512-28iF6xPQrP8Oa6uxE6a1biz+lWeTOAPKggvjB8HAs6nVMKZwf5bG++632Dx614hIWgUPkgivRfG+a8uAXGTIbA== + +prismjs@^1.13.0: + version "1.17.1" + resolved "https://registry.yarnpkg.com/prismjs/-/prismjs-1.17.1.tgz#e669fcbd4cdd873c35102881c33b14d0d68519be" + integrity sha512-PrEDJAFdUGbOP6xK/UsfkC5ghJsPJviKgnQOoxaDbBjwc8op68Quupwt1DeAFoG8GImPhiKXAvvsH7wDSLsu1Q== + optionalDependencies: + clipboard "^2.0.0" + +private@^0.1.6: + version "0.1.8" + resolved "https://registry.yarnpkg.com/private/-/private-0.1.8.tgz#2381edb3689f7a53d653190060fcf822d2f368ff" + integrity sha512-VvivMrbvd2nKkiG38qjULzlc+4Vx4wm/whI9pQD35YrARNnhxeiRktSOhSukRLFNlzg6Br/cJPet5J/u19r/mg== + +process-nextick-args@~2.0.0: + version "2.0.1" + resolved "https://registry.yarnpkg.com/process-nextick-args/-/process-nextick-args-2.0.1.tgz#7820d9b16120cc55ca9ae7792680ae7dba6d7fe2" + integrity sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag== + +process@^0.11.10: + version "0.11.10" + resolved "https://registry.yarnpkg.com/process/-/process-0.11.10.tgz#7332300e840161bda3e69a1d1d91a7d4bc16f182" + integrity sha1-czIwDoQBYb2j5podHZGn1LwW8YI= + +promise-inflight@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/promise-inflight/-/promise-inflight-1.0.1.tgz#98472870bf228132fcbdd868129bad12c3c029e3" + integrity sha1-mEcocL8igTL8vdhoEputEsPAKeM= + +proxy-addr@~2.0.5: + version "2.0.5" + resolved "https://registry.yarnpkg.com/proxy-addr/-/proxy-addr-2.0.5.tgz#34cbd64a2d81f4b1fd21e76f9f06c8a45299ee34" + integrity sha512-t/7RxHXPH6cJtP0pRG6smSr9QJidhB+3kXu0KgXnbGYMgzEnUxRQ4/LDdfOwZEMyIh3/xHb8PX3t+lfL9z+YVQ== + dependencies: + forwarded "~0.1.2" + ipaddr.js "1.9.0" + +prr@~1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/prr/-/prr-1.0.1.tgz#d3fc114ba06995a45ec6893f484ceb1d78f5f476" + integrity sha1-0/wRS6BplaRexok/SEzrHXj19HY= + +pseudomap@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/pseudomap/-/pseudomap-1.0.2.tgz#f052a28da70e618917ef0a8ac34c1ae5a68286b3" + integrity sha1-8FKijacOYYkX7wqKw0wa5aaChrM= + +psl@^1.1.24: + version "1.3.0" + resolved "https://registry.yarnpkg.com/psl/-/psl-1.3.0.tgz#e1ebf6a3b5564fa8376f3da2275da76d875ca1bd" + integrity sha512-avHdspHO+9rQTLbv1RO+MPYeP/SzsCoxofjVnHanETfQhTJrmB0HlDoW+EiN/R+C0BZ+gERab9NY0lPN2TxNag== + +public-encrypt@^4.0.0: + version "4.0.3" + resolved "https://registry.yarnpkg.com/public-encrypt/-/public-encrypt-4.0.3.tgz#4fcc9d77a07e48ba7527e7cbe0de33d0701331e0" + integrity sha512-zVpa8oKZSz5bTMTFClc1fQOnyyEzpl5ozpi1B5YcvBrdohMjH2rfsBtyXcuNuwjsDIXmBYlF2N5FlJYhR29t8Q== + dependencies: + bn.js "^4.1.0" + browserify-rsa "^4.0.0" + create-hash "^1.1.0" + parse-asn1 "^5.0.0" + randombytes "^2.0.1" + safe-buffer "^5.1.2" + +pump@^2.0.0: + version "2.0.1" + resolved "https://registry.yarnpkg.com/pump/-/pump-2.0.1.tgz#12399add6e4cf7526d973cbc8b5ce2e2908b3909" + integrity sha512-ruPMNRkN3MHP1cWJc9OWr+T/xDP0jhXYCLfJcBuX54hhfIBnaQmAUMfDcG4DM5UMWByBbJY69QSphm3jtDKIkA== + dependencies: + end-of-stream "^1.1.0" + once "^1.3.1" + +pump@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/pump/-/pump-3.0.0.tgz#b4a2116815bde2f4e1ea602354e8c75565107a64" + integrity sha512-LwZy+p3SFs1Pytd/jYct4wpv49HiYCqd9Rlc5ZVdk0V+8Yzv6jR5Blk3TRmPL1ft69TxP0IMZGJ+WPFU2BFhww== + dependencies: + end-of-stream "^1.1.0" + once "^1.3.1" + +pumpify@^1.3.3: + version "1.5.1" + resolved "https://registry.yarnpkg.com/pumpify/-/pumpify-1.5.1.tgz#36513be246ab27570b1a374a5ce278bfd74370ce" + integrity sha512-oClZI37HvuUJJxSKKrC17bZ9Cu0ZYhEAGPsPUy9KlMUmv9dKX2o77RUmq7f3XjIxbwyGwYzbzQ1L2Ks8sIradQ== + dependencies: + duplexify "^3.6.0" + inherits "^2.0.3" + pump "^2.0.0" + +punycode@1.3.2: + version "1.3.2" + resolved "https://registry.yarnpkg.com/punycode/-/punycode-1.3.2.tgz#9653a036fb7c1ee42342f2325cceefea3926c48d" + integrity sha1-llOgNvt8HuQjQvIyXM7v6jkmxI0= + +punycode@^1.2.4, punycode@^1.4.1: + version "1.4.1" + resolved "https://registry.yarnpkg.com/punycode/-/punycode-1.4.1.tgz#c0d5a63b2718800ad8e1eb0fa5269c84dd41845e" + integrity sha1-wNWmOycYgArY4esPpSachN1BhF4= + +punycode@^2.1.0: + version "2.1.1" + resolved "https://registry.yarnpkg.com/punycode/-/punycode-2.1.1.tgz#b58b010ac40c22c5657616c8d2c2c02c7bf479ec" + integrity sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A== + +q@^1.1.2: + version "1.5.1" + resolved "https://registry.yarnpkg.com/q/-/q-1.5.1.tgz#7e32f75b41381291d04611f1bf14109ac00651d7" + integrity sha1-fjL3W0E4EpHQRhHxvxQQmsAGUdc= + +qs@6.7.0: + version "6.7.0" + resolved "https://registry.yarnpkg.com/qs/-/qs-6.7.0.tgz#41dc1a015e3d581f1621776be31afb2876a9b1bc" + integrity sha512-VCdBRNFTX1fyE7Nb6FYoURo/SPe62QCaAyzJvUjwRaIsc+NePBEniHlvxFmmX56+HZphIGtV0XeCirBtpDrTyQ== + +qs@~6.5.2: + version "6.5.2" + resolved "https://registry.yarnpkg.com/qs/-/qs-6.5.2.tgz#cb3ae806e8740444584ef154ce8ee98d403f3e36" + integrity sha512-N5ZAX4/LxJmF+7wN74pUD6qAh9/wnvdQcjq9TZjevvXzSUo7bfmw91saqMjzGS2xq91/odN2dW/WOl7qQHNDGA== + +query-string@^5.0.1: + version "5.1.1" + resolved "https://registry.yarnpkg.com/query-string/-/query-string-5.1.1.tgz#a78c012b71c17e05f2e3fa2319dd330682efb3cb" + integrity sha512-gjWOsm2SoGlgLEdAGt7a6slVOk9mGiXmPFMqrEhLQ68rhQuBnpfs3+EmlvqKyxnCo9/PPlF+9MtY02S1aFg+Jw== + dependencies: + decode-uri-component "^0.2.0" + object-assign "^4.1.0" + strict-uri-encode "^1.0.0" + +querystring-es3@^0.2.0, querystring-es3@^0.2.1: + version "0.2.1" + resolved "https://registry.yarnpkg.com/querystring-es3/-/querystring-es3-0.2.1.tgz#9ec61f79049875707d69414596fd907a4d711e73" + integrity sha1-nsYfeQSYdXB9aUFFlv2Qek1xHnM= + +querystring@0.2.0: + version "0.2.0" + resolved "https://registry.yarnpkg.com/querystring/-/querystring-0.2.0.tgz#b209849203bb25df820da756e747005878521620" + integrity sha1-sgmEkgO7Jd+CDadW50cAWHhSFiA= + +querystringify@^2.1.1: + version "2.1.1" + resolved "https://registry.yarnpkg.com/querystringify/-/querystringify-2.1.1.tgz#60e5a5fd64a7f8bfa4d2ab2ed6fdf4c85bad154e" + integrity sha512-w7fLxIRCRT7U8Qu53jQnJyPkYZIaR4n5151KMfcJlO/A9397Wxb1amJvROTK6TOnp7PfoAmg/qXiNHI+08jRfA== + +randombytes@^2.0.0, randombytes@^2.0.1, randombytes@^2.0.5: + version "2.1.0" + resolved "https://registry.yarnpkg.com/randombytes/-/randombytes-2.1.0.tgz#df6f84372f0270dc65cdf6291349ab7a473d4f2a" + integrity sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ== + dependencies: + safe-buffer "^5.1.0" + +randomfill@^1.0.3: + version "1.0.4" + resolved "https://registry.yarnpkg.com/randomfill/-/randomfill-1.0.4.tgz#c92196fc86ab42be983f1bf31778224931d61458" + integrity sha512-87lcbR8+MhcWcUiQ+9e+Rwx8MyR2P7qnt15ynUlbm3TU/fjbgz4GsvfSUDTemtCCtVCqb4ZcEFlyPNTh9bBTLw== + dependencies: + randombytes "^2.0.5" + safe-buffer "^5.1.0" + +range-parser@^1.2.1, range-parser@~1.2.1: + version "1.2.1" + resolved "https://registry.yarnpkg.com/range-parser/-/range-parser-1.2.1.tgz#3cf37023d199e1c24d1a55b84800c2f3e6468031" + integrity sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg== + +raw-body@2.4.0: + version "2.4.0" + resolved "https://registry.yarnpkg.com/raw-body/-/raw-body-2.4.0.tgz#a1ce6fb9c9bc356ca52e89256ab59059e13d0332" + integrity sha512-4Oz8DUIwdvoa5qMJelxipzi/iJIi40O5cGV1wNYp5hvZP8ZN0T+jiNkL0QepXs+EsQ9XJ8ipEDoiH70ySUJP3Q== + dependencies: + bytes "3.1.0" + http-errors "1.7.2" + iconv-lite "0.4.24" + unpipe "1.0.0" + +rc@^1.2.7: + version "1.2.8" + resolved "https://registry.yarnpkg.com/rc/-/rc-1.2.8.tgz#cd924bf5200a075b83c188cd6b9e211b7fc0d3ed" + integrity sha512-y3bGgqKj3QBdxLbLkomlohkvsA8gdAiUQlSBJnBhfn+BPxg4bc62d8TcBW15wavDfgexCgccckhcZvywyQYPOw== + dependencies: + deep-extend "^0.6.0" + ini "~1.3.0" + minimist "^1.2.0" + strip-json-comments "~2.0.1" + +"readable-stream@1 || 2", readable-stream@^2.0.0, readable-stream@^2.0.1, readable-stream@^2.0.2, readable-stream@^2.0.6, readable-stream@^2.1.5, readable-stream@^2.2.2, readable-stream@^2.3.3, readable-stream@^2.3.6, readable-stream@~2.3.6: + version "2.3.6" + resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-2.3.6.tgz#b11c27d88b8ff1fbe070643cf94b0c79ae1b0aaf" + integrity sha512-tQtKA9WIAhBF3+VLAseyMqZeBjW0AHJoxOtYqSUZNJxauErmLbVm2FW1y+J/YA9dUrAC39ITejlZWhVIwawkKw== + dependencies: + core-util-is "~1.0.0" + inherits "~2.0.3" + isarray "~1.0.0" + process-nextick-args "~2.0.0" + safe-buffer "~5.1.1" + string_decoder "~1.1.1" + util-deprecate "~1.0.1" + +readable-stream@^3.0.6, readable-stream@^3.1.1: + version "3.4.0" + resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-3.4.0.tgz#a51c26754658e0a3c21dbf59163bd45ba6f447fc" + integrity sha512-jItXPLmrSR8jmTRmRWJXCnGJsfy85mB3Wd/uINMXA65yrnFo0cPClFIUWzo2najVNSl+mx7/4W8ttlLWJe99pQ== + dependencies: + inherits "^2.0.3" + string_decoder "^1.1.1" + util-deprecate "^1.0.1" + +readdirp@^2.2.1: + version "2.2.1" + resolved "https://registry.yarnpkg.com/readdirp/-/readdirp-2.2.1.tgz#0e87622a3325aa33e892285caf8b4e846529a525" + integrity sha512-1JU/8q+VgFZyxwrJ+SVIOsh+KywWGpds3NTqikiKpDMZWScmAYyKIgqkO+ARvNWJfXeXR1zxz7aHF4u4CyH6vQ== + dependencies: + graceful-fs "^4.1.11" + micromatch "^3.1.10" + readable-stream "^2.0.2" + +reduce@^1.0.1: + version "1.0.2" + resolved "https://registry.yarnpkg.com/reduce/-/reduce-1.0.2.tgz#0cd680ad3ffe0b060e57a5c68bdfce37168d361b" + integrity sha512-xX7Fxke/oHO5IfZSk77lvPa/7bjMh9BuCk4OOoX5XTXrM7s0Z+MkPfSDfz0q7r91BhhGSs8gii/VEN/7zhCPpQ== + dependencies: + object-keys "^1.1.0" + +regenerate-unicode-properties@^8.1.0: + version "8.1.0" + resolved "https://registry.yarnpkg.com/regenerate-unicode-properties/-/regenerate-unicode-properties-8.1.0.tgz#ef51e0f0ea4ad424b77bf7cb41f3e015c70a3f0e" + integrity sha512-LGZzkgtLY79GeXLm8Dp0BVLdQlWICzBnJz/ipWUgo59qBaZ+BHtq51P2q1uVZlppMuUAT37SDk39qUbjTWB7bA== + dependencies: + regenerate "^1.4.0" + +regenerate@^1.4.0: + version "1.4.0" + resolved "https://registry.yarnpkg.com/regenerate/-/regenerate-1.4.0.tgz#4a856ec4b56e4077c557589cae85e7a4c8869a11" + integrity sha512-1G6jJVDWrt0rK99kBjvEtziZNCICAuvIPkSiUFIQxVP06RCVpq3dmDo2oi6ABpYaDYaTRr67BEhL8r1wgEZZKg== + +regenerator-runtime@^0.13.2: + version "0.13.3" + resolved "https://registry.yarnpkg.com/regenerator-runtime/-/regenerator-runtime-0.13.3.tgz#7cf6a77d8f5c6f60eb73c5fc1955b2ceb01e6bf5" + integrity sha512-naKIZz2GQ8JWh///G7L3X6LaQUAMp2lvb1rvwwsURe/VXwD6VMfr+/1NuNw3ag8v2kY1aQ/go5SNn79O9JU7yw== + +regenerator-transform@^0.14.0: + version "0.14.1" + resolved "https://registry.yarnpkg.com/regenerator-transform/-/regenerator-transform-0.14.1.tgz#3b2fce4e1ab7732c08f665dfdb314749c7ddd2fb" + integrity sha512-flVuee02C3FKRISbxhXl9mGzdbWUVHubl1SMaknjxkFB1/iqpJhArQUvRxOOPEc/9tAiX0BaQ28FJH10E4isSQ== + dependencies: + private "^0.1.6" + +regex-not@^1.0.0, regex-not@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/regex-not/-/regex-not-1.0.2.tgz#1f4ece27e00b0b65e0247a6810e6a85d83a5752c" + integrity sha512-J6SDjUgDxQj5NusnOtdFxDwN/+HWykR8GELwctJ7mdqhcyy1xEc4SRFHUXvxTp661YaVKAjfRLZ9cCqS6tn32A== + dependencies: + extend-shallow "^3.0.2" + safe-regex "^1.1.0" + +regexp-tree@^0.1.6: + version "0.1.11" + resolved "https://registry.yarnpkg.com/regexp-tree/-/regexp-tree-0.1.11.tgz#c9c7f00fcf722e0a56c7390983a7a63dd6c272f3" + integrity sha512-7/l/DgapVVDzZobwMCCgMlqiqyLFJ0cduo/j+3BcDJIB+yJdsYCfKuI3l/04NV+H/rfNRdPIDbXNZHM9XvQatg== + +regexpu-core@^4.5.4: + version "4.5.5" + resolved "https://registry.yarnpkg.com/regexpu-core/-/regexpu-core-4.5.5.tgz#aaffe61c2af58269b3e516b61a73790376326411" + integrity sha512-FpI67+ky9J+cDizQUJlIlNZFKual/lUkFr1AG6zOCpwZ9cLrg8UUVakyUQJD7fCDIe9Z2nwTQJNPyonatNmDFQ== + dependencies: + regenerate "^1.4.0" + regenerate-unicode-properties "^8.1.0" + regjsgen "^0.5.0" + regjsparser "^0.6.0" + unicode-match-property-ecmascript "^1.0.4" + unicode-match-property-value-ecmascript "^1.1.0" + +regjsgen@^0.5.0: + version "0.5.0" + resolved "https://registry.yarnpkg.com/regjsgen/-/regjsgen-0.5.0.tgz#a7634dc08f89209c2049adda3525711fb97265dd" + integrity sha512-RnIrLhrXCX5ow/E5/Mh2O4e/oa1/jW0eaBKTSy3LaCj+M3Bqvm97GWDp2yUtzIs4LEn65zR2yiYGFqb2ApnzDA== + +regjsparser@^0.6.0: + version "0.6.0" + resolved "https://registry.yarnpkg.com/regjsparser/-/regjsparser-0.6.0.tgz#f1e6ae8b7da2bae96c99399b868cd6c933a2ba9c" + integrity sha512-RQ7YyokLiQBomUJuUG8iGVvkgOLxwyZM8k6d3q5SAXpg4r5TZJZigKFvC6PpD+qQ98bCDC5YelPeA3EucDoNeQ== + dependencies: + jsesc "~0.5.0" + +relateurl@0.2.x: + version "0.2.7" + resolved "https://registry.yarnpkg.com/relateurl/-/relateurl-0.2.7.tgz#54dbf377e51440aca90a4cd274600d3ff2d888a9" + integrity sha1-VNvzd+UUQKypCkzSdGANP/LYiKk= + +remove-trailing-separator@^1.0.1: + version "1.1.0" + resolved "https://registry.yarnpkg.com/remove-trailing-separator/-/remove-trailing-separator-1.1.0.tgz#c24bce2a283adad5bc3f58e0d48249b92379d8ef" + integrity sha1-wkvOKig62tW8P1jg1IJJuSN52O8= + +renderkid@^2.0.1: + version "2.0.3" + resolved "https://registry.yarnpkg.com/renderkid/-/renderkid-2.0.3.tgz#380179c2ff5ae1365c522bf2fcfcff01c5b74149" + integrity sha512-z8CLQp7EZBPCwCnncgf9C4XAi3WR0dv+uWu/PjIyhhAb5d6IJ/QZqlHFprHeKT+59//V6BNUsLbvN8+2LarxGA== + dependencies: + css-select "^1.1.0" + dom-converter "^0.2" + htmlparser2 "^3.3.0" + strip-ansi "^3.0.0" + utila "^0.4.0" + +repeat-element@^1.1.2: + version "1.1.3" + resolved "https://registry.yarnpkg.com/repeat-element/-/repeat-element-1.1.3.tgz#782e0d825c0c5a3bb39731f84efee6b742e6b1ce" + integrity sha512-ahGq0ZnV5m5XtZLMb+vP76kcAM5nkLqk0lpqAuojSKGgQtn4eRi4ZZGm2olo2zKFH+sMsWaqOCW1dqAnOru72g== + +repeat-string@^1.6.1: + version "1.6.1" + resolved "https://registry.yarnpkg.com/repeat-string/-/repeat-string-1.6.1.tgz#8dcae470e1c88abc2d600fff4a776286da75e637" + integrity sha1-jcrkcOHIirwtYA//Sndihtp15jc= + +request@^2.87.0: + version "2.88.0" + resolved "https://registry.yarnpkg.com/request/-/request-2.88.0.tgz#9c2fca4f7d35b592efe57c7f0a55e81052124fef" + integrity sha512-NAqBSrijGLZdM0WZNsInLJpkJokL72XYjUpnB0iwsRgxh7dB6COrHnTBNwN0E+lHDAJzu7kLAkDeY08z2/A0hg== + dependencies: + aws-sign2 "~0.7.0" + aws4 "^1.8.0" + caseless "~0.12.0" + combined-stream "~1.0.6" + extend "~3.0.2" + forever-agent "~0.6.1" + form-data "~2.3.2" + har-validator "~5.1.0" + http-signature "~1.2.0" + is-typedarray "~1.0.0" + isstream "~0.1.2" + json-stringify-safe "~5.0.1" + mime-types "~2.1.19" + oauth-sign "~0.9.0" + performance-now "^2.1.0" + qs "~6.5.2" + safe-buffer "^5.1.2" + tough-cookie "~2.4.3" + tunnel-agent "^0.6.0" + uuid "^3.3.2" + +require-directory@^2.1.1: + version "2.1.1" + resolved "https://registry.yarnpkg.com/require-directory/-/require-directory-2.1.1.tgz#8c64ad5fd30dab1c976e2344ffe7f792a6a6df42" + integrity sha1-jGStX9MNqxyXbiNE/+f3kqam30I= + +require-main-filename@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/require-main-filename/-/require-main-filename-1.0.1.tgz#97f717b69d48784f5f526a6c5aa8ffdda055a4d1" + integrity sha1-l/cXtp1IeE9fUmpsWqj/3aBVpNE= + +requires-port@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/requires-port/-/requires-port-1.0.0.tgz#925d2601d39ac485e091cf0da5c6e694dc3dcaff" + integrity sha1-kl0mAdOaxIXgkc8NpcbmlNw9yv8= + +reselect@^3.0.1: + version "3.0.1" + resolved "https://registry.yarnpkg.com/reselect/-/reselect-3.0.1.tgz#efdaa98ea7451324d092b2b2163a6a1d7a9a2147" + integrity sha1-79qpjqdFEyTQkrKyFjpqHXqaIUc= + +resolve-cwd@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/resolve-cwd/-/resolve-cwd-2.0.0.tgz#00a9f7387556e27038eae232caa372a6a59b665a" + integrity sha1-AKn3OHVW4nA46uIyyqNypqWbZlo= + dependencies: + resolve-from "^3.0.0" + +resolve-from@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/resolve-from/-/resolve-from-3.0.0.tgz#b22c7af7d9d6881bc8b6e653335eebcb0a188748" + integrity sha1-six699nWiBvItuZTM17rywoYh0g= + +resolve-url@^0.2.1: + version "0.2.1" + resolved "https://registry.yarnpkg.com/resolve-url/-/resolve-url-0.2.1.tgz#2c637fe77c893afd2a663fe21aa9080068e2052a" + integrity sha1-LGN/53yJOv0qZj/iGqkIAGjiBSo= + +resolve@^1.2.0, resolve@^1.3.2, resolve@^1.4.0, resolve@^1.8.1: + version "1.12.0" + resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.12.0.tgz#3fc644a35c84a48554609ff26ec52b66fa577df6" + integrity sha512-B/dOmuoAik5bKcD6s6nXDCjzUKnaDvdkRyAk6rsmsKLipWj4797iothd7jmmUhWTfinVMU+wc56rYKsit2Qy4w== + dependencies: + path-parse "^1.0.6" + +ret@~0.1.10: + version "0.1.15" + resolved "https://registry.yarnpkg.com/ret/-/ret-0.1.15.tgz#b8a4825d5bdb1fc3f6f53c2bc33f81388681c7bc" + integrity sha512-TTlYpa+OL+vMMNG24xSlQGEJ3B/RzEfUlLct7b5G/ytav+wPrplCpVMFuwzXbkecJrb6IYo1iFb0S9v37754mg== + +retry@^0.12.0: + version "0.12.0" + resolved "https://registry.yarnpkg.com/retry/-/retry-0.12.0.tgz#1b42a6266a21f07421d1b0b54b7dc167b01c013b" + integrity sha1-G0KmJmoh8HQh0bC1S33BZ7AcATs= + +rgb-regex@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/rgb-regex/-/rgb-regex-1.0.1.tgz#c0e0d6882df0e23be254a475e8edd41915feaeb1" + integrity sha1-wODWiC3w4jviVKR16O3UGRX+rrE= + +rgba-regex@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/rgba-regex/-/rgba-regex-1.0.0.tgz#43374e2e2ca0968b0ef1523460b7d730ff22eeb3" + integrity sha1-QzdOLiyglosO8VI0YLfXMP8i7rM= + +rimraf@^2.5.4, rimraf@^2.6.1, rimraf@^2.6.3: + version "2.7.1" + resolved "https://registry.yarnpkg.com/rimraf/-/rimraf-2.7.1.tgz#35797f13a7fdadc566142c29d4f07ccad483e3ec" + integrity sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w== + dependencies: + glob "^7.1.3" + +ripemd160@^2.0.0, ripemd160@^2.0.1: + version "2.0.2" + resolved "https://registry.yarnpkg.com/ripemd160/-/ripemd160-2.0.2.tgz#a1c1a6f624751577ba5d07914cbc92850585890c" + integrity sha512-ii4iagi25WusVoiC4B4lq7pbXfAp3D9v5CwfkY33vffw2+pkDjY1D8GaN7spsxvCSx8dkPqOZCEZyfxcmJG2IA== + dependencies: + hash-base "^3.0.0" + inherits "^2.0.1" + +run-queue@^1.0.0, run-queue@^1.0.3: + version "1.0.3" + resolved "https://registry.yarnpkg.com/run-queue/-/run-queue-1.0.3.tgz#e848396f057d223f24386924618e25694161ec47" + integrity sha1-6Eg5bwV9Ij8kOGkkYY4laUFh7Ec= + dependencies: + aproba "^1.1.1" + +safe-buffer@5.1.2, safe-buffer@~5.1.0, safe-buffer@~5.1.1: + version "5.1.2" + resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.1.2.tgz#991ec69d296e0313747d59bdfd2b745c35f8828d" + integrity sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g== + +safe-buffer@>=5.1.0, safe-buffer@^5.0.1, safe-buffer@^5.1.0, safe-buffer@^5.1.1, safe-buffer@^5.1.2, safe-buffer@~5.2.0: + version "5.2.0" + resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.2.0.tgz#b74daec49b1148f88c64b68d49b1e815c1f2f519" + integrity sha512-fZEwUGbVl7kouZs1jCdMLdt95hdIv0ZeHg6L7qPeciMZhZ+/gdesW4wgTARkrFWEpspjEATAzUGPG8N2jJiwbg== + +safe-regex@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/safe-regex/-/safe-regex-1.1.0.tgz#40a3669f3b077d1e943d44629e157dd48023bf2e" + integrity sha1-QKNmnzsHfR6UPURinhV91IAjvy4= + dependencies: + ret "~0.1.10" + +"safer-buffer@>= 2.1.2 < 3", safer-buffer@^2.0.2, safer-buffer@^2.1.0, safer-buffer@~2.1.0: + version "2.1.2" + resolved "https://registry.yarnpkg.com/safer-buffer/-/safer-buffer-2.1.2.tgz#44fa161b0187b9549dd84bb91802f9bd8385cd6a" + integrity sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg== + +sax@0.5.x: + version "0.5.8" + resolved "https://registry.yarnpkg.com/sax/-/sax-0.5.8.tgz#d472db228eb331c2506b0e8c15524adb939d12c1" + integrity sha1-1HLbIo6zMcJQaw6MFVJK25OdEsE= + +sax@^1.2.4, sax@~1.2.4: + version "1.2.4" + resolved "https://registry.yarnpkg.com/sax/-/sax-1.2.4.tgz#2816234e2378bddc4e5354fab5caa895df7100d9" + integrity sha512-NqVDv9TpANUjFm0N8uM5GxL36UgKi9/atZw+x7YFnQ8ckwFGKrl4xX4yWtrey3UJm5nP1kUbnYgLopqWNSRhWw== + +schema-utils@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/schema-utils/-/schema-utils-1.0.0.tgz#0b79a93204d7b600d4b2850d1f66c2a34951c770" + integrity sha512-i27Mic4KovM/lnGsy8whRCHhc7VicJajAjTrYg11K9zfZXnYIt4k5F+kZkwjnrhKzLic/HLU4j11mjsz2G/75g== + dependencies: + ajv "^6.1.0" + ajv-errors "^1.0.0" + ajv-keywords "^3.1.0" + +section-matter@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/section-matter/-/section-matter-1.0.0.tgz#e9041953506780ec01d59f292a19c7b850b84167" + integrity sha512-vfD3pmTzGpufjScBh50YHKzEu2lxBWhVEHsNGoEXmCmn2hKGfeNLYMzCJpe8cD7gqX7TJluOVpBkAequ6dgMmA== + dependencies: + extend-shallow "^2.0.1" + kind-of "^6.0.0" + +select-hose@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/select-hose/-/select-hose-2.0.0.tgz#625d8658f865af43ec962bfc376a37359a4994ca" + integrity sha1-Yl2GWPhlr0Psliv8N2o3NZpJlMo= + +select@^1.1.2: + version "1.1.2" + resolved "https://registry.yarnpkg.com/select/-/select-1.1.2.tgz#0e7350acdec80b1108528786ec1d4418d11b396d" + integrity sha1-DnNQrN7ICxEIUoeG7B1EGNEbOW0= + +selfsigned@^1.10.4: + version "1.10.4" + resolved "https://registry.yarnpkg.com/selfsigned/-/selfsigned-1.10.4.tgz#cdd7eccfca4ed7635d47a08bf2d5d3074092e2cd" + integrity sha512-9AukTiDmHXGXWtWjembZ5NDmVvP2695EtpgbCsxCa68w3c88B+alqbmZ4O3hZ4VWGXeGWzEVdvqgAJD8DQPCDw== + dependencies: + node-forge "0.7.5" + +semver@^5.1.0, semver@^5.3.0, semver@^5.4.1, semver@^5.5.0, semver@^5.5.1, semver@^5.6.0: + version "5.7.1" + resolved "https://registry.yarnpkg.com/semver/-/semver-5.7.1.tgz#a954f931aeba508d307bbf069eff0c01c96116f7" + integrity sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ== + +semver@^6.0.0, semver@^6.3.0: + version "6.3.0" + resolved "https://registry.yarnpkg.com/semver/-/semver-6.3.0.tgz#ee0a64c8af5e8ceea67687b133761e1becbd1d3d" + integrity sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw== + +send@0.17.1: + version "0.17.1" + resolved "https://registry.yarnpkg.com/send/-/send-0.17.1.tgz#c1d8b059f7900f7466dd4938bdc44e11ddb376c8" + integrity sha512-BsVKsiGcQMFwT8UxypobUKyv7irCNRHk1T0G680vk88yf6LBByGcZJOTJCrTP2xVN6yI+XjPJcNuE3V4fT9sAg== + dependencies: + debug "2.6.9" + depd "~1.1.2" + destroy "~1.0.4" + encodeurl "~1.0.2" + escape-html "~1.0.3" + etag "~1.8.1" + fresh "0.5.2" + http-errors "~1.7.2" + mime "1.6.0" + ms "2.1.1" + on-finished "~2.3.0" + range-parser "~1.2.1" + statuses "~1.5.0" + +serialize-javascript@^1.3.0, serialize-javascript@^1.7.0: + version "1.7.0" + resolved "https://registry.yarnpkg.com/serialize-javascript/-/serialize-javascript-1.7.0.tgz#d6e0dfb2a3832a8c94468e6eb1db97e55a192a65" + integrity sha512-ke8UG8ulpFOxO8f8gRYabHQe/ZntKlcig2Mp+8+URDP1D8vJZ0KUt7LYo07q25Z/+JVSgpr/cui9PIp5H6/+nA== + +serve-index@^1.9.1: + version "1.9.1" + resolved "https://registry.yarnpkg.com/serve-index/-/serve-index-1.9.1.tgz#d3768d69b1e7d82e5ce050fff5b453bea12a9239" + integrity sha1-03aNabHn2C5c4FD/9bRTvqEqkjk= + dependencies: + accepts "~1.3.4" + batch "0.6.1" + debug "2.6.9" + escape-html "~1.0.3" + http-errors "~1.6.2" + mime-types "~2.1.17" + parseurl "~1.3.2" + +serve-static@1.14.1: + version "1.14.1" + resolved "https://registry.yarnpkg.com/serve-static/-/serve-static-1.14.1.tgz#666e636dc4f010f7ef29970a88a674320898b2f9" + integrity sha512-JMrvUwE54emCYWlTI+hGrGv5I8dEwmco/00EvkzIIsR7MqrHonbD9pO2MOfFnpFntl7ecpZs+3mW+XbQZu9QCg== + dependencies: + encodeurl "~1.0.2" + escape-html "~1.0.3" + parseurl "~1.3.3" + send "0.17.1" + +set-blocking@^2.0.0, set-blocking@~2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/set-blocking/-/set-blocking-2.0.0.tgz#045f9782d011ae9a6803ddd382b24392b3d890f7" + integrity sha1-BF+XgtARrppoA93TgrJDkrPYkPc= + +set-value@^2.0.0, set-value@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/set-value/-/set-value-2.0.1.tgz#a18d40530e6f07de4228c7defe4227af8cad005b" + integrity sha512-JxHc1weCN68wRY0fhCoXpyK55m/XPHafOmK4UWD7m2CI14GMcFypt4w/0+NV5f/ZMby2F6S2wwA7fgynh9gWSw== + dependencies: + extend-shallow "^2.0.1" + is-extendable "^0.1.1" + is-plain-object "^2.0.3" + split-string "^3.0.1" + +setimmediate@^1.0.4: + version "1.0.5" + resolved "https://registry.yarnpkg.com/setimmediate/-/setimmediate-1.0.5.tgz#290cbb232e306942d7d7ea9b83732ab7856f8285" + integrity sha1-KQy7Iy4waULX1+qbg3Mqt4VvgoU= + +setprototypeof@1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/setprototypeof/-/setprototypeof-1.1.0.tgz#d0bd85536887b6fe7c0d818cb962d9d91c54e656" + integrity sha512-BvE/TwpZX4FXExxOxZyRGQQv651MSwmWKZGqvmPcRIjDqWub67kTKuIMx43cZZrS/cBBzwBcNDWoFxt2XEFIpQ== + +setprototypeof@1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/setprototypeof/-/setprototypeof-1.1.1.tgz#7e95acb24aa92f5885e0abef5ba131330d4ae683" + integrity sha512-JvdAWfbXeIGaZ9cILp38HntZSFSo3mWg6xGcJJsd+d4aRMOqauag1C63dJfDw7OaMYwEbHMOxEZ1lqVRYP2OAw== + +sha.js@^2.4.0, sha.js@^2.4.8: + version "2.4.11" + resolved "https://registry.yarnpkg.com/sha.js/-/sha.js-2.4.11.tgz#37a5cf0b81ecbc6943de109ba2960d1b26584ae7" + integrity sha512-QMEp5B7cftE7APOjk5Y6xgrbWu+WkLVQwk8JNjZ8nKRciZaByEW6MubieAiToS7+dwvrjGhH8jRXz3MVd0AYqQ== + dependencies: + inherits "^2.0.1" + safe-buffer "^5.0.1" + +shebang-command@^1.2.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/shebang-command/-/shebang-command-1.2.0.tgz#44aac65b695b03398968c39f363fee5deafdf1ea" + integrity sha1-RKrGW2lbAzmJaMOfNj/uXer98eo= + dependencies: + shebang-regex "^1.0.0" + +shebang-regex@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/shebang-regex/-/shebang-regex-1.0.0.tgz#da42f49740c0b42db2ca9728571cb190c98efea3" + integrity sha1-2kL0l0DAtC2yypcoVxyxkMmO/qM= + +signal-exit@^3.0.0: + version "3.0.2" + resolved "https://registry.yarnpkg.com/signal-exit/-/signal-exit-3.0.2.tgz#b5fdc08f1287ea1178628e415e25132b73646c6d" + integrity sha1-tf3AjxKH6hF4Yo5BXiUTK3NkbG0= + +simple-swizzle@^0.2.2: + version "0.2.2" + resolved "https://registry.yarnpkg.com/simple-swizzle/-/simple-swizzle-0.2.2.tgz#a4da6b635ffcccca33f70d17cb92592de95e557a" + integrity sha1-pNprY1/8zMoz9w0Xy5JZLeleVXo= + dependencies: + is-arrayish "^0.3.1" + +slash@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/slash/-/slash-1.0.0.tgz#c41f2f6c39fc16d1cd17ad4b5d896114ae470d55" + integrity sha1-xB8vbDn8FtHNF61LXYlhFK5HDVU= + +slash@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/slash/-/slash-2.0.0.tgz#de552851a1759df3a8f206535442f5ec4ddeab44" + integrity sha512-ZYKh3Wh2z1PpEXWr0MpSBZ0V6mZHAQfYevttO11c51CaWjGTaadiKZ+wVt1PbMlDV5qhMFslpZCemhwOK7C89A== + +snapdragon-node@^2.0.1: + version "2.1.1" + resolved "https://registry.yarnpkg.com/snapdragon-node/-/snapdragon-node-2.1.1.tgz#6c175f86ff14bdb0724563e8f3c1b021a286853b" + integrity sha512-O27l4xaMYt/RSQ5TR3vpWCAB5Kb/czIcqUFOM/C4fYcLnbZUc1PkjTAMjof2pBWaSTwOUd6qUHcFGVGj7aIwnw== + dependencies: + define-property "^1.0.0" + isobject "^3.0.0" + snapdragon-util "^3.0.1" + +snapdragon-util@^3.0.1: + version "3.0.1" + resolved "https://registry.yarnpkg.com/snapdragon-util/-/snapdragon-util-3.0.1.tgz#f956479486f2acd79700693f6f7b805e45ab56e2" + integrity sha512-mbKkMdQKsjX4BAL4bRYTj21edOf8cN7XHdYUJEe+Zn99hVEYcMvKPct1IqNe7+AZPirn8BCDOQBHQZknqmKlZQ== + dependencies: + kind-of "^3.2.0" + +snapdragon@^0.8.1: + version "0.8.2" + resolved "https://registry.yarnpkg.com/snapdragon/-/snapdragon-0.8.2.tgz#64922e7c565b0e14204ba1aa7d6964278d25182d" + integrity sha512-FtyOnWN/wCHTVXOMwvSv26d+ko5vWlIDD6zoUJ7LW8vh+ZBC8QdljveRP+crNrtBwioEUWy/4dMtbBjA4ioNlg== + dependencies: + base "^0.11.1" + debug "^2.2.0" + define-property "^0.2.5" + extend-shallow "^2.0.1" + map-cache "^0.2.2" + source-map "^0.5.6" + source-map-resolve "^0.5.0" + use "^3.1.0" + +sockjs-client@1.3.0: + version "1.3.0" + resolved "https://registry.yarnpkg.com/sockjs-client/-/sockjs-client-1.3.0.tgz#12fc9d6cb663da5739d3dc5fb6e8687da95cb177" + integrity sha512-R9jxEzhnnrdxLCNln0xg5uGHqMnkhPSTzUZH2eXcR03S/On9Yvoq2wyUZILRUhZCNVu2PmwWVoyuiPz8th8zbg== + dependencies: + debug "^3.2.5" + eventsource "^1.0.7" + faye-websocket "~0.11.1" + inherits "^2.0.3" + json3 "^3.3.2" + url-parse "^1.4.3" + +sockjs@0.3.19: + version "0.3.19" + resolved "https://registry.yarnpkg.com/sockjs/-/sockjs-0.3.19.tgz#d976bbe800af7bd20ae08598d582393508993c0d" + integrity sha512-V48klKZl8T6MzatbLlzzRNhMepEys9Y4oGFpypBFFn1gLI/QQ9HtLLyWJNbPlwGLelOVOEijUbTTJeLLI59jLw== + dependencies: + faye-websocket "^0.10.0" + uuid "^3.0.1" + +sort-keys@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/sort-keys/-/sort-keys-2.0.0.tgz#658535584861ec97d730d6cf41822e1f56684128" + integrity sha1-ZYU1WEhh7JfXMNbPQYIuH1ZoQSg= + dependencies: + is-plain-obj "^1.0.0" + +source-list-map@^2.0.0: + version "2.0.1" + resolved "https://registry.yarnpkg.com/source-list-map/-/source-list-map-2.0.1.tgz#3993bd873bfc48479cca9ea3a547835c7c154b34" + integrity sha512-qnQ7gVMxGNxsiL4lEuJwe/To8UnK7fAnmbGEEH8RpLouuKbeEm0lhbQVFIrNSuB+G7tVrAlVsZgETT5nljf+Iw== + +source-map-resolve@^0.5.0: + version "0.5.2" + resolved "https://registry.yarnpkg.com/source-map-resolve/-/source-map-resolve-0.5.2.tgz#72e2cc34095543e43b2c62b2c4c10d4a9054f259" + integrity sha512-MjqsvNwyz1s0k81Goz/9vRBe9SZdB09Bdw+/zYyO+3CuPk6fouTaxscHkgtE8jKvf01kVfl8riHzERQ/kefaSA== + dependencies: + atob "^2.1.1" + decode-uri-component "^0.2.0" + resolve-url "^0.2.1" + source-map-url "^0.4.0" + urix "^0.1.0" + +source-map-support@~0.5.12: + version "0.5.13" + resolved "https://registry.yarnpkg.com/source-map-support/-/source-map-support-0.5.13.tgz#31b24a9c2e73c2de85066c0feb7d44767ed52932" + integrity sha512-SHSKFHadjVA5oR4PPqhtAVdcBWwRYVd6g6cAXnIbRiIwc2EhPrTuKUBdSLvlEKyIP3GCf89fltvcZiP9MMFA1w== + dependencies: + buffer-from "^1.0.0" + source-map "^0.6.0" + +source-map-url@^0.4.0: + version "0.4.0" + resolved "https://registry.yarnpkg.com/source-map-url/-/source-map-url-0.4.0.tgz#3e935d7ddd73631b97659956d55128e87b5084a3" + integrity sha1-PpNdfd1zYxuXZZlW1VEo6HtQhKM= + +source-map@0.1.x: + version "0.1.43" + resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.1.43.tgz#c24bc146ca517c1471f5dacbe2571b2b7f9e3346" + integrity sha1-wkvBRspRfBRx9drL4lcbK3+eM0Y= + dependencies: + amdefine ">=0.0.4" + +source-map@0.5.6: + version "0.5.6" + resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.5.6.tgz#75ce38f52bf0733c5a7f0c118d81334a2bb5f412" + integrity sha1-dc449SvwczxafwwRjYEzSiu19BI= + +source-map@^0.5.0, source-map@^0.5.3, source-map@^0.5.6: + version "0.5.7" + resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.5.7.tgz#8a039d2d1021d22d1ea14c80d8ea468ba2ef3fcc" + integrity sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w= + +source-map@^0.6.0, source-map@^0.6.1, source-map@~0.6.0, source-map@~0.6.1: + version "0.6.1" + resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.6.1.tgz#74722af32e9614e9c287a8d0bbde48b5e2f1a263" + integrity sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g== + +spdy-transport@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/spdy-transport/-/spdy-transport-3.0.0.tgz#00d4863a6400ad75df93361a1608605e5dcdcf31" + integrity sha512-hsLVFE5SjA6TCisWeJXFKniGGOpBgMLmerfO2aCyCU5s7nJ/rpAepqmFifv/GCbSbueEeAJJnmSQ2rKC/g8Fcw== + dependencies: + debug "^4.1.0" + detect-node "^2.0.4" + hpack.js "^2.1.6" + obuf "^1.1.2" + readable-stream "^3.0.6" + wbuf "^1.7.3" + +spdy@^4.0.1: + version "4.0.1" + resolved "https://registry.yarnpkg.com/spdy/-/spdy-4.0.1.tgz#6f12ed1c5db7ea4f24ebb8b89ba58c87c08257f2" + integrity sha512-HeZS3PBdMA+sZSu0qwpCxl3DeALD5ASx8pAX0jZdKXSpPWbQ6SYGnlg3BBmYLx5LtiZrmkAZfErCm2oECBcioA== + dependencies: + debug "^4.1.0" + handle-thing "^2.0.0" + http-deceiver "^1.2.7" + select-hose "^2.0.0" + spdy-transport "^3.0.0" + +split-string@^3.0.1, split-string@^3.0.2: + version "3.1.0" + resolved "https://registry.yarnpkg.com/split-string/-/split-string-3.1.0.tgz#7cb09dda3a86585705c64b39a6466038682e8fe2" + integrity sha512-NzNVhJDYpwceVVii8/Hu6DKfD2G+NrQHlS/V/qgv763EYudVwEcMQNxd2lh+0VrUByXN/oJkl5grOhYWvQUYiw== + dependencies: + extend-shallow "^3.0.0" + +sprintf-js@~1.0.2: + version "1.0.3" + resolved "https://registry.yarnpkg.com/sprintf-js/-/sprintf-js-1.0.3.tgz#04e6926f662895354f3dd015203633b857297e2c" + integrity sha1-BOaSb2YolTVPPdAVIDYzuFcpfiw= + +sshpk@^1.7.0: + version "1.16.1" + resolved "https://registry.yarnpkg.com/sshpk/-/sshpk-1.16.1.tgz#fb661c0bef29b39db40769ee39fa70093d6f6877" + integrity sha512-HXXqVUq7+pcKeLqqZj6mHFUMvXtOJt1uoUx09pFW6011inTMxqI8BA8PM95myrIyyKwdnzjdFjLiE6KBPVtJIg== + dependencies: + asn1 "~0.2.3" + assert-plus "^1.0.0" + bcrypt-pbkdf "^1.0.0" + dashdash "^1.12.0" + ecc-jsbn "~0.1.1" + getpass "^0.1.1" + jsbn "~0.1.0" + safer-buffer "^2.0.2" + tweetnacl "~0.14.0" + +ssri@^6.0.1: + version "6.0.1" + resolved "https://registry.yarnpkg.com/ssri/-/ssri-6.0.1.tgz#2a3c41b28dd45b62b63676ecb74001265ae9edd8" + integrity sha512-3Wge10hNcT1Kur4PDFwEieXSCMCJs/7WvSACcrMYrNp+b8kDL1/0wJch5Ni2WrtwEa2IO8OsVfeKIciKCDx/QA== + dependencies: + figgy-pudding "^3.5.1" + +stable@^0.1.8: + version "0.1.8" + resolved "https://registry.yarnpkg.com/stable/-/stable-0.1.8.tgz#836eb3c8382fe2936feaf544631017ce7d47a3cf" + integrity sha512-ji9qxRnOVfcuLDySj9qzhGSEFVobyt1kIOSkj1qZzYLzq7Tos/oUUWvotUPQLlrsidqsK6tBH89Bc9kL5zHA6w== + +stack-utils@^1.0.1: + version "1.0.2" + resolved "https://registry.yarnpkg.com/stack-utils/-/stack-utils-1.0.2.tgz#33eba3897788558bebfc2db059dc158ec36cebb8" + integrity sha512-MTX+MeG5U994cazkjd/9KNAapsHnibjMLnfXodlkXw76JEea0UiNzrqidzo1emMwk7w5Qhc9jd4Bn9TBb1MFwA== + +static-extend@^0.1.1: + version "0.1.2" + resolved "https://registry.yarnpkg.com/static-extend/-/static-extend-0.1.2.tgz#60809c39cbff55337226fd5e0b520f341f1fb5c6" + integrity sha1-YICcOcv/VTNyJv1eC1IPNB8ftcY= + dependencies: + define-property "^0.2.5" + object-copy "^0.1.0" + +"statuses@>= 1.4.0 < 2", "statuses@>= 1.5.0 < 2", statuses@~1.5.0: + version "1.5.0" + resolved "https://registry.yarnpkg.com/statuses/-/statuses-1.5.0.tgz#161c7dac177659fd9811f43771fa99381478628c" + integrity sha1-Fhx9rBd2Wf2YEfQ3cfqZOBR4Yow= + +std-env@^2.2.1: + version "2.2.1" + resolved "https://registry.yarnpkg.com/std-env/-/std-env-2.2.1.tgz#2ffa0fdc9e2263e0004c1211966e960948a40f6b" + integrity sha512-IjYQUinA3lg5re/YMlwlfhqNRTzMZMqE+pezevdcTaHceqx8ngEi1alX9nNCk9Sc81fy1fLDeQoaCzeiW1yBOQ== + dependencies: + ci-info "^1.6.0" + +stream-browserify@^2.0.1: + version "2.0.2" + resolved "https://registry.yarnpkg.com/stream-browserify/-/stream-browserify-2.0.2.tgz#87521d38a44aa7ee91ce1cd2a47df0cb49dd660b" + integrity sha512-nX6hmklHs/gr2FuxYDltq8fJA1GDlxKQCz8O/IM4atRqBH8OORmBNgfvW5gG10GT/qQ9u0CzIvr2X5Pkt6ntqg== + dependencies: + inherits "~2.0.1" + readable-stream "^2.0.2" + +stream-each@^1.1.0: + version "1.2.3" + resolved "https://registry.yarnpkg.com/stream-each/-/stream-each-1.2.3.tgz#ebe27a0c389b04fbcc233642952e10731afa9bae" + integrity sha512-vlMC2f8I2u/bZGqkdfLQW/13Zihpej/7PmSiMQsbYddxuTsJp8vRe2x2FvVExZg7FaOds43ROAuFJwPR4MTZLw== + dependencies: + end-of-stream "^1.1.0" + stream-shift "^1.0.0" + +stream-http@^2.7.2: + version "2.8.3" + resolved "https://registry.yarnpkg.com/stream-http/-/stream-http-2.8.3.tgz#b2d242469288a5a27ec4fe8933acf623de6514fc" + integrity sha512-+TSkfINHDo4J+ZobQLWiMouQYB+UVYFttRA94FpEzzJ7ZdqcL4uUUQ7WkdkI4DSozGmgBUE/a47L+38PenXhUw== + dependencies: + builtin-status-codes "^3.0.0" + inherits "^2.0.1" + readable-stream "^2.3.6" + to-arraybuffer "^1.0.0" + xtend "^4.0.0" + +stream-shift@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/stream-shift/-/stream-shift-1.0.0.tgz#d5c752825e5367e786f78e18e445ea223a155952" + integrity sha1-1cdSgl5TZ+eG944Y5EXqIjoVWVI= + +strict-uri-encode@^1.0.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/strict-uri-encode/-/strict-uri-encode-1.1.0.tgz#279b225df1d582b1f54e65addd4352e18faa0713" + integrity sha1-J5siXfHVgrH1TmWt3UNS4Y+qBxM= + +string-width@^1.0.1: + version "1.0.2" + resolved "https://registry.yarnpkg.com/string-width/-/string-width-1.0.2.tgz#118bdf5b8cdc51a2a7e70d211e07e2b0b9b107d3" + integrity sha1-EYvfW4zcUaKn5w0hHgfisLmxB9M= + dependencies: + code-point-at "^1.0.0" + is-fullwidth-code-point "^1.0.0" + strip-ansi "^3.0.0" + +"string-width@^1.0.2 || 2", string-width@^2.0.0, string-width@^2.1.1: + version "2.1.1" + resolved "https://registry.yarnpkg.com/string-width/-/string-width-2.1.1.tgz#ab93f27a8dc13d28cac815c462143a6d9012ae9e" + integrity sha512-nOqH59deCq9SRHlxq1Aw85Jnt4w6KvLKqWVik6oA9ZklXLNIOlqg4F2yrT1MVaTjAqvVwdfeZ7w7aCvJD7ugkw== + dependencies: + is-fullwidth-code-point "^2.0.0" + strip-ansi "^4.0.0" + +string-width@^3.0.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/string-width/-/string-width-3.1.0.tgz#22767be21b62af1081574306f69ac51b62203961" + integrity sha512-vafcv6KjVZKSgz06oM/H6GDBrAtz8vdhQakGjFIvNrHA6y3HCF1CInLy+QLq8dTJPQ1b+KDUqDFctkdRW44e1w== + dependencies: + emoji-regex "^7.0.1" + is-fullwidth-code-point "^2.0.0" + strip-ansi "^5.1.0" + +string_decoder@^1.0.0, string_decoder@^1.1.1: + version "1.3.0" + resolved "https://registry.yarnpkg.com/string_decoder/-/string_decoder-1.3.0.tgz#42f114594a46cf1a8e30b0a84f56c78c3edac21e" + integrity sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA== + dependencies: + safe-buffer "~5.2.0" + +string_decoder@~1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/string_decoder/-/string_decoder-1.1.1.tgz#9cf1611ba62685d7030ae9e4ba34149c3af03fc8" + integrity sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg== + dependencies: + safe-buffer "~5.1.0" + +strip-ansi@^3.0.0, strip-ansi@^3.0.1: + version "3.0.1" + resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-3.0.1.tgz#6a385fb8853d952d5ff05d0e8aaf94278dc63dcf" + integrity sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8= + dependencies: + ansi-regex "^2.0.0" + +strip-ansi@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-4.0.0.tgz#a8479022eb1ac368a871389b635262c505ee368f" + integrity sha1-qEeQIusaw2iocTibY1JixQXuNo8= + dependencies: + ansi-regex "^3.0.0" + +strip-ansi@^5.0.0, strip-ansi@^5.1.0: + version "5.2.0" + resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-5.2.0.tgz#8c9a536feb6afc962bdfa5b104a5091c1ad9c0ae" + integrity sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA== + dependencies: + ansi-regex "^4.1.0" + +strip-bom-string@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/strip-bom-string/-/strip-bom-string-1.0.0.tgz#e5211e9224369fbb81d633a2f00044dc8cedad92" + integrity sha1-5SEekiQ2n7uB1jOi8ABE3IztrZI= + +strip-eof@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/strip-eof/-/strip-eof-1.0.0.tgz#bb43ff5598a6eb05d89b59fcd129c983313606bf" + integrity sha1-u0P/VZim6wXYm1n80SnJgzE2Br8= + +strip-json-comments@~2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/strip-json-comments/-/strip-json-comments-2.0.1.tgz#3c531942e908c2697c0ec344858c286c7ca0a60a" + integrity sha1-PFMZQukIwml8DsNEhYwobHygpgo= + +stylehacks@^4.0.0: + version "4.0.3" + resolved "https://registry.yarnpkg.com/stylehacks/-/stylehacks-4.0.3.tgz#6718fcaf4d1e07d8a1318690881e8d96726a71d5" + integrity sha512-7GlLk9JwlElY4Y6a/rmbH2MhVlTyVmiJd1PfTCqFaIBEGMYNsrO/v3SeGTdhBThLg4Z+NbOk/qFMwCa+J+3p/g== + dependencies: + browserslist "^4.0.0" + postcss "^7.0.0" + postcss-selector-parser "^3.0.0" + +stylus-loader@^3.0.2: + version "3.0.2" + resolved "https://registry.yarnpkg.com/stylus-loader/-/stylus-loader-3.0.2.tgz#27a706420b05a38e038e7cacb153578d450513c6" + integrity sha512-+VomPdZ6a0razP+zinir61yZgpw2NfljeSsdUF5kJuEzlo3khXhY19Fn6l8QQz1GRJGtMCo8nG5C04ePyV7SUA== + dependencies: + loader-utils "^1.0.2" + lodash.clonedeep "^4.5.0" + when "~3.6.x" + +stylus@^0.54.5: + version "0.54.5" + resolved "https://registry.yarnpkg.com/stylus/-/stylus-0.54.5.tgz#42b9560931ca7090ce8515a798ba9e6aa3d6dc79" + integrity sha1-QrlWCTHKcJDOhRWnmLqeaqPW3Hk= + dependencies: + css-parse "1.7.x" + debug "*" + glob "7.0.x" + mkdirp "0.5.x" + sax "0.5.x" + source-map "0.1.x" + +supports-color@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-2.0.0.tgz#535d045ce6b6363fa40117084629995e9df324c7" + integrity sha1-U10EXOa2Nj+kARcIRimZXp3zJMc= + +supports-color@^5.3.0: + version "5.5.0" + resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-5.5.0.tgz#e2e69a44ac8772f78a1ec0b35b689df6530efc8f" + integrity sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow== + dependencies: + has-flag "^3.0.0" + +supports-color@^6.1.0: + version "6.1.0" + resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-6.1.0.tgz#0764abc69c63d5ac842dd4867e8d025e880df8f3" + integrity sha512-qe1jfm1Mg7Nq/NSh6XE24gPXROEVsWHxC1LIx//XNlD9iw7YZQGjZNjYN7xGaEG6iKdA8EtNFW6R0gjnVXp+wQ== + dependencies: + has-flag "^3.0.0" + +svg-tags@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/svg-tags/-/svg-tags-1.0.0.tgz#58f71cee3bd519b59d4b2a843b6c7de64ac04764" + integrity sha1-WPcc7jvVGbWdSyqEO2x95krAR2Q= + +svgo@^1.0.0: + version "1.3.0" + resolved "https://registry.yarnpkg.com/svgo/-/svgo-1.3.0.tgz#bae51ba95ded9a33a36b7c46ce9c359ae9154313" + integrity sha512-MLfUA6O+qauLDbym+mMZgtXCGRfIxyQoeH6IKVcFslyODEe/ElJNwr0FohQ3xG4C6HK6bk3KYPPXwHVJk3V5NQ== + dependencies: + chalk "^2.4.1" + coa "^2.0.2" + css-select "^2.0.0" + css-select-base-adapter "^0.1.1" + css-tree "1.0.0-alpha.33" + csso "^3.5.1" + js-yaml "^3.13.1" + mkdirp "~0.5.1" + object.values "^1.1.0" + sax "~1.2.4" + stable "^0.1.8" + unquote "~1.1.1" + util.promisify "~1.0.0" + +tapable@^1.0.0, tapable@^1.1.3: + version "1.1.3" + resolved "https://registry.yarnpkg.com/tapable/-/tapable-1.1.3.tgz#a1fccc06b58db61fd7a45da2da44f5f3a3e67ba2" + integrity sha512-4WK/bYZmj8xLr+HUCODHGF1ZFzsYffasLUgEiMBY4fgtltdO6B4WJtlSbPaDTLpYTcGVwM2qLnFTICEcNxs3kA== + +tar@^4: + version "4.4.10" + resolved "https://registry.yarnpkg.com/tar/-/tar-4.4.10.tgz#946b2810b9a5e0b26140cf78bea6b0b0d689eba1" + integrity sha512-g2SVs5QIxvo6OLp0GudTqEf05maawKUxXru104iaayWA09551tFCTI8f1Asb4lPfkBr91k07iL4c11XO3/b0tA== + dependencies: + chownr "^1.1.1" + fs-minipass "^1.2.5" + minipass "^2.3.5" + minizlib "^1.2.1" + mkdirp "^0.5.0" + safe-buffer "^5.1.2" + yallist "^3.0.3" + +terser-webpack-plugin@^1.4.1: + version "1.4.1" + resolved "https://registry.yarnpkg.com/terser-webpack-plugin/-/terser-webpack-plugin-1.4.1.tgz#61b18e40eaee5be97e771cdbb10ed1280888c2b4" + integrity sha512-ZXmmfiwtCLfz8WKZyYUuuHf3dMYEjg8NrjHMb0JqHVHVOSkzp3cW2/XG1fP3tRhqEqSzMwzzRQGtAPbs4Cncxg== + dependencies: + cacache "^12.0.2" + find-cache-dir "^2.1.0" + is-wsl "^1.1.0" + schema-utils "^1.0.0" + serialize-javascript "^1.7.0" + source-map "^0.6.1" + terser "^4.1.2" + webpack-sources "^1.4.0" + worker-farm "^1.7.0" + +terser@^4.1.2: + version "4.1.4" + resolved "https://registry.yarnpkg.com/terser/-/terser-4.1.4.tgz#4478b6a08bb096a61e793fea1a4434408bab936c" + integrity sha512-+ZwXJvdSwbd60jG0Illav0F06GDJF0R4ydZ21Q3wGAFKoBGyJGo34F63vzJHgvYxc1ukOtIjvwEvl9MkjzM6Pg== + dependencies: + commander "^2.20.0" + source-map "~0.6.1" + source-map-support "~0.5.12" + +text-table@^0.2.0: + version "0.2.0" + resolved "https://registry.yarnpkg.com/text-table/-/text-table-0.2.0.tgz#7f5ee823ae805207c00af2df4a84ec3fcfa570b4" + integrity sha1-f17oI66AUgfACvLfSoTsP8+lcLQ= + +through2@^2.0.0: + version "2.0.5" + resolved "https://registry.yarnpkg.com/through2/-/through2-2.0.5.tgz#01c1e39eb31d07cb7d03a96a70823260b23132cd" + integrity sha512-/mrRod8xqpA+IHSLyGCQ2s8SPHiCDEeQJSep1jqLYeEUClOFG2Qsh+4FU6G9VeqpZnGW/Su8LQGc4YKni5rYSQ== + dependencies: + readable-stream "~2.3.6" + xtend "~4.0.1" + +through@~2.3.4: + version "2.3.8" + resolved "https://registry.yarnpkg.com/through/-/through-2.3.8.tgz#0dd4c9ffaabc357960b1b724115d7e0e86a2e1f5" + integrity sha1-DdTJ/6q8NXlgsbckEV1+Doai4fU= + +thunky@^1.0.2: + version "1.0.3" + resolved "https://registry.yarnpkg.com/thunky/-/thunky-1.0.3.tgz#f5df732453407b09191dae73e2a8cc73f381a826" + integrity sha512-YwT8pjmNcAXBZqrubu22P4FYsh2D4dxRmnWBOL8Jk8bUcRUtc5326kx32tuTmFDAZtLOGEVNl8POAR8j896Iow== + +timers-browserify@^2.0.4: + version "2.0.11" + resolved "https://registry.yarnpkg.com/timers-browserify/-/timers-browserify-2.0.11.tgz#800b1f3eee272e5bc53ee465a04d0e804c31211f" + integrity sha512-60aV6sgJ5YEbzUdn9c8kYGIqOubPoUdqQCul3SBAsRCZ40s6Y5cMcrW4dt3/k/EsbLVJNl9n6Vz3fTc+k2GeKQ== + dependencies: + setimmediate "^1.0.4" + +timsort@^0.3.0: + version "0.3.0" + resolved "https://registry.yarnpkg.com/timsort/-/timsort-0.3.0.tgz#405411a8e7e6339fe64db9a234de11dc31e02bd4" + integrity sha1-QFQRqOfmM5/mTbmiNN4R3DHgK9Q= + +tiny-emitter@^2.0.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/tiny-emitter/-/tiny-emitter-2.1.0.tgz#1d1a56edfc51c43e863cbb5382a72330e3555423" + integrity sha512-NB6Dk1A9xgQPMoGqC5CVXn123gWyte215ONT5Pp5a0yt4nlEoO1ZWeCwpncaekPHXO60i47ihFnZPiRPjRMq4Q== + +to-arraybuffer@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/to-arraybuffer/-/to-arraybuffer-1.0.1.tgz#7d229b1fcc637e466ca081180836a7aabff83f43" + integrity sha1-fSKbH8xjfkZsoIEYCDanqr/4P0M= + +to-factory@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/to-factory/-/to-factory-1.0.0.tgz#8738af8bd97120ad1d4047972ada5563bf9479b1" + integrity sha1-hzivi9lxIK0dQEeXKtpVY7+UebE= + +to-fast-properties@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/to-fast-properties/-/to-fast-properties-2.0.0.tgz#dc5e698cbd079265bc73e0377681a4e4e83f616e" + integrity sha1-3F5pjL0HkmW8c+A3doGk5Og/YW4= + +to-object-path@^0.3.0: + version "0.3.0" + resolved "https://registry.yarnpkg.com/to-object-path/-/to-object-path-0.3.0.tgz#297588b7b0e7e0ac08e04e672f85c1f4999e17af" + integrity sha1-KXWIt7Dn4KwI4E5nL4XB9JmeF68= + dependencies: + kind-of "^3.0.2" + +to-regex-range@^2.1.0: + version "2.1.1" + resolved "https://registry.yarnpkg.com/to-regex-range/-/to-regex-range-2.1.1.tgz#7c80c17b9dfebe599e27367e0d4dd5590141db38" + integrity sha1-fIDBe53+vlmeJzZ+DU3VWQFB2zg= + dependencies: + is-number "^3.0.0" + repeat-string "^1.6.1" + +to-regex@^3.0.1, to-regex@^3.0.2: + version "3.0.2" + resolved "https://registry.yarnpkg.com/to-regex/-/to-regex-3.0.2.tgz#13cfdd9b336552f30b51f33a8ae1b42a7a7599ce" + integrity sha512-FWtleNAtZ/Ki2qtqej2CXTOayOH9bHDQF+Q48VpWyDXjbYxA4Yz8iDB31zXOBUlOHHKidDbqGVrTUvQMPmBGBw== + dependencies: + define-property "^2.0.2" + extend-shallow "^3.0.2" + regex-not "^1.0.2" + safe-regex "^1.1.0" + +toidentifier@1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/toidentifier/-/toidentifier-1.0.0.tgz#7e1be3470f1e77948bc43d94a3c8f4d7752ba553" + integrity sha512-yaOH/Pk/VEhBWWTlhI+qXxDFXlejDGcQipMlyxda9nthulaxLZUNcUqFxokp0vcYnvteJln5FNQDRrxj3YcbVw== + +toml@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/toml/-/toml-3.0.0.tgz#342160f1af1904ec9d204d03a5d61222d762c5ee" + integrity sha512-y/mWCZinnvxjTKYhJ+pYxwD0mRLVvOtdS2Awbgxln6iEnt4rk0yBxeSBHkGJcPucRiG0e55mwWp+g/05rsrd6w== + +toposort@^1.0.0: + version "1.0.7" + resolved "https://registry.yarnpkg.com/toposort/-/toposort-1.0.7.tgz#2e68442d9f64ec720b8cc89e6443ac6caa950029" + integrity sha1-LmhELZ9k7HILjMieZEOsbKqVACk= + +tough-cookie@~2.4.3: + version "2.4.3" + resolved "https://registry.yarnpkg.com/tough-cookie/-/tough-cookie-2.4.3.tgz#53f36da3f47783b0925afa06ff9f3b165280f781" + integrity sha512-Q5srk/4vDM54WJsJio3XNn6K2sCG+CQ8G5Wz6bZhRZoAe/+TxjWB/GlFAnYEbkYVlON9FMk/fE3h2RLpPXo4lQ== + dependencies: + psl "^1.1.24" + punycode "^1.4.1" + +trim-right@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/trim-right/-/trim-right-1.0.1.tgz#cb2e1203067e0c8de1f614094b9fe45704ea6003" + integrity sha1-yy4SAwZ+DI3h9hQJS5/kVwTqYAM= + +tslib@^1.9.0: + version "1.10.0" + resolved "https://registry.yarnpkg.com/tslib/-/tslib-1.10.0.tgz#c3c19f95973fb0a62973fb09d90d961ee43e5c8a" + integrity sha512-qOebF53frne81cf0S9B41ByenJ3/IuH8yJKngAX35CmiZySA0khhkovshKK+jGCaMnVomla7gVlIcc3EvKPbTQ== + +tty-browserify@0.0.0: + version "0.0.0" + resolved "https://registry.yarnpkg.com/tty-browserify/-/tty-browserify-0.0.0.tgz#a157ba402da24e9bf957f9aa69d524eed42901a6" + integrity sha1-oVe6QC2iTpv5V/mqadUk7tQpAaY= + +tunnel-agent@^0.6.0: + version "0.6.0" + resolved "https://registry.yarnpkg.com/tunnel-agent/-/tunnel-agent-0.6.0.tgz#27a5dea06b36b04a0a9966774b290868f0fc40fd" + integrity sha1-J6XeoGs2sEoKmWZ3SykIaPD8QP0= + dependencies: + safe-buffer "^5.0.1" + +tweetnacl@^0.14.3, tweetnacl@~0.14.0: + version "0.14.5" + resolved "https://registry.yarnpkg.com/tweetnacl/-/tweetnacl-0.14.5.tgz#5ae68177f192d4456269d108afa93ff8743f4f64" + integrity sha1-WuaBd/GS1EViadEIr6k/+HQ/T2Q= + +type-fest@^0.5.2: + version "0.5.2" + resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-0.5.2.tgz#d6ef42a0356c6cd45f49485c3b6281fc148e48a2" + integrity sha512-DWkS49EQKVX//Tbupb9TFa19c7+MK1XmzkrZUR8TAktmE/DizXoaoJV6TZ/tSIPXipqNiRI6CyAe7x69Jb6RSw== + +type-is@~1.6.17, type-is@~1.6.18: + version "1.6.18" + resolved "https://registry.yarnpkg.com/type-is/-/type-is-1.6.18.tgz#4e552cd05df09467dcbc4ef739de89f2cf37c131" + integrity sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g== + dependencies: + media-typer "0.3.0" + mime-types "~2.1.24" + +typedarray@^0.0.6: + version "0.0.6" + resolved "https://registry.yarnpkg.com/typedarray/-/typedarray-0.0.6.tgz#867ac74e3864187b1d3d47d996a78ec5c8830777" + integrity sha1-hnrHTjhkGHsdPUfZlqeOxciDB3c= + +uc.micro@^1.0.1, uc.micro@^1.0.5: + version "1.0.6" + resolved "https://registry.yarnpkg.com/uc.micro/-/uc.micro-1.0.6.tgz#9c411a802a409a91fc6cf74081baba34b24499ac" + integrity sha512-8Y75pvTYkLJW2hWQHXxoqRgV7qb9B+9vFEtidML+7koHUFapnVJAZ6cKs+Qjz5Aw3aZWHMC6u0wJE3At+nSGwA== + +uglify-js@3.4.x: + version "3.4.10" + resolved "https://registry.yarnpkg.com/uglify-js/-/uglify-js-3.4.10.tgz#9ad9563d8eb3acdfb8d38597d2af1d815f6a755f" + integrity sha512-Y2VsbPVs0FIshJztycsO2SfPk7/KAF/T72qzv9u5EpQ4kB2hQoHlhNQTsNyy6ul7lQtqJN/AoWeS23OzEiEFxw== + dependencies: + commander "~2.19.0" + source-map "~0.6.1" + +unicode-canonical-property-names-ecmascript@^1.0.4: + version "1.0.4" + resolved "https://registry.yarnpkg.com/unicode-canonical-property-names-ecmascript/-/unicode-canonical-property-names-ecmascript-1.0.4.tgz#2619800c4c825800efdd8343af7dd9933cbe2818" + integrity sha512-jDrNnXWHd4oHiTZnx/ZG7gtUTVp+gCcTTKr8L0HjlwphROEW3+Him+IpvC+xcJEFegapiMZyZe02CyuOnRmbnQ== + +unicode-match-property-ecmascript@^1.0.4: + version "1.0.4" + resolved "https://registry.yarnpkg.com/unicode-match-property-ecmascript/-/unicode-match-property-ecmascript-1.0.4.tgz#8ed2a32569961bce9227d09cd3ffbb8fed5f020c" + integrity sha512-L4Qoh15vTfntsn4P1zqnHulG0LdXgjSO035fEpdtp6YxXhMT51Q6vgM5lYdG/5X3MjS+k/Y9Xw4SFCY9IkR0rg== + dependencies: + unicode-canonical-property-names-ecmascript "^1.0.4" + unicode-property-aliases-ecmascript "^1.0.4" + +unicode-match-property-value-ecmascript@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/unicode-match-property-value-ecmascript/-/unicode-match-property-value-ecmascript-1.1.0.tgz#5b4b426e08d13a80365e0d657ac7a6c1ec46a277" + integrity sha512-hDTHvaBk3RmFzvSl0UVrUmC3PuW9wKVnpoUDYH0JDkSIovzw+J5viQmeYHxVSBptubnr7PbH2e0fnpDRQnQl5g== + +unicode-property-aliases-ecmascript@^1.0.4: + version "1.0.5" + resolved "https://registry.yarnpkg.com/unicode-property-aliases-ecmascript/-/unicode-property-aliases-ecmascript-1.0.5.tgz#a9cc6cc7ce63a0a3023fc99e341b94431d405a57" + integrity sha512-L5RAqCfXqAwR3RriF8pM0lU0w4Ryf/GgzONwi6KnL1taJQa7x1TCxdJnILX59WIGOwR57IVxn7Nej0fz1Ny6fw== + +union-value@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/union-value/-/union-value-1.0.1.tgz#0b6fe7b835aecda61c6ea4d4f02c14221e109847" + integrity sha512-tJfXmxMeWYnczCVs7XAEvIV7ieppALdyepWMkHkwciRpZraG/xwT+s2JN8+pr1+8jCRf80FFzvr+MpQeeoF4Xg== + dependencies: + arr-union "^3.1.0" + get-value "^2.0.6" + is-extendable "^0.1.1" + set-value "^2.0.1" + +uniq@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/uniq/-/uniq-1.0.1.tgz#b31c5ae8254844a3a8281541ce2b04b865a734ff" + integrity sha1-sxxa6CVIRKOoKBVBzisEuGWnNP8= + +uniqs@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/uniqs/-/uniqs-2.0.0.tgz#ffede4b36b25290696e6e165d4a59edb998e6b02" + integrity sha1-/+3ks2slKQaW5uFl1KWe25mOawI= + +unique-filename@^1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/unique-filename/-/unique-filename-1.1.1.tgz#1d69769369ada0583103a1e6ae87681b56573230" + integrity sha512-Vmp0jIp2ln35UTXuryvjzkjGdRyf9b2lTXuSYUiPmzRcl3FDtYqAwOnTJkAngD9SWhnoJzDbTKwaOrZ+STtxNQ== + dependencies: + unique-slug "^2.0.0" + +unique-slug@^2.0.0: + version "2.0.2" + resolved "https://registry.yarnpkg.com/unique-slug/-/unique-slug-2.0.2.tgz#baabce91083fc64e945b0f3ad613e264f7cd4e6c" + integrity sha512-zoWr9ObaxALD3DOPfjPSqxt4fnZiWblxHIgeWqW8x7UqDzEtHEQLzji2cuJYQFCU6KmoJikOYAZlrTHHebjx2w== + dependencies: + imurmurhash "^0.1.4" + +universalify@^0.1.0: + version "0.1.2" + resolved "https://registry.yarnpkg.com/universalify/-/universalify-0.1.2.tgz#b646f69be3942dabcecc9d6639c80dc105efaa66" + integrity sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg== + +unpipe@1.0.0, unpipe@~1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/unpipe/-/unpipe-1.0.0.tgz#b2bf4ee8514aae6165b4817829d21b2ef49904ec" + integrity sha1-sr9O6FFKrmFltIF4KdIbLvSZBOw= + +unquote@~1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/unquote/-/unquote-1.1.1.tgz#8fded7324ec6e88a0ff8b905e7c098cdc086d544" + integrity sha1-j97XMk7G6IoP+LkF58CYzcCG1UQ= + +unset-value@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/unset-value/-/unset-value-1.0.0.tgz#8376873f7d2335179ffb1e6fc3a8ed0dfc8ab559" + integrity sha1-g3aHP30jNRef+x5vw6jtDfyKtVk= + dependencies: + has-value "^0.3.1" + isobject "^3.0.0" + +upath@^1.1.0, upath@^1.1.1: + version "1.1.2" + resolved "https://registry.yarnpkg.com/upath/-/upath-1.1.2.tgz#3db658600edaeeccbe6db5e684d67ee8c2acd068" + integrity sha512-kXpym8nmDmlCBr7nKdIx8P2jNBa+pBpIUFRnKJ4dr8htyYGJFokkr2ZvERRtUN+9SY+JqXouNgUPtv6JQva/2Q== + +upper-case@^1.1.1: + version "1.1.3" + resolved "https://registry.yarnpkg.com/upper-case/-/upper-case-1.1.3.tgz#f6b4501c2ec4cdd26ba78be7222961de77621598" + integrity sha1-9rRQHC7EzdJrp4vnIilh3ndiFZg= + +uri-js@^4.2.2: + version "4.2.2" + resolved "https://registry.yarnpkg.com/uri-js/-/uri-js-4.2.2.tgz#94c540e1ff772956e2299507c010aea6c8838eb0" + integrity sha512-KY9Frmirql91X2Qgjry0Wd4Y+YTdrdZheS8TFwvkbLWf/G5KNJDCh6pKL5OZctEW4+0Baa5idK2ZQuELRwPznQ== + dependencies: + punycode "^2.1.0" + +urix@^0.1.0: + version "0.1.0" + resolved "https://registry.yarnpkg.com/urix/-/urix-0.1.0.tgz#da937f7a62e21fec1fd18d49b35c2935067a6c72" + integrity sha1-2pN/emLiH+wf0Y1Js1wpNQZ6bHI= + +url-loader@^1.0.1: + version "1.1.2" + resolved "https://registry.yarnpkg.com/url-loader/-/url-loader-1.1.2.tgz#b971d191b83af693c5e3fea4064be9e1f2d7f8d8" + integrity sha512-dXHkKmw8FhPqu8asTc1puBfe3TehOCo2+RmOOev5suNCIYBcT626kxiWg1NBVkwc4rO8BGa7gP70W7VXuqHrjg== + dependencies: + loader-utils "^1.1.0" + mime "^2.0.3" + schema-utils "^1.0.0" + +url-parse@^1.4.3: + version "1.4.7" + resolved "https://registry.yarnpkg.com/url-parse/-/url-parse-1.4.7.tgz#a8a83535e8c00a316e403a5db4ac1b9b853ae278" + integrity sha512-d3uaVyzDB9tQoSXFvuSUNFibTd9zxd2bkVrDRvF5TmvWWQwqE4lgYJ5m+x1DbecWkw+LK4RNl2CU1hHuOKPVlg== + dependencies: + querystringify "^2.1.1" + requires-port "^1.0.0" + +url@^0.11.0: + version "0.11.0" + resolved "https://registry.yarnpkg.com/url/-/url-0.11.0.tgz#3838e97cfc60521eb73c525a8e55bfdd9e2e28f1" + integrity sha1-ODjpfPxgUh63PFJajlW/3Z4uKPE= + dependencies: + punycode "1.3.2" + querystring "0.2.0" + +use@^3.1.0: + version "3.1.1" + resolved "https://registry.yarnpkg.com/use/-/use-3.1.1.tgz#d50c8cac79a19fbc20f2911f56eb973f4e10070f" + integrity sha512-cwESVXlO3url9YWlFW/TA9cshCEhtu7IKJ/p5soJ/gGpj7vbvFrAY/eIioQ6Dw23KjZhYgiIo8HOs1nQ2vr/oQ== + +util-deprecate@^1.0.1, util-deprecate@~1.0.1: + version "1.0.2" + resolved "https://registry.yarnpkg.com/util-deprecate/-/util-deprecate-1.0.2.tgz#450d4dc9fa70de732762fbd2d4a28981419a0ccf" + integrity sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8= + +util.promisify@1.0.0, util.promisify@~1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/util.promisify/-/util.promisify-1.0.0.tgz#440f7165a459c9a16dc145eb8e72f35687097030" + integrity sha512-i+6qA2MPhvoKLuxnJNpXAGhg7HphQOSUq2LKMZD0m15EiskXUkMvKdF4Uui0WYeCUGea+o2cw/ZuwehtfsrNkA== + dependencies: + define-properties "^1.1.2" + object.getownpropertydescriptors "^2.0.3" + +util@0.10.3: + version "0.10.3" + resolved "https://registry.yarnpkg.com/util/-/util-0.10.3.tgz#7afb1afe50805246489e3db7fe0ed379336ac0f9" + integrity sha1-evsa/lCAUkZInj23/g7TeTNqwPk= + dependencies: + inherits "2.0.1" + +util@^0.11.0: + version "0.11.1" + resolved "https://registry.yarnpkg.com/util/-/util-0.11.1.tgz#3236733720ec64bb27f6e26f421aaa2e1b588d61" + integrity sha512-HShAsny+zS2TZfaXxD9tYj4HQGlBezXZMZuM/S5PKLLoZkShZiGk9o5CzukI1LVHZvjdvZ2Sj1aW/Ndn2NB/HQ== + dependencies: + inherits "2.0.3" + +utila@^0.4.0, utila@~0.4: + version "0.4.0" + resolved "https://registry.yarnpkg.com/utila/-/utila-0.4.0.tgz#8a16a05d445657a3aea5eecc5b12a4fa5379772c" + integrity sha1-ihagXURWV6Oupe7MWxKk+lN5dyw= + +utils-merge@1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/utils-merge/-/utils-merge-1.0.1.tgz#9f95710f50a267947b2ccc124741c1028427e713" + integrity sha1-n5VxD1CiZ5R7LMwSR0HBAoQn5xM= + +uuid@^3.0.1, uuid@^3.3.2: + version "3.3.2" + resolved "https://registry.yarnpkg.com/uuid/-/uuid-3.3.2.tgz#1b4af4955eb3077c501c23872fc6513811587131" + integrity sha512-yXJmeNaw3DnnKAOKJE51sL/ZaYfWJRl1pK9dr19YFCu0ObS231AB1/LbqTKRAQ5kw8A90rA6fr4riOUpTZvQZA== + +vary@~1.1.2: + version "1.1.2" + resolved "https://registry.yarnpkg.com/vary/-/vary-1.1.2.tgz#2299f02c6ded30d4a5961b0b9f74524a18f634fc" + integrity sha1-IpnwLG3tMNSllhsLn3RSShj2NPw= + +vendors@^1.0.0: + version "1.0.3" + resolved "https://registry.yarnpkg.com/vendors/-/vendors-1.0.3.tgz#a6467781abd366217c050f8202e7e50cc9eef8c0" + integrity sha512-fOi47nsJP5Wqefa43kyWSg80qF+Q3XA6MUkgi7Hp1HQaKDQW4cQrK2D0P7mmbFtsV1N89am55Yru/nyEwRubcw== + +verror@1.10.0: + version "1.10.0" + resolved "https://registry.yarnpkg.com/verror/-/verror-1.10.0.tgz#3a105ca17053af55d6e270c1f8288682e18da400" + integrity sha1-OhBcoXBTr1XW4nDB+CiGguGNpAA= + dependencies: + assert-plus "^1.0.0" + core-util-is "1.0.2" + extsprintf "^1.2.0" + +vm-browserify@^1.0.1: + version "1.1.0" + resolved "https://registry.yarnpkg.com/vm-browserify/-/vm-browserify-1.1.0.tgz#bd76d6a23323e2ca8ffa12028dc04559c75f9019" + integrity sha512-iq+S7vZJE60yejDYM0ek6zg308+UZsdtPExWP9VZoCFCz1zkJoXFnAX7aZfd/ZwrkidzdUZL0C/ryW+JwAiIGw== + +vue-hot-reload-api@^2.3.0: + version "2.3.3" + resolved "https://registry.yarnpkg.com/vue-hot-reload-api/-/vue-hot-reload-api-2.3.3.tgz#2756f46cb3258054c5f4723de8ae7e87302a1ccf" + integrity sha512-KmvZVtmM26BQOMK1rwUZsrqxEGeKiYSZGA7SNWE6uExx8UX/cj9hq2MRV/wWC3Cq6AoeDGk57rL9YMFRel/q+g== + +vue-loader@^15.2.4: + version "15.7.1" + resolved "https://registry.yarnpkg.com/vue-loader/-/vue-loader-15.7.1.tgz#6ccacd4122aa80f69baaac08ff295a62e3aefcfd" + integrity sha512-fwIKtA23Pl/rqfYP5TSGK7gkEuLhoTvRYW+TU7ER3q9GpNLt/PjG5NLv3XHRDiTg7OPM1JcckBgds+VnAc+HbA== + dependencies: + "@vue/component-compiler-utils" "^3.0.0" + hash-sum "^1.0.2" + loader-utils "^1.1.0" + vue-hot-reload-api "^2.3.0" + vue-style-loader "^4.1.0" + +vue-router@^3.0.2: + version "3.1.2" + resolved "https://registry.yarnpkg.com/vue-router/-/vue-router-3.1.2.tgz#2e0904703545dabdd42b2b7a2e617f02f99a1969" + integrity sha512-WssQEHSEvIS1/CI4CO2T8LJdoK4Q9Ngox28K7FDNMTfzNTk2WS5D0dDlqYCaPG+AG4Z8wJkn1KrBc7AhspZJUQ== + +vue-server-renderer@^2.5.16: + version "2.6.10" + resolved "https://registry.yarnpkg.com/vue-server-renderer/-/vue-server-renderer-2.6.10.tgz#cb2558842ead360ae2ec1f3719b75564a805b375" + integrity sha512-UYoCEutBpKzL2fKCwx8zlRtRtwxbPZXKTqbl2iIF4yRZUNO/ovrHyDAJDljft0kd+K0tZhN53XRHkgvCZoIhug== + dependencies: + chalk "^1.1.3" + hash-sum "^1.0.2" + he "^1.1.0" + lodash.template "^4.4.0" + lodash.uniq "^4.5.0" + resolve "^1.2.0" + serialize-javascript "^1.3.0" + source-map "0.5.6" + +vue-style-loader@^4.1.0: + version "4.1.2" + resolved "https://registry.yarnpkg.com/vue-style-loader/-/vue-style-loader-4.1.2.tgz#dedf349806f25ceb4e64f3ad7c0a44fba735fcf8" + integrity sha512-0ip8ge6Gzz/Bk0iHovU9XAUQaFt/G2B61bnWa2tCcqqdgfHs1lF9xXorFbE55Gmy92okFT+8bfmySuUOu13vxQ== + dependencies: + hash-sum "^1.0.2" + loader-utils "^1.0.2" + +vue-template-compiler@^2.5.16: + version "2.6.10" + resolved "https://registry.yarnpkg.com/vue-template-compiler/-/vue-template-compiler-2.6.10.tgz#323b4f3495f04faa3503337a82f5d6507799c9cc" + integrity sha512-jVZkw4/I/HT5ZMvRnhv78okGusqe0+qH2A0Em0Cp8aq78+NK9TII263CDVz2QXZsIT+yyV/gZc/j/vlwa+Epyg== + dependencies: + de-indent "^1.0.2" + he "^1.1.0" + +vue-template-es2015-compiler@^1.9.0: + version "1.9.1" + resolved "https://registry.yarnpkg.com/vue-template-es2015-compiler/-/vue-template-es2015-compiler-1.9.1.tgz#1ee3bc9a16ecbf5118be334bb15f9c46f82f5825" + integrity sha512-4gDntzrifFnCEvyoO8PqyJDmguXgVPxKiIxrBKjIowvL9l+N66196+72XVYR8BBf1Uv1Fgt3bGevJ+sEmxfZzw== + +vue@^2.5.16: + version "2.6.10" + resolved "https://registry.yarnpkg.com/vue/-/vue-2.6.10.tgz#a72b1a42a4d82a721ea438d1b6bf55e66195c637" + integrity sha512-ImThpeNU9HbdZL3utgMCq0oiMzAkt1mcgy3/E6zWC/G6AaQoeuFdsl9nDhTDU3X1R6FK7nsIUuRACVcjI+A2GQ== + +vuepress-html-webpack-plugin@^3.2.0: + version "3.2.0" + resolved "https://registry.yarnpkg.com/vuepress-html-webpack-plugin/-/vuepress-html-webpack-plugin-3.2.0.tgz#219be272ad510faa8750d2d4e70fd028bfd1c16e" + integrity sha512-BebAEl1BmWlro3+VyDhIOCY6Gef2MCBllEVAP3NUAtMguiyOwo/dClbwJ167WYmcxHJKLl7b0Chr9H7fpn1d0A== + dependencies: + html-minifier "^3.2.3" + loader-utils "^0.2.16" + lodash "^4.17.3" + pretty-error "^2.0.2" + tapable "^1.0.0" + toposort "^1.0.0" + util.promisify "1.0.0" + +vuepress-plugin-container@^2.0.0: + version "2.0.2" + resolved "https://registry.yarnpkg.com/vuepress-plugin-container/-/vuepress-plugin-container-2.0.2.tgz#3489cc732c7a210b31f202556e1346125dffeb73" + integrity sha512-SrGYYT7lkie7xlIlAVhn+9sDW42MytNCoxWL/2uDr+q9wZA4h1uYlQvfc2DVjy+FsM9PPPSslkeo/zCpYVY82g== + dependencies: + markdown-it-container "^2.0.0" + +vuepress@^1.0.3: + version "1.0.3" + resolved "https://registry.yarnpkg.com/vuepress/-/vuepress-1.0.3.tgz#7c744061b5a3244ab86d49ac8d66417258509f13" + integrity sha512-+wCbyhZjaboY6VGBceai+JCdho96ZO9hVFHLnGGsj1/Zt2sKHrwWwV7lvbBO9y/IGib0YYpifpEJcpzvy3MDVg== + dependencies: + "@vuepress/core" "^1.0.3" + "@vuepress/theme-default" "^1.0.3" + cac "^6.3.9" + envinfo "^7.2.0" + opencollective-postinstall "^2.0.2" + +watchpack@^1.6.0: + version "1.6.0" + resolved "https://registry.yarnpkg.com/watchpack/-/watchpack-1.6.0.tgz#4bc12c2ebe8aa277a71f1d3f14d685c7b446cd00" + integrity sha512-i6dHe3EyLjMmDlU1/bGQpEw25XSjkJULPuAVKCbNRefQVq48yXKUpwg538F7AZTf9kyr57zj++pQFltUa5H7yA== + dependencies: + chokidar "^2.0.2" + graceful-fs "^4.1.2" + neo-async "^2.5.0" + +wbuf@^1.1.0, wbuf@^1.7.3: + version "1.7.3" + resolved "https://registry.yarnpkg.com/wbuf/-/wbuf-1.7.3.tgz#c1d8d149316d3ea852848895cb6a0bfe887b87df" + integrity sha512-O84QOnr0icsbFGLS0O3bI5FswxzRr8/gHwWkDlQFskhSPryQXvrTMxjxGP4+iWYoauLoBvfDpkrOauZ+0iZpDA== + dependencies: + minimalistic-assert "^1.0.0" + +webpack-chain@^4.6.0, webpack-chain@^4.9.0: + version "4.12.1" + resolved "https://registry.yarnpkg.com/webpack-chain/-/webpack-chain-4.12.1.tgz#6c8439bbb2ab550952d60e1ea9319141906c02a6" + integrity sha512-BCfKo2YkDe2ByqkEWe1Rw+zko4LsyS75LVr29C6xIrxAg9JHJ4pl8kaIZ396SUSNp6b4815dRZPSTAS8LlURRQ== + dependencies: + deepmerge "^1.5.2" + javascript-stringify "^1.6.0" + +webpack-dev-middleware@^3.7.0: + version "3.7.0" + resolved "https://registry.yarnpkg.com/webpack-dev-middleware/-/webpack-dev-middleware-3.7.0.tgz#ef751d25f4e9a5c8a35da600c5fda3582b5c6cff" + integrity sha512-qvDesR1QZRIAZHOE3iQ4CXLZZSQ1lAUsSpnQmlB1PBfoN/xdRjmge3Dok0W4IdaVLJOGJy3sGI4sZHwjRU0PCA== + dependencies: + memory-fs "^0.4.1" + mime "^2.4.2" + range-parser "^1.2.1" + webpack-log "^2.0.0" + +webpack-dev-server@^3.5.1: + version "3.8.0" + resolved "https://registry.yarnpkg.com/webpack-dev-server/-/webpack-dev-server-3.8.0.tgz#06cc4fc2f440428508d0e9770da1fef10e5ef28d" + integrity sha512-Hs8K9yI6pyMvGkaPTeTonhD6JXVsigXDApYk9JLW4M7viVBspQvb1WdAcWxqtmttxNW4zf2UFLsLNe0y87pIGQ== + dependencies: + ansi-html "0.0.7" + bonjour "^3.5.0" + chokidar "^2.1.6" + compression "^1.7.4" + connect-history-api-fallback "^1.6.0" + debug "^4.1.1" + del "^4.1.1" + express "^4.17.1" + html-entities "^1.2.1" + http-proxy-middleware "^0.19.1" + import-local "^2.0.0" + internal-ip "^4.3.0" + ip "^1.1.5" + is-absolute-url "^3.0.0" + killable "^1.0.1" + loglevel "^1.6.3" + opn "^5.5.0" + p-retry "^3.0.1" + portfinder "^1.0.21" + schema-utils "^1.0.0" + selfsigned "^1.10.4" + semver "^6.3.0" + serve-index "^1.9.1" + sockjs "0.3.19" + sockjs-client "1.3.0" + spdy "^4.0.1" + strip-ansi "^3.0.1" + supports-color "^6.1.0" + url "^0.11.0" + webpack-dev-middleware "^3.7.0" + webpack-log "^2.0.0" + ws "^6.2.1" + yargs "12.0.5" + +webpack-log@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/webpack-log/-/webpack-log-2.0.0.tgz#5b7928e0637593f119d32f6227c1e0ac31e1b47f" + integrity sha512-cX8G2vR/85UYG59FgkoMamwHUIkSSlV3bBMRsbxVXVUk2j6NleCKjQ/WE9eYg9WY4w25O9w8wKP4rzNZFmUcUg== + dependencies: + ansi-colors "^3.0.0" + uuid "^3.3.2" + +webpack-merge@^4.1.2: + version "4.2.1" + resolved "https://registry.yarnpkg.com/webpack-merge/-/webpack-merge-4.2.1.tgz#5e923cf802ea2ace4fd5af1d3247368a633489b4" + integrity sha512-4p8WQyS98bUJcCvFMbdGZyZmsKuWjWVnVHnAS3FFg0HDaRVrPbkivx2RYCre8UiemD67RsiFFLfn4JhLAin8Vw== + dependencies: + lodash "^4.17.5" + +webpack-sources@^1.1.0, webpack-sources@^1.4.0, webpack-sources@^1.4.1: + version "1.4.3" + resolved "https://registry.yarnpkg.com/webpack-sources/-/webpack-sources-1.4.3.tgz#eedd8ec0b928fbf1cbfe994e22d2d890f330a933" + integrity sha512-lgTS3Xhv1lCOKo7SA5TjKXMjpSM4sBjNV5+q2bqesbSPs5FjGmU6jjtBSkX9b4qW87vDIsCIlUPOEhbZrMdjeQ== + dependencies: + source-list-map "^2.0.0" + source-map "~0.6.1" + +webpack@^4.8.1: + version "4.39.2" + resolved "https://registry.yarnpkg.com/webpack/-/webpack-4.39.2.tgz#c9aa5c1776d7c309d1b3911764f0288c8c2816aa" + integrity sha512-AKgTfz3xPSsEibH00JfZ9sHXGUwIQ6eZ9tLN8+VLzachk1Cw2LVmy+4R7ZiwTa9cZZ15tzySjeMui/UnSCAZhA== + dependencies: + "@webassemblyjs/ast" "1.8.5" + "@webassemblyjs/helper-module-context" "1.8.5" + "@webassemblyjs/wasm-edit" "1.8.5" + "@webassemblyjs/wasm-parser" "1.8.5" + acorn "^6.2.1" + ajv "^6.10.2" + ajv-keywords "^3.4.1" + chrome-trace-event "^1.0.2" + enhanced-resolve "^4.1.0" + eslint-scope "^4.0.3" + json-parse-better-errors "^1.0.2" + loader-runner "^2.4.0" + loader-utils "^1.2.3" + memory-fs "^0.4.1" + micromatch "^3.1.10" + mkdirp "^0.5.1" + neo-async "^2.6.1" + node-libs-browser "^2.2.1" + schema-utils "^1.0.0" + tapable "^1.1.3" + terser-webpack-plugin "^1.4.1" + watchpack "^1.6.0" + webpack-sources "^1.4.1" + +webpackbar@3.2.0: + version "3.2.0" + resolved "https://registry.yarnpkg.com/webpackbar/-/webpackbar-3.2.0.tgz#bdaad103fad11a4e612500e72aaae98b08ba493f" + integrity sha512-PC4o+1c8gWWileUfwabe0gqptlXUDJd5E0zbpr2xHP1VSOVlZVPBZ8j6NCR8zM5zbKdxPhctHXahgpNK1qFDPw== + dependencies: + ansi-escapes "^4.1.0" + chalk "^2.4.1" + consola "^2.6.0" + figures "^3.0.0" + pretty-time "^1.1.0" + std-env "^2.2.1" + text-table "^0.2.0" + wrap-ansi "^5.1.0" + +websocket-driver@>=0.5.1: + version "0.7.3" + resolved "https://registry.yarnpkg.com/websocket-driver/-/websocket-driver-0.7.3.tgz#a2d4e0d4f4f116f1e6297eba58b05d430100e9f9" + integrity sha512-bpxWlvbbB459Mlipc5GBzzZwhoZgGEZLuqPaR0INBGnPAY1vdBX6hPnoFXiw+3yWxDuHyQjO2oXTMyS8A5haFg== + dependencies: + http-parser-js ">=0.4.0 <0.4.11" + safe-buffer ">=5.1.0" + websocket-extensions ">=0.1.1" + +websocket-extensions@>=0.1.1: + version "0.1.3" + resolved "https://registry.yarnpkg.com/websocket-extensions/-/websocket-extensions-0.1.3.tgz#5d2ff22977003ec687a4b87073dfbbac146ccf29" + integrity sha512-nqHUnMXmBzT0w570r2JpJxfiSD1IzoI+HGVdd3aZ0yNi3ngvQ4jv1dtHt5VGxfI2yj5yqImPhOK4vmIh2xMbGg== + +when@~3.6.x: + version "3.6.4" + resolved "https://registry.yarnpkg.com/when/-/when-3.6.4.tgz#473b517ec159e2b85005497a13983f095412e34e" + integrity sha1-RztRfsFZ4rhQBUl6E5g/CVQS404= + +which-module@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/which-module/-/which-module-2.0.0.tgz#d9ef07dce77b9902b8a3a8fa4b31c3e3f7e6e87a" + integrity sha1-2e8H3Od7mQK4o6j6SzHD4/fm6Ho= + +which@^1.2.9: + version "1.3.1" + resolved "https://registry.yarnpkg.com/which/-/which-1.3.1.tgz#a45043d54f5805316da8d62f9f50918d3da70b0a" + integrity sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ== + dependencies: + isexe "^2.0.0" + +wide-align@^1.1.0: + version "1.1.3" + resolved "https://registry.yarnpkg.com/wide-align/-/wide-align-1.1.3.tgz#ae074e6bdc0c14a431e804e624549c633b000457" + integrity sha512-QGkOQc8XL6Bt5PwnsExKBPuMKBxnGxWWW3fU55Xt4feHozMUhdUMaBCk290qpm/wG5u/RSKzwdAC4i51YigihA== + dependencies: + string-width "^1.0.2 || 2" + +worker-farm@^1.7.0: + version "1.7.0" + resolved "https://registry.yarnpkg.com/worker-farm/-/worker-farm-1.7.0.tgz#26a94c5391bbca926152002f69b84a4bf772e5a8" + integrity sha512-rvw3QTZc8lAxyVrqcSGVm5yP/IJ2UcB3U0graE3LCFoZ0Yn2x4EoVSqJKdB/T5M+FLcRPjz4TDacRf3OCfNUzw== + dependencies: + errno "~0.1.7" + +wrap-ansi@^2.0.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-2.1.0.tgz#d8fc3d284dd05794fe84973caecdd1cf824fdd85" + integrity sha1-2Pw9KE3QV5T+hJc8rs3Rz4JP3YU= + dependencies: + string-width "^1.0.1" + strip-ansi "^3.0.1" + +wrap-ansi@^5.1.0: + version "5.1.0" + resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-5.1.0.tgz#1fd1f67235d5b6d0fee781056001bfb694c03b09" + integrity sha512-QC1/iN/2/RPVJ5jYK8BGttj5z83LmSKmvbvrXPNCLZSEb32KKVDJDl/MOt2N01qU2H/FkzEa9PKto1BqDjtd7Q== + dependencies: + ansi-styles "^3.2.0" + string-width "^3.0.0" + strip-ansi "^5.0.0" + +wrappy@1: + version "1.0.2" + resolved "https://registry.yarnpkg.com/wrappy/-/wrappy-1.0.2.tgz#b5243d8f3ec1aa35f1364605bc0d1036e30ab69f" + integrity sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8= + +ws@^6.2.1: + version "6.2.1" + resolved "https://registry.yarnpkg.com/ws/-/ws-6.2.1.tgz#442fdf0a47ed64f59b6a5d8ff130f4748ed524fb" + integrity sha512-GIyAXC2cB7LjvpgMt9EKS2ldqr0MTrORaleiOno6TweZ6r3TKtoFQWay/2PceJ3RuBasOHzXNn5Lrw1X0bEjqA== + dependencies: + async-limiter "~1.0.0" + +xtend@^4.0.0, xtend@~4.0.1: + version "4.0.2" + resolved "https://registry.yarnpkg.com/xtend/-/xtend-4.0.2.tgz#bb72779f5fa465186b1f438f674fa347fdb5db54" + integrity sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ== + +"y18n@^3.2.1 || ^4.0.0", y18n@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/y18n/-/y18n-4.0.0.tgz#95ef94f85ecc81d007c264e190a120f0a3c8566b" + integrity sha512-r9S/ZyXu/Xu9q1tYlpsLIsa3EeLXXk0VwlxqTcFRfg9EhMW+17kbt9G0NrgCmhGb5vT2hyhJZLfDGx+7+5Uj/w== + +yallist@^2.1.2: + version "2.1.2" + resolved "https://registry.yarnpkg.com/yallist/-/yallist-2.1.2.tgz#1c11f9218f076089a47dd512f93c6699a6a81d52" + integrity sha1-HBH5IY8HYImkfdUS+TxmmaaoHVI= + +yallist@^3.0.0, yallist@^3.0.2, yallist@^3.0.3: + version "3.0.3" + resolved "https://registry.yarnpkg.com/yallist/-/yallist-3.0.3.tgz#b4b049e314be545e3ce802236d6cd22cd91c3de9" + integrity sha512-S+Zk8DEWE6oKpV+vI3qWkaK+jSbIK86pCwe2IF/xwIpQ8jEuxpw9NyaGjmp9+BoJv5FV2piqCDcoCtStppiq2A== + +yargs-parser@^11.1.1: + version "11.1.1" + resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-11.1.1.tgz#879a0865973bca9f6bab5cbdf3b1c67ec7d3bcf4" + integrity sha512-C6kB/WJDiaxONLJQnF8ccx9SEeoTTLek8RVbaOIsrAUS8VrBEXfmeSnCZxygc+XC2sNMBIwOOnfcxiynjHsVSQ== + dependencies: + camelcase "^5.0.0" + decamelize "^1.2.0" + +yargs@12.0.5: + version "12.0.5" + resolved "https://registry.yarnpkg.com/yargs/-/yargs-12.0.5.tgz#05f5997b609647b64f66b81e3b4b10a368e7ad13" + integrity sha512-Lhz8TLaYnxq/2ObqHDql8dX8CJi97oHxrjUcYtzKbbykPtVW9WB+poxI+NM2UIzsMgNCZTIf0AQwsjK5yMAqZw== + dependencies: + cliui "^4.0.0" + decamelize "^1.2.0" + find-up "^3.0.0" + get-caller-file "^1.0.1" + os-locale "^3.0.0" + require-directory "^2.1.1" + require-main-filename "^1.0.1" + set-blocking "^2.0.0" + string-width "^2.0.0" + which-module "^2.0.0" + y18n "^3.2.1 || ^4.0.0" + yargs-parser "^11.1.1" + +zepto@^1.2.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/zepto/-/zepto-1.2.0.tgz#e127bd9e66fd846be5eab48c1394882f7c0e4f98" + integrity sha1-4Se9nmb9hGvl6rSME5SIL3wOT5g=